<commit_before>// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// 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.

#include <atomic>
#include <cinttypes>
#include <future>
#include <stdexcept>
#include <string>

#include "gtest/gtest.h"

#include "rclcpp/rclcpp.hpp"

#include "test_rclcpp/utils.hpp"

#include "test_rclcpp/msg/u_int32.hpp"
#include "test_rclcpp/srv/add_two_ints.hpp"

#ifdef RMW_IMPLEMENTATION
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
#else
# define CLASSNAME(NAME, SUFFIX) NAME
#endif

TEST(CLASSNAME(test_executor, RMW_IMPLEMENTATION), recursive_spin_call) {
  rclcpp::executors::SingleThreadedExecutor executor;
  auto node = rclcpp::Node::make_shared("recursive_spin_call");
  auto timer = node->create_wall_timer(0_s, [&executor]() {
    ASSERT_THROW(executor.spin_some(), std::runtime_error);
    ASSERT_THROW(executor.spin_once(), std::runtime_error);
    ASSERT_THROW(executor.spin(), std::runtime_error);
    executor.cancel();
  });
  executor.add_node(node);
  executor.spin();
}

TEST(CLASSNAME(test_executor, RMW_IMPLEMENTATION), multithreaded_spin_call) {
  rclcpp::executors::SingleThreadedExecutor executor;
  auto node = rclcpp::Node::make_shared("multithreaded_spin_call");
  std::mutex m;
  bool ready = false;
  std::condition_variable cv;
  std::thread t([&executor, &m, &cv, &ready]() {
    std::unique_lock<std::mutex> lock(m);
    cv.wait(lock, [&ready] {return ready; });
    ASSERT_THROW(executor.spin_some(), std::runtime_error);
    ASSERT_THROW(executor.spin_once(), std::runtime_error);
    ASSERT_THROW(executor.spin(), std::runtime_error);
    executor.cancel();
  });
  auto timer = node->create_wall_timer(0_s, [&m, &cv, &ready]() {
    if (!ready) {
      {
        std::lock_guard<std::mutex> lock(m);
        ready = true;
      }
      cv.notify_one();
    }
  });
  executor.add_node(node);
  executor.spin();
  t.join();
}

// Try spinning 2 single-threaded executors in two separate threads.
TEST(CLASSNAME(test_executor, RMW_IMPLEMENTATION), multiple_executors) {
  std::atomic_uint counter1;
  counter1 = 0;
  std::atomic_uint counter2;
  counter2 = 0;
  const uint32_t counter_goal = 20;

  // Initialize executor 1.
  rclcpp::executors::SingleThreadedExecutor executor1;
  auto callback1 = [&counter1, &counter_goal, &executor1]() {
      if (counter1 == counter_goal) {
        executor1.cancel();
        return;
      }
      ++counter1;
    };
  auto node1 = rclcpp::Node::make_shared("multiple_executors_1");
  auto timer1 = node1->create_wall_timer(1_ms, callback1);
  executor1.add_node(node1);

  // Initialize executor 2.
  rclcpp::executors::SingleThreadedExecutor executor2;

  auto callback2 = [&counter2, &counter_goal, &executor2]() {
      if (counter2 == counter_goal) {
        executor2.cancel();
        return;
      }
      ++counter2;
    };
  auto node2 = rclcpp::Node::make_shared("multiple_executors_2");
  auto timer2 = node2->create_wall_timer(1_ms, callback2);
  executor2.add_node(node2);

  auto spin_executor2 = [&executor2]() {
      executor2.spin();
    };

  // Launch both executors
  std::thread execution_thread(spin_executor2);

  executor1.spin();
  execution_thread.join();
  EXPECT_EQ(counter1.load(), counter_goal);
  EXPECT_EQ(counter2.load(), counter_goal);

  // Try to add node1 to executor2. It should throw, since node1 was already added to executor1.
  ASSERT_THROW(executor2.add_node(node1), std::runtime_error);
}

// Check that the executor is notified when a node adds a new timer, publisher, subscription,
// service or client.
TEST(CLASSNAME(test_executor, RMW_IMPLEMENTATION), notify) {
  rclcpp::executors::SingleThreadedExecutor executor;
  auto executor_spin_lambda = [&executor]() {
      executor.spin();
    };
  auto node = rclcpp::Node::make_shared("test_executor_notify");
  executor.add_node(node);
  {
    std::thread spin_thread(executor_spin_lambda);
    std::promise<void> timer_promise;
    std::shared_future<void> timer_future(timer_promise.get_future());

    auto timer = node->create_wall_timer(
      1_ms,
      [&timer_promise](rclcpp::TimerBase & timer)
    {
      timer_promise.set_value();
      timer.cancel();
    });
    EXPECT_EQ(std::future_status::ready, timer_future.wait_for(50_ms));
    executor.cancel();

    spin_thread.join();
  }

  {
    std::thread spin_thread(executor_spin_lambda);
    bool subscription_triggered = false;
    auto sub_callback =
      [&executor, &subscription_triggered](test_rclcpp::msg::UInt32::ConstSharedPtr msg) -> void
      {
        subscription_triggered = true;
        EXPECT_EQ(msg->data, 42);
        executor.cancel();
      };

    auto subscription = node->create_subscription<test_rclcpp::msg::UInt32>(
      "test_executor_notify_subscription",
      sub_callback,
      rmw_qos_profile_default);
    test_rclcpp::busy_wait_for_subscriber(node, "test_executor_notify_subscription");

    auto publisher = node->create_publisher<test_rclcpp::msg::UInt32>(
      "test_executor_notify_subscription", rmw_qos_profile_default);
    auto timer = node->create_wall_timer(
      1_ms,
      [&publisher]()
    {
      test_rclcpp::msg::UInt32 pub_msg;
      pub_msg.data = 42;
      publisher->publish(pub_msg);
    }
      );

    spin_thread.join();

    EXPECT_TRUE(subscription_triggered);
  }

  {
    std::thread spin_thread(executor_spin_lambda);

    auto service = node->create_service<test_rclcpp::srv::AddTwoInts>(
      "test_executor_notify_service",
      [](test_rclcpp::srv::AddTwoInts::Request::SharedPtr request,
      test_rclcpp::srv::AddTwoInts::Response::SharedPtr response)
    {
      response->sum = request->a + request->b;
    });

    auto client = node->create_client<test_rclcpp::srv::AddTwoInts>(
      "test_executor_notify_service"
      );
    auto request = std::make_shared<test_rclcpp::srv::AddTwoInts::Request>();
    request->a = 4;
    request->b = 2;
    auto future_result = client->async_send_request(request);
    EXPECT_EQ(future_result.get()->sum, 6);
    executor.cancel();
    spin_thread.join();
  }
}

// test removing a node

// test notify with multiple nodes

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>fix compiler warning<commit_after>// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// 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.

#include <atomic>
#include <cinttypes>
#include <future>
#include <stdexcept>
#include <string>

#include "gtest/gtest.h"

#include "rclcpp/rclcpp.hpp"

#include "test_rclcpp/utils.hpp"

#include "test_rclcpp/msg/u_int32.hpp"
#include "test_rclcpp/srv/add_two_ints.hpp"

#ifdef RMW_IMPLEMENTATION
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
#else
# define CLASSNAME(NAME, SUFFIX) NAME
#endif

TEST(CLASSNAME(test_executor, RMW_IMPLEMENTATION), recursive_spin_call) {
  rclcpp::executors::SingleThreadedExecutor executor;
  auto node = rclcpp::Node::make_shared("recursive_spin_call");
  auto timer = node->create_wall_timer(0_s, [&executor]() {
    ASSERT_THROW(executor.spin_some(), std::runtime_error);
    ASSERT_THROW(executor.spin_once(), std::runtime_error);
    ASSERT_THROW(executor.spin(), std::runtime_error);
    executor.cancel();
  });
  executor.add_node(node);
  executor.spin();
}

TEST(CLASSNAME(test_executor, RMW_IMPLEMENTATION), multithreaded_spin_call) {
  rclcpp::executors::SingleThreadedExecutor executor;
  auto node = rclcpp::Node::make_shared("multithreaded_spin_call");
  std::mutex m;
  bool ready = false;
  std::condition_variable cv;
  std::thread t([&executor, &m, &cv, &ready]() {
    std::unique_lock<std::mutex> lock(m);
    cv.wait(lock, [&ready] {return ready; });
    ASSERT_THROW(executor.spin_some(), std::runtime_error);
    ASSERT_THROW(executor.spin_once(), std::runtime_error);
    ASSERT_THROW(executor.spin(), std::runtime_error);
    executor.cancel();
  });
  auto timer = node->create_wall_timer(0_s, [&m, &cv, &ready]() {
    if (!ready) {
      {
        std::lock_guard<std::mutex> lock(m);
        ready = true;
      }
      cv.notify_one();
    }
  });
  executor.add_node(node);
  executor.spin();
  t.join();
}

// Try spinning 2 single-threaded executors in two separate threads.
TEST(CLASSNAME(test_executor, RMW_IMPLEMENTATION), multiple_executors) {
  std::atomic_uint counter1;
  counter1 = 0;
  std::atomic_uint counter2;
  counter2 = 0;
  const uint32_t counter_goal = 20;

  // Initialize executor 1.
  rclcpp::executors::SingleThreadedExecutor executor1;
  auto callback1 = [&counter1, &counter_goal, &executor1]() {
      if (counter1 == counter_goal) {
        executor1.cancel();
        return;
      }
      ++counter1;
    };
  auto node1 = rclcpp::Node::make_shared("multiple_executors_1");
  auto timer1 = node1->create_wall_timer(1_ms, callback1);
  executor1.add_node(node1);

  // Initialize executor 2.
  rclcpp::executors::SingleThreadedExecutor executor2;

  auto callback2 = [&counter2, &counter_goal, &executor2]() {
      if (counter2 == counter_goal) {
        executor2.cancel();
        return;
      }
      ++counter2;
    };
  auto node2 = rclcpp::Node::make_shared("multiple_executors_2");
  auto timer2 = node2->create_wall_timer(1_ms, callback2);
  executor2.add_node(node2);

  auto spin_executor2 = [&executor2]() {
      executor2.spin();
    };

  // Launch both executors
  std::thread execution_thread(spin_executor2);

  executor1.spin();
  execution_thread.join();
  EXPECT_EQ(counter1.load(), counter_goal);
  EXPECT_EQ(counter2.load(), counter_goal);

  // Try to add node1 to executor2. It should throw, since node1 was already added to executor1.
  ASSERT_THROW(executor2.add_node(node1), std::runtime_error);
}

// Check that the executor is notified when a node adds a new timer, publisher, subscription,
// service or client.
TEST(CLASSNAME(test_executor, RMW_IMPLEMENTATION), notify) {
  rclcpp::executors::SingleThreadedExecutor executor;
  auto executor_spin_lambda = [&executor]() {
      executor.spin();
    };
  auto node = rclcpp::Node::make_shared("test_executor_notify");
  executor.add_node(node);
  {
    std::thread spin_thread(executor_spin_lambda);
    std::promise<void> timer_promise;
    std::shared_future<void> timer_future(timer_promise.get_future());

    auto timer = node->create_wall_timer(
      1_ms,
      [&timer_promise](rclcpp::TimerBase & timer)
    {
      timer_promise.set_value();
      timer.cancel();
    });
    EXPECT_EQ(std::future_status::ready, timer_future.wait_for(50_ms));
    executor.cancel();

    spin_thread.join();
  }

  {
    std::thread spin_thread(executor_spin_lambda);
    bool subscription_triggered = false;
    auto sub_callback =
      [&executor, &subscription_triggered](test_rclcpp::msg::UInt32::ConstSharedPtr msg) -> void
      {
        subscription_triggered = true;
        EXPECT_EQ(msg->data, 42u);
        executor.cancel();
      };

    auto subscription = node->create_subscription<test_rclcpp::msg::UInt32>(
      "test_executor_notify_subscription",
      sub_callback,
      rmw_qos_profile_default);
    test_rclcpp::busy_wait_for_subscriber(node, "test_executor_notify_subscription");

    auto publisher = node->create_publisher<test_rclcpp::msg::UInt32>(
      "test_executor_notify_subscription", rmw_qos_profile_default);
    auto timer = node->create_wall_timer(
      1_ms,
      [&publisher]()
    {
      test_rclcpp::msg::UInt32 pub_msg;
      pub_msg.data = 42;
      publisher->publish(pub_msg);
    }
      );

    spin_thread.join();

    EXPECT_TRUE(subscription_triggered);
  }

  {
    std::thread spin_thread(executor_spin_lambda);

    auto service = node->create_service<test_rclcpp::srv::AddTwoInts>(
      "test_executor_notify_service",
      [](test_rclcpp::srv::AddTwoInts::Request::SharedPtr request,
      test_rclcpp::srv::AddTwoInts::Response::SharedPtr response)
    {
      response->sum = request->a + request->b;
    });

    auto client = node->create_client<test_rclcpp::srv::AddTwoInts>(
      "test_executor_notify_service"
      );
    auto request = std::make_shared<test_rclcpp::srv::AddTwoInts::Request>();
    request->a = 4;
    request->b = 2;
    auto future_result = client->async_send_request(request);
    EXPECT_EQ(future_result.get()->sum, 6);
    executor.cancel();
    spin_thread.join();
  }
}

// test removing a node

// test notify with multiple nodes

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: localoutputstream.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 04:05:17 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef CONFIGMGR_LOCALBE_LOCALOUTPUTSTREAM_HXX_
#include "localoutputstream.hxx"
#endif // CONFIGMGR_LOCALBE_LOCALOUTPUTSTREAM_HXX_

#ifndef _RTL_USTRBUF_HXX_
#include <rtl/ustrbuf.hxx>
#endif // _RTL_USTRBUF_HXX_

#ifndef _COM_SUN_STAR_CONFIGURATION_BACKEND_INSUFFICIENTACCESSRIGHTSEXCEPTION_HPP_
#include <com/sun/star/configuration/backend/InsufficientAccessRightsException.hpp>
#endif

namespace configmgr { namespace localbe {

//==============================================================================

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

LocalOutputStream::LocalOutputStream(const rtl::OUString& aFileUrl)
    throw (backend::BackendAccessException, uno::RuntimeException)
: mFileUrl(aFileUrl)
, mTemporaryFileUrl(mFileUrl)
, mWriteFile(NULL)
{
    // First, ensure the directory where the file is supposed to be
    // put exists.
    mTemporaryFileUrl += rtl::OUString::createFromAscii("_tmp") ;
    rtl::OUString parentDirectory = FileHelper::getParentDir(aFileUrl) ;

    if (osl::File::RC errorCode = FileHelper::mkdirs(parentDirectory))
    {
        rtl::OUStringBuffer message ;
        message.appendAscii("Cannot create directory \"") ;
        message.append(parentDirectory).appendAscii("\". Error is ") ;
        message.append(FileHelper::createOSLErrorString(errorCode));
        message.appendAscii(" [").append(sal_Int32(errorCode)).appendAscii("].") ;

        rtl::OUString const sIOMsg = message.makeStringAndClear();
        uno::Any ioe = uno::makeAny(io::IOException(sIOMsg,0));

        switch (errorCode)
        {
        case osl::File::E_ACCES:
        case osl::File::E_ROFS:
            message.appendAscii("Configuration LocalOutputStream - No Write Access: ");
            message.append(sIOMsg);
            throw backend::InsufficientAccessRightsException(message.makeStringAndClear(), NULL, ioe) ;

        case osl::File::E_None: OSL_ASSERT(!"can't happen");
        default:
            message.appendAscii("Configuration LocalOutputStream - IO Error: ");
            message.append(sIOMsg);
            throw backend::BackendAccessException(message.makeStringAndClear(), NULL, ioe) ;
        }
    }

    osl::File::remove(mTemporaryFileUrl) ;
    mWriteFile = new osl::File(mTemporaryFileUrl) ;

    if (osl::File::RC errorCode = mWriteFile->open(OpenFlag_Write |  OpenFlag_Create) )
    {
        delete mWriteFile, mWriteFile = NULL;

        rtl::OUStringBuffer message ;
        message.appendAscii("Cannot open file \"") ;
        message.append(mTemporaryFileUrl).appendAscii("\" for writing. ");
        message.appendAscii("Error is ").append(FileHelper::createOSLErrorString(errorCode));
        message.appendAscii(" [").append(sal_Int32(errorCode)).appendAscii("].") ;

        rtl::OUString const sIOMsg = message.makeStringAndClear();
        uno::Any ioe = uno::makeAny(io::IOException(sIOMsg,0));

        switch (errorCode)
        {
        case osl::File::E_EXIST: // take inability to remove as indicator of missing rights
        case osl::File::E_ACCES:
        case osl::File::E_ROFS:
            message.appendAscii("Configuration LocalOutputStream - No Write Access: ");
            message.append(sIOMsg);
            throw backend::InsufficientAccessRightsException(message.makeStringAndClear(), NULL, ioe) ;

        case osl::File::E_None: OSL_ASSERT(!"can't happen");
        default:
            message.appendAscii("Configuration LocalOutputStream - IO Error: ");
            message.append(sIOMsg);
            throw backend::BackendAccessException(message.makeStringAndClear(), NULL, ioe) ;
        }
    }
    mTemporaryFile = new OSLOutputStreamWrapper(*mWriteFile) ;
}
//------------------------------------------------------------------------------

LocalOutputStream::~LocalOutputStream()
{
    try
    {
        this->closeOutput();
    //  if (mWriteFile)  FileHelper::removeFile(mTemporaryFileUrl) ;
    }
    catch (uno::Exception&)
    {
        OSL_ENSURE(false,"Exception from closing LocalOutputStream ignored.");
    }

    delete mWriteFile;
}
//------------------------------------------------------------------------------

void LocalOutputStream::finishOutput()
    throw (backend::BackendAccessException, uno::RuntimeException)
{
    if (mWriteFile)
    try
    {
        this->closeOutput();
        delete mWriteFile, mWriteFile = NULL;

        FileHelper::replaceFile(mFileUrl, mTemporaryFileUrl) ;
    }
    catch (io::IOException& ioe)
    {
        rtl::OUStringBuffer message ;
        message.appendAscii("Configuration LocalOutputStream - IO Error: ");
        message.appendAscii("Cannot finish output to \"").append(mTemporaryFileUrl) ;
        message.appendAscii("\" or copy the result to \"").append(mFileUrl).appendAscii("\". ");
        message.appendAscii("Error is \"").append(ioe.Message).appendAscii("\". ");
        throw backend::BackendAccessException(message.makeStringAndClear(), *this, uno::makeAny(ioe));
    }
}
//------------------------------------------------------------------------------

inline
uno::Reference<io::XOutputStream> LocalOutputStream::getOutputFile()
{
    if (!mTemporaryFile.is())
    {
        throw io::NotConnectedException(
            rtl::OUString::createFromAscii("LocalOutputStream: no output file."),
            *this);
    }
    return mTemporaryFile;
}
//------------------------------------------------------------------------------

void SAL_CALL LocalOutputStream::writeBytes(const uno::Sequence<sal_Int8>& aData)
    throw (io::NotConnectedException,
            io::BufferSizeExceededException,
            io::IOException, uno::RuntimeException)
{
    getOutputFile()->writeBytes(aData) ;
}
//------------------------------------------------------------------------------

void SAL_CALL LocalOutputStream::flush()
    throw (io::NotConnectedException,
            io::BufferSizeExceededException,
            io::IOException, uno::RuntimeException)
{
    getOutputFile()->flush() ;
}
//------------------------------------------------------------------------------

void SAL_CALL LocalOutputStream::closeOutput()
    throw (io::NotConnectedException, io::BufferSizeExceededException,
            io::IOException, uno::RuntimeException)
{
    if (mTemporaryFile.is())
    {
        mTemporaryFile->closeOutput() ;

        mTemporaryFile.clear();
    }
}
//------------------------------------------------------------------------------

} } // configmgr.localbe
<commit_msg>INTEGRATION: CWS pchfix02 (1.5.60); FILE MERGED 2006/09/01 17:20:35 kaib 1.5.60.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: localoutputstream.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-16 15:12:32 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_configmgr.hxx"

#ifndef CONFIGMGR_LOCALBE_LOCALOUTPUTSTREAM_HXX_
#include "localoutputstream.hxx"
#endif // CONFIGMGR_LOCALBE_LOCALOUTPUTSTREAM_HXX_

#ifndef _RTL_USTRBUF_HXX_
#include <rtl/ustrbuf.hxx>
#endif // _RTL_USTRBUF_HXX_

#ifndef _COM_SUN_STAR_CONFIGURATION_BACKEND_INSUFFICIENTACCESSRIGHTSEXCEPTION_HPP_
#include <com/sun/star/configuration/backend/InsufficientAccessRightsException.hpp>
#endif

namespace configmgr { namespace localbe {

//==============================================================================

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

LocalOutputStream::LocalOutputStream(const rtl::OUString& aFileUrl)
    throw (backend::BackendAccessException, uno::RuntimeException)
: mFileUrl(aFileUrl)
, mTemporaryFileUrl(mFileUrl)
, mWriteFile(NULL)
{
    // First, ensure the directory where the file is supposed to be
    // put exists.
    mTemporaryFileUrl += rtl::OUString::createFromAscii("_tmp") ;
    rtl::OUString parentDirectory = FileHelper::getParentDir(aFileUrl) ;

    if (osl::File::RC errorCode = FileHelper::mkdirs(parentDirectory))
    {
        rtl::OUStringBuffer message ;
        message.appendAscii("Cannot create directory \"") ;
        message.append(parentDirectory).appendAscii("\". Error is ") ;
        message.append(FileHelper::createOSLErrorString(errorCode));
        message.appendAscii(" [").append(sal_Int32(errorCode)).appendAscii("].") ;

        rtl::OUString const sIOMsg = message.makeStringAndClear();
        uno::Any ioe = uno::makeAny(io::IOException(sIOMsg,0));

        switch (errorCode)
        {
        case osl::File::E_ACCES:
        case osl::File::E_ROFS:
            message.appendAscii("Configuration LocalOutputStream - No Write Access: ");
            message.append(sIOMsg);
            throw backend::InsufficientAccessRightsException(message.makeStringAndClear(), NULL, ioe) ;

        case osl::File::E_None: OSL_ASSERT(!"can't happen");
        default:
            message.appendAscii("Configuration LocalOutputStream - IO Error: ");
            message.append(sIOMsg);
            throw backend::BackendAccessException(message.makeStringAndClear(), NULL, ioe) ;
        }
    }

    osl::File::remove(mTemporaryFileUrl) ;
    mWriteFile = new osl::File(mTemporaryFileUrl) ;

    if (osl::File::RC errorCode = mWriteFile->open(OpenFlag_Write |  OpenFlag_Create) )
    {
        delete mWriteFile, mWriteFile = NULL;

        rtl::OUStringBuffer message ;
        message.appendAscii("Cannot open file \"") ;
        message.append(mTemporaryFileUrl).appendAscii("\" for writing. ");
        message.appendAscii("Error is ").append(FileHelper::createOSLErrorString(errorCode));
        message.appendAscii(" [").append(sal_Int32(errorCode)).appendAscii("].") ;

        rtl::OUString const sIOMsg = message.makeStringAndClear();
        uno::Any ioe = uno::makeAny(io::IOException(sIOMsg,0));

        switch (errorCode)
        {
        case osl::File::E_EXIST: // take inability to remove as indicator of missing rights
        case osl::File::E_ACCES:
        case osl::File::E_ROFS:
            message.appendAscii("Configuration LocalOutputStream - No Write Access: ");
            message.append(sIOMsg);
            throw backend::InsufficientAccessRightsException(message.makeStringAndClear(), NULL, ioe) ;

        case osl::File::E_None: OSL_ASSERT(!"can't happen");
        default:
            message.appendAscii("Configuration LocalOutputStream - IO Error: ");
            message.append(sIOMsg);
            throw backend::BackendAccessException(message.makeStringAndClear(), NULL, ioe) ;
        }
    }
    mTemporaryFile = new OSLOutputStreamWrapper(*mWriteFile) ;
}
//------------------------------------------------------------------------------

LocalOutputStream::~LocalOutputStream()
{
    try
    {
        this->closeOutput();
    //  if (mWriteFile)  FileHelper::removeFile(mTemporaryFileUrl) ;
    }
    catch (uno::Exception&)
    {
        OSL_ENSURE(false,"Exception from closing LocalOutputStream ignored.");
    }

    delete mWriteFile;
}
//------------------------------------------------------------------------------

void LocalOutputStream::finishOutput()
    throw (backend::BackendAccessException, uno::RuntimeException)
{
    if (mWriteFile)
    try
    {
        this->closeOutput();
        delete mWriteFile, mWriteFile = NULL;

        FileHelper::replaceFile(mFileUrl, mTemporaryFileUrl) ;
    }
    catch (io::IOException& ioe)
    {
        rtl::OUStringBuffer message ;
        message.appendAscii("Configuration LocalOutputStream - IO Error: ");
        message.appendAscii("Cannot finish output to \"").append(mTemporaryFileUrl) ;
        message.appendAscii("\" or copy the result to \"").append(mFileUrl).appendAscii("\". ");
        message.appendAscii("Error is \"").append(ioe.Message).appendAscii("\". ");
        throw backend::BackendAccessException(message.makeStringAndClear(), *this, uno::makeAny(ioe));
    }
}
//------------------------------------------------------------------------------

inline
uno::Reference<io::XOutputStream> LocalOutputStream::getOutputFile()
{
    if (!mTemporaryFile.is())
    {
        throw io::NotConnectedException(
            rtl::OUString::createFromAscii("LocalOutputStream: no output file."),
            *this);
    }
    return mTemporaryFile;
}
//------------------------------------------------------------------------------

void SAL_CALL LocalOutputStream::writeBytes(const uno::Sequence<sal_Int8>& aData)
    throw (io::NotConnectedException,
            io::BufferSizeExceededException,
            io::IOException, uno::RuntimeException)
{
    getOutputFile()->writeBytes(aData) ;
}
//------------------------------------------------------------------------------

void SAL_CALL LocalOutputStream::flush()
    throw (io::NotConnectedException,
            io::BufferSizeExceededException,
            io::IOException, uno::RuntimeException)
{
    getOutputFile()->flush() ;
}
//------------------------------------------------------------------------------

void SAL_CALL LocalOutputStream::closeOutput()
    throw (io::NotConnectedException, io::BufferSizeExceededException,
            io::IOException, uno::RuntimeException)
{
    if (mTemporaryFile.is())
    {
        mTemporaryFile->closeOutput() ;

        mTemporaryFile.clear();
    }
}
//------------------------------------------------------------------------------

} } // configmgr.localbe
<|endoftext|>


<commit_before>/*
  Copyright (c) 2011-2012 - Tőkés Attila
  Copyright (C) 2015 Daniel Nicoletti <dantti12@gmail.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  See the LICENSE file for more details.
*/

#include "mimepart_p.h"
#include "quotedprintable.h"

#include <QtCore/QIODevice>
#include <QtCore/QBuffer>
#include <QtCore/QDebug>

using namespace SimpleMail;

MimePart::MimePart() : d_ptr(new MimePartPrivate)
{
    qDebug() << Q_FUNC_INFO;
}

MimePart::MimePart(const MimePart &other)
{
    Q_D(MimePart);
    d->contentCharset = other.charset();

    if (d->contentDevice) {
        delete d->contentDevice;
    }
    d->contentDevice = new QBuffer;
    d->contentDevice->open(QBuffer::ReadWrite);
    d->contentDevice->write(other.content());

    d->contentId = other.contentId();
    d->contentName = other.contentName();
    d->contentType = other.contentType();
    d->contentEncoding = other.encoding();
    d->header = other.header();
}

MimePart::~MimePart()
{
    qDebug() << Q_FUNC_INFO;
}

MimePart &MimePart::operator=(const MimePart &other)
{
    Q_D(MimePart);
    d->contentCharset = other.charset();

    if (d->contentDevice) {
        delete d->contentDevice;
    }
    d->contentDevice = new QBuffer;
    d->contentDevice->open(QBuffer::ReadWrite);
    d->contentDevice->write(other.content());

    d->contentId = other.contentId();
    d->contentName = other.contentName();
    d->contentType = other.contentType();
    d->contentEncoding = other.encoding();
    d->header = other.header();

    return *this;
}

void MimePart::setContent(const QByteArray &content)
{
    Q_D(MimePart);
    if (d->contentDevice) {
        delete d->contentDevice;
    }
    d->contentDevice = new QBuffer;
    d->contentDevice->open(QBuffer::ReadWrite);
    d->contentDevice->write(content);
}

void MimePart::setHeader(const QByteArray &header)
{
    Q_D(MimePart);
    d->header = header;
}

void MimePart::addHeaderLine(const QByteArray &line)
{
    Q_D(MimePart);
    d->header.append(line + "\r\n");
}

QByteArray MimePart::header() const
{
    Q_D(const MimePart);
    return d->header;
}

QByteArray MimePart::content() const
{
    Q_D(const MimePart);
    if (d->contentDevice && d->contentDevice->seek(0)) {
        return d->contentDevice->readAll();
    }
    return QByteArray();
}

void MimePart::setContentId(const QByteArray &cId)
{
    Q_D(MimePart);
    d->contentId = cId;
}

QByteArray MimePart::contentId() const
{
    Q_D(const MimePart);
    return d->contentId;
}

void MimePart::setContentName(const QByteArray &contentName)
{
    Q_D(MimePart);
    d->contentName = contentName;
}

QByteArray MimePart::contentName() const
{
    Q_D(const MimePart);
    return d->contentName;
}

void MimePart::setContentType(const QByteArray &contentType)
{
    Q_D(MimePart);
    d->contentType = contentType;
}

QByteArray MimePart::contentType() const
{
    Q_D(const MimePart);
    return d->contentType;
}

void MimePart::setCharset(const QByteArray &charset)
{
    Q_D(MimePart);
    d->contentCharset = charset;
}

QByteArray MimePart::charset() const
{
    Q_D(const MimePart);
    return d->contentCharset;
}

void MimePart::setEncoding(Encoding enc)
{
    Q_D(MimePart);
    d->contentEncoding = enc;
}

MimePart::Encoding MimePart::encoding() const
{
    Q_D(const MimePart);
    return d->contentEncoding;
}

void MimePart::setData(const QString &data)
{
    Q_D(MimePart);

    if (d->contentDevice) {
        delete d->contentDevice;
    }
    d->contentDevice = new QBuffer;
    d->contentDevice->open(QBuffer::ReadWrite);

    switch (d->contentEncoding) {
    case _7Bit:
        d->contentDevice->write(data.toLatin1());
        break;
    case _8Bit:
    case Base64:
    case QuotedPrintable:
        d->contentDevice->write(data.toUtf8());
        break;
    }
}

QString MimePart::data() const
{
    Q_D(const MimePart);

    if (!d->contentDevice || !d->contentDevice->seek(0)) {
        return QString();
    }

    QString ret;
    switch (d->contentEncoding) {
    case _7Bit:
        ret = QString::fromLatin1(d->contentDevice->readAll());
        break;
    case _8Bit:
        ret = QString::fromUtf8(d->contentDevice->readAll());
        break;
    case Base64:
        ret = QString::fromUtf8(QByteArray::fromBase64(d->contentDevice->readAll()));
        break;
    case QuotedPrintable:
        ret = QString::fromUtf8(QuotedPrintable::decode(d->contentDevice->readAll()));
        break;
    }
    return ret;
}

MimeContentFormatter *MimePart::contentFormatter()
{
    Q_D(MimePart);
    return &d->formatter;
}

bool MimePart::write(QIODevice *device)
{
    Q_D(const MimePart);

    QByteArray headers;

    // Content-Type
    headers.append("Content-Type: " + d->contentType);
    if (!d->contentName.isEmpty()) {
        headers.append("; name=\"" + d->contentName);
    }
    if (!d->contentCharset.isEmpty()) {
        headers.append("; charset=" + d->contentCharset);
    }
    if (!d->contentBoundary.isEmpty()) {
        headers.append("; boundary=" + d->contentBoundary);
    }
    headers.append("\r\n");

    // Content-Transfer-Encoding
    switch (d->contentEncoding) {
    case _7Bit:
        headers.append("Content-Transfer-Encoding: 7bit\r\n");
        break;
    case _8Bit:
        headers.append("Content-Transfer-Encoding: 8bit\r\n");
        break;
    case Base64:
        headers.append("Content-Transfer-Encoding: base64\r\n");
        break;
    case QuotedPrintable:
        headers.append("Content-Transfer-Encoding: quoted-printable\r\n");
        break;
    }

    // Content-Id
    if (!d->contentId.isNull()) {
        headers.append("Content-ID: <" + d->contentId + ">\r\n");
    }

    // Addition header lines
    headers.append(d->header + "\r\n");

    // Write headers
    if (device->write(headers) != headers.size()) {
        return false;
    }

    // Write content data
    return writeData(device);
}

MimePart::MimePart(MimePartPrivate *d) : d_ptr(d)
{

}

bool MimePart::writeData(QIODevice *device)
{
    Q_D(MimePart);

    /* === Content === */
    QIODevice *input = d->contentDevice;
    if (!input->isOpen()) {
        if (!input->open(QIODevice::ReadOnly)) {
            return false;
        }
    } else if (!input->seek(0)) {
        return false;
    }

    switch (d->contentEncoding) {
    case MimePart::_7Bit:
    case MimePart::_8Bit:
        if (!d->writeRaw(input, device)) {
            return false;
        }
        break;
    case MimePart::Base64:
        if (!d->writeBase64(input, device)) {
            return false;
        }
        break;
    case MimePart::QuotedPrintable:
        if (!d->writeQuotedPrintable(input, device)) {
            return false;
        }
        break;
    }

    if (device->write("\r\n", 2) != 2) {
        return false;
    }

    return true;
}

MimePartPrivate *MimePart::d_func()
{
    return d_ptr.data();
}

MimePartPrivate::~MimePartPrivate()
{
    delete contentDevice;
}

bool MimePartPrivate::writeRaw(QIODevice *input, QIODevice *out)
{
    char block[4096];
    qint64 totalRead = 0;
    while (!input->atEnd()) {
        qint64 in = input->read(block, sizeof(block));
        if (in <= 0) {
            break;
        }

        totalRead += in;
        if (in != out->write(block, in)) {
            return false;
        }
    }
    return true;
}

bool MimePartPrivate::writeBase64(QIODevice *input, QIODevice *out)
{
    char block[6000]; // Must be powers of 6
    qint64 totalRead = 0;
    int chars = 0;
    while (!input->atEnd()) {
        qint64 in = input->read(block, sizeof(block));
        if (in <= 0) {
            break;
        }

        totalRead += in;
        QByteArray encoded = QByteArray(block, in).toBase64(QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals);
        encoded = formatter.format(encoded, chars);
        if (encoded.size() != out->write(encoded)) {
            return false;
        }
    }
    return true;
}

bool MimePartPrivate::writeQuotedPrintable(QIODevice *input, QIODevice *out)
{
    char block[4096];
    qint64 totalRead = 0;
    int chars = 0;
    while (!input->atEnd()) {
        qint64 in = input->read(block, sizeof(block));
        if (in <= 0) {
            break;
        }

        totalRead += in;
        QByteArray encoded = QuotedPrintable::encode(QByteArray(block, in), false);
        encoded = formatter.formatQuotedPrintable(encoded, chars);
        if (encoded.size() != out->write(encoded)) {
            return false;
        }
    }
    return true;
}
<commit_msg>Patch 1 (#21)<commit_after>/*
  Copyright (c) 2011-2012 - Tőkés Attila
  Copyright (C) 2015 Daniel Nicoletti <dantti12@gmail.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  See the LICENSE file for more details.
*/

#include "mimepart_p.h"
#include "quotedprintable.h"

#include <QtCore/QIODevice>
#include <QtCore/QBuffer>
#include <QtCore/QDebug>

using namespace SimpleMail;

MimePart::MimePart() : d_ptr(new MimePartPrivate)
{
    qDebug() << Q_FUNC_INFO;
}

MimePart::MimePart(const MimePart &other)
{
    Q_D(MimePart);
    d->contentCharset = other.charset();

    if (d->contentDevice) {
        delete d->contentDevice;
    }
    d->contentDevice = new QBuffer;
    d->contentDevice->open(QBuffer::ReadWrite);
    d->contentDevice->write(other.content());

    d->contentId = other.contentId();
    d->contentName = other.contentName();
    d->contentType = other.contentType();
    d->contentEncoding = other.encoding();
    d->header = other.header();
}

MimePart::~MimePart()
{
    qDebug() << Q_FUNC_INFO;
}

MimePart &MimePart::operator=(const MimePart &other)
{
    Q_D(MimePart);
    d->contentCharset = other.charset();

    if (d->contentDevice) {
        delete d->contentDevice;
    }
    d->contentDevice = new QBuffer;
    d->contentDevice->open(QBuffer::ReadWrite);
    d->contentDevice->write(other.content());

    d->contentId = other.contentId();
    d->contentName = other.contentName();
    d->contentType = other.contentType();
    d->contentEncoding = other.encoding();
    d->header = other.header();

    return *this;
}

void MimePart::setContent(const QByteArray &content)
{
    Q_D(MimePart);
    if (d->contentDevice) {
        delete d->contentDevice;
    }
    d->contentDevice = new QBuffer;
    d->contentDevice->open(QBuffer::ReadWrite);
    d->contentDevice->write(content);
}

void MimePart::setHeader(const QByteArray &header)
{
    Q_D(MimePart);
    d->header = header;
}

void MimePart::addHeaderLine(const QByteArray &line)
{
    Q_D(MimePart);
    d->header.append(line + "\r\n");
}

QByteArray MimePart::header() const
{
    Q_D(const MimePart);
    return d->header;
}

QByteArray MimePart::content() const
{
    Q_D(const MimePart);
    if (d->contentDevice && d->contentDevice->seek(0)) {
        return d->contentDevice->readAll();
    }
    return QByteArray();
}

void MimePart::setContentId(const QByteArray &cId)
{
    Q_D(MimePart);
    d->contentId = cId;
}

QByteArray MimePart::contentId() const
{
    Q_D(const MimePart);
    return d->contentId;
}

void MimePart::setContentName(const QByteArray &contentName)
{
    Q_D(MimePart);
    d->contentName = contentName;
}

QByteArray MimePart::contentName() const
{
    Q_D(const MimePart);
    return d->contentName;
}

void MimePart::setContentType(const QByteArray &contentType)
{
    Q_D(MimePart);
    d->contentType = contentType;
}

QByteArray MimePart::contentType() const
{
    Q_D(const MimePart);
    return d->contentType;
}

void MimePart::setCharset(const QByteArray &charset)
{
    Q_D(MimePart);
    d->contentCharset = charset;
}

QByteArray MimePart::charset() const
{
    Q_D(const MimePart);
    return d->contentCharset;
}

void MimePart::setEncoding(Encoding enc)
{
    Q_D(MimePart);
    d->contentEncoding = enc;
}

MimePart::Encoding MimePart::encoding() const
{
    Q_D(const MimePart);
    return d->contentEncoding;
}

void MimePart::setData(const QString &data)
{
    Q_D(MimePart);

    if (d->contentDevice) {
        delete d->contentDevice;
    }
    d->contentDevice = new QBuffer;
    d->contentDevice->open(QBuffer::ReadWrite);

    switch (d->contentEncoding) {
    case _7Bit:
        d->contentDevice->write(data.toLatin1());
        break;
    case _8Bit:
    case Base64:
    case QuotedPrintable:
        d->contentDevice->write(data.toUtf8());
        break;
    }
}

QString MimePart::data() const
{
    Q_D(const MimePart);

    if (!d->contentDevice || !d->contentDevice->seek(0)) {
        return QString();
    }

    QString ret;
    switch (d->contentEncoding) {
    case _7Bit:
        ret = QString::fromLatin1(d->contentDevice->readAll());
        break;
    case _8Bit:
        ret = QString::fromUtf8(d->contentDevice->readAll());
        break;
    case Base64:
        ret = QString::fromUtf8(QByteArray::fromBase64(d->contentDevice->readAll()));
        break;
    case QuotedPrintable:
        ret = QString::fromUtf8(QuotedPrintable::decode(d->contentDevice->readAll()));
        break;
    }
    return ret;
}

MimeContentFormatter *MimePart::contentFormatter()
{
    Q_D(MimePart);
    return &d->formatter;
}

bool MimePart::write(QIODevice *device)
{
    Q_D(const MimePart);

    QByteArray headers;

    // Content-Type
    headers.append("Content-Type: " + d->contentType);
    if (!d->contentName.isEmpty()) {
        headers.append("; name=\"" + d->contentName + "\"");
    }
    if (!d->contentCharset.isEmpty()) {
        headers.append("; charset=" + d->contentCharset);
    }
    if (!d->contentBoundary.isEmpty()) {
        headers.append("; boundary=" + d->contentBoundary);
    }
    headers.append("\r\n");

    // Content-Transfer-Encoding
    switch (d->contentEncoding) {
    case _7Bit:
        headers.append("Content-Transfer-Encoding: 7bit\r\n");
        break;
    case _8Bit:
        headers.append("Content-Transfer-Encoding: 8bit\r\n");
        break;
    case Base64:
        headers.append("Content-Transfer-Encoding: base64\r\n");
        break;
    case QuotedPrintable:
        headers.append("Content-Transfer-Encoding: quoted-printable\r\n");
        break;
    }

    // Content-Id
    if (!d->contentId.isNull()) {
        headers.append("Content-ID: <" + d->contentId + ">\r\n");
    }

    // Addition header lines
    headers.append(d->header + "\r\n");

    // Write headers
    if (device->write(headers) != headers.size()) {
        return false;
    }

    // Write content data
    return writeData(device);
}

MimePart::MimePart(MimePartPrivate *d) : d_ptr(d)
{

}

bool MimePart::writeData(QIODevice *device)
{
    Q_D(MimePart);

    /* === Content === */
    QIODevice *input = d->contentDevice;
    if (!input->isOpen()) {
        if (!input->open(QIODevice::ReadOnly)) {
            return false;
        }
    } else if (!input->seek(0)) {
        return false;
    }

    switch (d->contentEncoding) {
    case MimePart::_7Bit:
    case MimePart::_8Bit:
        if (!d->writeRaw(input, device)) {
            return false;
        }
        break;
    case MimePart::Base64:
        if (!d->writeBase64(input, device)) {
            return false;
        }
        break;
    case MimePart::QuotedPrintable:
        if (!d->writeQuotedPrintable(input, device)) {
            return false;
        }
        break;
    }

    if (device->write("\r\n", 2) != 2) {
        return false;
    }

    return true;
}

MimePartPrivate *MimePart::d_func()
{
    return d_ptr.data();
}

MimePartPrivate::~MimePartPrivate()
{
    delete contentDevice;
}

bool MimePartPrivate::writeRaw(QIODevice *input, QIODevice *out)
{
    char block[4096];
    qint64 totalRead = 0;
    while (!input->atEnd()) {
        qint64 in = input->read(block, sizeof(block));
        if (in <= 0) {
            break;
        }

        totalRead += in;
        if (in != out->write(block, in)) {
            return false;
        }
    }
    return true;
}

bool MimePartPrivate::writeBase64(QIODevice *input, QIODevice *out)
{
    char block[6000]; // Must be powers of 6
    qint64 totalRead = 0;
    int chars = 0;
    while (!input->atEnd()) {
        qint64 in = input->read(block, sizeof(block));
        if (in <= 0) {
            break;
        }

        totalRead += in;
        QByteArray encoded = QByteArray(block, in).toBase64(QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals);
        encoded = formatter.format(encoded, chars);
        if (encoded.size() != out->write(encoded)) {
            return false;
        }
    }
    return true;
}

bool MimePartPrivate::writeQuotedPrintable(QIODevice *input, QIODevice *out)
{
    char block[4096];
    qint64 totalRead = 0;
    int chars = 0;
    while (!input->atEnd()) {
        qint64 in = input->read(block, sizeof(block));
        if (in <= 0) {
            break;
        }

        totalRead += in;
        QByteArray encoded = QuotedPrintable::encode(QByteArray(block, in), false);
        encoded = formatter.formatQuotedPrintable(encoded, chars);
        if (encoded.size() != out->write(encoded)) {
            return false;
        }
    }
    return true;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011 Tommi Maekitalo
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "responder.h"
#include "rpcserverimpl.h"
#include <cxxtools/bin/parser.h>
#include <cxxtools/serviceprocedure.h>
#include <cxxtools/remoteexception.h>
#include <cxxtools/log.h>

log_define("cxxtools.bin.responder")

namespace cxxtools
{
namespace bin
{
Responder::~Responder()
{
    if (_proc)
        _serviceRegistry.releaseProcedure(_proc);
}

void Responder::reply(IOStream& out)
{
    log_info("send reply");

    out << '\xc1';
    _formatter.begin(out);
    _result->format(_formatter);
    _formatter.finish();
    out << '\xff';
}

void Responder::replyError(IOStream& out, const char* msg, int rc)
{
    log_info("send error \"" << msg << '"');

    out << '\xc2'
        << static_cast<char>(static_cast<uint32_t>(rc) >> 24)
        << static_cast<char>(static_cast<uint32_t>(rc) >> 16)
        << static_cast<char>(static_cast<uint32_t>(rc) >> 8)
        << static_cast<char>(static_cast<uint32_t>(rc))
        << msg
        << '\0' << '\xff';
}

bool Responder::onInput(IOStream& ios)
{
    while (ios.buffer().in_avail() > 0)
    {
        if (advance(ios.buffer().sbumpc()))
        {
            if (_failed)
            {
                replyError(ios, _errorMessage.c_str(), 0);
            }
            else
            {
                try
                {
                    _result = _proc->endCall();
                    reply(ios);
                }
                catch (const RemoteException& e)
                {
                    ios.buffer().discard();
                    replyError(ios, e.what(), e.rc());
                }
                catch (const std::exception& e)
                {
                    ios.buffer().discard();
                    replyError(ios, e.what(), 0);
                }
            }

            _serviceRegistry.releaseProcedure(_proc);
            _proc = 0;
            _args = 0;
            _result = 0;
            _state = state_0;
            _failed = false;
            _errorMessage.clear();

            return true;
        }
    }

    return false;
}

bool Responder::advance(char ch)
{
    switch (_state)
    {
        case state_0:
            if (ch == '\xc0')
                _state = state_method;
            else if (ch == '\xc3')
                _state = state_domain;
            else
                throw std::runtime_error("domain or method name expected");
            break;

        case state_domain:
            if (ch == '\0')
            {
                log_info("rpc method domain \"" << _domain << '"');
                _state = state_method;
            }
            else
                _domain += ch;
            break;

        case state_method:
            if (ch == '\0')
            {
                log_info("rpc method \"" << _methodName << '"');

                _proc = _serviceRegistry.getProcedure(_domain.empty() ? _methodName : _domain + '\0' + _methodName);

                if (_proc)
                {
                    _args = _proc->beginCall();
                    _state = state_params;
                }
                else
                {
                    _failed = true;
                    _errorMessage = "unknown method \"" + _methodName + '"';
                    _state = state_params_skip;
                }

                _methodName.clear();
                _domain.clear();
            }
            else
                _methodName += ch;
            break;

        case state_params:
            if (ch == '\xff')
            {
                if (_args && *_args)
                {
                    _failed = true;
                    _errorMessage = "argument expected";
                }

                return true;
            }
            else
            {
                if (_args == 0 || *_args == 0)
                {
                    _failed = true;
                    _errorMessage = "too many arguments";
                    _state = state_params_skip;
                }
                else
                {
                    _deserializer.begin(false);
                    _deserializer.advance(ch);
                    _state = state_param;
                }
            }
            break;

        case state_params_skip:
            if (ch == '\xff')
                return true;
            else
            {
                _deserializer.skip();
                _deserializer.advance(ch);
                _state = state_param_skip;
            }

            break;

        case state_param:
            if (_deserializer.advance(ch))
            {
                try
                {
                    (*_args)->fixup(_deserializer.si());
                    ++_args;
                    _state = state_params;
                }
                catch (const std::exception& e)
                {
                    _failed = true;
                    _errorMessage = e.what();
                    _state = state_params_skip;
                }
            }
            break;

        case state_param_skip:
            if (_deserializer.advance(ch))
                _state = state_params_skip;

            break;
    }

    return false;
}

}
}
<commit_msg>fix binary rpc server<commit_after>/*
 * Copyright (C) 2011 Tommi Maekitalo
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "responder.h"
#include "rpcserverimpl.h"
#include <cxxtools/bin/parser.h>
#include <cxxtools/serviceprocedure.h>
#include <cxxtools/remoteexception.h>
#include <cxxtools/log.h>

log_define("cxxtools.bin.responder")

namespace cxxtools
{
namespace bin
{
Responder::~Responder()
{
    if (_proc)
        _serviceRegistry.releaseProcedure(_proc);
}

void Responder::reply(IOStream& out)
{
    log_info("send reply");

    out << '\xc1';
    _formatter.begin(out);
    _result->format(_formatter);
    _formatter.finish();
    out << '\xff';
}

void Responder::replyError(IOStream& out, const char* msg, int rc)
{
    log_info("send error \"" << msg << '"');

    out << '\xc2'
        << static_cast<char>(static_cast<uint32_t>(rc) >> 24)
        << static_cast<char>(static_cast<uint32_t>(rc) >> 16)
        << static_cast<char>(static_cast<uint32_t>(rc) >> 8)
        << static_cast<char>(static_cast<uint32_t>(rc))
        << msg
        << '\0' << '\xff';
}

bool Responder::onInput(IOStream& ios)
{
    while (ios.buffer().in_avail() > 0)
    {
        if (advance(ios.buffer().sbumpc()))
        {
            if (_failed)
            {
                replyError(ios, _errorMessage.c_str(), 0);
            }
            else
            {
                try
                {
                    _result = _proc->endCall();
                    reply(ios);
                }
                catch (const RemoteException& e)
                {
                    ios.buffer().discard();
                    replyError(ios, e.what(), e.rc());
                }
                catch (const std::exception& e)
                {
                    ios.buffer().discard();
                    replyError(ios, e.what(), 0);
                }
            }

            _serviceRegistry.releaseProcedure(_proc);
            _proc = 0;
            _args = 0;
            _result = 0;
            _state = state_0;
            _failed = false;
            _errorMessage.clear();
            _deserializer.begin();

            return true;
        }
    }

    return false;
}

bool Responder::advance(char ch)
{
    switch (_state)
    {
        case state_0:
            if (ch == '\xc0')
                _state = state_method;
            else if (ch == '\xc3')
                _state = state_domain;
            else
                throw std::runtime_error("domain or method name expected");
            break;

        case state_domain:
            if (ch == '\0')
            {
                log_info("rpc method domain \"" << _domain << '"');
                _state = state_method;
            }
            else
                _domain += ch;
            break;

        case state_method:
            if (ch == '\0')
            {
                log_info("rpc method \"" << _methodName << '"');

                _proc = _serviceRegistry.getProcedure(_domain.empty() ? _methodName : _domain + '\0' + _methodName);

                if (_proc)
                {
                    _args = _proc->beginCall();
                    _state = state_params;
                }
                else
                {
                    _failed = true;
                    _errorMessage = "unknown method \"" + _methodName + '"';
                    _state = state_params_skip;
                }

                _methodName.clear();
                _domain.clear();
            }
            else
                _methodName += ch;
            break;

        case state_params:
            if (ch == '\xff')
            {
                if (_args && *_args)
                {
                    _failed = true;
                    _errorMessage = "argument expected";
                }

                return true;
            }
            else
            {
                if (_args == 0 || *_args == 0)
                {
                    _failed = true;
                    _errorMessage = "too many arguments";
                    _state = state_params_skip;
                }
                else
                {
                    _deserializer.begin(false);
                    _deserializer.advance(ch);
                    _state = state_param;
                }
            }
            break;

        case state_params_skip:
            if (ch == '\xff')
                return true;
            else
            {
                _deserializer.skip();
                _deserializer.advance(ch);
                _state = state_param_skip;
            }

            break;

        case state_param:
            if (_deserializer.advance(ch))
            {
                try
                {
                    (*_args)->fixup(_deserializer.si());
                    ++_args;
                    _state = state_params;
                }
                catch (const std::exception& e)
                {
                    _failed = true;
                    _errorMessage = e.what();
                    _state = state_params_skip;
                }
            }
            break;

        case state_param_skip:
            if (_deserializer.advance(ch))
                _state = state_params_skip;

            break;
    }

    return false;
}

}
}
<|endoftext|>


<commit_before>/**
 * v8cgi - apache module.
 * Extends v8cgi_App by adding customized initialization and (std)IO routines
 */
#include <stdlib.h>
#include <string.h>

#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h"
#include "util_script.h"
#include "apr_pools.h"

#include "apr_base64.h"
#include "apr_strings.h"

#include "js_app.h"
#include "js_path.h"
#include "js_macros.h"

typedef struct {
	const char * config;
} v8cgi_config;

/**
 * First module declaration
 */
extern "C" module AP_MODULE_DECLARE_DATA v8cgi_module;

class v8cgi_Module : public v8cgi_App {
public:
	size_t reader(char * destination, size_t amount) {
		return (size_t) ap_get_client_block(this->request, destination, amount);
	}

	size_t writer(const char * data, size_t amount) {
		if (this->output_started) {
			/* response data */
			return (size_t) ap_rwrite(data, amount, this->request);
		} else {
			/* header or content separator */
			char * end = strchr((char *) data, '\r');
			if (!end) { end = strchr((char *) data, '\n'); }
			/* header or separator must end with a newline */
			if (!end) { return 0; }

			if (end == data) {
				/* content separator */
				this->output_started = true;
				return 0;
			} else {
				/* header */
				char * colon = strchr((char *) data, ':');
				/* header without colon is a bad header */
				if (!colon) { return 0; }
				size_t namelen = colon - data;

				/* skip space after colon */
				if ((size_t) (colon-data+1) < amount && *(colon+1) == ' ') { colon++; }

				size_t valuelen = end - colon - 1;
				char * name = (char *) apr_palloc(request->pool, namelen + 1);
				char * value = (char *) apr_palloc(request->pool, valuelen + 1);
				name[namelen] = '\0';
				value[valuelen] = '\0';
				strncpy(name, data, namelen);
				strncpy(value, colon+1, valuelen);

				this->header(name, value);
				return (size_t) amount;
			}
		}
	}

	/**
	 * We log errors to apache errorlog
	 */
	void error(const char * data, const char * file, int line) {
		ap_log_rerror(file, line, APLOG_ERR, 0, this->request, "%s", data);
	}

	/**
	 * Remember apache request structure and continue as usually
	 */
	int execute(request_rec * request, char ** envp) {
		this->output_started = false;
		this->request = request;
		this->mainfile = std::string(request->filename);
		path_chdir(path_dirname(this->mainfile));
		return v8cgi_App::execute(envp);
	}

	void init(v8cgi_config * cfg) {
		v8cgi_App::init();
		this->cfgfile = cfg->config;
	}

private:
	request_rec * request;
	bool output_started;

	/**
	 * Set a HTTP response header
	 * @param {char *} name
	 * @param {char *} value
	 */
	void header(const char * name, const char * value) {
		if (strcasecmp(name, "content-type") == 0) {
			char * ct =  (char *) apr_palloc(request->pool, strlen(value)+1);
			strcpy(ct, value);
			this->request->content_type = ct;
		} else if (strcasecmp(name, "status") == 0) {
			char * line = (char *) apr_palloc(request->pool, strlen(value)+1);
			strcpy(line, value);
			this->request->status_line = line;
			this->request->status = atoi(value);
		} else {
			apr_table_set(this->request->headers_out, name, value);
		}
	}
};

static v8cgi_Module app;

/**
 * This is called from Apache every time request arrives
 */
static int mod_v8cgi_handler(request_rec *r) {
	const apr_array_header_t *arr;
	const apr_table_entry_t *elts;

	if (!r->handler || strcmp(r->handler, "v8cgi-script")) { return DECLINED; }

	ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
	ap_add_common_vars(r);
	ap_add_cgi_vars(r);

    if (r->headers_in) {
		const char *auth;
		auth = apr_table_get(r->headers_in, "Authorization");
		if (auth && auth[0] != 0 && strncmp(auth, "Basic ", 6) == 0) {

		    char *user = NULL;
		    char *pass = NULL;
		    int length;

	    	user = (char *)apr_palloc(r->pool, apr_base64_decode_len(auth+6) + 1);
	    	length = apr_base64_decode(user, auth + 6);

	    	/* Null-terminate the string. */
	    	user[length] = '\0';

		    if (user) {
				pass = strchr(user, ':');
				if (pass) {
				    *pass++ = '\0';

				    apr_table_setn(r->subprocess_env, "AUTH_USER", user);
				    apr_table_setn(r->subprocess_env, "AUTH_PW", pass);
				}
		    }
		}
    }

	/* extract the CGI environment  */
	arr = apr_table_elts(r->subprocess_env);
	elts = (const apr_table_entry_t*) arr->elts;

	char ** envp = new char *[arr->nelts + 1];
	envp[arr->nelts] = NULL;

	size_t size = 0;
	size_t len1 = 0;
	size_t len2 = 0;
	for (int i=0;i<arr->nelts;i++) {
		len1 = strlen(elts[i].key);
		len2 = strlen(elts[i].val);
		size = len1 + len2 + 2;

		envp[i] = new char[size];
		envp[i][size-1] = '\0';
		envp[i][len1] = '=';

		strncpy(envp[i], elts[i].key, len1);
		strncpy(&(envp[i][len1+1]), elts[i].val, len2);
	}

	app.execute(r, envp);

	for (int i=0;i<arr->nelts;i++) {
		free(envp[i]);
	}
	free(envp);

//  Ok is safer, because HTTP_INTERNAL_SERVER_ERROR overwrites any content already generated
//	if (result) {
//		return HTTP_INTERNAL_SERVER_ERROR;
//	} else {
		return OK;
//	}
}

/**
 * Module initialization
 */
static int mod_v8cgi_init_handler(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) {
	ap_add_version_component(p, "mod_v8cgi");
	v8cgi_config * cfg = (v8cgi_config *) ap_get_module_config(s->module_config, &v8cgi_module);
	app.init(cfg);
    return OK;
}

/**
 * Child initialization
 * FIXME: what is this for?
 */
static void mod_v8cgi_child_init(apr_pool_t *p, server_rec *s) {
}

/**
 * Register relevant hooks
 */
static void mod_v8cgi_register_hooks(apr_pool_t *p ) {
	ap_hook_handler(mod_v8cgi_handler, NULL, NULL, APR_HOOK_MIDDLE);
	ap_hook_post_config(mod_v8cgi_init_handler, NULL, NULL, APR_HOOK_MIDDLE);
	ap_hook_child_init(mod_v8cgi_child_init, NULL, NULL, APR_HOOK_MIDDLE);
}

/**
 * Create initial configuration values
 */
static void * mod_v8cgi_create_config(apr_pool_t *p, server_rec *s) {
	v8cgi_config * newcfg = (v8cgi_config *) apr_pcalloc(p, sizeof(v8cgi_config));
	newcfg->config = STRING(CONFIG_PATH);
	return (void *) newcfg;
}

/**
 * Callback executed for every configuration change
 */
static const char * set_v8cgi_config(cmd_parms * parms, void * mconfig, const char * arg) {
	v8cgi_config * cfg = (v8cgi_config *) ap_get_module_config(parms->server->module_config, &v8cgi_module);
	cfg->config = (char *) arg;
	return NULL;
}

typedef const char * (* CONFIG_HANDLER) ();
/* list of configurations */
static const command_rec mod_v8cgi_cmds[] = {
	AP_INIT_TAKE1(
		"v8cgi_Config",
		(CONFIG_HANDLER) set_v8cgi_config,
		NULL,
		RSRC_CONF,
		"Path to v8cgi configuration file."
	),
	{NULL}
};

/**
 * Module (re-)declaration
 */
extern "C" {
	module AP_MODULE_DECLARE_DATA v8cgi_module = {
		STANDARD20_MODULE_STUFF,
		NULL,
		NULL,
		mod_v8cgi_create_config,
		NULL,
		mod_v8cgi_cmds,
		mod_v8cgi_register_hooks,
	};
}
<commit_msg>issue #44, thanks to neocoder<commit_after>/**
 * v8cgi - apache module.
 * Extends v8cgi_App by adding customized initialization and (std)IO routines
 */
#include <stdlib.h>
#include <string.h>

#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h"
#include "util_script.h"
#include "apr_pools.h"

#include "apr_base64.h"
#include "apr_strings.h"

#include "js_app.h"
#include "js_path.h"
#include "js_macros.h"

typedef struct {
	const char * config;
} v8cgi_config;

/**
 * First module declaration
 */
extern "C" module AP_MODULE_DECLARE_DATA v8cgi_module;

class v8cgi_Module : public v8cgi_App {
public:
	size_t reader(char * destination, size_t amount) {
		return (size_t) ap_get_client_block(this->request, destination, amount);
	}

	size_t writer(const char * data, size_t amount) {
		if (this->output_started) {
			/* response data */
			return (size_t) ap_rwrite(data, amount, this->request);
		} else {
			/* header or content separator */
			char * end = strchr((char *) data, '\r');
			if (!end) { end = strchr((char *) data, '\n'); }
			/* header or separator must end with a newline */
			if (!end) { return 0; }

			if (end == data) {
				/* content separator */
				this->output_started = true;
				return 0;
			} else {
				/* header */
				char * colon = strchr((char *) data, ':');
				/* header without colon is a bad header */
				if (!colon) { return 0; }
				size_t namelen = colon - data;

				/* skip space after colon */
				if ((size_t) (colon-data+1) < amount && *(colon+1) == ' ') { colon++; }

				size_t valuelen = end - colon - 1;
				char * name = (char *) apr_palloc(request->pool, namelen + 1);
				char * value = (char *) apr_palloc(request->pool, valuelen + 1);
				name[namelen] = '\0';
				value[valuelen] = '\0';
				strncpy(name, data, namelen);
				strncpy(value, colon+1, valuelen);

				this->header(name, value);
				return (size_t) amount;
			}
		}
	}

	/**
	 * We log errors to apache errorlog
	 */
	void error(const char * data, const char * file, int line) {
		ap_log_rerror(file, line, APLOG_ERR, 0, this->request, "%s", data);
	}

	/**
	 * Remember apache request structure and continue as usually
	 */
	int execute(request_rec * request, char ** envp) {
		this->output_started = false;
		this->request = request;
		this->mainfile = std::string(request->filename);
		path_chdir(path_dirname(this->mainfile));
		return v8cgi_App::execute(envp);
	}

	void init(v8cgi_config * cfg) {
		v8cgi_App::init();
		this->cfgfile = cfg->config;
	}

private:
	request_rec * request;
	bool output_started;

	/**
	 * Set a HTTP response header
	 * @param {char *} name
	 * @param {char *} value
	 */
	void header(const char * name, const char * value) {
		if (strcasecmp(name, "content-type") == 0) {
			char * ct =  (char *) apr_palloc(request->pool, strlen(value)+1);
			strcpy(ct, value);
			this->request->content_type = ct;
		} else if (strcasecmp(name, "status") == 0) {
			char * line = (char *) apr_palloc(request->pool, strlen(value)+1);
			strcpy(line, value);
			this->request->status_line = line;
			this->request->status = atoi(value);
		} else {
			apr_table_addn(this->request->headers_out, name, value);
		}
	}
};

static v8cgi_Module app;

/**
 * This is called from Apache every time request arrives
 */
static int mod_v8cgi_handler(request_rec *r) {
	const apr_array_header_t *arr;
	const apr_table_entry_t *elts;

	if (!r->handler || strcmp(r->handler, "v8cgi-script")) { return DECLINED; }

	ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
	ap_add_common_vars(r);
	ap_add_cgi_vars(r);

    if (r->headers_in) {
		const char *auth;
		auth = apr_table_get(r->headers_in, "Authorization");
		if (auth && auth[0] != 0 && strncmp(auth, "Basic ", 6) == 0) {

		    char *user = NULL;
		    char *pass = NULL;
		    int length;

	    	user = (char *)apr_palloc(r->pool, apr_base64_decode_len(auth+6) + 1);
	    	length = apr_base64_decode(user, auth + 6);

	    	/* Null-terminate the string. */
	    	user[length] = '\0';

		    if (user) {
				pass = strchr(user, ':');
				if (pass) {
				    *pass++ = '\0';

				    apr_table_setn(r->subprocess_env, "AUTH_USER", user);
				    apr_table_setn(r->subprocess_env, "AUTH_PW", pass);
				}
		    }
		}
    }

	/* extract the CGI environment  */
	arr = apr_table_elts(r->subprocess_env);
	elts = (const apr_table_entry_t*) arr->elts;

	char ** envp = new char *[arr->nelts + 1];
	envp[arr->nelts] = NULL;

	size_t size = 0;
	size_t len1 = 0;
	size_t len2 = 0;
	for (int i=0;i<arr->nelts;i++) {
		len1 = strlen(elts[i].key);
		len2 = strlen(elts[i].val);
		size = len1 + len2 + 2;

		envp[i] = new char[size];
		envp[i][size-1] = '\0';
		envp[i][len1] = '=';

		strncpy(envp[i], elts[i].key, len1);
		strncpy(&(envp[i][len1+1]), elts[i].val, len2);
	}

	app.execute(r, envp);

	for (int i=0;i<arr->nelts;i++) {
		free(envp[i]);
	}
	free(envp);

//  Ok is safer, because HTTP_INTERNAL_SERVER_ERROR overwrites any content already generated
//	if (result) {
//		return HTTP_INTERNAL_SERVER_ERROR;
//	} else {
		return OK;
//	}
}

/**
 * Module initialization
 */
static int mod_v8cgi_init_handler(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) {
	ap_add_version_component(p, "mod_v8cgi");
	v8cgi_config * cfg = (v8cgi_config *) ap_get_module_config(s->module_config, &v8cgi_module);
	app.init(cfg);
    return OK;
}

/**
 * Child initialization
 * FIXME: what is this for?
 */
static void mod_v8cgi_child_init(apr_pool_t *p, server_rec *s) {
}

/**
 * Register relevant hooks
 */
static void mod_v8cgi_register_hooks(apr_pool_t *p ) {
	ap_hook_handler(mod_v8cgi_handler, NULL, NULL, APR_HOOK_MIDDLE);
	ap_hook_post_config(mod_v8cgi_init_handler, NULL, NULL, APR_HOOK_MIDDLE);
	ap_hook_child_init(mod_v8cgi_child_init, NULL, NULL, APR_HOOK_MIDDLE);
}

/**
 * Create initial configuration values
 */
static void * mod_v8cgi_create_config(apr_pool_t *p, server_rec *s) {
	v8cgi_config * newcfg = (v8cgi_config *) apr_pcalloc(p, sizeof(v8cgi_config));
	newcfg->config = STRING(CONFIG_PATH);
	return (void *) newcfg;
}

/**
 * Callback executed for every configuration change
 */
static const char * set_v8cgi_config(cmd_parms * parms, void * mconfig, const char * arg) {
	v8cgi_config * cfg = (v8cgi_config *) ap_get_module_config(parms->server->module_config, &v8cgi_module);
	cfg->config = (char *) arg;
	return NULL;
}

typedef const char * (* CONFIG_HANDLER) ();
/* list of configurations */
static const command_rec mod_v8cgi_cmds[] = {
	AP_INIT_TAKE1(
		"v8cgi_Config",
		(CONFIG_HANDLER) set_v8cgi_config,
		NULL,
		RSRC_CONF,
		"Path to v8cgi configuration file."
	),
	{NULL}
};

/**
 * Module (re-)declaration
 */
extern "C" {
	module AP_MODULE_DECLARE_DATA v8cgi_module = {
		STANDARD20_MODULE_STUFF,
		NULL,
		NULL,
		mod_v8cgi_create_config,
		NULL,
		mod_v8cgi_cmds,
		mod_v8cgi_register_hooks,
	};
}
<|endoftext|>


<commit_before>/* This file is part of the KDE libraries
   Copyright (C) 2002, 2003, 2004 Anders Lund <anders.lund@lund.tdcadsl.dk>
   Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "katebookmarks.h"
#include "katebookmarks.moc"

#include "katedocument.h"
#include "kateview.h"

#include <klocale.h>
#include <kaction.h>
#include <kicon.h>
#include <kmenu.h>
#include <kstringhandler.h>
#include <kxmlguiclient.h>
#include <kxmlguifactory.h>
#include <ktoggleaction.h>
#include <kactionmenu.h>

#include <QRegExp>
#include <QEvent>
#include <QVector>

namespace KTextEditor{ class Document; }

/**
   Utility: selection sort
   sort a QMemArray<uint> in ascending order.
   max it the largest (zerobased) index to sort.
   To sort the entire array: ssort( *array, array.size() -1 );
   This is only efficient if ran only once.
*/
static void ssort( QVector<uint> &a, int max )
{
  uint tmp, j, maxpos;
  for ( uint h = max; h >= 1; h-- )
  {
    maxpos = 0;
    for ( j = 0; j <= h; j++ )
      maxpos = a[j] > a[maxpos] ? j : maxpos;
    tmp = a[maxpos];
    a[maxpos] = a[h];
    a[h] = tmp;
  }
}

// TODO add a insort() or bubble_sort - more efficient for aboutToShow() ?

KateBookmarks::KateBookmarks( KateView* view, Sorting sort )
  : QObject( view )
  , m_view( view )
  , m_sorting( sort )
{
  setObjectName( "kate bookmarks" );
  connect (view->doc(), SIGNAL( marksChanged( KTextEditor::Document* ) ), this, SLOT( marksChanged() ));
  _tries=0;
  m_bookmarksMenu = 0L;
}

KateBookmarks::~KateBookmarks()
{
}

void KateBookmarks::createActions( KActionCollection* ac )
{
  m_bookmarkToggle = new KToggleAction( i18n("Set &Bookmark"), ac, "bookmarks_toggle" );
  m_bookmarkToggle->setIcon( KIcon( "bookmark" ) );
  m_bookmarkToggle->setShortcut( Qt::CTRL+Qt::Key_B );
  m_bookmarkToggle->setWhatsThis(i18n("If a line has no bookmark then add one, otherwise remove it."));
  m_bookmarkToggle->setCheckedState( i18n("Clear &Bookmark") );
  connect( m_bookmarkToggle, SIGNAL( triggered() ), this, SLOT(toggleBookmark()) );

  m_bookmarkClear = new KAction( i18n("Clear &All Bookmarks"), ac, "bookmarks_clear");
  m_bookmarkClear->setWhatsThis(i18n("Remove all bookmarks of the current document."));
  connect( m_bookmarkClear, SIGNAL( triggered() ), this, SLOT(clearBookmarks()) );

  m_goNext = new KAction( i18n("Next Bookmark"), ac, "bookmarks_next");
  m_goNext->setIcon( KIcon( "next" ) );
  m_goNext->setShortcut( Qt::ALT + Qt::Key_PageDown );
  m_goNext->setWhatsThis(i18n("Go to the next bookmark."));
  connect( m_goNext, SIGNAL( triggered() ), this, SLOT(goNext()) );

  m_goPrevious = new KAction( i18n("Previous Bookmark"), ac, "bookmarks_previous");
  m_goPrevious->setIcon( KIcon( "previous" ) );
  m_goPrevious->setShortcut( Qt::ALT + Qt::Key_PageUp );
  m_goPrevious->setWhatsThis(i18n("Go to the previous bookmark."));
  connect( m_goPrevious, SIGNAL( triggered() ), this, SLOT(goPrevious()) );

  m_bookmarksMenu = (new KActionMenu(i18n("&Bookmarks"), ac, "bookmarks"))->kMenu();

  connect( m_bookmarksMenu, SIGNAL(aboutToShow()), this, SLOT(bookmarkMenuAboutToShow()));

  marksChanged ();

  // Always want the actions with shortcuts plugged into something so their shortcuts can work
  m_view->addAction(m_bookmarkToggle);
  m_view->addAction(m_bookmarkClear);
  m_view->addAction(m_goNext);
  m_view->addAction(m_goPrevious);
}

void KateBookmarks::toggleBookmark ()
{
  uint mark = m_view->doc()->mark( m_view->cursorPosition().line() );
  if( mark & KTextEditor::MarkInterface::markType01 )
    m_view->doc()->removeMark( m_view->cursorPosition().line(),
        KTextEditor::MarkInterface::markType01 );
  else
    m_view->doc()->addMark( m_view->cursorPosition().line(),
        KTextEditor::MarkInterface::markType01 );
}

void KateBookmarks::clearBookmarks ()
{
  QHash<int, KTextEditor::Mark*> m = m_view->doc()->marks();
  for (QHash<int, KTextEditor::Mark*>::const_iterator i = m.constBegin(); i != m.constEnd(); ++i)
    m_view->doc()->removeMark( i.value()->line, KTextEditor::MarkInterface::markType01 );

  // just to be sure ;)
  // dominik: the following line can be deleted afaics, as Document::removeMark emits this signal.
  marksChanged ();
}

void KateBookmarks::insertBookmarks( QMenu& menu )
{
  int line = m_view->cursorPosition().line();
  const QRegExp re("&(?!&)");
  int idx( -1 );
  KTextEditor::Mark *next = 0;
  KTextEditor::Mark *prev = 0;

  const QHash<int, KTextEditor::Mark*> &m = m_view->doc()->marks();
  QVector<uint> sortArray( m.size() );

  if ( !m.isEmpty() )
    menu.addSeparator();

  int i = 0;
  QAction* firstNewAction = 0L;
  for (QHash<int, KTextEditor::Mark*>::const_iterator it = m.constBegin(); it != m.constEnd(); ++it, ++i)
  {
    if( it.value()->type & KTextEditor::MarkInterface::markType01 )
    {
      QString bText = KStringHandler::rEmSqueeze
                      ( m_view->doc()->line( it.value()->line ),
                        menu.fontMetrics(), 32 );
      bText.replace(re, "&&"); // kill undesired accellerators!
      bText.replace('\t', ' '); // kill tabs, as they are interpreted as shortcuts

      QAction *before=0;
      if ( m_sorting == Position )
      {
        sortArray[i] = it.value()->line;
        ssort( sortArray, i );

        for (int i=0; i < sortArray.size(); ++i)
        {
          if ((int)sortArray[i] == it.value()->line)
          {
            idx = i + 3;
            if (idx>=menu.actions().size()) before=0;
            else before=menu.actions()[idx];
            break;
          }
        }
      }

      if (before) {
        QAction *a=new QAction(QString("%1 - \"%2\"").arg( it.value()->line+1 ).arg( bText ),&menu);
        menu.insertAction(before,a);
        connect(a,SIGNAL(activated()),this,SLOT(gotoLine()));
        a->setData(it.value()->line);
        if (!firstNewAction) firstNewAction = a;

      } else {
        QAction* a = menu.addAction(QString("%1 - \"%2\"").arg( it.value()->line+1 ).arg( bText ), this, SLOT(gotoLine()));
        a->setData(it.value()->line);
        if (!firstNewAction) firstNewAction = a;
      }

      if ( it.value()->line < line )
      {
        if ( ! prev || prev->line < it.value()->line )
          prev = (*it);
      }

      else if ( it.value()->line > line )
      {
        if ( ! next || next->line > it.value()->line )
          next = it.value();
      }
    }
  }

  if ( next )
  {
    m_goNext->setText( i18n("&Next: %1 - \"%2\"",  next->line + 1 ,
          KStringHandler::rsqueeze( m_view->doc()->line( next->line ), 24 ) ) );
    menu.insertAction(firstNewAction, m_goNext);
    firstNewAction = m_goNext;
  }
  if ( prev )
  {
    m_goPrevious->setText( i18n("&Previous: %1 - \"%2\"", prev->line + 1 ,
          KStringHandler::rsqueeze( m_view->doc()->line( prev->line ), 24 ) ) );
    menu.insertAction(firstNewAction, m_goPrevious);
    firstNewAction = m_goPrevious;
  }

  if ( next || prev )
    menu.insertSeparator(firstNewAction);
}

void KateBookmarks::gotoLine()
{
  if (!sender()) return;
  gotoLine(((QAction*)(sender()))->data().toInt());
}

void KateBookmarks::gotoLine (int line)
{
  m_view->setCursorPosition(KTextEditor::Cursor(line, 0));
}

void KateBookmarks::bookmarkMenuAboutToShow()
{
  m_bookmarksMenu->clear();
  m_bookmarkToggle->setChecked( m_view->doc()->mark( m_view->cursorPosition().line() )
                                & KTextEditor::MarkInterface::markType01 );
  m_bookmarksMenu->addAction(m_bookmarkToggle);
  m_bookmarksMenu->addAction(m_bookmarkClear);

  m_goNext->setText( i18n("Next Bookmark") );
  m_goPrevious->setText( i18n("Previous Bookmark") );

  insertBookmarks(*m_bookmarksMenu);
}

void KateBookmarks::goNext()
{
  const QHash<int, KTextEditor::Mark*> &m = m_view->doc()->marks();
  if (m.isEmpty())
    return;

  int line = m_view->cursorPosition().line();
  int found = -1;

  for (QHash<int, KTextEditor::Mark*>::const_iterator it = m.constBegin(); it != m.constEnd(); ++it)
  {
    if ( (it.value()->line > line) && ((found == -1) || (found > it.value()->line)) )
      found = it.value()->line;
  }

  if (found != -1)
    gotoLine ( found );
}

void KateBookmarks::goPrevious()
{
  const QHash<int, KTextEditor::Mark*> &m = m_view->doc()->marks();
  if (m.isEmpty())
    return;

  int line = m_view->cursorPosition().line();
  int found = -1;

  for (QHash<int, KTextEditor::Mark*>::const_iterator it = m.constBegin(); it != m.constEnd(); ++it)
  {
    if ((it.value()->line < line) && ((found == -1) || (found < it.value()->line)))
      found = it.value()->line;
  }

  if (found != -1)
    gotoLine ( found );
}

void KateBookmarks::marksChanged ()
{
  m_bookmarkClear->setEnabled( !m_view->doc()->marks().isEmpty() );
}

// kate: space-indent on; indent-width 2; replace-tabs on;
<commit_msg>bah, missed this one<commit_after>/* This file is part of the KDE libraries
   Copyright (C) 2002, 2003, 2004 Anders Lund <anders.lund@lund.tdcadsl.dk>
   Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "katebookmarks.h"
#include "katebookmarks.moc"

#include "katedocument.h"
#include "kateview.h"

#include <klocale.h>
#include <kaction.h>
#include <kguiitem.h>
#include <kicon.h>
#include <kmenu.h>
#include <kstringhandler.h>
#include <kxmlguiclient.h>
#include <kxmlguifactory.h>
#include <ktoggleaction.h>
#include <kactionmenu.h>

#include <QRegExp>
#include <QEvent>
#include <QVector>

namespace KTextEditor{ class Document; }

/**
   Utility: selection sort
   sort a QMemArray<uint> in ascending order.
   max it the largest (zerobased) index to sort.
   To sort the entire array: ssort( *array, array.size() -1 );
   This is only efficient if ran only once.
*/
static void ssort( QVector<uint> &a, int max )
{
  uint tmp, j, maxpos;
  for ( uint h = max; h >= 1; h-- )
  {
    maxpos = 0;
    for ( j = 0; j <= h; j++ )
      maxpos = a[j] > a[maxpos] ? j : maxpos;
    tmp = a[maxpos];
    a[maxpos] = a[h];
    a[h] = tmp;
  }
}

// TODO add a insort() or bubble_sort - more efficient for aboutToShow() ?

KateBookmarks::KateBookmarks( KateView* view, Sorting sort )
  : QObject( view )
  , m_view( view )
  , m_sorting( sort )
{
  setObjectName( "kate bookmarks" );
  connect (view->doc(), SIGNAL( marksChanged( KTextEditor::Document* ) ), this, SLOT( marksChanged() ));
  _tries=0;
  m_bookmarksMenu = 0L;
}

KateBookmarks::~KateBookmarks()
{
}

void KateBookmarks::createActions( KActionCollection* ac )
{
  m_bookmarkToggle = new KToggleAction( i18n("Set &Bookmark"), ac, "bookmarks_toggle" );
  m_bookmarkToggle->setIcon( KIcon( "bookmark" ) );
  m_bookmarkToggle->setShortcut( Qt::CTRL+Qt::Key_B );
  m_bookmarkToggle->setWhatsThis(i18n("If a line has no bookmark then add one, otherwise remove it."));
  m_bookmarkToggle->setCheckedState( i18n("Clear &Bookmark") );
  connect( m_bookmarkToggle, SIGNAL( triggered() ), this, SLOT(toggleBookmark()) );

  m_bookmarkClear = new KAction( i18n("Clear &All Bookmarks"), ac, "bookmarks_clear");
  m_bookmarkClear->setWhatsThis(i18n("Remove all bookmarks of the current document."));
  connect( m_bookmarkClear, SIGNAL( triggered() ), this, SLOT(clearBookmarks()) );

  m_goNext = new KAction( i18n("Next Bookmark"), ac, "bookmarks_next");
  m_goNext->setIcon( KIcon( "next" ) );
  m_goNext->setShortcut( Qt::ALT + Qt::Key_PageDown );
  m_goNext->setWhatsThis(i18n("Go to the next bookmark."));
  connect( m_goNext, SIGNAL( triggered() ), this, SLOT(goNext()) );

  m_goPrevious = new KAction( i18n("Previous Bookmark"), ac, "bookmarks_previous");
  m_goPrevious->setIcon( KIcon( "previous" ) );
  m_goPrevious->setShortcut( Qt::ALT + Qt::Key_PageUp );
  m_goPrevious->setWhatsThis(i18n("Go to the previous bookmark."));
  connect( m_goPrevious, SIGNAL( triggered() ), this, SLOT(goPrevious()) );

  m_bookmarksMenu = (new KActionMenu(i18n("&Bookmarks"), ac, "bookmarks"))->kMenu();

  connect( m_bookmarksMenu, SIGNAL(aboutToShow()), this, SLOT(bookmarkMenuAboutToShow()));

  marksChanged ();

  // Always want the actions with shortcuts plugged into something so their shortcuts can work
  m_view->addAction(m_bookmarkToggle);
  m_view->addAction(m_bookmarkClear);
  m_view->addAction(m_goNext);
  m_view->addAction(m_goPrevious);
}

void KateBookmarks::toggleBookmark ()
{
  uint mark = m_view->doc()->mark( m_view->cursorPosition().line() );
  if( mark & KTextEditor::MarkInterface::markType01 )
    m_view->doc()->removeMark( m_view->cursorPosition().line(),
        KTextEditor::MarkInterface::markType01 );
  else
    m_view->doc()->addMark( m_view->cursorPosition().line(),
        KTextEditor::MarkInterface::markType01 );
}

void KateBookmarks::clearBookmarks ()
{
  QHash<int, KTextEditor::Mark*> m = m_view->doc()->marks();
  for (QHash<int, KTextEditor::Mark*>::const_iterator i = m.constBegin(); i != m.constEnd(); ++i)
    m_view->doc()->removeMark( i.value()->line, KTextEditor::MarkInterface::markType01 );

  // just to be sure ;)
  // dominik: the following line can be deleted afaics, as Document::removeMark emits this signal.
  marksChanged ();
}

void KateBookmarks::insertBookmarks( QMenu& menu )
{
  int line = m_view->cursorPosition().line();
  const QRegExp re("&(?!&)");
  int idx( -1 );
  KTextEditor::Mark *next = 0;
  KTextEditor::Mark *prev = 0;

  const QHash<int, KTextEditor::Mark*> &m = m_view->doc()->marks();
  QVector<uint> sortArray( m.size() );

  if ( !m.isEmpty() )
    menu.addSeparator();

  int i = 0;
  QAction* firstNewAction = 0L;
  for (QHash<int, KTextEditor::Mark*>::const_iterator it = m.constBegin(); it != m.constEnd(); ++it, ++i)
  {
    if( it.value()->type & KTextEditor::MarkInterface::markType01 )
    {
      QString bText = KStringHandler::rEmSqueeze
                      ( m_view->doc()->line( it.value()->line ),
                        menu.fontMetrics(), 32 );
      bText.replace(re, "&&"); // kill undesired accellerators!
      bText.replace('\t', ' '); // kill tabs, as they are interpreted as shortcuts

      QAction *before=0;
      if ( m_sorting == Position )
      {
        sortArray[i] = it.value()->line;
        ssort( sortArray, i );

        for (int i=0; i < sortArray.size(); ++i)
        {
          if ((int)sortArray[i] == it.value()->line)
          {
            idx = i + 3;
            if (idx>=menu.actions().size()) before=0;
            else before=menu.actions()[idx];
            break;
          }
        }
      }

      if (before) {
        QAction *a=new QAction(QString("%1 - \"%2\"").arg( it.value()->line+1 ).arg( bText ),&menu);
        menu.insertAction(before,a);
        connect(a,SIGNAL(activated()),this,SLOT(gotoLine()));
        a->setData(it.value()->line);
        if (!firstNewAction) firstNewAction = a;

      } else {
        QAction* a = menu.addAction(QString("%1 - \"%2\"").arg( it.value()->line+1 ).arg( bText ), this, SLOT(gotoLine()));
        a->setData(it.value()->line);
        if (!firstNewAction) firstNewAction = a;
      }

      if ( it.value()->line < line )
      {
        if ( ! prev || prev->line < it.value()->line )
          prev = (*it);
      }

      else if ( it.value()->line > line )
      {
        if ( ! next || next->line > it.value()->line )
          next = it.value();
      }
    }
  }

  if ( next )
  {
    m_goNext->setText( i18n("&Next: %1 - \"%2\"",  next->line + 1 ,
          KStringHandler::rsqueeze( m_view->doc()->line( next->line ), 24 ) ) );
    menu.insertAction(firstNewAction, m_goNext);
    firstNewAction = m_goNext;
  }
  if ( prev )
  {
    m_goPrevious->setText( i18n("&Previous: %1 - \"%2\"", prev->line + 1 ,
          KStringHandler::rsqueeze( m_view->doc()->line( prev->line ), 24 ) ) );
    menu.insertAction(firstNewAction, m_goPrevious);
    firstNewAction = m_goPrevious;
  }

  if ( next || prev )
    menu.insertSeparator(firstNewAction);
}

void KateBookmarks::gotoLine()
{
  if (!sender()) return;
  gotoLine(((QAction*)(sender()))->data().toInt());
}

void KateBookmarks::gotoLine (int line)
{
  m_view->setCursorPosition(KTextEditor::Cursor(line, 0));
}

void KateBookmarks::bookmarkMenuAboutToShow()
{
  m_bookmarksMenu->clear();
  m_bookmarkToggle->setChecked( m_view->doc()->mark( m_view->cursorPosition().line() )
                                & KTextEditor::MarkInterface::markType01 );
  m_bookmarksMenu->addAction(m_bookmarkToggle);
  m_bookmarksMenu->addAction(m_bookmarkClear);

  m_goNext->setText( i18n("Next Bookmark") );
  m_goPrevious->setText( i18n("Previous Bookmark") );

  insertBookmarks(*m_bookmarksMenu);
}

void KateBookmarks::goNext()
{
  const QHash<int, KTextEditor::Mark*> &m = m_view->doc()->marks();
  if (m.isEmpty())
    return;

  int line = m_view->cursorPosition().line();
  int found = -1;

  for (QHash<int, KTextEditor::Mark*>::const_iterator it = m.constBegin(); it != m.constEnd(); ++it)
  {
    if ( (it.value()->line > line) && ((found == -1) || (found > it.value()->line)) )
      found = it.value()->line;
  }

  if (found != -1)
    gotoLine ( found );
}

void KateBookmarks::goPrevious()
{
  const QHash<int, KTextEditor::Mark*> &m = m_view->doc()->marks();
  if (m.isEmpty())
    return;

  int line = m_view->cursorPosition().line();
  int found = -1;

  for (QHash<int, KTextEditor::Mark*>::const_iterator it = m.constBegin(); it != m.constEnd(); ++it)
  {
    if ((it.value()->line < line) && ((found == -1) || (found < it.value()->line)))
      found = it.value()->line;
  }

  if (found != -1)
    gotoLine ( found );
}

void KateBookmarks::marksChanged ()
{
  m_bookmarkClear->setEnabled( !m_view->doc()->marks().isEmpty() );
}

// kate: space-indent on; indent-width 2; replace-tabs on;
<|endoftext|>


<commit_before>//!@todo: feature: Thread id keyword & attribute.
//!@todo: feature: Verbosity filter from file.(2d)
//!@todo: api: Ability to set global attribute mapper (?)
//!@todo: api: Microseconds support in timestamps.(1-2h)

//!@todo: benchmark: File logging comparing with boost::log.
//!@todo: benchmark: Socket logging with json.

//!@todo: files_t: Make file naming by pattern.
//!@todo: files_t: logrotate support - ability to handle signals to reopen current file (SIGHUP) (?).

//!@todo: performance: Experiment with std::ostringstream or format library for performance check.
//!@todo: performance: Current naive implementation of timestamp formatter is suck and have large performance troubles. Fix it.

//!@todo: api: Make fallback logger. Make it configurable.
//!@todo: aux: Make internal exception class with attribute keeping, e.g. line, file or path.
//!@todo: api: Maybe squish repository_t::init and ::configure methods?
//!@todo: msgpack_t: Attribute mappings.
//!@todo: socket_t: Make asynchronous TCP backend.
<commit_msg>One task less, one task more.<commit_after>//!@todo: feature: Process id keyword & attribute.
//!@todo: feature: Verbosity filter from file.(2d)
//!@todo: api: Ability to set global attribute mapper (?)
//!@todo: api: Microseconds support in timestamps.(1-2h)

//!@todo: benchmark: File logging comparing with boost::log.
//!@todo: benchmark: Socket logging with json.

//!@todo: files_t: Make file naming by pattern.
//!@todo: files_t: logrotate support - ability to handle signals to reopen current file (SIGHUP) (?).

//!@todo: performance: Experiment with std::ostringstream or format library for performance check.
//!@todo: performance: Current naive implementation of timestamp formatter is suck and have large performance troubles. Fix it.

//!@todo: api: Make fallback logger. Make it configurable.
//!@todo: aux: Make internal exception class with attribute keeping, e.g. line, file or path.
//!@todo: api: Maybe squish repository_t::init and ::configure methods?
//!@todo: msgpack_t: Attribute mappings.
//!@todo: socket_t: Make asynchronous TCP backend.
<|endoftext|>