<commit_before>#pragma once

#include <algorithm> // find
#include <iosfwd>
#include <memory>    // unique_ptr
#include <typeinfo>  // typeid

#include "sdd/dd/definition.hh"
#include "sdd/hom/context_fwd.hh"
#include "sdd/hom/definition_fwd.hh"
#include "sdd/util/packed.hh"
#include "sdd/order/carrier.hh"
#include "sdd/order/order.hh"

namespace sdd { namespace hom {

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename C>
struct function_base
{
  /// @brief The type of a set of values.
  using values_type = typename C::Values;

  /// @brief Destructor.
  virtual
  ~function_base()
  {}

  /// @brief Tell if the user's function is a selector.
  virtual
  bool
  selector() const noexcept = 0;

  /// @brief Tell if the user's function is a shifter.
  virtual
  bool
  shifter() const noexcept = 0;

  /// @brief Apply the user function.
  virtual
  values_type
  operator()(const values_type&) const = 0;

  /// @brief Compare values_base.
  virtual
  bool
  operator==(const function_base&) const noexcept = 0;

  /// @brief Get the user's function hash value.
  virtual
  std::size_t
  hash() const noexcept = 0;

  /// @brief Get the user's function textual representation.
  virtual
  void
  print(std::ostream&) const = 0;
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename C, typename User>
struct function_derived
  : public function_base<C>
{
  /// @brief The user's values function.
  const User fun;

  /// @brief The type of a set of values.
  using values_type = typename C::Values;

  /// @brief Constructor.
  function_derived(const User& f)
    : fun(f)
  {}

  /// @brief Tell if the user's function is a selector.
  bool
  selector()
  const noexcept override
  {
    return selector_impl(fun, 0);
  }

  /// @brief Tell if the user's function is a shifter.
  bool
  shifter()
  const noexcept override
  {
    return shifter_impl(fun, 0);
  }

  /// @brief Apply the user function.
  values_type
  operator()(const values_type& val)
  const override
  {
    return fun(val);
  }

  /// @brief Compare values_derived.
  bool
  operator==(const function_base<C>& other)
  const noexcept override
  {
    return typeid(*this) == typeid(other)
         ? fun == reinterpret_cast<const function_derived&>(other).fun
         : false;
  }

  /// @brief Get the user's function hash value.
  std::size_t
  hash()
  const noexcept override
  {
    return std::hash<User>()(fun);
  }

  /// @brief Get the user's values function textual representation.
  void
  print(std::ostream& os)
  const override
  {
    print_impl(os, fun, 0);
  }

private:

  /// @brief Called when the user's function has selector().
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  selector_impl(const T& x, int)
  noexcept
  -> decltype(x.selector())
  {
    return x.selector();
  }

  /// @brief Called when the user's function doesn't have selector().
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  selector_impl(const T&, long)
  noexcept
  -> decltype(false)
  {
    return false;
  }

  /// @brief Called when the user's function has shifter().
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  shifter_impl(const T& x, int)
  noexcept
  -> decltype(x.shifter())
  {
    return x.shifter();
  }

  /// @brief Called when the user's function doesn't have shifter().
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  shifter_impl(const T&, long)
  noexcept
  -> decltype(false)
  {
    return false;
  }

  /// @brief Called when the user's function has operator<<(ostream&).
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  print_impl(std::ostream& os, const T& x, int)
  -> decltype(operator<<(os, x))
  {
    return os << x;
  }

  /// @brief Called when the user's function doesn't have operator<<(ostream&).
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  print_impl(std::ostream& os, const T& x, long)
  -> decltype(void())
  {
    os << "function(" << &x << ")";
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename C>
struct LIBSDD_ATTRIBUTE_PACKED _function
{
  /// @brief The type of a valuation on a flat node.
  using values_type = typename C::Values;

  /// @brief The identifier on which the user function is applied.
  const order_position_type target;

  /// @brief Ownership of the user's values function.
  const std::unique_ptr<const function_base<C>> fun_ptr;

  /// @brief Dispatch the Values homomorphism evaluation.
  struct helper
  {
    /// @brief |0| case, should never happen.
    SDD<C>
    operator()(const zero_terminal<C>&, const function_base<C>&, context<C>&, const order<C>&)
    const noexcept
    {
      assert(false);
      __builtin_unreachable();
    }

    /// @brief |1| case.
    SDD<C>
    operator()(const one_terminal<C>&, const function_base<C>&, context<C>&, const order<C>&)
    const
    {
      return one<C>();
    }

    /// @brief A function can't be applied on an hierarchical node.
    SDD<C>
    operator()(const hierarchical_node<C>&, const function_base<C>&, context<C>&, const order<C>&)
    const
    {
      assert(false && "Apply function on an hierarchical node");
      __builtin_unreachable();
    }

    /// @brief Evaluation on a flat node.
    SDD<C>
    operator()( const flat_node<C>& node, const function_base<C>& fun, context<C>& cxt
              , const order<C>& o)
    const
    {
      if (fun.selector() or fun.shifter())
      {
        dd::alpha_builder<C, values_type> alpha_builder(cxt.sdd_context());
        alpha_builder.reserve(node.size());
        for (const auto& arc : node)
        {
          values_type val = fun(arc.valuation());
          if (not val.empty())
          {
            alpha_builder.add(std::move(val), arc.successor());
          }
        }
        return {o.variable(), std::move(alpha_builder)};
      }
      else
      {
        dd::sum_builder<C, SDD<C>> sum_operands(cxt.sdd_context());
        sum_operands.reserve(node.size());
        for (const auto& arc : node)
        {
          sum_operands.add(SDD<C>(o.variable(), fun(arc.valuation()), arc.successor()));
        }
        return dd::sum(cxt.sdd_context(), std::move(sum_operands));
      }
    }
  };

  /// @brief Constructor.
  _function(order_position_type pos, std::unique_ptr<const function_base<C>> f)
    : target(pos), fun_ptr(std::move(f))
  {}

  /// @brief Skip variable predicate.
  bool
  skip(const order<C>& o)
  const noexcept
  {
    return target != o.position();
  }

  /// @brief Selector predicate
  bool
  selector()
  const noexcept
  {
    return fun_ptr->selector();
  }

  /// @brief Evaluation.
  SDD<C>
  operator()(context<C>& cxt, const order<C>& o, const SDD<C>& x)
  const
  {
    return visit(helper(), x, *fun_ptr, cxt, o);
  }

  friend
  bool
  operator==(const _function& lhs, const _function& rhs)
  noexcept
  {
    return lhs.target == rhs.target and *lhs.fun_ptr == *rhs.fun_ptr;
  }

  friend
  std::ostream&
  operator<<(std::ostream& os, const _function& x)
  {
    os << "fun(" << x.target << ", ";
    x.fun_ptr->print(os);
    return os << ")";
  }
};

/*------------------------------------------------------------------------------------------------*/

} // namespace hom

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Create the Function homomorphism.
/// @related homomorphism
template <typename C, typename User>
homomorphism<C>
function(order_position_type pos, const User& u)
{
  return homomorphism<C>::create( mem::construct<hom::_function<C>>()
                                , pos, std::make_unique<hom::function_derived<C, User>>(u));
}

/// @brief Create the Function homomorphism.
/// @param i The target identifier, must belong to o.
/// @related homomorphism
///
/// If the target is in a nested hierarchy, the succession of Local to access it is automatically
/// created.
template <typename C, typename User>
homomorphism<C>
function(const order<C>& o, const typename C::Identifier& id, const User& u)
{
  /// @todo Check that id is a flat identifier.
  return carrier(o, id, function<C>(o.node(id).position(), u));
}

/*------------------------------------------------------------------------------------------------*/

} // namespace sdd

namespace std {

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Hash specialization for sdd::hom::_function.
template <typename C>
struct hash<sdd::hom::_function<C>>
{
  std::size_t
  operator()(const sdd::hom::_function<C>& x)
  const noexcept
  {
    using namespace sdd::hash;
    return seed(x.fun_ptr->hash()) (val(x.target));
  }
};

/*------------------------------------------------------------------------------------------------*/

} // namespace std
<commit_msg>Maximize sharing of function<commit_after>#pragma once

#include <algorithm> // find
#include <iosfwd>
#include <memory>    // unique_ptr
#include <typeinfo>  // typeid

#include "sdd/dd/definition.hh"
#include "sdd/hom/context_fwd.hh"
#include "sdd/hom/definition_fwd.hh"
#include "sdd/util/packed.hh"
#include "sdd/order/carrier.hh"
#include "sdd/order/order.hh"
#include "sdd/order/order_node.hh"

namespace sdd { namespace hom {

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename C>
struct function_base
{
  /// @brief The type of a set of values.
  using values_type = typename C::Values;

  /// @brief Destructor.
  virtual
  ~function_base()
  {}

  /// @brief Tell if the user's function is a selector.
  virtual
  bool
  selector() const noexcept = 0;

  /// @brief Tell if the user's function is a shifter.
  virtual
  bool
  shifter() const noexcept = 0;

  /// @brief Apply the user function.
  virtual
  values_type
  operator()(const values_type&) const = 0;

  /// @brief Compare values_base.
  virtual
  bool
  operator==(const function_base&) const noexcept = 0;

  /// @brief Get the user's function hash value.
  virtual
  std::size_t
  hash() const noexcept = 0;

  /// @brief Get the user's function textual representation.
  virtual
  void
  print(std::ostream&) const = 0;
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename C, typename User>
struct function_derived
  : public function_base<C>
{
  /// @brief The user's values function.
  const User fun;

  /// @brief The type of a set of values.
  using values_type = typename C::Values;

  /// @brief Constructor.
  function_derived(const User& f)
    : fun(f)
  {}

  /// @brief Tell if the user's function is a selector.
  bool
  selector()
  const noexcept override
  {
    return selector_impl(fun, 0);
  }

  /// @brief Tell if the user's function is a shifter.
  bool
  shifter()
  const noexcept override
  {
    return shifter_impl(fun, 0);
  }

  /// @brief Apply the user function.
  values_type
  operator()(const values_type& val)
  const override
  {
    return fun(val);
  }

  /// @brief Compare values_derived.
  bool
  operator==(const function_base<C>& other)
  const noexcept override
  {
    return typeid(*this) == typeid(other)
         ? fun == reinterpret_cast<const function_derived&>(other).fun
         : false;
  }

  /// @brief Get the user's function hash value.
  std::size_t
  hash()
  const noexcept override
  {
    return std::hash<User>()(fun);
  }

  /// @brief Get the user's values function textual representation.
  void
  print(std::ostream& os)
  const override
  {
    print_impl(os, fun, 0);
  }

private:

  /// @brief Called when the user's function has selector().
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  selector_impl(const T& x, int)
  noexcept
  -> decltype(x.selector())
  {
    return x.selector();
  }

  /// @brief Called when the user's function doesn't have selector().
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  selector_impl(const T&, long)
  noexcept
  -> decltype(false)
  {
    return false;
  }

  /// @brief Called when the user's function has shifter().
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  shifter_impl(const T& x, int)
  noexcept
  -> decltype(x.shifter())
  {
    return x.shifter();
  }

  /// @brief Called when the user's function doesn't have shifter().
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  shifter_impl(const T&, long)
  noexcept
  -> decltype(false)
  {
    return false;
  }

  /// @brief Called when the user's function has operator<<(ostream&).
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  print_impl(std::ostream& os, const T& x, int)
  -> decltype(operator<<(os, x))
  {
    return os << x;
  }

  /// @brief Called when the user's function doesn't have operator<<(ostream&).
  ///
  /// Compile-time dispatch.
  template <typename T>
  static auto
  print_impl(std::ostream& os, const T& x, long)
  -> decltype(void())
  {
    os << "function(" << &x << ")";
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename C>
struct LIBSDD_ATTRIBUTE_PACKED _function
{
  /// @brief The type of a valuation on a flat node.
  using values_type = typename C::Values;

  /// @brief The identifier on which the user function is applied.
  const order_node<C>& o_node;

  /// @brief Ownership of the user's values function.
  const std::unique_ptr<const function_base<C>> fun_ptr;

  /// @brief Dispatch the Values homomorphism evaluation.
  struct helper
  {
    /// @brief |0| case, should never happen.
    SDD<C>
    operator()(const zero_terminal<C>&, const function_base<C>&, context<C>&, const order<C>&)
    const noexcept
    {
      assert(false);
      __builtin_unreachable();
    }

    /// @brief |1| case.
    SDD<C>
    operator()(const one_terminal<C>&, const function_base<C>&, context<C>&, const order<C>&)
    const
    {
      return one<C>();
    }

    /// @brief A function can't be applied on an hierarchical node.
    SDD<C>
    operator()(const hierarchical_node<C>&, const function_base<C>&, context<C>&, const order<C>&)
    const
    {
      assert(false && "Apply function on an hierarchical node");
      __builtin_unreachable();
    }

    /// @brief Evaluation on a flat node.
    SDD<C>
    operator()( const flat_node<C>& node, const function_base<C>& fun, context<C>& cxt
              , const order<C>& o)
    const
    {
      if (fun.selector() or fun.shifter())
      {
        dd::alpha_builder<C, values_type> alpha_builder(cxt.sdd_context());
        alpha_builder.reserve(node.size());
        for (const auto& arc : node)
        {
          values_type val = fun(arc.valuation());
          if (not val.empty())
          {
            alpha_builder.add(std::move(val), arc.successor());
          }
        }
        return {o.variable(), std::move(alpha_builder)};
      }
      else
      {
        dd::sum_builder<C, SDD<C>> sum_operands(cxt.sdd_context());
        sum_operands.reserve(node.size());
        for (const auto& arc : node)
        {
          sum_operands.add(SDD<C>(o.variable(), fun(arc.valuation()), arc.successor()));
        }
        return dd::sum(cxt.sdd_context(), std::move(sum_operands));
      }
    }
  };

  /// @brief Constructor.
  _function(const order_node<C>& n, std::unique_ptr<const function_base<C>> f)
    : o_node(n), fun_ptr(std::move(f))
  {}

  /// @brief Skip variable predicate.
  bool
  skip(const order<C>& o)
  const noexcept
  {
    return o_node.variable() != o.variable();
  }

  /// @brief Selector predicate
  bool
  selector()
  const noexcept
  {
    return fun_ptr->selector();
  }

  /// @brief Evaluation.
  SDD<C>
  operator()(context<C>& cxt, const order<C>& o, const SDD<C>& x)
  const
  {
    return visit(helper(), x, *fun_ptr, cxt, o);
  }

  friend
  bool
  operator==(const _function& lhs, const _function& rhs)
  noexcept
  {
    return lhs.o_node.variable() == rhs.o_node.variable() and *lhs.fun_ptr == *rhs.fun_ptr;
  }

  friend
  std::ostream&
  operator<<(std::ostream& os, const _function& x)
  {
    os << "fun(" << x.o_node.identifier() << ", ";
    x.fun_ptr->print(os);
    return os << ")";
  }
};

/*------------------------------------------------------------------------------------------------*/

} // namespace hom

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Create the Function homomorphism.
/// @related homomorphism
template <typename C, typename User>
homomorphism<C>
function(const order_node<C>& n, const User& u)
{
  return homomorphism<C>::create( mem::construct<hom::_function<C>>()
                                , n, std::make_unique<hom::function_derived<C, User>>(u));
}

/// @brief Create the Function homomorphism.
/// @param i The target identifier, must belong to o.
/// @related homomorphism
///
/// If the target is in a nested hierarchy, the succession of Local to access it is automatically
/// created.
template <typename C, typename User>
homomorphism<C>
function(const order<C>& o, const typename C::Identifier& id, const User& u)
{
  /// @todo Check that id is a flat identifier.
  return carrier(o, id, function<C>(o.node(id), u));
}

/*------------------------------------------------------------------------------------------------*/

} // namespace sdd

namespace std {

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Hash specialization for sdd::hom::_function.
template <typename C>
struct hash<sdd::hom::_function<C>>
{
  std::size_t
  operator()(const sdd::hom::_function<C>& x)
  const noexcept
  {
    using namespace sdd::hash;
    return seed(x.fun_ptr->hash()) (val(x.o_node.variable()));
  }
};

/*------------------------------------------------------------------------------------------------*/

} // namespace std
<|endoftext|>


<commit_before>/*
 * File:   ScanCache.h
 *
 * Copyright 2013 Heinrich Schuchardt <xypron.glpk@gmx.de>
 *
 * Licensed 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.
 *
 */

/**
 * @file ScanCache.h
 * @brief Cache for virus scanning results.
 */
#include <iostream>
#include <sstream>
#include <time.h>
#include "Messaging.h"
#include "ScanCache.h"

ScanCache::ScanCache(Environment *env) {
    e = env;
    s = new std::set<ScanResult *, ScanResultComperator>();
    hits = 0;
    misses = 0;
    root.left = &root;
    root.right = &root;
    pthread_mutex_init(&mutex, NULL);
}

/**
 * @brief Adds scan result to cache.
 * @param stat File status as returned by fstat()
 * @param response Response to be used for fanotify (FAN_ALLOW, FAN_DENY)
 */
void ScanCache::add(const struct stat *stat, const unsigned int response) {
    std::set<ScanResult *, ScanResultComperator>::iterator it;
    std::pair < std::set<ScanResult *, ScanResultComperator>::iterator, bool> pair;

    ScanResult *scr = new ScanResult();
    scr->dev = stat->st_dev;
    scr->ino = stat->st_ino;
    scr->mtime = stat->st_mtime;
    scr->response = response;
    gmtime(&(scr->age));

    pthread_mutex_lock(&mutex);
    it = s->find(scr);
    if (it != s->end()) {
        // Old matching entry found. Remove from linked list and delete.
        (*it)->left->right = (*it)->right;
        (*it)->right->left = (*it)->left;
        delete *it;
        s->erase(it);
    } else while (s->size() >= e->getCacheMaxSize()) {
            // Cache size too big. Get last element.
            it = s->find(root.left);
            if (it != s->end()) {
                // Remove from linked list and delete.
                (*it)->left->right = (*it)->right;
                (*it)->right->left = (*it)->left;
                delete *it;
                s->erase(it);
            } else {
                break;
            }
        }
    pair = s->insert(scr);
    if (pair.second) {
        // Successful insertion. Introduce leftmost in linked list.
        root.right->left = scr;
        scr->right = root.right;
        scr->left = &root;
        root.right = scr;
    } else {
        // element already existed
        delete scr;
    }
    pthread_mutex_unlock(&mutex);
}

void ScanCache::clear() {
    pthread_mutex_lock(&mutex);
    std::set<ScanResult *, ScanResultComperator>::iterator pos;
    for (pos = s->begin(); pos != s->end(); pos++) {
        delete *pos;
    }
    s->clear();
    Messaging::message(Messaging::DEBUG, "Cache cleared.");
    pthread_mutex_unlock(&mutex);
}

/**
 * @brief Adds scan result to cache.
 * @param stat file status as returned by fstat()
 * @return response to be used for fanotify (FAN_ALLOW, FAN_DENY) or CACHE_MISS
 */
int ScanCache::get(const struct stat *stat) {
    int ret;
    std::set<ScanResult *, ScanResultComperator>::iterator it;
    ScanResult *scr = new ScanResult();
    scr->dev = stat->st_dev;
    scr->ino = stat->st_ino;

    pthread_mutex_lock(&mutex);
    it = s->find(scr);
    delete scr;
    if (it == s->end()) {
        ret = CACHE_MISS;
        misses++;
    } else {
        scr = *it;
        // Check modification time.
        if (scr->mtime == stat->st_mtime) {
            // Element is valid. Remove it from linked list.
            scr->left->right = scr->right;
            scr->right->left = scr->left;
            // Insert it leftmost.
            root.right->left = scr;
            scr->right = root.right;
            scr->left = &root;
            root.right = scr;
            ret = scr->response;
            hits++;
        } else {
            // Remove outdated element from linked list and delete it.
            (*it)->left->right = (*it)->right;
            (*it)->right->left = (*it)->left;
            delete *it;
            s->erase(it);
            ret = CACHE_MISS;
            misses++;
        }
    }
    pthread_mutex_unlock(&mutex);
    return ret;
}

/**
 * @brief Remove scan result from cache.
 * @param stat file status as returned by fstat()
 */
void ScanCache::remove(const struct stat *stat) {
    std::set<ScanResult *, ScanResultComperator>::iterator it;
    ScanResult *scr = new ScanResult();
    scr->dev = stat->st_dev;
    scr->ino = stat->st_ino;

    pthread_mutex_lock(&mutex);
    it = s->find(scr);
    if (it != s->end()) {
        // Remove from linked list and delete.
        (*it)->left->right = (*it)->right;
        (*it)->right->left = (*it)->left;
        delete *it;
        s->erase(it);
    }
    pthread_mutex_unlock(&mutex);
    delete scr;
}

ScanCache::~ScanCache() {
    std::stringstream msg;
    msg << "Cache size " << s->size() <<
            ", cache hits " << hits << ", cache misses " << misses << ".";
    clear();
    pthread_mutex_destroy(&mutex);
    Messaging::message(Messaging::INFORMATION, msg.str());
}
<commit_msg>ScanCache: Observe cache size 0.<commit_after>/*
 * File:   ScanCache.h
 *
 * Copyright 2013 Heinrich Schuchardt <xypron.glpk@gmx.de>
 *
 * Licensed 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.
 *
 */

/**
 * @file ScanCache.h
 * @brief Cache for virus scanning results.
 */
#include <iostream>
#include <sstream>
#include <time.h>
#include "Messaging.h"
#include "ScanCache.h"

ScanCache::ScanCache(Environment *env) {
    e = env;
    s = new std::set<ScanResult *, ScanResultComperator>();
    hits = 0;
    misses = 0;
    root.left = &root;
    root.right = &root;
    pthread_mutex_init(&mutex, NULL);
}

/**
 * @brief Adds scan result to cache.
 * @param stat File status as returned by fstat()
 * @param response Response to be used for fanotify (FAN_ALLOW, FAN_DENY)
 */
void ScanCache::add(const struct stat *stat, const unsigned int response) {
    std::set<ScanResult *, ScanResultComperator>::iterator it;
    std::pair < std::set<ScanResult *, ScanResultComperator>::iterator, bool> pair;
    unsigned int cacheMaxSize = e->getCacheMaxSize();

    if (0 == cacheMaxSize) {
        return;
    }

    ScanResult *scr = new ScanResult();
    scr->dev = stat->st_dev;
    scr->ino = stat->st_ino;
    scr->mtime = stat->st_mtime;
    scr->response = response;
    gmtime(&(scr->age));

    pthread_mutex_lock(&mutex);
    it = s->find(scr);
    if (it != s->end()) {
        // Old matching entry found. Remove from linked list and delete.
        (*it)->left->right = (*it)->right;
        (*it)->right->left = (*it)->left;
        delete *it;
        s->erase(it);
    } else while (s->size() >= cacheMaxSize) {
            // Cache size too big. Get last element.
            it = s->find(root.left);
            if (it != s->end()) {
                // Remove from linked list and delete.
                (*it)->left->right = (*it)->right;
                (*it)->right->left = (*it)->left;
                delete *it;
                s->erase(it);
            } else {
                break;
            }
        }
    pair = s->insert(scr);
    if (pair.second) {
        // Successful insertion. Introduce leftmost in linked list.
        root.right->left = scr;
        scr->right = root.right;
        scr->left = &root;
        root.right = scr;
    } else {
        // element already existed
        delete scr;
    }
    pthread_mutex_unlock(&mutex);
}

void ScanCache::clear() {
    pthread_mutex_lock(&mutex);
    std::set<ScanResult *, ScanResultComperator>::iterator pos;
    for (pos = s->begin(); pos != s->end(); pos++) {
        delete *pos;
    }
    s->clear();
    Messaging::message(Messaging::DEBUG, "Cache cleared.");
    pthread_mutex_unlock(&mutex);
}

/**
 * @brief Adds scan result to cache.
 * @param stat file status as returned by fstat()
 * @return response to be used for fanotify (FAN_ALLOW, FAN_DENY) or CACHE_MISS
 */
int ScanCache::get(const struct stat *stat) {
    int ret;
    std::set<ScanResult *, ScanResultComperator>::iterator it;
    ScanResult *scr = new ScanResult();
    scr->dev = stat->st_dev;
    scr->ino = stat->st_ino;

    pthread_mutex_lock(&mutex);
    it = s->find(scr);
    delete scr;
    if (it == s->end()) {
        ret = CACHE_MISS;
        misses++;
    } else {
        scr = *it;
        // Check modification time.
        if (scr->mtime == stat->st_mtime) {
            // Element is valid. Remove it from linked list.
            scr->left->right = scr->right;
            scr->right->left = scr->left;
            // Insert it leftmost.
            root.right->left = scr;
            scr->right = root.right;
            scr->left = &root;
            root.right = scr;
            ret = scr->response;
            hits++;
        } else {
            // Remove outdated element from linked list and delete it.
            (*it)->left->right = (*it)->right;
            (*it)->right->left = (*it)->left;
            delete *it;
            s->erase(it);
            ret = CACHE_MISS;
            misses++;
        }
    }
    pthread_mutex_unlock(&mutex);
    return ret;
}

/**
 * @brief Remove scan result from cache.
 * @param stat file status as returned by fstat()
 */
void ScanCache::remove(const struct stat *stat) {
    std::set<ScanResult *, ScanResultComperator>::iterator it;
    ScanResult *scr = new ScanResult();
    scr->dev = stat->st_dev;
    scr->ino = stat->st_ino;

    pthread_mutex_lock(&mutex);
    it = s->find(scr);
    if (it != s->end()) {
        // Remove from linked list and delete.
        (*it)->left->right = (*it)->right;
        (*it)->right->left = (*it)->left;
        delete *it;
        s->erase(it);
    }
    pthread_mutex_unlock(&mutex);
    delete scr;
}

ScanCache::~ScanCache() {
    std::stringstream msg;
    msg << "Cache size " << s->size() <<
            ", cache hits " << hits << ", cache misses " << misses << ".";
    clear();
    pthread_mutex_destroy(&mutex);
    Messaging::message(Messaging::INFORMATION, msg.str());
}
<|endoftext|>


<commit_before>/*
 * Worldvisions Weaver Software:
 *   Copyright (C) 1997-2003 Net Integration Technologies, Inc.
 *
 * Standalone WvDial program, for testing the WvDialer class.
 *
 * Created:	Sept 30 1997		D. Coombs
 */

#include "wvdialer.h"
#include "wvver.h"
#include "wvlog.h"
#include "wvlogrcv.h"
#include "wvlogfile.h"
#include "wvsyslog.h"
#include "wvcrash.h"

#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>

volatile bool want_to_die = false;

// use no prefix string for app "Modem", and an arrow for everything else.
// This makes the output of the wvdial application look nicer.
class WvDialLogger : public WvLogConsole
/**************************************/
{
public:
    WvDialLogger() : WvLogConsole(dup(2)) // log to stderr (fd 2)
        { }

protected:
    virtual void _make_prefix();
};

void WvDialLogger::_make_prefix()
/*******************************/
{
    WvString name = appname(last_source);
    if(name == "WvDial Modem")
    {
	prefix = "";
	prelen = 0;
    }
    else
    {
	prefix = "--> ";
	prelen = 4;
    }
}

static void print_version()
/*************************/
{
    printf( "%s", wvdial_version_text );
}

static void print_help()
/**********************/
{
    print_version();
    printf( "\n%s", wvdial_help_text );
}

static void signalhandler(int sig)
/***********************************/
{
    fprintf(stderr, "Caught signal %d:  Attempting to exit gracefully...\n", sig);
    want_to_die = true;
    signal(sig, SIG_DFL);
}

int main(int argc, char **argv)
/********************************/
{
#if DEBUG
    free( malloc( 1 ) ); // for electric fence
#endif

    WvDialLogger 	rc;
    WvSyslog		*syslog = NULL;
    WvLogFile           *filelog = NULL;
    UniConfRoot         uniconf("temp:");
    WvConf              cfg(uniconf);
    WvStringList	sections;
    WvStringList	cmdlineopts;
    WvLog		log( "WvDial", WvLog::Debug );
    WvString		homedir = getenv("HOME");
    int			haveconfig = 0;
    int			havecmdlineopts = 0;

    bool chat_mode = false;
    bool write_syslog = true;

    signal(SIGTERM, signalhandler);
    signal(SIGINT, signalhandler);
    signal(SIGHUP, signalhandler);

    if(argc > 1)
    {
	for(int i=1; i < argc; i++)
	{
	    if(!strcmp(argv[i], "--config" ))
	    {
		if (!access(argv[++i < argc ? i : i - 1], F_OK))
		{
		    haveconfig = 1;
		    cfg.load_file(WvString(argv[i]));
		    continue;
		}
		else
		{
		    log("Error: --config requires a valid argument\n");
		    print_help();
		    return 1;
		}
	    }
            if(strchr(argv[i], '=' ))
	    {
                havecmdlineopts = 1;
                cmdlineopts.append(new WvString(argv[i]),true);
                continue;
            }
	    if(!strcmp(argv[i], "--chat" ))
	    {
		chat_mode = true;
		continue;
	    }
	    if(!strcmp( argv[i], "--no-syslog" ))
	    {
		write_syslog = false;
		continue;
	    }
	    if( !strcmp(argv[i], "--help"))
	    {
		print_help();
		return 1;
	    }
	    else if(!strcmp(argv[i], "--version"))
	    {
		print_version();
		return 1;
	    }
	    else if(argv[i][0] == '-')
	    {
		print_help();
		return 1;
	    }
	    sections.append(new WvString("Dialer %s", argv[i]), true);
	}
    }
    else
    {
	sections.append(new WvString("Dialer Defaults"), true);
    }

    if( !haveconfig)
    {
	// Load the system file first...
	WvString stdconfig("/etc/wvdial.conf");

	if (!access(stdconfig, F_OK))
	    cfg.load_file(stdconfig);

	// Then the user specific one...
	if (homedir)
        {
	    WvString rcfile("%s/.wvdialrc", homedir);

	    if (!access(rcfile, F_OK))
		cfg.load_file(rcfile);
	}
    }

    // Inject all of the command line options on into the cfg file in a new
    // section called Command-Line if there are command line options.
    if (havecmdlineopts == 1)
    {
        WvStringList::Iter i(cmdlineopts);
        for (i.rewind();i.next();)
        {
            char *name = i().edit();
            char *value = strchr(name,'=');

            // Value should never be null since it can't get into the list
            // if it doesn't have an = in i()
            //
            *value = 0;
	    value++;
            name = trim_string(name);
            value = trim_string(value);
            cfg.set("Command-Line", name, value);
        }
        sections.append(new WvString("Command-Line"), true);
    }

    if(!cfg.isok())
    {
	return 1;
    }

    if (chat_mode)
    {
	if (write_syslog)
	{
	    WvString buf("wvdial[%s]", getpid());
	    syslog = new WvSyslog( buf, false, WvLog::Debug2,
				   WvLog::Debug2 );
	}
	else
	{
	    // Direct logging to /dev/null as otherwise WvLog hasn't any
	    // receivers and thus will use WvLogConsole to log to stderr.
	    // That can disturb the communication with the modem on
	    // stdin/stdout. - Fixes a bug reported by SUSE on 04/05/04
	    filelog = new WvLogFile( "/dev/null", WvLog::Debug2 );
	}
    }

    WvDialer dialer(cfg, &sections, chat_mode);

    if (!chat_mode)
	if (dialer.isok() && dialer.options.ask_password)
	    dialer.ask_password();

    if (dialer.dial() == false)
	return  1;

    while (!want_to_die && dialer.isok()
	   && dialer.status() != WvDialer::Idle)
    {
	dialer.select(100);
	dialer.callback();
    }

    int retval;

    if (want_to_die)
    {
	// Probably dieing from a user signal
        retval = 2;
    }

    if ((dialer.status() != WvDialer::Idle) || !dialer.isok())
    {
	retval = 1;
    }
    else
    {
	retval = 0;
    }

    dialer.hangup();

    RELEASE(filelog);
    if (syslog) delete syslog;

    return(retval);
}
<commit_msg>HEAD: Make WvMapi/WvTnef and the Evolution Connector compile again after Pierre's futzing.<commit_after>/*
 * Worldvisions Weaver Software:
 *   Copyright (C) 1997-2003 Net Integration Technologies, Inc.
 *
 * Standalone WvDial program, for testing the WvDialer class.
 *
 * Created:	Sept 30 1997		D. Coombs
 */

#include "wvdialer.h"
#include "wvver.h"
#include "wvlog.h"
#include "wvlogrcv.h"
#include "wvlogfile.h"
#include "wvsyslog.h"
#include "wvcrash.h"

#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>

volatile bool want_to_die = false;

// use no prefix string for app "Modem", and an arrow for everything else.
// This makes the output of the wvdial application look nicer.
class WvDialLogger : public WvLogConsole
/**************************************/
{
public:
    WvDialLogger() : WvLogConsole(dup(2)) // log to stderr (fd 2)
        { }

protected:
    virtual void _make_prefix();
};

void WvDialLogger::_make_prefix()
/*******************************/
{
    WvString name = appname(last_source);
    if(name == "WvDial Modem")
    {
	prefix = "";
	prelen = 0;
    }
    else
    {
	prefix = "--> ";
	prelen = 4;
    }
}

static void print_version()
/*************************/
{
    printf( "%s", wvdial_version_text );
}

static void print_help()
/**********************/
{
    print_version();
    printf( "\n%s", wvdial_help_text );
}

static void signalhandler(int sig)
/***********************************/
{
    fprintf(stderr, "Caught signal %d:  Attempting to exit gracefully...\n", sig);
    want_to_die = true;
    signal(sig, SIG_DFL);
}

int main(int argc, char **argv)
/********************************/
{
#if DEBUG
    free( malloc( 1 ) ); // for electric fence
#endif

    WvDialLogger 	rc;
    WvSyslog		*syslog = NULL;
    WvLogFile           *filelog = NULL;
    UniConfRoot         uniconf("temp:");
    WvConf              cfg(uniconf);
    WvStringList	sections;
    WvStringList	cmdlineopts;
    WvLog		log( "WvDial", WvLog::Debug );
    WvString		homedir = getenv("HOME");
    int			haveconfig = 0;
    int			havecmdlineopts = 0;

    bool chat_mode = false;
    bool write_syslog = true;

    signal(SIGTERM, signalhandler);
    signal(SIGINT, signalhandler);
    signal(SIGHUP, signalhandler);

    if(argc > 1)
    {
	for(int i=1; i < argc; i++)
	{
	    if(!strcmp(argv[i], "--config" ))
	    {
		if (!access(argv[++i < argc ? i : i - 1], F_OK))
		{
		    haveconfig = 1;
		    cfg.load_file(WvString(argv[i]));
		    continue;
		}
		else
		{
		    log("Error: --config requires a valid argument\n");
		    print_help();
		    return 1;
		}
	    }
            if(strchr(argv[i], '=' ))
	    {
                havecmdlineopts = 1;
                cmdlineopts.append(new WvString(argv[i]),true);
                continue;
            }
	    if(!strcmp(argv[i], "--chat" ))
	    {
		chat_mode = true;
		continue;
	    }
	    if(!strcmp( argv[i], "--no-syslog" ))
	    {
		write_syslog = false;
		continue;
	    }
	    if( !strcmp(argv[i], "--help"))
	    {
		print_help();
		return 1;
	    }
	    else if(!strcmp(argv[i], "--version"))
	    {
		print_version();
		return 1;
	    }
	    else if(argv[i][0] == '-')
	    {
		print_help();
		return 1;
	    }
	    sections.append(new WvString("Dialer %s", argv[i]), true);
	}
    }
    else
    {
	sections.append(new WvString("Dialer Defaults"), true);
    }

    if( !haveconfig)
    {
	// Load the system file first...
	WvString stdconfig("/etc/wvdial.conf");

	if (!access(stdconfig, F_OK))
	    cfg.load_file(stdconfig);

	// Then the user specific one...
	if (homedir)
        {
	    WvString rcfile("%s/.wvdialrc", homedir);

	    if (!access(rcfile, F_OK))
		cfg.load_file(rcfile);
	}
    }

    // Inject all of the command line options on into the cfg file in a new
    // section called Command-Line if there are command line options.
    if (havecmdlineopts == 1)
    {
        WvStringList::Iter i(cmdlineopts);
        for (i.rewind();i.next();)
        {
            char *name = i().edit();
            char *value = strchr(name,'=');

            // Value should never be null since it can't get into the list
            // if it doesn't have an = in i()
            //
            *value = 0;
	    value++;
            name = trim_string(name);
            value = trim_string(value);
            cfg.set("Command-Line", name, value);
        }
        sections.append(new WvString("Command-Line"), true);
    }

    if(!cfg.isok())
    {
	return 1;
    }

    if (chat_mode)
    {
	if (write_syslog)
	{
	    WvString buf("wvdial[%s]", getpid());
	    syslog = new WvSyslog( buf, false, WvLog::Debug2,
				   WvLog::Debug2 );
	}
	else
	{
	    // Direct logging to /dev/null as otherwise WvLog hasn't any
	    // receivers and thus will use WvLogConsole to log to stderr.
	    // That can disturb the communication with the modem on
	    // stdin/stdout. - Fixes a bug reported by SUSE on 04/05/04
	    filelog = new WvLogFile( "/dev/null", WvLog::Debug2 );
	}
    }

    WvDialer dialer(cfg, &sections, chat_mode);

    if (!chat_mode)
	if (dialer.isok() && dialer.options.ask_password)
	    dialer.ask_password();

    if (dialer.dial() == false)
	return  1;

    while (!want_to_die && dialer.isok()
	   && dialer.status() != WvDialer::Idle)
    {
	dialer.select(100);
	dialer.callback();
    }

    int retval;

    if (want_to_die)
    {
	// Probably dieing from a user signal
        retval = 2;
    }

    if ((dialer.status() != WvDialer::Idle) || !dialer.isok())
    {
	retval = 1;
    }
    else
    {
	retval = 0;
    }

    dialer.hangup();

    RELEASE(filelog);
    delete syslog;

    return(retval);
}
<|endoftext|>


<commit_before>#include <sys/types.h>
#include <string>
#include <stdexcept>

class MultipartParser {
public:
	typedef void (*Callback)(const char *buffer, size_t start, size_t end, void *userData);

private:
	static const char CR     = 13;
	static const char LF     = 10;
	static const char SPACE  = 32;
	static const char HYPHEN = 45;
	static const char COLON  = 58;
	static const char A      = 97;
	static const char Z      = 122;
	static const size_t UNMARKED = (size_t) -1;

	enum State {
		ERROR,
		START,
		START_BOUNDARY,
		HEADER_FIELD_START,
		HEADER_FIELD,
		HEADER_VALUE_START,
		HEADER_VALUE,
		HEADER_VALUE_ALMOST_DONE,
		HEADERS_ALMOST_DONE,
		PART_DATA_START,
		PART_DATA,
		PART_END,
		END
	};

	enum Flags {
		PART_BOUNDARY = 1,
		LAST_BOUNDARY = 2
	};

	std::string boundary;
	char *lookbehind;
	size_t lookbehindSize;
	State state;
	int flags;
	size_t index;
	size_t headerFieldMark;
	size_t headerValueMark;
	size_t partDataMark;
	const char *errorReason;

	void callback(Callback cb, const char *buffer = NULL, size_t start = UNMARKED, size_t end = UNMARKED) {
		if (start != UNMARKED && start == end) {
			return;
		}
		if (cb != NULL) {
			cb(buffer, start, end, userData);
		}
	}

	void dataCallback(Callback cb, size_t &mark, const char *buffer, size_t i, size_t bufferLen, bool clear) {
		if (mark == UNMARKED) {
			return;
		}

		if (!clear) {
			callback(cb, buffer, mark, bufferLen);
			mark = 0;
		} else {
			callback(cb, buffer, mark, i);
			mark = UNMARKED;
		}
	}

	char lower(char c) const {
		return c | 0x20;
	}

	bool isBoundaryChar(char c) const {
		const char *current = boundary.c_str();
		const char *end = current + boundary.size();

		while (current < end) {
			if (*current == c) {
				return true;
			}
			current++;
		}
		return false;
	}

	void setError(const char *message) {
		state = ERROR;
		errorReason = message;
	}

public:
	Callback onPartBegin;
	Callback onHeaderField;
	Callback onHeaderValue;
	Callback onPartData;
	Callback onPartEnd;
	Callback onEnd;
	void *userData;

	MultipartParser() {
		lookbehind = NULL;
		reset();
	}

	MultipartParser(const std::string &boundary) {
		lookbehind = NULL;
		setBoundary(boundary);
	}

	~MultipartParser() {
		delete[] lookbehind;
	}

	void reset() {
		delete[] lookbehind;
		state = ERROR;
		lookbehind = NULL;
		lookbehindSize = 0;
		flags = 0;
		index = 0;
		headerFieldMark = UNMARKED;
		headerValueMark = UNMARKED;
		partDataMark    = UNMARKED;
		errorReason = NULL;

		onPartBegin   = NULL;
		onHeaderField = NULL;
		onHeaderValue = NULL;
		onPartData    = NULL;
		onPartEnd     = NULL;
		onEnd         = NULL;
		userData      = NULL;
	}

	void setBoundary(const std::string &boundary) {
		reset();
		this->boundary = "\r\n--" + boundary;
		lookbehind = new char[this->boundary.size() + 8];
		lookbehindSize = this->boundary.size() + 8;
		state = START;
	}

	size_t feed(const char *buffer, size_t len) {
		State state         = this->state;
		int flags           = this->flags;
		size_t prevIndex    = this->index;
		size_t index        = this->index;
		size_t boundarySize = boundary.size();
		size_t boundaryEnd  = boundarySize - 1;
		size_t i;
		char c, cl;

		for (i = 0; i < len; i++) {
			c = buffer[i];

			switch (state) {
			case ERROR:
				return i;
			case START:
				index = 0;
				state = START_BOUNDARY;
			case START_BOUNDARY:
				if (index == boundarySize - 2) {
					if (c != CR) {
						return i;
					}
					index++;
					break;
				} else if (index - 1 == boundarySize - 2) {
					if (c != LF) {
						return i;
					}
					index = 0;
					callback(onPartBegin);
					state = HEADER_FIELD_START;
					break;
				}
				if (c != boundary[index + 2]) {
					return i;
				}
				index++;
				break;
			case HEADER_FIELD_START:
				state = HEADER_FIELD;
				headerFieldMark = i;
			case HEADER_FIELD:
				if (c == CR) {
					headerFieldMark = UNMARKED;
					state = HEADERS_ALMOST_DONE;
					break;
				}

				if (c == HYPHEN) {
					break;
				}

				if (c == COLON) {
					dataCallback(onHeaderField, headerFieldMark, buffer, i, len, true);
					state = HEADER_VALUE_START;
					break;
				}

				cl = lower(c);
				if (cl < A || cl > Z) {
					setError("Malformed header name.");
					return i;
				}
				break;
			case HEADER_VALUE_START:
				if (c == SPACE) {
					break;
				}

				headerValueMark = i;
				state = HEADER_VALUE;
			case HEADER_VALUE:
				if (c == CR) {
					dataCallback(onHeaderValue, headerValueMark, buffer, i, len, true);
					state = HEADER_VALUE_ALMOST_DONE;
				}
				break;
			case HEADER_VALUE_ALMOST_DONE:
				if (c != LF) {
					setError("Malformed header value: LF expected after CR");
					return i;
				}

				state = HEADER_FIELD_START;
				break;
			case HEADERS_ALMOST_DONE:
				if (c != LF) {
					setError("Malformed header ending: LF expected after CR");
					return i;
				}

				state = PART_DATA_START;
				break;
			case PART_DATA_START:
				state = PART_DATA;
				partDataMark = i;
			case PART_DATA:
				prevIndex = index;

				if (index == 0) {
					// boyer-moore derrived algorithm to safely skip non-boundary data
					while (i + boundary.size() <= len) {
						if (isBoundaryChar(buffer[i + boundaryEnd])) {
							break;
						}

						i += boundary.size();
					}
					c = buffer[i];
				}

				if (index < boundary.size()) {
					if (boundary[index] == c) {
						if (index == 0) {
							dataCallback(onPartData, partDataMark, buffer, i, len, true);
						}
						index++;
					} else {
						index = 0;
					}
				} else if (index == boundary.size()) {
					index++;
					if (c == CR) {
						// CR = part boundary
						flags |= PART_BOUNDARY;
					} else if (c == HYPHEN) {
						// HYPHEN = end boundary
						flags |= LAST_BOUNDARY;
					} else {
						index = 0;
					}
				} else if (index - 1 == boundary.size()) {
					if (flags & PART_BOUNDARY) {
						index = 0;
						if (c == LF) {
							// unset the PART_BOUNDARY flag
							flags &= ~PART_BOUNDARY;
							callback(onPartEnd);
							callback(onPartBegin);
							state = HEADER_FIELD_START;
							break;
						}
					} else if (flags & LAST_BOUNDARY) {
						if (c == HYPHEN) {
							index++;
						} else {
							index = 0;
						}
					} else {
						index = 0;
					}
				} else if (index - 2 == boundary.size()) {
					if (c == CR) {
						index++;
					} else {
						index = 0;
					}
				} else if (index - boundary.size() == 3) {
					index = 0;
					if (c == LF) {
						callback(onPartEnd);
						callback(onEnd);
						state = END;
						break;
					}
				}

				if (index > 0) {
					// when matching a possible boundary, keep a lookbehind reference
					// in case it turns out to be a false lead
					if (index - 1 >= lookbehindSize) {
						throw std::out_of_range("index overflows lookbehind buffer");
					}
					lookbehind[index - 1] = c;
				} else if (prevIndex > 0) {
					// if our boundary turned out to be rubbish, the captured lookbehind
					// belongs to partData
					callback(onPartData, lookbehind, 0, prevIndex);
					prevIndex = 0;
					partDataMark = i;
				}

				break;
			default:
				return i;
			}
		}

		dataCallback(onHeaderField, headerFieldMark, buffer, i, len, false);
		dataCallback(onHeaderValue, headerValueMark, buffer, i, len, false);
		dataCallback(onPartData, partDataMark, buffer, i, len, false);

		this->index = index;
		this->state = state;
		this->flags = flags;

		return len;
	}

	bool succeeded() const {
		return state == END;
	}

	bool hasError() const {
		return state == ERROR;
	}

	bool stopped() const {
		return state == ERROR || state == END;
	}

	const char *getErrorMessage() const {
		return errorReason;
	}
};

#include <stdio.h>
using namespace std;

static void
onPartBegin(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onPartBegin\n");
}

static void
onHeaderField(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onHeaderField: (%s)\n", string(buffer + start, end - start).c_str());
}

static void
onHeaderValue(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onHeaderValue: (%s)\n", string(buffer + start, end - start).c_str());
}

static void
onPartData(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onPartData: (%s)\n", string(buffer + start, end - start).c_str());
}

static void
onPartEnd(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onPartEnd\n");
}

static void
onEnd(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onEnd\n");
}

int
main() {
	MultipartParser parser("abcd");
	parser.onPartBegin = onPartBegin;
	parser.onHeaderField = onHeaderField;
	parser.onHeaderValue = onHeaderValue;
	parser.onPartData = onPartData;
	parser.onPartEnd = onPartEnd;
	parser.onEnd = onEnd;

	while (!parser.stopped() && !feof(stdin)) {
		char buf[5];
		size_t len = fread(buf, 1, sizeof(buf), stdin);
		size_t fed = 0;
		do {
			size_t ret = parser.feed(buf + fed, len - fed);
			fed += ret;
			printf("accepted %d bytes\n", (int) ret);
		} while (fed < len && !parser.stopped());
	}
	printf("%s\n", parser.getErrorMessage());
	return 0;
}
<commit_msg>Properly abort on error state.<commit_after>#include <sys/types.h>
#include <string>
#include <stdexcept>

class MultipartParser {
public:
	typedef void (*Callback)(const char *buffer, size_t start, size_t end, void *userData);

private:
	static const char CR     = 13;
	static const char LF     = 10;
	static const char SPACE  = 32;
	static const char HYPHEN = 45;
	static const char COLON  = 58;
	static const char A      = 97;
	static const char Z      = 122;
	static const size_t UNMARKED = (size_t) -1;

	enum State {
		ERROR,
		START,
		START_BOUNDARY,
		HEADER_FIELD_START,
		HEADER_FIELD,
		HEADER_VALUE_START,
		HEADER_VALUE,
		HEADER_VALUE_ALMOST_DONE,
		HEADERS_ALMOST_DONE,
		PART_DATA_START,
		PART_DATA,
		PART_END,
		END
	};

	enum Flags {
		PART_BOUNDARY = 1,
		LAST_BOUNDARY = 2
	};

	std::string boundary;
	char *lookbehind;
	size_t lookbehindSize;
	State state;
	int flags;
	size_t index;
	size_t headerFieldMark;
	size_t headerValueMark;
	size_t partDataMark;
	const char *errorReason;

	void callback(Callback cb, const char *buffer = NULL, size_t start = UNMARKED, size_t end = UNMARKED) {
		if (start != UNMARKED && start == end) {
			return;
		}
		if (cb != NULL) {
			cb(buffer, start, end, userData);
		}
	}

	void dataCallback(Callback cb, size_t &mark, const char *buffer, size_t i, size_t bufferLen, bool clear) {
		if (mark == UNMARKED) {
			return;
		}

		if (!clear) {
			callback(cb, buffer, mark, bufferLen);
			mark = 0;
		} else {
			callback(cb, buffer, mark, i);
			mark = UNMARKED;
		}
	}

	char lower(char c) const {
		return c | 0x20;
	}

	bool isBoundaryChar(char c) const {
		const char *current = boundary.c_str();
		const char *end = current + boundary.size();

		while (current < end) {
			if (*current == c) {
				return true;
			}
			current++;
		}
		return false;
	}

	void setError(const char *message) {
		state = ERROR;
		errorReason = message;
	}

public:
	Callback onPartBegin;
	Callback onHeaderField;
	Callback onHeaderValue;
	Callback onPartData;
	Callback onPartEnd;
	Callback onEnd;
	void *userData;

	MultipartParser() {
		lookbehind = NULL;
		reset();
	}

	MultipartParser(const std::string &boundary) {
		lookbehind = NULL;
		setBoundary(boundary);
	}

	~MultipartParser() {
		delete[] lookbehind;
	}

	void reset() {
		delete[] lookbehind;
		state = ERROR;
		lookbehind = NULL;
		lookbehindSize = 0;
		flags = 0;
		index = 0;
		headerFieldMark = UNMARKED;
		headerValueMark = UNMARKED;
		partDataMark    = UNMARKED;
		errorReason = NULL;

		onPartBegin   = NULL;
		onHeaderField = NULL;
		onHeaderValue = NULL;
		onPartData    = NULL;
		onPartEnd     = NULL;
		onEnd         = NULL;
		userData      = NULL;
	}

	void setBoundary(const std::string &boundary) {
		reset();
		this->boundary = "\r\n--" + boundary;
		lookbehind = new char[this->boundary.size() + 8];
		lookbehindSize = this->boundary.size() + 8;
		state = START;
	}

	size_t feed(const char *buffer, size_t len) {
		if (state == ERROR) {
			return 0;
		}

		State state         = this->state;
		int flags           = this->flags;
		size_t prevIndex    = this->index;
		size_t index        = this->index;
		size_t boundarySize = boundary.size();
		size_t boundaryEnd  = boundarySize - 1;
		size_t i;
		char c, cl;

		for (i = 0; i < len; i++) {
			c = buffer[i];

			switch (state) {
			case ERROR:
				return i;
			case START:
				index = 0;
				state = START_BOUNDARY;
			case START_BOUNDARY:
				if (index == boundarySize - 2) {
					if (c != CR) {
						return i;
					}
					index++;
					break;
				} else if (index - 1 == boundarySize - 2) {
					if (c != LF) {
						return i;
					}
					index = 0;
					callback(onPartBegin);
					state = HEADER_FIELD_START;
					break;
				}
				if (c != boundary[index + 2]) {
					return i;
				}
				index++;
				break;
			case HEADER_FIELD_START:
				state = HEADER_FIELD;
				headerFieldMark = i;
			case HEADER_FIELD:
				if (c == CR) {
					headerFieldMark = UNMARKED;
					state = HEADERS_ALMOST_DONE;
					break;
				}

				if (c == HYPHEN) {
					break;
				}

				if (c == COLON) {
					dataCallback(onHeaderField, headerFieldMark, buffer, i, len, true);
					state = HEADER_VALUE_START;
					break;
				}

				cl = lower(c);
				if (cl < A || cl > Z) {
					setError("Malformed header name.");
					return i;
				}
				break;
			case HEADER_VALUE_START:
				if (c == SPACE) {
					break;
				}

				headerValueMark = i;
				state = HEADER_VALUE;
			case HEADER_VALUE:
				if (c == CR) {
					dataCallback(onHeaderValue, headerValueMark, buffer, i, len, true);
					state = HEADER_VALUE_ALMOST_DONE;
				}
				break;
			case HEADER_VALUE_ALMOST_DONE:
				if (c != LF) {
					setError("Malformed header value: LF expected after CR");
					return i;
				}

				state = HEADER_FIELD_START;
				break;
			case HEADERS_ALMOST_DONE:
				if (c != LF) {
					setError("Malformed header ending: LF expected after CR");
					return i;
				}

				state = PART_DATA_START;
				break;
			case PART_DATA_START:
				state = PART_DATA;
				partDataMark = i;
			case PART_DATA:
				prevIndex = index;

				if (index == 0) {
					// boyer-moore derrived algorithm to safely skip non-boundary data
					while (i + boundary.size() <= len) {
						if (isBoundaryChar(buffer[i + boundaryEnd])) {
							break;
						}

						i += boundary.size();
					}
					c = buffer[i];
				}

				if (index < boundary.size()) {
					if (boundary[index] == c) {
						if (index == 0) {
							dataCallback(onPartData, partDataMark, buffer, i, len, true);
						}
						index++;
					} else {
						index = 0;
					}
				} else if (index == boundary.size()) {
					index++;
					if (c == CR) {
						// CR = part boundary
						flags |= PART_BOUNDARY;
					} else if (c == HYPHEN) {
						// HYPHEN = end boundary
						flags |= LAST_BOUNDARY;
					} else {
						index = 0;
					}
				} else if (index - 1 == boundary.size()) {
					if (flags & PART_BOUNDARY) {
						index = 0;
						if (c == LF) {
							// unset the PART_BOUNDARY flag
							flags &= ~PART_BOUNDARY;
							callback(onPartEnd);
							callback(onPartBegin);
							state = HEADER_FIELD_START;
							break;
						}
					} else if (flags & LAST_BOUNDARY) {
						if (c == HYPHEN) {
							index++;
						} else {
							index = 0;
						}
					} else {
						index = 0;
					}
				} else if (index - 2 == boundary.size()) {
					if (c == CR) {
						index++;
					} else {
						index = 0;
					}
				} else if (index - boundary.size() == 3) {
					index = 0;
					if (c == LF) {
						callback(onPartEnd);
						callback(onEnd);
						state = END;
						break;
					}
				}

				if (index > 0) {
					// when matching a possible boundary, keep a lookbehind reference
					// in case it turns out to be a false lead
					if (index - 1 >= lookbehindSize) {
						throw std::out_of_range("index overflows lookbehind buffer");
					}
					lookbehind[index - 1] = c;
				} else if (prevIndex > 0) {
					// if our boundary turned out to be rubbish, the captured lookbehind
					// belongs to partData
					callback(onPartData, lookbehind, 0, prevIndex);
					prevIndex = 0;
					partDataMark = i;
				}

				break;
			default:
				return i;
			}
		}

		dataCallback(onHeaderField, headerFieldMark, buffer, i, len, false);
		dataCallback(onHeaderValue, headerValueMark, buffer, i, len, false);
		dataCallback(onPartData, partDataMark, buffer, i, len, false);

		this->index = index;
		this->state = state;
		this->flags = flags;

		return len;
	}

	bool succeeded() const {
		return state == END;
	}

	bool hasError() const {
		return state == ERROR;
	}

	bool stopped() const {
		return state == ERROR || state == END;
	}

	const char *getErrorMessage() const {
		return errorReason;
	}
};

#include <stdio.h>
using namespace std;

static void
onPartBegin(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onPartBegin\n");
}

static void
onHeaderField(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onHeaderField: (%s)\n", string(buffer + start, end - start).c_str());
}

static void
onHeaderValue(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onHeaderValue: (%s)\n", string(buffer + start, end - start).c_str());
}

static void
onPartData(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onPartData: (%s)\n", string(buffer + start, end - start).c_str());
}

static void
onPartEnd(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onPartEnd\n");
}

static void
onEnd(const char *buffer, size_t start, size_t end, void *userData) {
	printf("onEnd\n");
}

int
main() {
	MultipartParser parser("abcd");
	parser.onPartBegin = onPartBegin;
	parser.onHeaderField = onHeaderField;
	parser.onHeaderValue = onHeaderValue;
	parser.onPartData = onPartData;
	parser.onPartEnd = onPartEnd;
	parser.onEnd = onEnd;

	while (!parser.stopped() && !feof(stdin)) {
		char buf[5];
		size_t len = fread(buf, 1, sizeof(buf), stdin);
		size_t fed = 0;
		do {
			size_t ret = parser.feed(buf + fed, len - fed);
			fed += ret;
			printf("accepted %d bytes\n", (int) ret);
		} while (fed < len && !parser.stopped());
	}
	printf("%s\n", parser.getErrorMessage());
	return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Disable exception handler warnings.
#pragma warning( disable : 4530 )

#include "client/windows/sender/crash_report_sender.h"
#include "common/windows/http_upload.h"

#if _MSC_VER < 1400  // MSVC 2005/8
// Older MSVC doesn't have fscanf_s, but they are compatible as long as
// we don't use the string conversions (%s/%c/%S/%C).
#define fscanf_s fscanf
#endif

namespace google_breakpad {

static const char kCheckpointSignature[] = "GBP1\n";

CrashReportSender::CrashReportSender(const wstring &checkpoint_file)
    : checkpoint_file_(checkpoint_file),
      max_reports_per_day_(-1),
      last_sent_date_(-1),
      reports_sent_(0) {
  FILE *fd;
  if (OpenCheckpointFile(L"r", &fd) == 0) {
    ReadCheckpoint(fd);
    fclose(fd);
  }
}

ReportResult CrashReportSender::SendCrashReport(
    const wstring &url, const map<wstring, wstring> &parameters,
    const wstring &dump_file_name, wstring *report_code) {
  int today = GetCurrentDate();
  if (today == last_sent_date_ &&
      max_reports_per_day_ != -1 &&
      reports_sent_ >= max_reports_per_day_) {
    return RESULT_THROTTLED;
  }

  int http_response = 0;
  bool result = HTTPUpload::SendRequest(
    url, parameters, dump_file_name, L"upload_file_minidump", report_code,
    &http_response);
  ReportSent(today);

  if (result) {
    return RESULT_SUCCEEDED;
  } else if (http_response == 400) {  // TODO: update if/when the server
                                      //       switches to a different code
    return RESULT_REJECTED;
  } else {
    return RESULT_FAILED;
  }
}

void CrashReportSender::ReadCheckpoint(FILE *fd) {
  char buf[128];
  if (!fgets(buf, sizeof(buf), fd) ||
      strcmp(buf, kCheckpointSignature) != 0) {
    return;
  }

  if (fscanf_s(fd, "%d\n", &last_sent_date_) != 1) {
    last_sent_date_ = -1;
    return;
  }
  if (fscanf_s(fd, "%d\n", &reports_sent_) != 1) {
    reports_sent_ = 0;
    return;
  }
}

void CrashReportSender::ReportSent(int today) {
  // Update the report stats
  if (today != last_sent_date_) {
    last_sent_date_ = today;
    reports_sent_ = 0;
  }
  ++reports_sent_;

  // Update the checkpoint file
  FILE *fd;
  if (OpenCheckpointFile(L"w", &fd) == 0) {
    fputs(kCheckpointSignature, fd);
    fprintf(fd, "%d\n", last_sent_date_);
    fprintf(fd, "%d\n", reports_sent_);
    fclose(fd);
  }
}

int CrashReportSender::GetCurrentDate() const {
  SYSTEMTIME system_time;
  GetSystemTime(&system_time);
  return (system_time.wYear * 10000) + (system_time.wMonth * 100) +
      system_time.wDay;
}

int CrashReportSender::OpenCheckpointFile(const wchar_t *mode, FILE **fd) {
#if _MSC_VER >= 1400  // MSVC 2005/8
  return _wfopen_s(fd, checkpoint_file_.c_str(), mode);
#else
  *fd = _wfopen(checkpoint_file_.c_str(), mode);
  if (*fd == NULL) {
    return errno;
  }
  return 0;
#endif
}

}  // namespace google_breakpad
<commit_msg>Assertion in CrashReportSender (windows) when no checkpoint file is desired (#216).  Patch by Ben Turner <bent.mozilla>.  r=me.<commit_after>// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Disable exception handler warnings.
#pragma warning( disable : 4530 )

#include "client/windows/sender/crash_report_sender.h"
#include "common/windows/http_upload.h"

#if _MSC_VER < 1400  // MSVC 2005/8
// Older MSVC doesn't have fscanf_s, but they are compatible as long as
// we don't use the string conversions (%s/%c/%S/%C).
#define fscanf_s fscanf
#endif

namespace google_breakpad {

static const char kCheckpointSignature[] = "GBP1\n";

CrashReportSender::CrashReportSender(const wstring &checkpoint_file)
    : checkpoint_file_(checkpoint_file),
      max_reports_per_day_(-1),
      last_sent_date_(-1),
      reports_sent_(0) {
  FILE *fd;
  if (OpenCheckpointFile(L"r", &fd) == 0) {
    ReadCheckpoint(fd);
    fclose(fd);
  }
}

ReportResult CrashReportSender::SendCrashReport(
    const wstring &url, const map<wstring, wstring> &parameters,
    const wstring &dump_file_name, wstring *report_code) {
  int today = GetCurrentDate();
  if (today == last_sent_date_ &&
      max_reports_per_day_ != -1 &&
      reports_sent_ >= max_reports_per_day_) {
    return RESULT_THROTTLED;
  }

  int http_response = 0;
  bool result = HTTPUpload::SendRequest(
    url, parameters, dump_file_name, L"upload_file_minidump", report_code,
    &http_response);
  ReportSent(today);

  if (result) {
    return RESULT_SUCCEEDED;
  } else if (http_response == 400) {  // TODO: update if/when the server
                                      //       switches to a different code
    return RESULT_REJECTED;
  } else {
    return RESULT_FAILED;
  }
}

void CrashReportSender::ReadCheckpoint(FILE *fd) {
  char buf[128];
  if (!fgets(buf, sizeof(buf), fd) ||
      strcmp(buf, kCheckpointSignature) != 0) {
    return;
  }

  if (fscanf_s(fd, "%d\n", &last_sent_date_) != 1) {
    last_sent_date_ = -1;
    return;
  }
  if (fscanf_s(fd, "%d\n", &reports_sent_) != 1) {
    reports_sent_ = 0;
    return;
  }
}

void CrashReportSender::ReportSent(int today) {
  // Update the report stats
  if (today != last_sent_date_) {
    last_sent_date_ = today;
    reports_sent_ = 0;
  }
  ++reports_sent_;

  // Update the checkpoint file
  FILE *fd;
  if (OpenCheckpointFile(L"w", &fd) == 0) {
    fputs(kCheckpointSignature, fd);
    fprintf(fd, "%d\n", last_sent_date_);
    fprintf(fd, "%d\n", reports_sent_);
    fclose(fd);
  }
}

int CrashReportSender::GetCurrentDate() const {
  SYSTEMTIME system_time;
  GetSystemTime(&system_time);
  return (system_time.wYear * 10000) + (system_time.wMonth * 100) +
      system_time.wDay;
}

int CrashReportSender::OpenCheckpointFile(const wchar_t *mode, FILE **fd) {
  if (checkpoint_file_.empty()) {
    return ENOENT;
  }
#if _MSC_VER >= 1400  // MSVC 2005/8
  return _wfopen_s(fd, checkpoint_file_.c_str(), mode);
#else
  *fd = _wfopen(checkpoint_file_.c_str(), mode);
  if (*fd == NULL) {
    return errno;
  }
  return 0;
#endif
}

}  // namespace google_breakpad
<|endoftext|>


<commit_before><commit_msg>Why did I have that four times?<commit_after><|endoftext|>


<commit_before>#ifdef _MSC_VER
//fixes "fatal error C1189: #error :  WinSock.h has already been included"
#	include <boost/asio.hpp>
#endif
#include <server/scan_directory.hpp>
#include <server/directory_listing.hpp>
#include <server/sha256.hpp>
#include <server/hexadecimal.hpp>
#include <server/path.hpp>
#include <silicium/asio/tcp_acceptor.hpp>
#include <silicium/observable/spawn_coroutine.hpp>
#include <silicium/asio/writing_observable.hpp>
#include <silicium/asio/reading_observable.hpp>
#include <silicium/source/received_from_socket_source.hpp>
#include <silicium/observable/transform_if_initialized.hpp>
#include <silicium/observable/erase_shared.hpp>
#include <silicium/source/observable_source.hpp>
#include <silicium/observable/for_each.hpp>
#include <silicium/optional.hpp>
#include <silicium/observable/thread.hpp>
#include <silicium/source/file_source.hpp>
#include <silicium/http/http.hpp>
#include <silicium/http/uri.hpp>
#include <silicium/to_unique.hpp>
#include <silicium/observable/thread_generator.hpp>
#include <silicium/source/buffering_source.hpp>
#include <silicium/open.hpp>
#include <silicium/read_file.hpp>
#include <silicium/source/memory_source.hpp>
#include <silicium/std_threading.hpp>
#include <silicium/source/virtualized_source.hpp>
#include <silicium/source/transforming_source.hpp>
#include <silicium/observable/end.hpp>
#include <silicium/source/single_source.hpp>
#include <silicium/sink/iterator_sink.hpp>
#include <boost/interprocess/sync/null_mutex.hpp>
#include <boost/unordered_map.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/program_options.hpp>
#include <boost/container/vector.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/thread/mutex.hpp>

namespace fileserver
{
	using response_part = Si::memory_range;

	typedef Si::fast_variant<unknown_digest, Si::noexcept_string> any_reference;

	struct get_request
	{
		any_reference what;
	};

	struct browse_request
	{
		any_reference what;
	};

	typedef Si::fast_variant<browse_request, get_request> parsed_request;

	template <class PathElementRange>
	Si::optional<any_reference> parse_any_reference(PathElementRange const &path)
	{
		if (path.empty())
		{
			return any_reference{Si::noexcept_string{}};
		}
		if (boost::range::equal(path.front(), Si::make_c_str_range("name")))
		{
			auto name_begin = path.begin() + 1;
			if (name_begin == path.end())
			{
				return any_reference{Si::noexcept_string{}};
			}
			return any_reference{Si::noexcept_string(name_begin->begin(), name_begin->end())};
		}
		if (boost::range::equal(path.front(), Si::make_c_str_range("hash")))
		{
			auto hash_begin = path.begin() + 1;
			if (hash_begin == path.end())
			{
				return Si::none;
			}
			auto digest = parse_digest(*hash_begin);
			if (!digest)
			{
				return Si::none;
			}
			return any_reference{std::move(*digest)};
		}
		return Si::none;
	}

	Si::optional<parsed_request> parse_request_path(Si::memory_range const &path)
	{
		boost::optional<Si::http::uri> const parsed_path = Si::http::parse_uri(path);
		if (!parsed_path || parsed_path->path.empty())
		{
			return Si::none;
		}

		if (boost::range::equal(parsed_path->path.front(), Si::make_c_str_range("browse")))
		{
			Si::optional<any_reference> ref = parse_any_reference(Si::make_iterator_range(parsed_path->path.begin() + 1, parsed_path->path.end()));
			if (!ref)
			{
				return Si::none;
			}
			return parsed_request{browse_request{std::move(*ref)}};
		}

		if (boost::range::equal(parsed_path->path.front(), Si::make_c_str_range("get")))
		{
			Si::optional<any_reference> ref = parse_any_reference(Si::make_iterator_range(parsed_path->path.begin() + 1, parsed_path->path.end()));
			if (!ref)
			{
				return Si::none;
			}
			return parsed_request{get_request{std::move(*ref)}};
		}

		return Si::none;
	}

	Si::http::response make_not_found_response()
	{
		Si::http::response header;
		header.http_version = "HTTP/1.0";
		header.status = 404;
		header.status_text = "Not Found";
		header.arguments = Si::make_unique<Si::http::response::arguments_table>();
		(*header.arguments)["Connection"] = "close";
		return header;
	}

	std::vector<char> serialize_response(Si::http::response const &header)
	{
		std::vector<char> serialized;
		auto sink = Si::make_container_sink(serialized);
		Si::http::generate_response(sink, header);
		return serialized;
	}

	enum class request_type
	{
		get,
		head
	};

	template <class String>
	request_type determine_request_type(String const &method)
	{
		if (boost::algorithm::iequals("HEAD", method))
		{
			return request_type::head;
		}
		else
		{
			return request_type::get;
		}
	}

	template <class YieldContext, class MakeSender>
	void respond(
		YieldContext &yield,
		MakeSender const &make_sender,
		Si::http::request const &header,
		file_repository const &repository,
		digest const &root)
	{
		auto const try_send = [&yield, &make_sender](std::vector<char> const &data)
		{
			char const * const begin = data.data();
			auto sender = make_sender(Si::make_memory_range(begin, begin + data.size()));
			auto result = yield.get_one(sender);
			assert(result);
			return !*result;
		};

		auto const request = parse_request_path(Si::make_memory_range(header.path));
		if (!request)
		{
			try_send(serialize_response(make_not_found_response()));
			return;
		}

		std::vector<location> const * const found_file_locations = repository.find_location(
			Si::visit<unknown_digest>(
				Si::visit<any_reference const &>(
					*request,
					[](get_request const &request) -> any_reference const & { return request.what; },
					[](browse_request const &request) -> any_reference const & { return request.what; }
				),
				[](unknown_digest const &digest) { return digest; },
				[&root](Si::noexcept_string const &name)
				{
					//TODO: resolve the name
					boost::ignore_unused(name);
					return to_unknown_digest(root);
				}
			)
		);
		if (!found_file_locations)
		{
			try_send(serialize_response(make_not_found_response()));
			return;
		}
		assert(!found_file_locations->empty());

		//just try the first entry for now
		auto &found_file = (*found_file_locations)[0];

		{
			Si::http::response response;
			response.arguments = Si::make_unique<std::map<Si::noexcept_string, Si::noexcept_string>>();
			response.http_version = "HTTP/1.0";
			response.status_text = "OK";
			response.status = 200;
			(*response.arguments)["Content-Length"] = boost::lexical_cast<Si::noexcept_string>(location_file_size(found_file));
			(*response.arguments)["Connection"] = "close";

			std::vector<char> response_header = serialize_response(response);
			if (!try_send(response_header))
			{
				return;
			}
		}

		switch (determine_request_type(header.method))
		{
		case request_type::get:
			{
				Si::visit<Si::nothing>(
					*request,
					[&try_send, &found_file, &yield](get_request const &) -> Si::nothing
					{
						auto reading = Si::make_thread_generator<std::vector<char>, Si::std_threading>([&](Si::push_context<std::vector<char>> &yield) -> Si::nothing
						{
							yield(Si::visit<std::vector<char>>(
								found_file,
								[](file_system_location const &location)
								{
									return Si::read_file(location.where.to_boost_path());
								},
								[](in_memory_location const &location)
								{
									return location.content;
								}
							));
							return {};
						});
						auto const &body = yield.get_one(Si::ref(reading));
						if (!body)
						{
							return Si::nothing();
						}

						if (body->size() != location_file_size(found_file))
						{
							return Si::nothing();
						}

						if (!try_send(*body))
						{
							return Si::nothing();
						}

						return Si::nothing();
					},
					[](browse_request const &)
					{
						//TODO
						return Si::nothing();
					}
				);
				break;
			}

		case request_type::head:
			{
				break;
			}
		}
	}

	template <class YieldContext, class ReceiveObservable, class MakeSender, class Shutdown>
	void serve_client(
		YieldContext &yield,
		ReceiveObservable &receive,
		MakeSender const &make_sender,
		Shutdown const &shutdown,
		file_repository const &repository,
		digest const &root)
	{
		auto receive_sync = Si::virtualize_source(Si::make_observable_source(Si::ref(receive), yield));
		Si::received_from_socket_source receive_bytes(receive_sync);
		auto header = Si::http::parse_request(receive_bytes);
		if (!header)
		{
			return;
		}

		respond(yield, make_sender, *header, repository, root);
		shutdown();

		while (Si::get(receive_bytes))
		{
		}
	}

	//TODO: use unique_observable
	using session_handle = Si::shared_observable<Si::nothing>;

	std::pair<std::vector<char>, content_type> directory_listing_to_json_bytes(directory_listing const &listing)
	{
		std::vector<char> bytes;
		serialize_json(Si::make_container_sink(bytes), listing);
		return std::make_pair(std::move(bytes), json_listing_content_type);
	}

	void serve_directory(boost::filesystem::path const &served_dir)
	{
		boost::asio::io_service io;
		boost::asio::ip::tcp::acceptor acceptor(io, boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4(), 8080));
		auto clients = Si::asio::make_tcp_acceptor(&acceptor);

		std::pair<file_repository, typed_reference> const scanned = scan_directory(served_dir, directory_listing_to_json_bytes, detail::hash_file);
		std::cerr << "Scan complete. Tree hash value ";
		typed_reference const &root = scanned.second;
		print(std::cerr, root);
		std::cerr << "\n";
		file_repository const &files = scanned.first;
		digest const &root_digest = root.referenced;

		Si::spawn_coroutine([&clients, &files, &root_digest](Si::spawn_context &yield)
		{
			for (;;)
			{
				auto accepted = yield.get_one(Si::ref(clients));
				if (!accepted)
				{
					return;
				}
				std::shared_ptr<boost::asio::ip::tcp::socket> socket = accepted->get(); //TODO handle error
				auto prepare_socket = [socket, &files, &root_digest](Si::spawn_context &yield)
				{
					std::array<char, 1024> receive_buffer;
					auto received = Si::asio::make_reading_observable(*socket, Si::make_iterator_range(receive_buffer.data(), receive_buffer.data() + receive_buffer.size()));
					auto make_sender = [socket](Si::memory_range sent)
					{
						auto sender = Si::asio::make_writing_observable(*socket);
						sender.set_buffer(sent);
						return sender;
					};
					auto shutdown = [socket]()
					{
						boost::system::error_code ec; //ignored
						socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
					};
					serve_client(yield, received, make_sender, shutdown, files, root_digest);
				};
				Si::spawn_coroutine(prepare_socket);
			}
		});

		io.run();
	}
}

int main(int argc, char **argv)
{
	std::string verb;
	boost::filesystem::path where = boost::filesystem::current_path();

	boost::program_options::options_description desc("Allowed options");
	desc.add_options()
	    ("help", "produce help message")
		("verb", boost::program_options::value(&verb), "what to do (serve)")
		("where", boost::program_options::value(&where), "which filesystem directory to use")
	;

	boost::program_options::positional_options_description positional;
	positional.add("verb", 1);
	positional.add("where", 1);
	boost::program_options::variables_map vm;
	try
	{
		boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(positional).run(), vm);
	}
	catch (boost::program_options::error const &ex)
	{
		std::cerr
			<< ex.what() << '\n'
			<< desc << "\n";
		return 1;
	}

	boost::program_options::notify(vm);

	if (vm.count("help"))
	{
	    std::cerr << desc << "\n";
	    return 1;
	}

	if (verb == "serve")
	{
		fileserver::serve_directory(where);
		return 0;
	}
	else
	{
		std::cerr
			<< "Unknown verb\n"
			<< desc << "\n";
	    return 1;
	}
}
<commit_msg>Si::visit can return void<commit_after>#ifdef _MSC_VER
//fixes "fatal error C1189: #error :  WinSock.h has already been included"
#	include <boost/asio.hpp>
#endif
#include <server/scan_directory.hpp>
#include <server/directory_listing.hpp>
#include <server/sha256.hpp>
#include <server/hexadecimal.hpp>
#include <server/path.hpp>
#include <silicium/asio/tcp_acceptor.hpp>
#include <silicium/observable/spawn_coroutine.hpp>
#include <silicium/asio/writing_observable.hpp>
#include <silicium/asio/reading_observable.hpp>
#include <silicium/source/received_from_socket_source.hpp>
#include <silicium/observable/transform_if_initialized.hpp>
#include <silicium/observable/erase_shared.hpp>
#include <silicium/source/observable_source.hpp>
#include <silicium/observable/for_each.hpp>
#include <silicium/optional.hpp>
#include <silicium/observable/thread.hpp>
#include <silicium/source/file_source.hpp>
#include <silicium/http/http.hpp>
#include <silicium/http/uri.hpp>
#include <silicium/to_unique.hpp>
#include <silicium/observable/thread_generator.hpp>
#include <silicium/source/buffering_source.hpp>
#include <silicium/open.hpp>
#include <silicium/read_file.hpp>
#include <silicium/source/memory_source.hpp>
#include <silicium/std_threading.hpp>
#include <silicium/source/virtualized_source.hpp>
#include <silicium/source/transforming_source.hpp>
#include <silicium/observable/end.hpp>
#include <silicium/source/single_source.hpp>
#include <silicium/sink/iterator_sink.hpp>
#include <boost/interprocess/sync/null_mutex.hpp>
#include <boost/unordered_map.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/program_options.hpp>
#include <boost/container/vector.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/thread/mutex.hpp>

namespace fileserver
{
	using response_part = Si::memory_range;

	typedef Si::fast_variant<unknown_digest, Si::noexcept_string> any_reference;

	struct get_request
	{
		any_reference what;
	};

	struct browse_request
	{
		any_reference what;
	};

	typedef Si::fast_variant<browse_request, get_request> parsed_request;

	template <class PathElementRange>
	Si::optional<any_reference> parse_any_reference(PathElementRange const &path)
	{
		if (path.empty())
		{
			return any_reference{Si::noexcept_string{}};
		}
		if (boost::range::equal(path.front(), Si::make_c_str_range("name")))
		{
			auto name_begin = path.begin() + 1;
			if (name_begin == path.end())
			{
				return any_reference{Si::noexcept_string{}};
			}
			return any_reference{Si::noexcept_string(name_begin->begin(), name_begin->end())};
		}
		if (boost::range::equal(path.front(), Si::make_c_str_range("hash")))
		{
			auto hash_begin = path.begin() + 1;
			if (hash_begin == path.end())
			{
				return Si::none;
			}
			auto digest = parse_digest(*hash_begin);
			if (!digest)
			{
				return Si::none;
			}
			return any_reference{std::move(*digest)};
		}
		return Si::none;
	}

	Si::optional<parsed_request> parse_request_path(Si::memory_range const &path)
	{
		boost::optional<Si::http::uri> const parsed_path = Si::http::parse_uri(path);
		if (!parsed_path || parsed_path->path.empty())
		{
			return Si::none;
		}

		if (boost::range::equal(parsed_path->path.front(), Si::make_c_str_range("browse")))
		{
			Si::optional<any_reference> ref = parse_any_reference(Si::make_iterator_range(parsed_path->path.begin() + 1, parsed_path->path.end()));
			if (!ref)
			{
				return Si::none;
			}
			return parsed_request{browse_request{std::move(*ref)}};
		}

		if (boost::range::equal(parsed_path->path.front(), Si::make_c_str_range("get")))
		{
			Si::optional<any_reference> ref = parse_any_reference(Si::make_iterator_range(parsed_path->path.begin() + 1, parsed_path->path.end()));
			if (!ref)
			{
				return Si::none;
			}
			return parsed_request{get_request{std::move(*ref)}};
		}

		return Si::none;
	}

	Si::http::response make_not_found_response()
	{
		Si::http::response header;
		header.http_version = "HTTP/1.0";
		header.status = 404;
		header.status_text = "Not Found";
		header.arguments = Si::make_unique<Si::http::response::arguments_table>();
		(*header.arguments)["Connection"] = "close";
		return header;
	}

	std::vector<char> serialize_response(Si::http::response const &header)
	{
		std::vector<char> serialized;
		auto sink = Si::make_container_sink(serialized);
		Si::http::generate_response(sink, header);
		return serialized;
	}

	enum class request_type
	{
		get,
		head
	};

	template <class String>
	request_type determine_request_type(String const &method)
	{
		if (boost::algorithm::iequals("HEAD", method))
		{
			return request_type::head;
		}
		else
		{
			return request_type::get;
		}
	}

	template <class YieldContext, class MakeSender>
	void respond(
		YieldContext &yield,
		MakeSender const &make_sender,
		Si::http::request const &header,
		file_repository const &repository,
		digest const &root)
	{
		auto const try_send = [&yield, &make_sender](std::vector<char> const &data)
		{
			char const * const begin = data.data();
			auto sender = make_sender(Si::make_memory_range(begin, begin + data.size()));
			auto result = yield.get_one(sender);
			assert(result);
			return !*result;
		};

		auto const request = parse_request_path(Si::make_memory_range(header.path));
		if (!request)
		{
			try_send(serialize_response(make_not_found_response()));
			return;
		}

		std::vector<location> const * const found_file_locations = repository.find_location(
			Si::visit<unknown_digest>(
				Si::visit<any_reference const &>(
					*request,
					[](get_request const &request) -> any_reference const & { return request.what; },
					[](browse_request const &request) -> any_reference const & { return request.what; }
				),
				[](unknown_digest const &digest) { return digest; },
				[&root](Si::noexcept_string const &name)
				{
					//TODO: resolve the name
					boost::ignore_unused(name);
					return to_unknown_digest(root);
				}
			)
		);
		if (!found_file_locations)
		{
			try_send(serialize_response(make_not_found_response()));
			return;
		}
		assert(!found_file_locations->empty());

		//just try the first entry for now
		auto &found_file = (*found_file_locations)[0];

		{
			Si::http::response response;
			response.arguments = Si::make_unique<std::map<Si::noexcept_string, Si::noexcept_string>>();
			response.http_version = "HTTP/1.0";
			response.status_text = "OK";
			response.status = 200;
			(*response.arguments)["Content-Length"] = boost::lexical_cast<Si::noexcept_string>(location_file_size(found_file));
			(*response.arguments)["Connection"] = "close";

			std::vector<char> response_header = serialize_response(response);
			if (!try_send(response_header))
			{
				return;
			}
		}

		switch (determine_request_type(header.method))
		{
		case request_type::get:
			{
				Si::visit<void>(
					*request,
					[&try_send, &found_file, &yield](get_request const &)
					{
						auto reading = Si::make_thread_generator<std::vector<char>, Si::std_threading>([&](Si::push_context<std::vector<char>> &yield) -> Si::nothing
						{
							yield(Si::visit<std::vector<char>>(
								found_file,
								[](file_system_location const &location)
								{
									return Si::read_file(location.where.to_boost_path());
								},
								[](in_memory_location const &location)
								{
									return location.content;
								}
							));
							return {};
						});
						auto const &body = yield.get_one(Si::ref(reading));
						if (!body)
						{
							return;
						}

						if (body->size() != location_file_size(found_file))
						{
							return;
						}

						try_send(*body);
					},
					[](browse_request const &)
					{
						//TODO
					}
				);
				break;
			}

		case request_type::head:
			{
				break;
			}
		}
	}

	template <class YieldContext, class ReceiveObservable, class MakeSender, class Shutdown>
	void serve_client(
		YieldContext &yield,
		ReceiveObservable &receive,
		MakeSender const &make_sender,
		Shutdown const &shutdown,
		file_repository const &repository,
		digest const &root)
	{
		auto receive_sync = Si::virtualize_source(Si::make_observable_source(Si::ref(receive), yield));
		Si::received_from_socket_source receive_bytes(receive_sync);
		auto header = Si::http::parse_request(receive_bytes);
		if (!header)
		{
			return;
		}

		respond(yield, make_sender, *header, repository, root);
		shutdown();

		while (Si::get(receive_bytes))
		{
		}
	}

	//TODO: use unique_observable
	using session_handle = Si::shared_observable<Si::nothing>;

	std::pair<std::vector<char>, content_type> directory_listing_to_json_bytes(directory_listing const &listing)
	{
		std::vector<char> bytes;
		serialize_json(Si::make_container_sink(bytes), listing);
		return std::make_pair(std::move(bytes), json_listing_content_type);
	}

	void serve_directory(boost::filesystem::path const &served_dir)
	{
		boost::asio::io_service io;
		boost::asio::ip::tcp::acceptor acceptor(io, boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4(), 8080));
		auto clients = Si::asio::make_tcp_acceptor(&acceptor);

		std::pair<file_repository, typed_reference> const scanned = scan_directory(served_dir, directory_listing_to_json_bytes, detail::hash_file);
		std::cerr << "Scan complete. Tree hash value ";
		typed_reference const &root = scanned.second;
		print(std::cerr, root);
		std::cerr << "\n";
		file_repository const &files = scanned.first;
		digest const &root_digest = root.referenced;

		Si::spawn_coroutine([&clients, &files, &root_digest](Si::spawn_context &yield)
		{
			for (;;)
			{
				auto accepted = yield.get_one(Si::ref(clients));
				if (!accepted)
				{
					return;
				}
				std::shared_ptr<boost::asio::ip::tcp::socket> socket = accepted->get(); //TODO handle error
				auto prepare_socket = [socket, &files, &root_digest](Si::spawn_context &yield)
				{
					std::array<char, 1024> receive_buffer;
					auto received = Si::asio::make_reading_observable(*socket, Si::make_iterator_range(receive_buffer.data(), receive_buffer.data() + receive_buffer.size()));
					auto make_sender = [socket](Si::memory_range sent)
					{
						auto sender = Si::asio::make_writing_observable(*socket);
						sender.set_buffer(sent);
						return sender;
					};
					auto shutdown = [socket]()
					{
						boost::system::error_code ec; //ignored
						socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
					};
					serve_client(yield, received, make_sender, shutdown, files, root_digest);
				};
				Si::spawn_coroutine(prepare_socket);
			}
		});

		io.run();
	}
}

int main(int argc, char **argv)
{
	std::string verb;
	boost::filesystem::path where = boost::filesystem::current_path();

	boost::program_options::options_description desc("Allowed options");
	desc.add_options()
	    ("help", "produce help message")
		("verb", boost::program_options::value(&verb), "what to do (serve)")
		("where", boost::program_options::value(&where), "which filesystem directory to use")
	;

	boost::program_options::positional_options_description positional;
	positional.add("verb", 1);
	positional.add("where", 1);
	boost::program_options::variables_map vm;
	try
	{
		boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(positional).run(), vm);
	}
	catch (boost::program_options::error const &ex)
	{
		std::cerr
			<< ex.what() << '\n'
			<< desc << "\n";
		return 1;
	}

	boost::program_options::notify(vm);

	if (vm.count("help"))
	{
	    std::cerr << desc << "\n";
	    return 1;
	}

	if (verb == "serve")
	{
		fileserver::serve_directory(where);
		return 0;
	}
	else
	{
		std::cerr
			<< "Unknown verb\n"
			<< desc << "\n";
	    return 1;
	}
}
<|endoftext|>