<commit_before>//-----------------------------------
// Copyright Pierric Gimmig 2013-2017
//-----------------------------------

#include <iostream>
#include <fstream>

#include "LinuxUtils.h"
#include "Utils.h"
#include "PrintVar.h"
#include "Capture.h"
#include "Callstack.h"
#include "SamplingProfiler.h"
#include "EventBuffer.h"
#include "Capture.h"
#include "ScopeTimer.h"
#include "OrbitProcess.h"
#include "OrbitModule.h"
#include "ConnectionManager.h"
#include "TcpClient.h"
#include "EventBuffer.h"
#include "EventTracer.h"

#if __linux__
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <linux/types.h>
#include <linux/perf_event.h>
#include <asm/unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <string.h>
#include <iomanip>
#include <signal.h>
#include <sys/wait.h>
#include <sstream>
#include <string>
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
#endif

//-----------------------------------------------------------------------------
LinuxPerf::LinuxPerf( uint32_t a_PID, uint32_t a_Freq )
        : m_PID(a_PID)
        , m_Frequency(a_Freq)
{
    m_OutputFile = "/tmp/perf.data";
    m_ReportFile = Replace(m_OutputFile, ".data", ".txt");
}

//-----------------------------------------------------------------------------
void LinuxPerf::Start()
{
#if __linux__
    m_IsRunning = true;

    std::string path = ws2s(Path::GetBasePath());

    pid_t PID = fork();
    if(PID == 0) {
        execl   ( "/usr/bin/perf"
                , "/usr/bin/perf"
                , "record"
                , "-k", "monotonic"
                , "-F", Format("%u", m_Frequency).c_str()
                , "-p", Format("%u", m_PID).c_str()
                , "-g"
                , "-o", m_OutputFile.c_str(), (const char*)nullptr);
        exit(1);
    }
    else
    {
        m_ForkedPID = PID;
        PRINT_VAR(m_ForkedPID);
    }
#endif
}

//-----------------------------------------------------------------------------
void LinuxPerf::Stop()
{
#if __linux__
    if( m_ForkedPID != 0 )
    {
        kill(m_ForkedPID, 15);
        int status = 0;
        waitpid(m_ForkedPID, &status, 0);
    }

    m_IsRunning = false;

    //m_Thread = std::make_shared<std::thread>([this]{
    std::string cmd = Format("perf script -i %s > %s", m_OutputFile.c_str(), m_ReportFile.c_str());
    PRINT_VAR(cmd);
    LinuxUtils::ExecuteCommand(cmd.c_str());

    if( ConnectionManager::Get().IsRemote() )
    {
        std::ifstream t(m_ReportFile);
        std::stringstream buffer;
        buffer << t.rdbuf();
        std::string perfData = buffer.str();
        GTcpServer->Send(Msg_RemotePerf, (void*)perfData.data(), perfData.size());
    }
    //else
    {
        LoadPerfData(m_ReportFile);
    }
    // });

    // m_Thread->detach();
#endif
}

//-----------------------------------------------------------------------------
bool ParseStackLine( const std::string& a_Line, uint64_t& o_Address, std::string& o_Name, std::string& o_Module )
{
    // Module
    std::size_t moduleBegin = a_Line.find_last_of("(");
    if( moduleBegin == std::string::npos )
        return false;
    o_Module = Replace(a_Line.substr(moduleBegin+1), ")", "");

    // Function name
    std::string line = LTrim(a_Line.substr(0, moduleBegin));
    std::size_t nameBegin = line.find_first_of(" ");
    if( nameBegin == std::string::npos )
        return false;
    o_Name = line.substr(nameBegin);

    // Address
    std::string address = line.substr(0, nameBegin);
    o_Address = std::stoull(address, nullptr, 16);

    return true;
}

//-----------------------------------------------------------------------------
void LinuxPerf::LoadPerfData( const std::string& a_FileName )
{
    SCOPE_TIMER_LOG(L"LoadPerfData");
    std::ifstream inFile(a_FileName);
    if( inFile.fail() )
    {
        PRINT_VAR("Could not open input file");
        PRINT_VAR(a_FileName);
        return;
    }

    LoadPerfData(inFile);
    inFile.close();
}

//-----------------------------------------------------------------------------
void LinuxPerf::LoadPerfData( std::istream& a_Stream )
{
    std::string header;
    uint32_t tid = 0;
    uint64_t time = 0;
    uint64_t numCallstacks = 0;

    CallStack CS;

    for (std::string line; std::getline(a_Stream, line); )
    {
        bool isHeader = !line.empty() && !StartsWith(line, "\t");
        bool isStackLine = !isHeader && !line.empty();
        bool isEndBlock = !isStackLine && line.empty() && !header.empty();

        if(isHeader)
        {
            header = line;
            auto tokens = Tokenize(line);
            time = tokens.size() > 2 ? GetMicros(tokens[2])*1000 : 0;
            tid  = tokens.size() > 1 ? atoi(tokens[1].c_str()) : 0;
        }
        else if(isStackLine)
        {
            std::string module;
            std::string function;
            uint64_t    address;

            if( !ParseStackLine( line, address, function, module ) )
            {
                PRINT_VAR("ParseStackLine error");
                PRINT_VAR(line);
                continue;
            }

            std::wstring moduleName = ToLower(Path::GetFileName(s2ws(module)));
            std::shared_ptr<Module> moduleFromName = Capture::GTargetProcess->GetModuleFromName( moduleName );

            // Debug - Temporary
            if( Contains(function, "Renderer::render") )
            {
                PRINT_VAR(function);
                PRINT_VAR(moduleName);
                PRINT_VAR(address);
            }

            if( moduleFromName )
            {
                uint64_t new_address = moduleFromName->ValidateAddress(address);

                // Debug - Temporary
                if( Contains(function, "Renderer::render") )
                {
                    std::cout << std::hex << address << " -> " << std::hex << new_address << std::endl;
                }
                address = new_address;
            }
            else
            {
                // Debug - Temporary
                if( Contains(function, "Renderer::render") ) {
                    PRINT_VAR("could not validate address");
                    PRINT_VAR(moduleName);
                }
            }

            CS.m_Data.push_back(address);

            if( Capture::GTargetProcess && !Capture::GTargetProcess->HasSymbol(address))
            {
                auto symbol = std::make_shared<LinuxSymbol>();
                symbol->m_Name = function;
                symbol->m_Module = module;
                Capture::GTargetProcess->AddSymbol( address, symbol );
            }
        }
        else if(isEndBlock)
        {
            if( CS.m_Data.size() )
            {
                CS.m_Depth = CS.m_Data.size();
                CS.m_ThreadId = tid;
                Capture::GSamplingProfiler->AddCallStack( CS );
                GEventTracer.GetEventBuffer().AddCallstackEvent( time, CS );
                ++numCallstacks;
            }

            CS.m_Data.clear();
            header = "";
            time = 0;
            tid = 0;
        }
    }

    PRINT_VAR(numCallstacks);
    PRINT_FUNC;
}<commit_msg>Fix build.<commit_after>//-----------------------------------
// Copyright Pierric Gimmig 2013-2017
//-----------------------------------

#include <iostream>
#include <fstream>

#include "LinuxUtils.h"
#include "Utils.h"
#include "PrintVar.h"
#include "Capture.h"
#include "Callstack.h"
#include "SamplingProfiler.h"
#include "EventBuffer.h"
#include "Capture.h"
#include "ScopeTimer.h"
#include "OrbitProcess.h"
#include "OrbitModule.h"
#include "ConnectionManager.h"
#include "TcpServer.h"
#include "EventBuffer.h"
#include "EventTracer.h"

#if __linux__
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <linux/types.h>
#include <linux/perf_event.h>
#include <asm/unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <string.h>
#include <iomanip>
#include <signal.h>
#include <sys/wait.h>
#include <sstream>
#include <string>
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
#endif

//-----------------------------------------------------------------------------
LinuxPerf::LinuxPerf( uint32_t a_PID, uint32_t a_Freq )
        : m_PID(a_PID)
        , m_Frequency(a_Freq)
{
    m_OutputFile = "/tmp/perf.data";
    m_ReportFile = Replace(m_OutputFile, ".data", ".txt");
}

//-----------------------------------------------------------------------------
void LinuxPerf::Start()
{
#if __linux__
    m_IsRunning = true;

    std::string path = ws2s(Path::GetBasePath());

    pid_t PID = fork();
    if(PID == 0) {
        execl   ( "/usr/bin/perf"
                , "/usr/bin/perf"
                , "record"
                , "-k", "monotonic"
                , "-F", Format("%u", m_Frequency).c_str()
                , "-p", Format("%u", m_PID).c_str()
                , "-g"
                , "-o", m_OutputFile.c_str(), (const char*)nullptr);
        exit(1);
    }
    else
    {
        m_ForkedPID = PID;
        PRINT_VAR(m_ForkedPID);
    }
#endif
}

//-----------------------------------------------------------------------------
void LinuxPerf::Stop()
{
#if __linux__
    if( m_ForkedPID != 0 )
    {
        kill(m_ForkedPID, 15);
        int status = 0;
        waitpid(m_ForkedPID, &status, 0);
    }

    m_IsRunning = false;

    //m_Thread = std::make_shared<std::thread>([this]{
    std::string cmd = Format("perf script -i %s > %s", m_OutputFile.c_str(), m_ReportFile.c_str());
    PRINT_VAR(cmd);
    LinuxUtils::ExecuteCommand(cmd.c_str());

    if( ConnectionManager::Get().IsRemote() )
    {
        std::ifstream t(m_ReportFile);
        std::stringstream buffer;
        buffer << t.rdbuf();
        std::string perfData = buffer.str();
        GTcpServer->Send(Msg_RemotePerf, (void*)perfData.data(), perfData.size());
    }
    //else
    {
        LoadPerfData(m_ReportFile);
    }
    // });

    // m_Thread->detach();
#endif
}

//-----------------------------------------------------------------------------
bool ParseStackLine( const std::string& a_Line, uint64_t& o_Address, std::string& o_Name, std::string& o_Module )
{
    // Module
    std::size_t moduleBegin = a_Line.find_last_of("(");
    if( moduleBegin == std::string::npos )
        return false;
    o_Module = Replace(a_Line.substr(moduleBegin+1), ")", "");

    // Function name
    std::string line = LTrim(a_Line.substr(0, moduleBegin));
    std::size_t nameBegin = line.find_first_of(" ");
    if( nameBegin == std::string::npos )
        return false;
    o_Name = line.substr(nameBegin);

    // Address
    std::string address = line.substr(0, nameBegin);
    o_Address = std::stoull(address, nullptr, 16);

    return true;
}

//-----------------------------------------------------------------------------
void LinuxPerf::LoadPerfData( const std::string& a_FileName )
{
    SCOPE_TIMER_LOG(L"LoadPerfData");
    std::ifstream inFile(a_FileName);
    if( inFile.fail() )
    {
        PRINT_VAR("Could not open input file");
        PRINT_VAR(a_FileName);
        return;
    }

    LoadPerfData(inFile);
    inFile.close();
}

//-----------------------------------------------------------------------------
void LinuxPerf::LoadPerfData( std::istream& a_Stream )
{
    std::string header;
    uint32_t tid = 0;
    uint64_t time = 0;
    uint64_t numCallstacks = 0;

    CallStack CS;

    for (std::string line; std::getline(a_Stream, line); )
    {
        bool isHeader = !line.empty() && !StartsWith(line, "\t");
        bool isStackLine = !isHeader && !line.empty();
        bool isEndBlock = !isStackLine && line.empty() && !header.empty();

        if(isHeader)
        {
            header = line;
            auto tokens = Tokenize(line);
            time = tokens.size() > 2 ? GetMicros(tokens[2])*1000 : 0;
            tid  = tokens.size() > 1 ? atoi(tokens[1].c_str()) : 0;
        }
        else if(isStackLine)
        {
            std::string module;
            std::string function;
            uint64_t    address;

            if( !ParseStackLine( line, address, function, module ) )
            {
                PRINT_VAR("ParseStackLine error");
                PRINT_VAR(line);
                continue;
            }

            std::wstring moduleName = ToLower(Path::GetFileName(s2ws(module)));
            std::shared_ptr<Module> moduleFromName = Capture::GTargetProcess->GetModuleFromName( moduleName );

            // Debug - Temporary
            if( Contains(function, "Renderer::render") )
            {
                PRINT_VAR(function);
                PRINT_VAR(moduleName);
                PRINT_VAR(address);
            }

            if( moduleFromName )
            {
                uint64_t new_address = moduleFromName->ValidateAddress(address);

                // Debug - Temporary
                if( Contains(function, "Renderer::render") )
                {
                    std::cout << std::hex << address << " -> " << std::hex << new_address << std::endl;
                }
                address = new_address;
            }
            else
            {
                // Debug - Temporary
                if( Contains(function, "Renderer::render") ) {
                    PRINT_VAR("could not validate address");
                    PRINT_VAR(moduleName);
                }
            }

            CS.m_Data.push_back(address);

            if( Capture::GTargetProcess && !Capture::GTargetProcess->HasSymbol(address))
            {
                auto symbol = std::make_shared<LinuxSymbol>();
                symbol->m_Name = function;
                symbol->m_Module = module;
                Capture::GTargetProcess->AddSymbol( address, symbol );
            }
        }
        else if(isEndBlock)
        {
            if( CS.m_Data.size() )
            {
                CS.m_Depth = CS.m_Data.size();
                CS.m_ThreadId = tid;
                Capture::GSamplingProfiler->AddCallStack( CS );
                GEventTracer.GetEventBuffer().AddCallstackEvent( time, CS );
                ++numCallstacks;
            }

            CS.m_Data.clear();
            header = "";
            time = 0;
            tid = 0;
        }
    }

    PRINT_VAR(numCallstacks);
    PRINT_FUNC;
}<|endoftext|>


<commit_before>
#include "PN532_HSU.h"
#include "PN532_debug.h"

PN532_HSU::PN532_HSU(HardwareSerial &serial)
{
    _serial = &serial;
    command = 0;
}

void PN532_HSU::begin()
{
    _serial->begin(115200);
}

void PN532_HSU::wakeup()
{
    _serial->write(0x55);
    _serial->write(0x55);
    _serial->write(0);
    _serial->write(0);
    _serial->write(0);

    /** dump serial buffer */
    if(_serial->available()){
        DMSG("Dump serial buffer: ");
    }
    while(_serial->available()){
        uint8_t ret = _serial->read();
        DMSG_HEX(ret);
    }

}

int8_t PN532_HSU::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen)
{

    /** dump serial buffer */
    if(_serial->available()){
        DMSG("Dump serial buffer: ");
    }
    while(_serial->available()){
        uint8_t ret = _serial->read();
        DMSG_HEX(ret);
    }

    command = header[0];

    _serial->write(PN532_PREAMBLE);
    _serial->write(PN532_STARTCODE1);
    _serial->write(PN532_STARTCODE2);

    uint8_t length = hlen + blen + 1;   // length of data field: TFI + DATA
    _serial->write(length);
    _serial->write(~length + 1);         // checksum of length

    _serial->write(PN532_HOSTTOPN532);
    uint8_t sum = PN532_HOSTTOPN532;    // sum of TFI + DATA

    DMSG("\nWrite: ");

    _serial->write(header, hlen);
    for (uint8_t i = 0; i < hlen; i++) {
        sum += header[i];

        DMSG_HEX(header[i]);
    }

    _serial->write(body, blen);
    for (uint8_t i = 0; i < blen; i++) {
        sum += body[i];

        DMSG_HEX(body[i]);
    }

    uint8_t checksum = ~sum + 1;            // checksum of TFI + DATA
    _serial->write(checksum);
    _serial->write(PN532_POSTAMBLE);

    return readAckFrame();
}

int16_t PN532_HSU::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout)
{
    uint8_t tmp[3];

    DMSG("\nRead:  ");

    /** Frame Preamble and Start Code */
    if(receive(tmp, 3, timeout)<=0){
        return PN532_TIMEOUT;
    }
    if(0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]){
        DMSG("Preamble error");
        return PN532_INVALID_FRAME;
    }

    /** receive length and check */
    uint8_t length[2];
    if(receive(length, 2, timeout) <= 0){
        return PN532_TIMEOUT;
    }
    if( 0 != (uint8_t)(length[0] + length[1]) ){
        DMSG("Length error");
        return PN532_INVALID_FRAME;
    }
    length[0] -= 2;
    if( length[0] > len){
        return PN532_NO_SPACE;
    }

    /** receive command byte */
    uint8_t cmd = command + 1;               // response command
    if(receive(tmp, 2, timeout) <= 0){
        return PN532_TIMEOUT;
    }
    if( PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]){
        DMSG("Command error");
        return PN532_INVALID_FRAME;
    }

    if(receive(buf, length[0], timeout) != length[0]){
        return PN532_TIMEOUT;
    }
    uint8_t sum = PN532_PN532TOHOST + cmd;
    for(uint8_t i=0; i<length[0]; i++){
        sum += buf[i];
    }

    /** checksum and postamble */
    if(receive(tmp, 2, timeout) <= 0){
        return PN532_TIMEOUT;
    }
    if( 0 != (uint8_t)(sum + tmp[0]) || 0 != tmp[1] ){
        DMSG("Checksum error");
        return PN532_INVALID_FRAME;
    }

    return length[0];
}

int8_t PN532_HSU::readAckFrame()
{
    const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0};
    uint8_t ackBuf[sizeof(PN532_ACK)];

    DMSG("\nAck: ");

    if( receive(ackBuf, sizeof(PN532_ACK), PN532_ACK_WAIT_TIME) <= 0 ){
        DMSG("Timeout\n");
        return PN532_TIMEOUT;
    }

    if( memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK)) ){
        DMSG("Invalid\n");
        return PN532_INVALID_ACK;
    }
    return 0;
}

/**
    @brief receive data .
    @param buf --> return value buffer.
           len --> length expect to receive.
           timeout --> time of reveiving
    @retval number of received bytes, 0 means no data received.
*/
int8_t PN532_HSU::receive(uint8_t *buf, int len, uint16_t timeout)
{
  int read_bytes = 0;
  int ret;
  unsigned long start_millis;

  while (read_bytes < len) {
    start_millis = millis();
    do {
      ret = _serial->read();
      if (ret >= 0) {
        break;
     }
    } while((timeout == 0) || ((millis()- start_millis ) < timeout));

    if (ret < 0) {
        if(read_bytes){
            return read_bytes;
        }else{
            return PN532_TIMEOUT;
        }
    }
    buf[read_bytes] = (uint8_t)ret;
    DMSG_HEX(ret);
    read_bytes++;
  }
  return read_bytes;
}
<commit_msg>maybe give some time for Android to response? this modification makes P2P working on Nexus 5 Android 5.1.<commit_after>
#include "PN532_HSU.h"
#include "PN532_debug.h"

PN532_HSU::PN532_HSU(HardwareSerial &serial)
{
    _serial = &serial;
    command = 0;
}

void PN532_HSU::begin()
{
    _serial->begin(115200);
}

void PN532_HSU::wakeup()
{
    _serial->write(0x55);
    _serial->write(0x55);
    _serial->write(0);
    _serial->write(0);
    _serial->write(0);

    /** dump serial buffer */
    if(_serial->available()){
        DMSG("Dump serial buffer: ");
    }
    while(_serial->available()){
        uint8_t ret = _serial->read();
        DMSG_HEX(ret);
    }

}

int8_t PN532_HSU::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen)
{

    /** dump serial buffer */
    if(_serial->available()){
        DMSG("Dump serial buffer: ");
    }
    while(_serial->available()){
        uint8_t ret = _serial->read();
        DMSG_HEX(ret);
    }

    command = header[0];

    _serial->write(PN532_PREAMBLE);
    _serial->write(PN532_STARTCODE1);
    _serial->write(PN532_STARTCODE2);

    uint8_t length = hlen + blen + 1;   // length of data field: TFI + DATA
    _serial->write(length);
    _serial->write(~length + 1);         // checksum of length

    _serial->write(PN532_HOSTTOPN532);
    uint8_t sum = PN532_HOSTTOPN532;    // sum of TFI + DATA

    DMSG("\nWrite: ");

    _serial->write(header, hlen);
    for (uint8_t i = 0; i < hlen; i++) {
        sum += header[i];

        DMSG_HEX(header[i]);
    }

    _serial->write(body, blen);
    for (uint8_t i = 0; i < blen; i++) {
        sum += body[i];

        DMSG_HEX(body[i]);
    }

    uint8_t checksum = ~sum + 1;            // checksum of TFI + DATA
    _serial->write(checksum);
    _serial->write(PN532_POSTAMBLE);

    return readAckFrame();
}

int16_t PN532_HSU::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout)
{
    uint8_t tmp[3];

    delay(100);

    DMSG("\nRead:  ");

    /** Frame Preamble and Start Code */
    if(receive(tmp, 3, timeout)<=0){
        return PN532_TIMEOUT;
    }
    if(0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]){
        DMSG("Preamble error");
        return PN532_INVALID_FRAME;
    }

    /** receive length and check */
    uint8_t length[2];
    if(receive(length, 2, timeout) <= 0){
        return PN532_TIMEOUT;
    }
    if( 0 != (uint8_t)(length[0] + length[1]) ){
        DMSG("Length error");
        return PN532_INVALID_FRAME;
    }
    length[0] -= 2;
    if( length[0] > len){
        return PN532_NO_SPACE;
    }

    /** receive command byte */
    uint8_t cmd = command + 1;               // response command
    if(receive(tmp, 2, timeout) <= 0){
        return PN532_TIMEOUT;
    }
    if( PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]){
        DMSG("Command error");
        return PN532_INVALID_FRAME;
    }

    if(receive(buf, length[0], timeout) != length[0]){
        return PN532_TIMEOUT;
    }
    uint8_t sum = PN532_PN532TOHOST + cmd;
    for(uint8_t i=0; i<length[0]; i++){
        sum += buf[i];
    }

    /** checksum and postamble */
    if(receive(tmp, 2, timeout) <= 0){
        return PN532_TIMEOUT;
    }
    if( 0 != (uint8_t)(sum + tmp[0]) || 0 != tmp[1] ){
        DMSG("Checksum error");
        return PN532_INVALID_FRAME;
    }

    return length[0];
}

int8_t PN532_HSU::readAckFrame()
{
    const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0};
    uint8_t ackBuf[sizeof(PN532_ACK)];

    delay(100);

    DMSG("\nAck: ");

    if( receive(ackBuf, sizeof(PN532_ACK), PN532_ACK_WAIT_TIME) <= 0 ){
        DMSG("Timeout\n");
        return PN532_TIMEOUT;
    }

    if( memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK)) ){
        DMSG("Invalid\n");
        return PN532_INVALID_ACK;
    }
    return 0;
}

/**
    @brief receive data .
    @param buf --> return value buffer.
           len --> length expect to receive.
           timeout --> time of reveiving
    @retval number of received bytes, 0 means no data received.
*/
int8_t PN532_HSU::receive(uint8_t *buf, int len, uint16_t timeout)
{
  int read_bytes = 0;
  int ret;
  unsigned long start_millis;

  while (read_bytes < len) {
    start_millis = millis();
    do {
      ret = _serial->read();
      if (ret >= 0) {
        break;
     }
     delay(10);
    } while((timeout == 0) || ((millis()- start_millis ) < timeout));

    if (ret < 0) {
        if(read_bytes){
            return read_bytes;
        }else{
            return PN532_TIMEOUT;
        }
    }
    buf[read_bytes] = (uint8_t)ret;
    DMSG_HEX(ret);
    read_bytes++;
  }
  return read_bytes;
}
<|endoftext|>


<commit_before>/*
 * SSL/TLS configuration.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "ssl_factory.hxx"
#include "ssl_config.hxx"
#include "ssl_domain.hxx"
#include "util/Error.hxx"

#include <inline/compiler.h>

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <assert.h>

struct ssl_cert_key {
    X509 *cert;
    EVP_PKEY *key;

    ssl_cert_key():cert(nullptr), key(nullptr) {}

    ssl_cert_key(X509 *_cert, EVP_PKEY *_key)
        :cert(_cert), key(_key) {}

    ssl_cert_key(ssl_cert_key &&other)
        :cert(other.cert), key(other.key) {
        other.cert = nullptr;
        other.key = nullptr;
    }

    ~ssl_cert_key() {
        if (cert != nullptr)
            X509_free(cert);
        if (key != nullptr)
            EVP_PKEY_free(key);
    }

    ssl_cert_key &operator=(ssl_cert_key &&other) {
        std::swap(cert, other.cert);
        std::swap(key, other.key);
        return *this;
    }

    bool Load(const ssl_cert_key_config &config, Error &error);
};

struct ssl_factory {
    SSL_CTX *const ssl_ctx;

    std::vector<ssl_cert_key> cert_key;

    const bool server;

    ssl_factory(SSL_CTX *_ssl_ctx, bool _server)
        :ssl_ctx(_ssl_ctx), server(_server) {}

    ~ssl_factory() {
        SSL_CTX_free(ssl_ctx);
    }

    bool EnableSNI(Error &error);

    SSL *Make();

    unsigned Flush(long tm);
};

static int
verify_callback(int ok, gcc_unused X509_STORE_CTX *ctx)
{
    return ok;
}

static BIO *
bio_open_file(const char *path, Error &error)
{
    BIO *bio = BIO_new_file(path, "r");
    if (bio == nullptr)
        error.Format(ssl_domain, "Failed to open file %s", path);

    return bio;
}

static EVP_PKEY *
read_key_file(const char *path, Error &error)
{
    BIO *bio = bio_open_file(path, error);
    if (bio == nullptr)
        return nullptr;

    EVP_PKEY *key = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
    BIO_free(bio);
    if (key == nullptr)
        error.Format(ssl_domain, "Failed to load key file %s", path);

    return key;
}

static X509 *
read_cert_file(const char *path, Error &error)
{
    BIO *bio = bio_open_file(path, error);
    if (bio == nullptr)
        return nullptr;

    X509 *cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
    BIO_free(bio);
    if (cert == nullptr)
        error.Format(ssl_domain, "Failed to load certificate file %s", path);

    return cert;
}

/**
 * Are both public keys equal?
 */
gcc_pure
static bool
MatchModulus(EVP_PKEY *key1, EVP_PKEY *key2)
{
    assert(key1 != nullptr);
    assert(key2 != nullptr);

    if (key1->type != key2->type)
        return false;

    switch (key1->type) {
    case EVP_PKEY_RSA:
        return BN_cmp(key1->pkey.rsa->n, key2->pkey.rsa->n) == 0;

    case EVP_PKEY_DSA:
        return BN_cmp(key1->pkey.dsa->pub_key, key2->pkey.dsa->pub_key) == 0;

    default:
        return false;
    }
}

/**
 * Does the certificate belong to the given key?
 */
gcc_pure
static bool
MatchModulus(X509 *cert, EVP_PKEY *key)
{
    assert(cert != nullptr);
    assert(key != nullptr);

    EVP_PKEY *public_key = X509_get_pubkey(cert);
    if (public_key == nullptr)
        return false;

    const bool result = MatchModulus(public_key, key);
    EVP_PKEY_free(public_key);
    return result;
}

bool
ssl_cert_key::Load(const ssl_cert_key_config &config, Error &error)
{
    assert(key == nullptr);
    assert(cert == nullptr);

    key = read_key_file(config.key_file.c_str(), error);
    if (key == nullptr)
        return false;

    cert = read_cert_file(config.cert_file.c_str(), error);
    if (cert == nullptr)
        return false;

    if (!MatchModulus(cert, key)) {
        error.Format(ssl_domain, "Key '%s' does not match certificate '%s'",
                    config.key_file.c_str(), config.cert_file.c_str());
        return false;
    }

    return true;
}

static bool
load_certs_keys(ssl_factory &factory, const ssl_config &config,
                Error &error)
{
    factory.cert_key.reserve(config.cert_key.size());

    for (const auto &c : config.cert_key) {
        ssl_cert_key ck;
        if (!ck.Load(c, error))
            return false;

        factory.cert_key.emplace_back(std::move(ck));
    }

    return true;
}

static bool
apply_server_config(SSL_CTX *ssl_ctx, const ssl_config &config,
                    Error &error)
{
    assert(!config.cert_key.empty());

    ERR_clear_error();

    if (SSL_CTX_use_RSAPrivateKey_file(ssl_ctx,
                                       config.cert_key[0].key_file.c_str(),
                                       SSL_FILETYPE_PEM) != 1) {
        ERR_print_errors_fp(stderr);
        error.Format(ssl_domain, "Failed to load key file %s",
                     config.cert_key[0].key_file.c_str());
        return false;
    }

    if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
                                           config.cert_key[0].cert_file.c_str()) != 1) {
        ERR_print_errors_fp(stderr);
        error.Format(ssl_domain, "Failed to load certificate file %s",
                     config.cert_key[0].cert_file.c_str());
        return false;
    }

    if (!config.ca_cert_file.empty()) {
        if (SSL_CTX_load_verify_locations(ssl_ctx,
                                          config.ca_cert_file.c_str(),
                                          nullptr) != 1) {
            error.Format(ssl_domain, "Failed to load CA certificate file %s",
                         config.ca_cert_file.c_str());
            return false;
        }

        /* send all certificates from this file to the client (list of
           acceptable CA certificates) */

        STACK_OF(X509_NAME) *list =
            SSL_load_client_CA_file(config.ca_cert_file.c_str());
        if (list == nullptr) {
            error.Format(ssl_domain,
                         "Failed to load CA certificate list from file %s",
                         config.ca_cert_file.c_str());
            return false;
        }

        SSL_CTX_set_client_CA_list(ssl_ctx, list);
    }

    if (config.verify != ssl_verify::NO) {
        /* enable client certificates */
        int mode = SSL_VERIFY_PEER;

        if (config.verify == ssl_verify::YES)
            mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;

        SSL_CTX_set_verify(ssl_ctx, mode, verify_callback);
    }

    return true;
}

static bool
match_cn(X509_NAME *subject, const char *host_name, size_t hn_length)
{
    char common_name[256];
    if (X509_NAME_get_text_by_NID(subject, NID_commonName, common_name,
                                  sizeof(common_name)) < 0)
        return false;

    if (strcmp(host_name, common_name) == 0)
        return true;

    if (common_name[0] == '*' && common_name[1] == '.' &&
        common_name[2] != 0) {
        const size_t cn_length = strlen(common_name);
        if (hn_length >= cn_length &&
            /* match only one segment (no dots) */
            memchr(host_name, '.', hn_length - cn_length + 1) == nullptr &&
            memcmp(host_name + hn_length - cn_length + 1,
                   common_name + 1, cn_length - 1) == 0)
            return true;
    }

    return false;
}

static bool
use_cert_key(SSL *ssl, const ssl_cert_key &ck)
{
    return SSL_use_certificate(ssl, ck.cert) == 1 &&
        SSL_use_PrivateKey(ssl, ck.key) == 1;
}

static int
ssl_servername_callback(SSL *ssl, gcc_unused int *al,
                        const ssl_factory &factory)
{
    const char *host_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
    if (host_name == nullptr)
        return SSL_TLSEXT_ERR_OK;

    const size_t length = strlen(host_name);

    /* find the first certificate that matches */

    for (const auto &ck : factory.cert_key) {
        X509_NAME *subject = X509_get_subject_name(ck.cert);
        if (subject != nullptr && match_cn(subject, host_name, length)) {
            /* found it - now use it */
            use_cert_key(ssl, ck);
            break;
        }
    }

    return SSL_TLSEXT_ERR_OK;
}

inline bool
ssl_factory::EnableSNI(Error &error)
{
    if (!SSL_CTX_set_tlsext_servername_callback(ssl_ctx,
                                                ssl_servername_callback) ||
        !SSL_CTX_set_tlsext_servername_arg(ssl_ctx, this)) {
        error.Format(ssl_domain,
                     "SSL_CTX_set_tlsext_servername_callback() failed");
        return false;
    }

    return true;
}

inline SSL *
ssl_factory::Make()
{
    SSL *ssl = SSL_new(ssl_ctx);
    if (ssl == nullptr)
        return nullptr;

    if (server)
        SSL_set_accept_state(ssl);
    else
        SSL_set_connect_state(ssl);

    return ssl;
}

inline unsigned
ssl_factory::Flush(long tm)
{
    unsigned before = SSL_CTX_sess_number(ssl_ctx);
    SSL_CTX_flush_sessions(ssl_ctx, tm);
    unsigned after = SSL_CTX_sess_number(ssl_ctx);
    return after < before ? before - after : 0;
}

/**
 * Enable Elliptic curve Diffie-Hellman (ECDH) for perfect forward
 * secrecy.  By default, it OpenSSL disables it.
 */
static bool
enable_ecdh(SSL_CTX *ssl_ctx, Error &error)
{
    /* OpenSSL 1.0.2 will allow this instead:

       SSL_CTX_set_ecdh_auto(ssl_ctx, 1)
    */

    EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
    if (ecdh == nullptr) {
        error.Set(ssl_domain, "EC_KEY_new_by_curve_name() failed");
        return nullptr;
    }

    bool success = SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh) == 1;
    EC_KEY_free(ecdh);
    if (!success)
        error.Set(ssl_domain, "SSL_CTX_set_tmp_ecdh() failed");

    return success;
}

struct ssl_factory *
ssl_factory_new(const ssl_config &config,
                bool server,
                Error &error)
{
    assert(!config.cert_key.empty() || !server);

    ERR_clear_error();

    /* don't be fooled - we want TLS, not SSL - but TLSv1_method()
       will only allow TLSv1.0 and will refuse TLSv1.1 and TLSv1.2;
       only SSLv23_method() supports all (future) TLS protocol
       versions, even if we don't want any SSL at all */
    auto method = server
        ? SSLv23_server_method()
        : SSLv23_client_method();

    SSL_CTX *ssl_ctx = SSL_CTX_new(method);
    if (ssl_ctx == nullptr) {
        ERR_print_errors_fp(stderr);
        error.Format(ssl_domain, "SSL_CTX_new() failed");
        return nullptr;
    }

    long mode = SSL_MODE_ENABLE_PARTIAL_WRITE
        | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
#ifdef SSL_MODE_RELEASE_BUFFERS
    /* requires libssl 1.0.0 */
    mode |= SSL_MODE_RELEASE_BUFFERS;
#endif

    /* without this flag, OpenSSL attempts to verify the whole local
       certificate chain for each connection, which is a waste of CPU
       time */
    mode |= SSL_MODE_NO_AUTO_CHAIN;

    SSL_CTX_set_mode(ssl_ctx, mode);

    if (server && !enable_ecdh(ssl_ctx, error)) {
        SSL_CTX_free(ssl_ctx);
        return nullptr;
    }

    /* disable protocols that are known to be insecure */
    SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);

    /* disable weak ciphers */
    SSL_CTX_set_cipher_list(ssl_ctx, "DEFAULT:!EXPORT:!LOW");

    ssl_factory *factory = new ssl_factory(ssl_ctx, server);

    if (server) {
        if (!apply_server_config(ssl_ctx, config, error) ||
            !load_certs_keys(*factory, config, error)) {
            delete factory;
            return nullptr;
        }
    } else {
        assert(config.cert_key.empty());
        assert(config.ca_cert_file.empty());
        assert(config.verify == ssl_verify::NO);
    }

    if (factory->cert_key.size() > 1 && !factory->EnableSNI(error)) {
        delete factory;
        return nullptr;
    }

    return factory;
}

void
ssl_factory_free(struct ssl_factory *factory)
{
    delete factory;
}

SSL *
ssl_factory_make(ssl_factory &factory)
{
    return factory.Make();
}

unsigned
ssl_factory_flush(struct ssl_factory &factory, long tm)
{
    return factory.Flush(tm);
}
<commit_msg>ssl_factory: move code to CreateBasicSslCtx()<commit_after>/*
 * SSL/TLS configuration.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "ssl_factory.hxx"
#include "ssl_config.hxx"
#include "ssl_domain.hxx"
#include "util/Error.hxx"

#include <inline/compiler.h>

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <assert.h>

struct ssl_cert_key {
    X509 *cert;
    EVP_PKEY *key;

    ssl_cert_key():cert(nullptr), key(nullptr) {}

    ssl_cert_key(X509 *_cert, EVP_PKEY *_key)
        :cert(_cert), key(_key) {}

    ssl_cert_key(ssl_cert_key &&other)
        :cert(other.cert), key(other.key) {
        other.cert = nullptr;
        other.key = nullptr;
    }

    ~ssl_cert_key() {
        if (cert != nullptr)
            X509_free(cert);
        if (key != nullptr)
            EVP_PKEY_free(key);
    }

    ssl_cert_key &operator=(ssl_cert_key &&other) {
        std::swap(cert, other.cert);
        std::swap(key, other.key);
        return *this;
    }

    bool Load(const ssl_cert_key_config &config, Error &error);
};

struct ssl_factory {
    SSL_CTX *const ssl_ctx;

    std::vector<ssl_cert_key> cert_key;

    const bool server;

    ssl_factory(SSL_CTX *_ssl_ctx, bool _server)
        :ssl_ctx(_ssl_ctx), server(_server) {}

    ~ssl_factory() {
        SSL_CTX_free(ssl_ctx);
    }

    bool EnableSNI(Error &error);

    SSL *Make();

    unsigned Flush(long tm);
};

static int
verify_callback(int ok, gcc_unused X509_STORE_CTX *ctx)
{
    return ok;
}

static BIO *
bio_open_file(const char *path, Error &error)
{
    BIO *bio = BIO_new_file(path, "r");
    if (bio == nullptr)
        error.Format(ssl_domain, "Failed to open file %s", path);

    return bio;
}

static EVP_PKEY *
read_key_file(const char *path, Error &error)
{
    BIO *bio = bio_open_file(path, error);
    if (bio == nullptr)
        return nullptr;

    EVP_PKEY *key = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
    BIO_free(bio);
    if (key == nullptr)
        error.Format(ssl_domain, "Failed to load key file %s", path);

    return key;
}

static X509 *
read_cert_file(const char *path, Error &error)
{
    BIO *bio = bio_open_file(path, error);
    if (bio == nullptr)
        return nullptr;

    X509 *cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
    BIO_free(bio);
    if (cert == nullptr)
        error.Format(ssl_domain, "Failed to load certificate file %s", path);

    return cert;
}

/**
 * Are both public keys equal?
 */
gcc_pure
static bool
MatchModulus(EVP_PKEY *key1, EVP_PKEY *key2)
{
    assert(key1 != nullptr);
    assert(key2 != nullptr);

    if (key1->type != key2->type)
        return false;

    switch (key1->type) {
    case EVP_PKEY_RSA:
        return BN_cmp(key1->pkey.rsa->n, key2->pkey.rsa->n) == 0;

    case EVP_PKEY_DSA:
        return BN_cmp(key1->pkey.dsa->pub_key, key2->pkey.dsa->pub_key) == 0;

    default:
        return false;
    }
}

/**
 * Does the certificate belong to the given key?
 */
gcc_pure
static bool
MatchModulus(X509 *cert, EVP_PKEY *key)
{
    assert(cert != nullptr);
    assert(key != nullptr);

    EVP_PKEY *public_key = X509_get_pubkey(cert);
    if (public_key == nullptr)
        return false;

    const bool result = MatchModulus(public_key, key);
    EVP_PKEY_free(public_key);
    return result;
}

bool
ssl_cert_key::Load(const ssl_cert_key_config &config, Error &error)
{
    assert(key == nullptr);
    assert(cert == nullptr);

    key = read_key_file(config.key_file.c_str(), error);
    if (key == nullptr)
        return false;

    cert = read_cert_file(config.cert_file.c_str(), error);
    if (cert == nullptr)
        return false;

    if (!MatchModulus(cert, key)) {
        error.Format(ssl_domain, "Key '%s' does not match certificate '%s'",
                    config.key_file.c_str(), config.cert_file.c_str());
        return false;
    }

    return true;
}

static bool
load_certs_keys(ssl_factory &factory, const ssl_config &config,
                Error &error)
{
    factory.cert_key.reserve(config.cert_key.size());

    for (const auto &c : config.cert_key) {
        ssl_cert_key ck;
        if (!ck.Load(c, error))
            return false;

        factory.cert_key.emplace_back(std::move(ck));
    }

    return true;
}

static bool
apply_server_config(SSL_CTX *ssl_ctx, const ssl_config &config,
                    Error &error)
{
    assert(!config.cert_key.empty());

    ERR_clear_error();

    if (SSL_CTX_use_RSAPrivateKey_file(ssl_ctx,
                                       config.cert_key[0].key_file.c_str(),
                                       SSL_FILETYPE_PEM) != 1) {
        ERR_print_errors_fp(stderr);
        error.Format(ssl_domain, "Failed to load key file %s",
                     config.cert_key[0].key_file.c_str());
        return false;
    }

    if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
                                           config.cert_key[0].cert_file.c_str()) != 1) {
        ERR_print_errors_fp(stderr);
        error.Format(ssl_domain, "Failed to load certificate file %s",
                     config.cert_key[0].cert_file.c_str());
        return false;
    }

    if (!config.ca_cert_file.empty()) {
        if (SSL_CTX_load_verify_locations(ssl_ctx,
                                          config.ca_cert_file.c_str(),
                                          nullptr) != 1) {
            error.Format(ssl_domain, "Failed to load CA certificate file %s",
                         config.ca_cert_file.c_str());
            return false;
        }

        /* send all certificates from this file to the client (list of
           acceptable CA certificates) */

        STACK_OF(X509_NAME) *list =
            SSL_load_client_CA_file(config.ca_cert_file.c_str());
        if (list == nullptr) {
            error.Format(ssl_domain,
                         "Failed to load CA certificate list from file %s",
                         config.ca_cert_file.c_str());
            return false;
        }

        SSL_CTX_set_client_CA_list(ssl_ctx, list);
    }

    if (config.verify != ssl_verify::NO) {
        /* enable client certificates */
        int mode = SSL_VERIFY_PEER;

        if (config.verify == ssl_verify::YES)
            mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;

        SSL_CTX_set_verify(ssl_ctx, mode, verify_callback);
    }

    return true;
}

static bool
match_cn(X509_NAME *subject, const char *host_name, size_t hn_length)
{
    char common_name[256];
    if (X509_NAME_get_text_by_NID(subject, NID_commonName, common_name,
                                  sizeof(common_name)) < 0)
        return false;

    if (strcmp(host_name, common_name) == 0)
        return true;

    if (common_name[0] == '*' && common_name[1] == '.' &&
        common_name[2] != 0) {
        const size_t cn_length = strlen(common_name);
        if (hn_length >= cn_length &&
            /* match only one segment (no dots) */
            memchr(host_name, '.', hn_length - cn_length + 1) == nullptr &&
            memcmp(host_name + hn_length - cn_length + 1,
                   common_name + 1, cn_length - 1) == 0)
            return true;
    }

    return false;
}

static bool
use_cert_key(SSL *ssl, const ssl_cert_key &ck)
{
    return SSL_use_certificate(ssl, ck.cert) == 1 &&
        SSL_use_PrivateKey(ssl, ck.key) == 1;
}

static int
ssl_servername_callback(SSL *ssl, gcc_unused int *al,
                        const ssl_factory &factory)
{
    const char *host_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
    if (host_name == nullptr)
        return SSL_TLSEXT_ERR_OK;

    const size_t length = strlen(host_name);

    /* find the first certificate that matches */

    for (const auto &ck : factory.cert_key) {
        X509_NAME *subject = X509_get_subject_name(ck.cert);
        if (subject != nullptr && match_cn(subject, host_name, length)) {
            /* found it - now use it */
            use_cert_key(ssl, ck);
            break;
        }
    }

    return SSL_TLSEXT_ERR_OK;
}

inline bool
ssl_factory::EnableSNI(Error &error)
{
    if (!SSL_CTX_set_tlsext_servername_callback(ssl_ctx,
                                                ssl_servername_callback) ||
        !SSL_CTX_set_tlsext_servername_arg(ssl_ctx, this)) {
        error.Format(ssl_domain,
                     "SSL_CTX_set_tlsext_servername_callback() failed");
        return false;
    }

    return true;
}

inline SSL *
ssl_factory::Make()
{
    SSL *ssl = SSL_new(ssl_ctx);
    if (ssl == nullptr)
        return nullptr;

    if (server)
        SSL_set_accept_state(ssl);
    else
        SSL_set_connect_state(ssl);

    return ssl;
}

inline unsigned
ssl_factory::Flush(long tm)
{
    unsigned before = SSL_CTX_sess_number(ssl_ctx);
    SSL_CTX_flush_sessions(ssl_ctx, tm);
    unsigned after = SSL_CTX_sess_number(ssl_ctx);
    return after < before ? before - after : 0;
}

/**
 * Enable Elliptic curve Diffie-Hellman (ECDH) for perfect forward
 * secrecy.  By default, it OpenSSL disables it.
 */
static bool
enable_ecdh(SSL_CTX *ssl_ctx, Error &error)
{
    /* OpenSSL 1.0.2 will allow this instead:

       SSL_CTX_set_ecdh_auto(ssl_ctx, 1)
    */

    EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
    if (ecdh == nullptr) {
        error.Set(ssl_domain, "EC_KEY_new_by_curve_name() failed");
        return nullptr;
    }

    bool success = SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh) == 1;
    EC_KEY_free(ecdh);
    if (!success)
        error.Set(ssl_domain, "SSL_CTX_set_tmp_ecdh() failed");

    return success;
}

static bool
SetupBasicSslCtx(SSL_CTX *ssl_ctx, bool server, Error &error)
{
    long mode = SSL_MODE_ENABLE_PARTIAL_WRITE
        | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
#ifdef SSL_MODE_RELEASE_BUFFERS
    /* requires libssl 1.0.0 */
    mode |= SSL_MODE_RELEASE_BUFFERS;
#endif

    /* without this flag, OpenSSL attempts to verify the whole local
       certificate chain for each connection, which is a waste of CPU
       time */
    mode |= SSL_MODE_NO_AUTO_CHAIN;

    SSL_CTX_set_mode(ssl_ctx, mode);

    if (server && !enable_ecdh(ssl_ctx, error))
        return false;

    /* disable protocols that are known to be insecure */
    SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);

    /* disable weak ciphers */
    SSL_CTX_set_cipher_list(ssl_ctx, "DEFAULT:!EXPORT:!LOW");

    return true;
}

static SSL_CTX *
CreateBasicSslCtx(bool server, Error &error)
{
    ERR_clear_error();

    /* don't be fooled - we want TLS, not SSL - but TLSv1_method()
       will only allow TLSv1.0 and will refuse TLSv1.1 and TLSv1.2;
       only SSLv23_method() supports all (future) TLS protocol
       versions, even if we don't want any SSL at all */
    auto method = server
        ? SSLv23_server_method()
        : SSLv23_client_method();

    SSL_CTX *ssl_ctx = SSL_CTX_new(method);
    if (ssl_ctx == nullptr) {
        ERR_print_errors_fp(stderr);
        error.Format(ssl_domain, "SSL_CTX_new() failed");
        return nullptr;
    }

    if (!SetupBasicSslCtx(ssl_ctx, server, error)) {
        SSL_CTX_free(ssl_ctx);
        return nullptr;
    }

    return ssl_ctx;
}

struct ssl_factory *
ssl_factory_new(const ssl_config &config,
                bool server,
                Error &error)
{
    assert(!config.cert_key.empty() || !server);

    SSL_CTX *ssl_ctx = CreateBasicSslCtx(server, error);
    if (ssl_ctx == nullptr)
        return nullptr;

    ssl_factory *factory = new ssl_factory(ssl_ctx, server);

    if (server) {
        if (!apply_server_config(ssl_ctx, config, error) ||
            !load_certs_keys(*factory, config, error)) {
            delete factory;
            return nullptr;
        }
    } else {
        assert(config.cert_key.empty());
        assert(config.ca_cert_file.empty());
        assert(config.verify == ssl_verify::NO);
    }

    if (factory->cert_key.size() > 1 && !factory->EnableSNI(error)) {
        delete factory;
        return nullptr;
    }

    return factory;
}

void
ssl_factory_free(struct ssl_factory *factory)
{
    delete factory;
}

SSL *
ssl_factory_make(ssl_factory &factory)
{
    return factory.Make();
}

unsigned
ssl_factory_flush(struct ssl_factory &factory, long tm)
{
    return factory.Flush(tm);
}
<|endoftext|>


<commit_before>/*
Project: TUC
File: syntax_tree.cpp
Author: Leonardo Banderali
Created: September 6, 2015
Last Modified: November 4, 2015

Description:
    TUC is a simple, experimental compiler designed for learning and experimenting.
    It is not intended to have any useful purpose other than being a way to learn
    how compilers work.

Copyright (C) 2015 Leonardo Banderali

License:

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

// project headers
#include "syntax_tree.hpp"
#include "compiler_exceptions.hpp"

// standard libraries
#include <stdexcept>
#include <sstream>
#include <iostream>

//~class implementations~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

tuc::SyntaxNode::SyntaxNode(NodeType _type) : syntaxNodeType{_type} {}

tuc::SyntaxNode::SyntaxNode(NodeType _type, const TextEntity& _textValue)
: syntaxNodeType{_type}, textValue{_textValue} {}

/*
constructs a node from a syntax token
*/
tuc::SyntaxNode::SyntaxNode(const Token& _token)
: textValue{_token.text()} {
    switch (_token.type()) {
    case TokenType::TYPE:       syntaxNodeType = NodeType::TYPE; break;
    case TokenType::HASTYPE:    syntaxNodeType = NodeType::HASTYPE; break;
    case TokenType::ASSIGN:     syntaxNodeType = NodeType::ASSIGN; break;
    case TokeType::MAPTO:       syntaxNodeType = NodeType::MAPTO; break;
    case TokenType::ADD:        syntaxNodeType = NodeType::ADD; break;
    case TokenType::SUBTRACT:   syntaxNodeType = NodeType::SUBTRACT; break;
    case TokenType::MULTIPLY:   syntaxNodeType = NodeType::MULTIPLY; break;
    case TokenType::DIVIDE:     syntaxNodeType = NodeType::DIVIDE; break;
    case TokenType::INTEGER:    syntaxNodeType = NodeType::INTEGER; break;
    case TokenType::SEMICOL:    syntaxNodeType = NodeType::SEMICOL; break;
    case TokenType::IDENTIFIER: syntaxNodeType = NodeType::IDENTIFIER; break;
    default:                    syntaxNodeType = NodeType::UNKNOWN;
    }
}

tuc::SyntaxNode* tuc::SyntaxNode::parent() noexcept {
    return parentNode;
}

/*
returns child with index `i`
*/
tuc::SyntaxNode* tuc::SyntaxNode::child(int i) noexcept {
    return children[i].get();
}

int tuc::SyntaxNode::child_count() const noexcept {
    return children.size();
}

void tuc::SyntaxNode::append_child(NodeType _type, const TextEntity& _textValue) {
    children.push_back(std::make_unique<SyntaxNode>(_type, _textValue));
}

void tuc::SyntaxNode::append_child(const Token& _token) {
    children.push_back(std::make_unique<SyntaxNode>(_token));
}

/*  appends an already existing (allocated) child; since this node must "own" the child, move semantics *must*
    be used to transfer ownership.
*/
void tuc::SyntaxNode::append_child(std::unique_ptr<SyntaxNode>&& c) noexcept {
    children.push_back(std::move(c));
}

tuc::SyntaxNode::NodeType tuc::SyntaxNode::type() const noexcept {
    return syntaxNodeType;
}

bool tuc::SyntaxNode::is_operator() const noexcept {
    return syntaxNodeType == NodeType::ADD || syntaxNodeType == NodeType::SUBTRACT ||
            syntaxNodeType == NodeType::MULTIPLY || syntaxNodeType == NodeType::DIVIDE;
}

std::string tuc::SyntaxNode::value() const noexcept {
    return textValue.text();
}

int tuc::SyntaxNode::position() const noexcept {
    return textValue.position();
}

tuc::TextEntity tuc::SyntaxNode::text() const noexcept {
    return textValue;
}

//~function implementations~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/*
generate a syntax tree from a list of tokens
*/
std::tuple<std::unique_ptr<tuc::SyntaxNode>, tuc::SymbolTable> tuc::gen_syntax_tree(const std::vector<tuc::Token>& tokenList) {
    auto treeRoot = std::make_unique<tuc::SyntaxNode>(tuc::SyntaxNode::NodeType::PROGRAM);
    auto symTable = SymbolTable{};
    auto rpnExpr = std::vector<tuc::Token>{};
    auto opStack = std::vector<tuc::Token>{};

    /*##########################################################################################################
    ### To parse mathematical expressions, first use a variation of Dijkstra's shunting yard algorithm to     ##
    ### transform the expression into Reverse Polish Notation (RPN).  Once transformed, the expression can be ##
    ### read into the syntax tree.                                                                            ##
    ##########################################################################################################*/

    for (const auto token: tokenList) {
        if (token.is_operator() /*|| token.type() == tuc::TokenType::IDENTIFIER || token.type() == tuc::TokenType::HASTYPE*/) {
            while(!opStack.empty() && opStack.back().is_operator() && (
                        (token.fixity() == Associativity::LEFT && token.precedence() <= opStack.back().precedence()) ||
                        (token.fixity() == Associativity::RIGHT && token.precedence() < opStack.back().precedence()) )) {
                rpnExpr.push_back(opStack.back());
                opStack.pop_back();
            }
            opStack.push_back(token);
        }
        else if (token.type() == tuc::TokenType::IDENTIFIER) {
            auto symIterator = symTable.find(token.lexeme());
            if (symIterator != symTable.end()) {   // test if symbol is defined in the symbol table
                // treat symbol as an operator
                while(!opStack.empty() && opStack.back().is_operator() && (
                            (token.fixity() == Associativity::LEFT && token.precedence() <= opStack.back().precedence()) ||
                            (token.fixity() == Associativity::RIGHT && token.precedence() < opStack.back().precedence()) )) {
                    rpnExpr.push_back(opStack.back());
                    opStack.pop_back();
                }
                opStack.push_back(token);
            }
            else {
                throw CompilerException::UnknownSymbol{token.text()};
            }
        }
        else if (token.type() == tuc::TokenType::INTEGER) {
            rpnExpr.push_back(token);
        }
        else if (token.type() == tuc::TokenType::LPAREN) {
            opStack.push_back(token);
        }
        else if (token.type() == tuc::TokenType::RPAREN) {
            auto t = opStack.back();
            while(t.type() != tuc::TokenType::LPAREN) {
                rpnExpr.push_back(t);
                opStack.pop_back();
                t = opStack.back();
            }
        }
        else if (token.type() == tuc::TokenType::SEMICOL) {
            while (!opStack.empty()) {
                rpnExpr.push_back(opStack.back());
                opStack.pop_back();
            }
            auto nodeStack = std::vector<std::unique_ptr<tuc::SyntaxNode>>{};
            for (auto t : rpnExpr) {
                auto node = std::make_unique<tuc::SyntaxNode>(t);
                if (node->is_operator()) {
                    auto secondVal = std::move(nodeStack.back());   // using `std::move()` causes `.back()` to exist
                    nodeStack.pop_back();                           // also note that node come out in reverse order
                    auto firstVal = std::move(nodeStack.back());
                    nodeStack.pop_back();
                    node->append_child(std::move(firstVal));
                    node->append_child(std::move(secondVal));
                    nodeStack.push_back(std::move(node));
                } else if (t.type() == tuc::TokenType::INTEGER) {
                    nodeStack.push_back(std::move(node));
                }
            }
            treeRoot->append_child(std::move(nodeStack.back()));
            rpnExpr.clear();
        }
    }

    return std::make_tuple(std::move(treeRoot), symTable);
}
<commit_msg>Remove transformation to RPN from praser.<commit_after>/*
Project: TUC
File: syntax_tree.cpp
Author: Leonardo Banderali
Created: September 6, 2015
Last Modified: November 4, 2015

Description:
    TUC is a simple, experimental compiler designed for learning and experimenting.
    It is not intended to have any useful purpose other than being a way to learn
    how compilers work.

Copyright (C) 2015 Leonardo Banderali

License:

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

// project headers
#include "syntax_tree.hpp"
#include "compiler_exceptions.hpp"

// standard libraries
#include <stdexcept>
#include <sstream>
#include <iostream>

//~class implementations~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

tuc::SyntaxNode::SyntaxNode(NodeType _type) : syntaxNodeType{_type} {}

tuc::SyntaxNode::SyntaxNode(NodeType _type, const TextEntity& _textValue)
: syntaxNodeType{_type}, textValue{_textValue} {}

/*
constructs a node from a syntax token
*/
tuc::SyntaxNode::SyntaxNode(const Token& _token)
: textValue{_token.text()} {
    switch (_token.type()) {
    case TokenType::TYPE:       syntaxNodeType = NodeType::TYPE; break;
    case TokenType::HASTYPE:    syntaxNodeType = NodeType::HASTYPE; break;
    case TokenType::ASSIGN:     syntaxNodeType = NodeType::ASSIGN; break;
    case TokenType::MAPTO:      syntaxNodeType = NodeType::MAPTO; break;
    case TokenType::ADD:        syntaxNodeType = NodeType::ADD; break;
    case TokenType::SUBTRACT:   syntaxNodeType = NodeType::SUBTRACT; break;
    case TokenType::MULTIPLY:   syntaxNodeType = NodeType::MULTIPLY; break;
    case TokenType::DIVIDE:     syntaxNodeType = NodeType::DIVIDE; break;
    case TokenType::INTEGER:    syntaxNodeType = NodeType::INTEGER; break;
    case TokenType::SEMICOL:    syntaxNodeType = NodeType::SEMICOL; break;
    case TokenType::IDENTIFIER: syntaxNodeType = NodeType::IDENTIFIER; break;
    default:                    syntaxNodeType = NodeType::UNKNOWN;
    }
}

tuc::SyntaxNode* tuc::SyntaxNode::parent() noexcept {
    return parentNode;
}

/*
returns child with index `i`
*/
tuc::SyntaxNode* tuc::SyntaxNode::child(int i) noexcept {
    return children[i].get();
}

int tuc::SyntaxNode::child_count() const noexcept {
    return children.size();
}

void tuc::SyntaxNode::append_child(NodeType _type, const TextEntity& _textValue) {
    children.push_back(std::make_unique<SyntaxNode>(_type, _textValue));
}

void tuc::SyntaxNode::append_child(const Token& _token) {
    children.push_back(std::make_unique<SyntaxNode>(_token));
}

/*  appends an already existing (allocated) child; since this node must "own" the child, move semantics *must*
    be used to transfer ownership.
*/
void tuc::SyntaxNode::append_child(std::unique_ptr<SyntaxNode>&& c) noexcept {
    children.push_back(std::move(c));
}

tuc::SyntaxNode::NodeType tuc::SyntaxNode::type() const noexcept {
    return syntaxNodeType;
}

bool tuc::SyntaxNode::is_operator() const noexcept {
    return syntaxNodeType == NodeType::ADD || syntaxNodeType == NodeType::SUBTRACT ||
            syntaxNodeType == NodeType::MULTIPLY || syntaxNodeType == NodeType::DIVIDE;
}

std::string tuc::SyntaxNode::value() const noexcept {
    return textValue.text();
}

int tuc::SyntaxNode::position() const noexcept {
    return textValue.position();
}

tuc::TextEntity tuc::SyntaxNode::text() const noexcept {
    return textValue;
}

//~function implementations~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/*
generate a syntax tree from a list of tokens
*/
std::tuple<std::unique_ptr<tuc::SyntaxNode>, tuc::SymbolTable> tuc::gen_syntax_tree(const std::vector<tuc::Token>& tokenList) {
    auto treeRoot = std::make_unique<tuc::SyntaxNode>(tuc::SyntaxNode::NodeType::PROGRAM);
    auto symTable = SymbolTable{};
    auto nodeStack = std::vector<std::unique_ptr<tuc::SyntaxNode>>{};
    auto opStack = std::vector<tuc::Token>{};

    /*##########################################################################################################
    ### To parse expressions, use a variation of Dijkstra's shunting yard algorithm. Instead of generating a  ##
    ### Reverse Polish Notation (RPN) expression, create syntax trees in equivalent to the expression and     ##
    ### add it to the main syntax tree at the end.                                                            ##
    ##########################################################################################################*/

    for (const auto token: tokenList) {
        if (token.is_operator() /*|| token.type() == tuc::TokenType::IDENTIFIER || token.type() == tuc::TokenType::HASTYPE*/) {
            while(!opStack.empty() && opStack.back().is_operator() && (
                        (token.fixity() == Associativity::LEFT && token.precedence() <= opStack.back().precedence()) ||
                        (token.fixity() == Associativity::RIGHT && token.precedence() < opStack.back().precedence()) )) {
                auto op = std::make_unique<tuc::SyntaxNode>(opStack.back());

                auto n2 = std::move(nodeStack.back());
                nodeStack.pop_back();
                auto n1 = std::move(nodeStack.back());
                nodeStack.pop_back();
                op->append_child(std::move(n1));
                op->append_child(std::move(n2));

                opStack.pop_back();
                nodeStack.push_back(std::move(op));
            }
            opStack.push_back(token);
        }
        else if (token.type() == tuc::TokenType::IDENTIFIER) {
            auto symIterator = symTable.find(token.lexeme());
            if (symIterator != symTable.end()) {   // test if symbol is defined in the symbol table
                // treat symbol as an operator
                while(!opStack.empty() && opStack.back().is_operator() && (
                            (token.fixity() == Associativity::LEFT && token.precedence() <= opStack.back().precedence()) ||
                            (token.fixity() == Associativity::RIGHT && token.precedence() < opStack.back().precedence()) )) {
                    auto op = std::make_unique<tuc::SyntaxNode>(opStack.back());

                    auto n2 = std::move(nodeStack.back());
                    nodeStack.pop_back();
                    auto n1 = std::move(nodeStack.back());
                    nodeStack.pop_back();
                    op->append_child(std::move(n1));
                    op->append_child(std::move(n2));

                    opStack.pop_back();
                    nodeStack.push_back(std::move(op));
                }
                opStack.push_back(token);
            }
            else {
                throw CompilerException::UnknownSymbol{token.text()};
            }
        }
        else if (token.type() == tuc::TokenType::INTEGER) {
            nodeStack.push_back(std::make_unique<tuc::SyntaxNode>(token));

        }
        else if (token.type() == tuc::TokenType::LPAREN) {
            opStack.push_back(token);
        }
        else if (token.type() == tuc::TokenType::RPAREN) {
            auto t = opStack.back();
            while(t.type() != tuc::TokenType::LPAREN) {
                auto op = std::make_unique<tuc::SyntaxNode>(opStack.back());

                auto n2 = std::move(nodeStack.back());
                nodeStack.pop_back();
                auto n1 = std::move(nodeStack.back());
                nodeStack.pop_back();
                op->append_child(std::move(n1));
                op->append_child(std::move(n2));

                nodeStack.push_back(std::move(op));
                opStack.pop_back();
                t = opStack.back();
            }
            opStack.pop_back();
        }
        else if (token.type() == tuc::TokenType::SEMICOL) {
            while (!opStack.empty()) {
                auto op = std::make_unique<tuc::SyntaxNode>(opStack.back());

                auto n2 = std::move(nodeStack.back());
                nodeStack.pop_back();
                auto n1 = std::move(nodeStack.back());
                nodeStack.pop_back();
                op->append_child(std::move(n1));
                op->append_child(std::move(n2));

                nodeStack.push_back(std::move(op));
                opStack.pop_back();
            }
            treeRoot->append_child(std::move(nodeStack.back()));
            nodeStack.clear();
        }
    }

    return std::make_tuple(std::move(treeRoot), symTable);
}
<|endoftext|>


<commit_before>// <<<<<<< HEAD
// #include "game.hpp"
// #include "component_drawable.hpp"
// #include "component_position.hpp"

// #include "strapon/resource_manager/resource_manager.hpp"

// #include "entityx/entityx.h"
// #include <glm/vec2.hpp>
// #include <SDL2/SDL.h>
// #include <SDL2/SDL_image.h>

// // class DrawSystem : public entityx::System<DrawSystem>
// {
// public:
// 	DrawSystem(Game *game) : m_game(game) {
//         int w, h;
//         SDL_RenderGetLogicalSize(game->get_renderer(), &w, &h);
//         m_camera = SDL_Rect{0, 0, w, h};
//     }

// 	void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override
// 	{
//         SDL_SetRenderDrawColor(m_game->get_renderer(), 0, 100, 200, 255);
//         SDL_RenderClear(m_game->get_renderer());

// 		entityx::ComponentHandle<Drawable> drawable;
// 		entityx::ComponentHandle<Position> position;
// 		for (entityx::Entity entity : es.entities_with_components(drawable, position))
// 		{

// 			(void)entity;
// 			SDL_Rect dest;

// 			dest.x = position->get_position()[0];
// 			std::cout << position->get_position()[0] << std::endl;
// 			dest.y = position->get_position()[1];
// 			std::cout << position->get_position()[1] << std::endl;
// 			dest.w = drawable->get_width();
// 			std::cout << drawable->get_width() << std::endl;
// 			dest.h = drawable->get_height();
// 			std::cout << drawable->get_height() << std::endl;
// 			SDL_RenderCopy(m_game->get_renderer(),
// 						   m_game->get_res_manager().get_texture(drawable->get_texture_key()),
// 						   NULL,
// 						   &dest);
//     	}

//         SDL_SetRenderDrawColor(m_game->get_renderer(), 255, 100, 200, 255);
//         SDL_Rect rect{0, 0, 400, 400};
//         SDL_RenderFillRect(m_game->get_renderer(), &rect);
//         SDL_RenderPresent(m_game->get_renderer());
// 	}

// private:
// 	Game *m_game;
//     SDL_Rect m_camera;
// };
// =======
#include "game.hpp"
#include "component_drawable.hpp"
#include "component_position.hpp"

#include "strapon/resource_manager/resource_manager.hpp"

#include "entityx/entityx.h"
#include <glm/vec2.hpp>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>

class DrawSystem : public entityx::System<DrawSystem>
{
public:
	DrawSystem(Game *game) : m_game(game) {
        int w, h;
        SDL_RenderGetLogicalSize(game->get_renderer(), &w, &h);
        m_camera = SDL_Rect{0, 0, w, h};
        m_drawtex = SDL_CreateTexture(game->get_renderer(), SDL_PIXELTYPE_UNKNOWN, SDL_TEXTUREACCESS_TARGET, game->get_worldsize().w, game->get_worldsize().h);
    }

    ~DrawSystem() {
        SDL_DestroyTexture(m_drawtex);
    }

	void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override
	{
        SDL_SetRenderTarget(m_game->get_renderer(), m_drawtex);
        SDL_SetRenderDrawColor(m_game->get_renderer(), 0, 100, 200, 255);
        SDL_RenderClear(m_game->get_renderer());

		entityx::ComponentHandle<Drawable> drawable;
		entityx::ComponentHandle<Position> position;
		for (entityx::Entity entity : es.entities_with_components(drawable, position))
		{
			(void)entity;

			SDL_Rect dest;

			dest.x = position->get_position()[0];
			std::cout << position->get_position()[0] << std::endl;
			dest.y = position->get_position()[1];
			std::cout << position->get_position()[1] << std::endl;
			dest.w = drawable->get_width();
			std::cout << drawable->get_width() << std::endl;
			dest.h = drawable->get_height();
			std::cout << drawable->get_height() << std::endl;

			SDL_RenderCopy(m_game->get_renderer(),
						   m_game->get_res_manager().get_texture(drawable->get_texture_key()),
						   NULL,
						   &dest);
    	}

        SDL_SetRenderDrawColor(m_game->get_renderer(), 255, 100, 200, 255);
        SDL_Rect rect{0, 0, 400, 400};
        SDL_RenderFillRect(m_game->get_renderer(), &rect);

        int x, y;
        SDL_GetMouseState(&x, &y);
        m_camera.x = x - m_camera.w/2;
        m_camera.y = y - m_camera.h/2;

        if (m_camera.x < 0)
            m_camera.x = 0;
        else if (m_camera.x > m_game->get_worldsize().w - m_camera.w)
            m_camera.x = m_game->get_worldsize().w - m_camera.w;

        if (m_camera.y < 0)
            m_camera.y = 0;
        else if (m_camera.y > m_game->get_worldsize().h - m_camera.h)
            m_camera.y = m_game->get_worldsize().h - m_camera.h;

        std::cout << m_camera.x << " " << m_camera.y << std::endl;

        SDL_SetRenderTarget(m_game->get_renderer(), nullptr);
        SDL_RenderCopy(m_game->get_renderer(), m_drawtex, &m_camera, nullptr);
        SDL_RenderPresent(m_game->get_renderer());
	}

private:
	Game *m_game;
    SDL_Rect m_camera;
    SDL_Texture *m_drawtex;
};
<commit_msg>camera now follows the player<commit_after>// <<<<<<< HEAD
// #include "game.hpp"
// #include "component_drawable.hpp"
// #include "component_position.hpp"

// #include "strapon/resource_manager/resource_manager.hpp"

// #include "entityx/entityx.h"
// #include <glm/vec2.hpp>
// #include <SDL2/SDL.h>
// #include <SDL2/SDL_image.h>

// // class DrawSystem : public entityx::System<DrawSystem>
// {
// public:
// 	DrawSystem(Game *game) : m_game(game) {
//         int w, h;
//         SDL_RenderGetLogicalSize(game->get_renderer(), &w, &h);
//         m_camera = SDL_Rect{0, 0, w, h};
//     }

// 	void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override
// 	{
//         SDL_SetRenderDrawColor(m_game->get_renderer(), 0, 100, 200, 255);
//         SDL_RenderClear(m_game->get_renderer());

// 		entityx::ComponentHandle<Drawable> drawable;
// 		entityx::ComponentHandle<Position> position;
// 		for (entityx::Entity entity : es.entities_with_components(drawable, position))
// 		{

// 			(void)entity;
// 			SDL_Rect dest;

// 			dest.x = position->get_position()[0];
// 			std::cout << position->get_position()[0] << std::endl;
// 			dest.y = position->get_position()[1];
// 			std::cout << position->get_position()[1] << std::endl;
// 			dest.w = drawable->get_width();
// 			std::cout << drawable->get_width() << std::endl;
// 			dest.h = drawable->get_height();
// 			std::cout << drawable->get_height() << std::endl;
// 			SDL_RenderCopy(m_game->get_renderer(),
// 						   m_game->get_res_manager().get_texture(drawable->get_texture_key()),
// 						   NULL,
// 						   &dest);
//     	}

//         SDL_SetRenderDrawColor(m_game->get_renderer(), 255, 100, 200, 255);
//         SDL_Rect rect{0, 0, 400, 400};
//         SDL_RenderFillRect(m_game->get_renderer(), &rect);
//         SDL_RenderPresent(m_game->get_renderer());
// 	}

// private:
// 	Game *m_game;
//     SDL_Rect m_camera;
// };
// =======
#include "game.hpp"
#include "component_drawable.hpp"
#include "component_position.hpp"

#include "strapon/resource_manager/resource_manager.hpp"

#include "entityx/entityx.h"
#include <glm/vec2.hpp>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>

class DrawSystem : public entityx::System<DrawSystem>
{
public:
	DrawSystem(Game *game) : m_game(game) {
        int w, h;
        SDL_RenderGetLogicalSize(game->get_renderer(), &w, &h);
        m_camera = SDL_Rect{0, 0, w, h};
        m_drawtex = SDL_CreateTexture(game->get_renderer(), SDL_PIXELTYPE_UNKNOWN, SDL_TEXTUREACCESS_TARGET, game->get_worldsize().w, game->get_worldsize().h);
    }

    ~DrawSystem() {
        SDL_DestroyTexture(m_drawtex);
    }

	void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override
	{
        SDL_SetRenderTarget(m_game->get_renderer(), m_drawtex);
        SDL_SetRenderDrawColor(m_game->get_renderer(), 0, 100, 200, 255);
        SDL_RenderClear(m_game->get_renderer());

		entityx::ComponentHandle<Drawable> drawable;
		entityx::ComponentHandle<Position> position;
		entityx::ComponentHandle<Controlable> controlable;
		for (entityx::Entity entity : es.entities_with_components(drawable, position))
		{
			(void)entity;

			SDL_Rect dest;

			dest.x = position->get_position()[0];
			std::cout << position->get_position()[0] << std::endl;
			dest.y = position->get_position()[1];
			std::cout << position->get_position()[1] << std::endl;
			dest.w = drawable->get_width();
			std::cout << drawable->get_width() << std::endl;
			dest.h = drawable->get_height();
			std::cout << drawable->get_height() << std::endl;

			SDL_RenderCopy(m_game->get_renderer(),
						   m_game->get_res_manager().get_texture(drawable->get_texture_key()),
						   NULL,
						   &dest);
    	}

        SDL_SetRenderDrawColor(m_game->get_renderer(), 255, 100, 200, 255);
        SDL_Rect rect{0, 0, 400, 400};
        SDL_RenderFillRect(m_game->get_renderer(), &rect);
        for (entityx::Entity player : es.entities_with_components(controlable, position))
 		{
 			(void)player;
	        //SDL_GetMouseState(&x, &y);
	        m_camera.x = position->get_position()[0] - m_camera.w/2;
	        m_camera.y = position->get_position()[1] - m_camera.h/2;

	        if (m_camera.x < 0)
	            m_camera.x = 0;
	        else if (m_camera.x > m_game->get_worldsize().w - m_camera.w)
	            m_camera.x = m_game->get_worldsize().w - m_camera.w;

	        if (m_camera.y < 0)
	            m_camera.y = 0;
	        else if (m_camera.y > m_game->get_worldsize().h - m_camera.h)
	            m_camera.y = m_game->get_worldsize().h - m_camera.h;

	        std::cout << m_camera.x << " " << m_camera.y << std::endl;
        }
        SDL_SetRenderTarget(m_game->get_renderer(), nullptr);
        SDL_RenderCopy(m_game->get_renderer(), m_drawtex, &m_camera, nullptr);
        SDL_RenderPresent(m_game->get_renderer());
	}

private:
	Game *m_game;
    SDL_Rect m_camera;
    SDL_Texture *m_drawtex;
};
<|endoftext|>


<commit_before>#include "tarsnaptask.h"
#include "utils.h"
#include "debug.h"

#if defined Q_OS_UNIX
#include <signal.h>
#endif

#define DEFAULT_TIMEOUT_MS 5000
#define LOG_MAX_LENGTH 10240

TarsnapTask::TarsnapTask()
    : QObject(), _process(nullptr), _truncateLogOutput(false)
{
}

TarsnapTask::~TarsnapTask()
{
}

void TarsnapTask::run()
{
    _process = new QProcess();
    if(_standardOutFile.isEmpty())
        _process->setProcessChannelMode(QProcess::MergedChannels);
    else
        _process->setStandardOutputFile(_standardOutFile);
    _process->setProgram(_command);
    _process->setArguments(_arguments);
    LOG << tr("Executing command:\n[%1 %2]")
               .arg(_process->program())
               .arg(Utils::quoteCommandLine(_process->arguments()));
    _process->start();
    if(_process->waitForStarted(DEFAULT_TIMEOUT_MS))
    {
        emit started(_data);
    }
    else
    {
        processError();
        goto cleanup;
    }
    if(!_standardIn.isEmpty())
    {
        QByteArray password(_standardIn.toUtf8());
        _process->write(password.data(), password.size());
        _process->closeWriteChannel();
    }
    if(_process->waitForFinished(-1))
    {
        readProcessOutput();
        processFinished();
    }
    else
    {
        processError();
        goto cleanup;
    }
cleanup:
    delete _process;
    _process = nullptr;
    emit dequeue();
}

void TarsnapTask::stop(bool kill)
{
    if(_process->state() == QProcess::Running)
    {
        _process->terminate();
        if(kill && (false == _process->waitForFinished(DEFAULT_TIMEOUT_MS)))
            _process->kill();
    }
}

void TarsnapTask::interrupt()
{
#if defined Q_OS_UNIX
    kill(_process->pid(), SIGQUIT);
#endif
}

void TarsnapTask::cancel()
{
    emit canceled(_data);
}

bool TarsnapTask::waitForTask()
{
    return _process->waitForFinished(-1);
}

QProcess::ProcessState TarsnapTask::taskStatus()
{
    return _process->state();
}

QString TarsnapTask::command() const
{
    return _command;
}

void TarsnapTask::setCommand(const QString &command)
{
    _command = command;
}

QStringList TarsnapTask::arguments() const
{
    return _arguments;
}

void TarsnapTask::setArguments(const QStringList &arguments)
{
    _arguments = arguments;
}

QString TarsnapTask::standardIn() const
{
    return _standardIn;
}

void TarsnapTask::setStandardIn(const QString &standardIn)
{
    _standardIn = standardIn;
}

void TarsnapTask::setStandardOutputFile(const QString &fileName)
{
    _standardOutFile = fileName;
}

QVariant TarsnapTask::data() const
{
    return _data;
}

void TarsnapTask::setData(const QVariant &data)
{
    _data = data;
}

bool TarsnapTask::truncateLogOutput() const
{
    return _truncateLogOutput;
}

void TarsnapTask::setTruncateLogOutput(bool truncateLogOutput)
{
    _truncateLogOutput = truncateLogOutput;
}

void TarsnapTask::readProcessOutput()
{
    if(_process->processChannelMode() == QProcess::MergedChannels)
        _processOutput.append(_process->readAll());
    else
        _processOutput.append(_process->readAllStandardError());
}

void TarsnapTask::processFinished()
{
    switch(_process->exitStatus())
    {
    case QProcess::NormalExit:
    {
        QString output(_processOutput);
        output = output.trimmed();
        emit finished(_data, _process->exitCode(), output);
        if(!output.isEmpty())
        {
            if(_truncateLogOutput && (output.size() > LOG_MAX_LENGTH))
            {
                output.truncate(LOG_MAX_LENGTH);
                output.append(tr("\n...\n-- Output truncated by Tarsnap GUI --"));
            }
            LOG << tr("Command finished with exit code %3 and output:\n[%1 %2]\n%4")
                       .arg(_command)
                       .arg(Utils::quoteCommandLine(_arguments))
                       .arg(_process->exitCode())
                       .arg(output);
        }
        else
        {
            LOG << tr("Command finished with exit code %3 and no output:\n[%1 %2]")
                       .arg(_command)
                       .arg(Utils::quoteCommandLine(_arguments))
                       .arg(_process->exitCode());
        }
    }
        break;
    case QProcess::CrashExit:
        processError();
        break;
    }
}

void TarsnapTask::processError()
{
    LOG << tr("Tarsnap process error %1 (%2) occured (exit code %3):\n%4")
               .arg(_process->error())
               .arg(_process->errorString())
               .arg(_process->exitCode())
               .arg(QString(_processOutput).trimmed());
    cancel();
}
<commit_msg>Add missing command-line to log message.<commit_after>#include "tarsnaptask.h"
#include "utils.h"
#include "debug.h"

#if defined Q_OS_UNIX
#include <signal.h>
#endif

#define DEFAULT_TIMEOUT_MS 5000
#define LOG_MAX_LENGTH 10240

TarsnapTask::TarsnapTask()
    : QObject(), _process(nullptr), _truncateLogOutput(false)
{
}

TarsnapTask::~TarsnapTask()
{
}

void TarsnapTask::run()
{
    _process = new QProcess();
    if(_standardOutFile.isEmpty())
        _process->setProcessChannelMode(QProcess::MergedChannels);
    else
        _process->setStandardOutputFile(_standardOutFile);
    _process->setProgram(_command);
    _process->setArguments(_arguments);
    LOG << tr("Executing command:\n[%1 %2]")
               .arg(_process->program())
               .arg(Utils::quoteCommandLine(_process->arguments()));
    _process->start();
    if(_process->waitForStarted(DEFAULT_TIMEOUT_MS))
    {
        emit started(_data);
    }
    else
    {
        processError();
        goto cleanup;
    }
    if(!_standardIn.isEmpty())
    {
        QByteArray password(_standardIn.toUtf8());
        _process->write(password.data(), password.size());
        _process->closeWriteChannel();
    }
    if(_process->waitForFinished(-1))
    {
        readProcessOutput();
        processFinished();
    }
    else
    {
        processError();
        goto cleanup;
    }
cleanup:
    delete _process;
    _process = nullptr;
    emit dequeue();
}

void TarsnapTask::stop(bool kill)
{
    if(_process->state() == QProcess::Running)
    {
        _process->terminate();
        if(kill && (false == _process->waitForFinished(DEFAULT_TIMEOUT_MS)))
            _process->kill();
    }
}

void TarsnapTask::interrupt()
{
#if defined Q_OS_UNIX
    kill(_process->pid(), SIGQUIT);
#endif
}

void TarsnapTask::cancel()
{
    emit canceled(_data);
}

bool TarsnapTask::waitForTask()
{
    return _process->waitForFinished(-1);
}

QProcess::ProcessState TarsnapTask::taskStatus()
{
    return _process->state();
}

QString TarsnapTask::command() const
{
    return _command;
}

void TarsnapTask::setCommand(const QString &command)
{
    _command = command;
}

QStringList TarsnapTask::arguments() const
{
    return _arguments;
}

void TarsnapTask::setArguments(const QStringList &arguments)
{
    _arguments = arguments;
}

QString TarsnapTask::standardIn() const
{
    return _standardIn;
}

void TarsnapTask::setStandardIn(const QString &standardIn)
{
    _standardIn = standardIn;
}

void TarsnapTask::setStandardOutputFile(const QString &fileName)
{
    _standardOutFile = fileName;
}

QVariant TarsnapTask::data() const
{
    return _data;
}

void TarsnapTask::setData(const QVariant &data)
{
    _data = data;
}

bool TarsnapTask::truncateLogOutput() const
{
    return _truncateLogOutput;
}

void TarsnapTask::setTruncateLogOutput(bool truncateLogOutput)
{
    _truncateLogOutput = truncateLogOutput;
}

void TarsnapTask::readProcessOutput()
{
    if(_process->processChannelMode() == QProcess::MergedChannels)
        _processOutput.append(_process->readAll());
    else
        _processOutput.append(_process->readAllStandardError());
}

void TarsnapTask::processFinished()
{
    switch(_process->exitStatus())
    {
    case QProcess::NormalExit:
    {
        QString output(_processOutput);
        output = output.trimmed();
        emit finished(_data, _process->exitCode(), output);
        if(!output.isEmpty())
        {
            if(_truncateLogOutput && (output.size() > LOG_MAX_LENGTH))
            {
                output.truncate(LOG_MAX_LENGTH);
                output.append(tr("\n...\n-- Output truncated by Tarsnap GUI --"));
            }
            LOG << tr("Command finished with exit code %3 and output:\n[%1 %2]\n%4")
                       .arg(_command)
                       .arg(Utils::quoteCommandLine(_arguments))
                       .arg(_process->exitCode())
                       .arg(output);
        }
        else
        {
            LOG << tr("Command finished with exit code %3 and no output:\n[%1 %2]")
                       .arg(_command)
                       .arg(Utils::quoteCommandLine(_arguments))
                       .arg(_process->exitCode());
        }
    }
        break;
    case QProcess::CrashExit:
        processError();
        break;
    }
}

void TarsnapTask::processError()
{
    LOG << tr("Tarsnap process error %3 (%4) occured (exit code %5):\n[%1 %2]\n%6")
               .arg(_command)
               .arg(Utils::quoteCommandLine(_arguments))
               .arg(_process->error())
               .arg(_process->errorString())
               .arg(_process->exitCode())
               .arg(QString(_processOutput).trimmed());
    cancel();
}
<|endoftext|>


<commit_before>#ifndef __TEXT_SERVER_H__
#define __TEXT_SERVER_H__

#include "connection.hpp"

#include <arpa/inet.h>
#include <errno.h>
#include <string.h> // memset
#include <unistd.h>

#include <thread>

namespace httptth
{

class scoped_fd
{
public:
    explicit scoped_fd(int descriptor) : fd(descriptor) {}
    ~scoped_fd() { close(fd); }
    scoped_fd(const scoped_fd &) = delete;
    scoped_fd(scoped_fd &&that) : fd(that.fd) { that.fd = -1; }
    scoped_fd &operator =(const scoped_fd &) = delete;

    inline operator int() { return fd; }
private:
    int fd;
};

// generic line-based text server
template<typename handler>
class text_server
{
public:
    explicit text_server(const handler &h) :
        prototype(h),
        stop_requested(false)
    {}
    explicit text_server(handler &&h) :
        prototype(std::move(h)),
        stop_requested(false)
    {}
    text_server(const text_server &) = delete;
    text_server &operator =(const text_server &) = delete;
    text_server(const text_server &&) = delete;
    text_server &operator =(const text_server &&) = delete;

    void listen(const char *address, int port);
    inline void stop() { stop_requested = true; }

protected:
    struct context
    {
        handler    h;
        connection conn;
        // context must always copy the handler
        context(int fd, const handler &hdl) :
            h(hdl),
            conn(fd, ([&](const char *s, size_t l) { return h(s, l); }))
        {}
    };

private:
    handler prototype;
    bool    stop_requested;
    static const time_t timeout = 5;

    void process_client(int fd);

};

template<typename handler>
void text_server<handler>::listen(const char *address, int port)
{
    sockaddr_in addr;

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);

    if (address) {
        int status = inet_pton(AF_INET, address, &addr.sin_addr);
        if (status <= 0) {
            throw generic_exception();
        }
    } else {
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
    }

    scoped_fd listenfd(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
    if (listenfd < 0) {
        perror("socket");
        throw network_exception();
    }

    int e = 1;
    int rc = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &e, sizeof e);
    if (rc < 0) {
        dprintf(logger::ERR, "setsockopt");
        throw network_exception();
    }

    rc = bind(listenfd, (sockaddr *)&addr, sizeof(addr));
    if (rc < 0) {
        perror("bind");
        throw network_exception();
    }

    rc = ::listen(listenfd, 128);
    if (rc < 0) {
        perror("listen");
        throw network_exception();
    }

    // trivial event loop
    // later: init "listen thread" and detach it
    struct timeval tv;
    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    stop_requested = false;

    while (true) {
        // TODO: loop exit condition
        if (stop_requested) {
            // possibly log something later
            break;
        }

        sockaddr_in client_addr;
        socklen_t l = sizeof(client_addr);
        int fd = accept(listenfd, (sockaddr *)&client_addr, &l);
        if (fd < 0) {
            perror("accept failed");
            continue;
        }

        setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval));
        // status: read() times out, but this is not handled yet

        std::thread t(&text_server<handler>::process_client, this, fd);
        t.detach();
    } // while (true
}

template<typename handler>
void text_server<handler>::process_client(int fd)
{
    try {
        dprintf(logger::DEBUG, "client fd=%d", fd);
        context ctx(fd, prototype);

        // bind the third parameter (or use std::bind instead)
        auto read_handler = [&ctx](const char *line, size_t length) {
            dprintf(logger::DEBUG, "\\(l=%zu) %.*s", length, (int)length, line);
            ctx.h(line, length, ctx.conn);
        };
        readable::handler_type f(std::ref(read_handler));

        // actually, that could be enough:
        // while (ctx.conn.read_line(f) == readable::status::ok) ;

        while (true) {
            readable::status status = ctx.conn.read_line(f);

            if (status == readable::status::eof) {
                break;
            }

            if (status == readable::status::again) {
                // consider this a timeout
                // a problem: some leftover bytes might still be in the buffer
                // but do we care, as we are about to disconnect this client?
                dprintf(logger::DEBUG, "client %d timed out", fd);
                break;
            }

        } // while (true)
        dprintf(logger::DEBUG, "client fd=%d: done", fd);
        // close (and therefore flush) should be called automatically
    } catch (generic_exception &e) {
        dprintf(logger::WARNING, "excpetion: %s", e.what());
    } catch (std::exception &e) {
        dprintf(logger::WARNING, "std::excpetion: %s", e.what());
    }
}

// default implementation (with triggering on each new line)

// a handler that is a pure function, without a trigger
using stateless_handler = std::function<void(const char *, size_t, writable &)>;

// handler_type implementation that triggers client function on each new line
struct on_new_line
{
    on_new_line(const stateless_handler &f) : h(f) {}
    on_new_line(stateless_handler &&f) : h(std::move(f)) {}
    void operator()(const char *s, size_t l, writable &dst) { h(s, l, dst); }
    bool operator()(const char *s, size_t l) { return s[l - 1] == '\n'; }
    stateless_handler h;
};

// text server with on_new_line handler
class line_feed_text_server : public text_server<on_new_line> {
public:
    line_feed_text_server(stateless_handler h) :
        text_server<on_new_line>(on_new_line(h))
    {}
};

} // namespace httptth

#endif // __TEXT_SERVER_H__
<commit_msg>separate constructors for const lvalue and rvalue references in line feed server, length check in on new line trigger<commit_after>#ifndef __TEXT_SERVER_H__
#define __TEXT_SERVER_H__

#include "connection.hpp"

#include <arpa/inet.h>
#include <errno.h>
#include <string.h> // memset
#include <unistd.h>

#include <thread>

namespace httptth
{

class scoped_fd
{
public:
    explicit scoped_fd(int descriptor) : fd(descriptor) {}
    ~scoped_fd() { close(fd); }
    scoped_fd(const scoped_fd &) = delete;
    scoped_fd(scoped_fd &&that) : fd(that.fd) { that.fd = -1; }
    scoped_fd &operator =(const scoped_fd &) = delete;

    inline operator int() { return fd; }
private:
    int fd;
};

// generic line-based text server
template<typename handler>
class text_server
{
public:
    explicit text_server(const handler &h) :
        prototype(h),
        stop_requested(false)
    {}
    explicit text_server(handler &&h) :
        prototype(std::move(h)),
        stop_requested(false)
    {}
    text_server(const text_server &) = delete;
    text_server &operator =(const text_server &) = delete;
    text_server(const text_server &&) = delete;
    text_server &operator =(const text_server &&) = delete;

    void listen(const char *address, int port);
    inline void stop() { stop_requested = true; }

protected:
    struct context
    {
        handler    h;
        connection conn;
        // context must always copy the handler
        context(int fd, const handler &hdl) :
            h(hdl),
            conn(fd, ([&](const char *s, size_t l) { return h(s, l); }))
        {}
    };

private:
    handler prototype;
    bool    stop_requested;
    static const time_t timeout = 5;

    void process_client(int fd);

};

template<typename handler>
void text_server<handler>::listen(const char *address, int port)
{
    sockaddr_in addr;

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);

    if (address) {
        int status = inet_pton(AF_INET, address, &addr.sin_addr);
        if (status <= 0) {
            throw generic_exception();
        }
    } else {
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
    }

    scoped_fd listenfd(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
    if (listenfd < 0) {
        perror("socket");
        throw network_exception();
    }

    int e = 1;
    int rc = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &e, sizeof e);
    if (rc < 0) {
        dprintf(logger::ERR, "setsockopt");
        throw network_exception();
    }

    rc = bind(listenfd, (sockaddr *)&addr, sizeof(addr));
    if (rc < 0) {
        perror("bind");
        throw network_exception();
    }

    rc = ::listen(listenfd, 128);
    if (rc < 0) {
        perror("listen");
        throw network_exception();
    }

    // trivial event loop
    // later: init "listen thread" and detach it
    struct timeval tv;
    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    stop_requested = false;

    while (true) {
        // TODO: loop exit condition
        if (stop_requested) {
            // possibly log something later
            break;
        }

        sockaddr_in client_addr;
        socklen_t l = sizeof(client_addr);
        int fd = accept(listenfd, (sockaddr *)&client_addr, &l);
        if (fd < 0) {
            perror("accept failed");
            continue;
        }

        setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval));
        // status: read() times out, but this is not handled yet

        std::thread t(&text_server<handler>::process_client, this, fd);
        t.detach();
    } // while (true
}

template<typename handler>
void text_server<handler>::process_client(int fd)
{
    try {
        dprintf(logger::DEBUG, "client fd=%d", fd);
        context ctx(fd, prototype);

        // bind the third parameter (or use std::bind instead)
        auto read_handler = [&ctx](const char *line, size_t length) {
            dprintf(logger::DEBUG, "\\(l=%zu) %.*s", length, (int)length, line);
            ctx.h(line, length, ctx.conn);
        };
        readable::handler_type f(std::ref(read_handler));

        // actually, that could be enough:
        // while (ctx.conn.read_line(f) == readable::status::ok) ;

        while (true) {
            readable::status status = ctx.conn.read_line(f);

            if (status == readable::status::eof) {
                break;
            }

            if (status == readable::status::again) {
                // consider this a timeout
                // a problem: some leftover bytes might still be in the buffer
                // but do we care, as we are about to disconnect this client?
                dprintf(logger::DEBUG, "client %d timed out", fd);
                break;
            }

        } // while (true)
        dprintf(logger::DEBUG, "client fd=%d: done", fd);
        // close (and therefore flush) should be called automatically
    } catch (generic_exception &e) {
        dprintf(logger::WARNING, "excpetion: %s", e.what());
    } catch (std::exception &e) {
        dprintf(logger::WARNING, "std::excpetion: %s", e.what());
    }
}

// default implementation (with triggering on each new line)

// a handler that is a pure function, without a trigger
using stateless_handler = std::function<void(const char *, size_t, writable &)>;

// handler_type implementation that triggers client function on each new line
struct on_new_line
{
    on_new_line(const stateless_handler &f) : h(f) {}
    on_new_line(stateless_handler &&f) : h(std::move(f)) {}
    void operator()(const char *s, size_t l, writable &dst) { h(s, l, dst); }
    bool operator()(const char *s, size_t l) { return l && s[l - 1] == '\n'; }
    stateless_handler h;
};

// text server with on_new_line handler
class line_feed_text_server : public text_server<on_new_line> {
public:
    line_feed_text_server(const stateless_handler &h) :
        text_server<on_new_line>(on_new_line(h))
    {}
    line_feed_text_server(stateless_handler &&h) :
        text_server<on_new_line>(on_new_line(std::move(h)))
    {}
};

} // namespace httptth

#endif // __TEXT_SERVER_H__
<|endoftext|>