<commit_before>#include "core/os.h"
#include "core/stringutils.h"

// https://stackoverflow.com/questions/612097/how-can-i-get-the-list-of-files-in-a-directory-using-c-or-c
// look into using https://github.com/cxong/tinydir instead of current platform hack...?

#include <iostream>

////////////////////////////////////////////////////////////////////////////////
// copied from https://stackoverflow.com/a/145309/180307

#include <cstdio> /* defines FILENAME_MAX */
#include <utility>
#ifdef _MSC_VER
#include "windows.h"
#include <direct.h>
#define GET_CURRENT_DIR _getcwd
#else
#include <dirent.h>
#include <unistd.h>
#include <vector>

#define GET_CURRENT_DIR getcwd
#endif

std::string
GetCurrentDirectory()
{
  char current_directory[FILENAME_MAX];

  if(!GET_CURRENT_DIR(
         static_cast<char*>(current_directory), sizeof(current_directory)))
  {
    return "";
  }

  current_directory[sizeof(current_directory) - 1] = 0;

  const std::string ret = static_cast<char*>(current_directory);
  return ret;
}

////////////////////////////////////////////////////////////////////////////////

#ifdef _MSC_VER

DirectoryList
ListDirectory(const std::string& path)
{
  const std::string search_path = Str() << path << "*.*";
  WIN32_FIND_DATA   fd;
  HANDLE            hFind = ::FindFirstFile(search_path.c_str(), &fd);

  if(hFind != INVALID_HANDLE_VALUE)
  {
    DirectoryList ret;
    ret.valid = true;

    do
    {
      // read all (real) files in current folder, delete '!' read other 2
      // default folder . and ..
      if(!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
      {
        ret.files.push_back(fd.cFileName);
      }
      else  // Put folders into vector
      {
        ret.directories.push_back(fd.cFileName);
      }
    } while(::FindNextFile(hFind, &fd));
    ::FindClose(hFind);

    return ret;
  }
  else
  {
    DirectoryList ret;
    ret.valid = false;
    return ret;
  }
}
#else
DirectoryList
ListDirectory(const std::string& path)
{
  DIR*           dir;
  struct dirent* ent;
  dir = opendir(path.c_str());
  if(dir != nullptr)
  {
    DirectoryList ret;
    ret.valid = true;

    /* print all the files and directories within directory */
    while((ent = readdir(dir)) != nullptr)
    {
      const std::string name = ent->d_name;
      if(ent->d_type == DT_REG)
      {
        ret.files.emplace_back(name);
      }
      else if(ent->d_type == DT_DIR)
      {
        if(name == "." || name == "..")
        {
        }
        else
        {
          ret.directories.emplace_back(name);
        }
      }
    }

    closedir(dir);
    return ret;
  }
  else
  {
    DirectoryList ret;
    ret.valid = false;
    return ret;
  }
}
#endif

bool
EndsWith(const std::string& str, char c)
{
  const auto l = str.length();

  if(l == 0)
  {
    return false;
  }

  return str[l - 1] == c;
}

std::string
JoinPath(const std::string& left, const std::string& right)
{
  if(EndsWith(left, PATH_SEPARATOR))
  {
    return left + right;
  }
  else
  {
    return left + PATH_SEPARATOR + right;
  }
}

std::string
GetExtension(const std::string& path)
{
  return LastStrings(path, '.').second;
}

std::string
GetFileNameIncludingExtension(const std::string& path)
{
  const auto r = LastStrings(path, PATH_SEPARATOR).second;
  if(r.empty())
  {
    return r;
  }
  else
  {
    // skip leading path_separator
    return r.substr(1);
  }
}

std::string
GetFileNameWithoutExtension(const std::string& path)
{
  return LastStrings(GetFileNameIncludingExtension(path), '.').first;
}
<commit_msg>possible fix for windows macros<commit_after>#include "core/os.h"
#include "core/stringutils.h"

// https://stackoverflow.com/questions/612097/how-can-i-get-the-list-of-files-in-a-directory-using-c-or-c
// look into using https://github.com/cxong/tinydir instead of current platform
// hack...?

#include <iostream>

////////////////////////////////////////////////////////////////////////////////
// copied from https://stackoverflow.com/a/145309/180307

#include <cstdio> /* defines FILENAME_MAX */
#include <utility>
#ifdef _MSC_VER
#include "windows.h"
#include <direct.h>
#define GET_CURRENT_DIR _getcwd

#ifdef GetCurrentDirectory
#undef GetCurrentDirectory
#endif
#else
#include <dirent.h>
#include <unistd.h>
#include <vector>

#define GET_CURRENT_DIR getcwd
#endif

std::string
GetCurrentDirectory()
{
  char current_directory[FILENAME_MAX];

  if(!GET_CURRENT_DIR(
         static_cast<char*>(current_directory), sizeof(current_directory)))
  {
    return "";
  }

  current_directory[sizeof(current_directory) - 1] = 0;

  const std::string ret = static_cast<char*>(current_directory);
  return ret;
}

////////////////////////////////////////////////////////////////////////////////

#ifdef _MSC_VER

DirectoryList
ListDirectory(const std::string& path)
{
  const std::string search_path = Str() << path << "*.*";
  WIN32_FIND_DATA   fd;
  HANDLE            hFind = ::FindFirstFile(search_path.c_str(), &fd);

  if(hFind != INVALID_HANDLE_VALUE)
  {
    DirectoryList ret;
    ret.valid = true;

    do
    {
      // read all (real) files in current folder, delete '!' read other 2
      // default folder . and ..
      if(!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
      {
        ret.files.push_back(fd.cFileName);
      }
      else  // Put folders into vector
      {
        ret.directories.push_back(fd.cFileName);
      }
    } while(::FindNextFile(hFind, &fd));
    ::FindClose(hFind);

    return ret;
  }
  else
  {
    DirectoryList ret;
    ret.valid = false;
    return ret;
  }
}
#else
DirectoryList
ListDirectory(const std::string& path)
{
  DIR*           dir;
  struct dirent* ent;
  dir = opendir(path.c_str());
  if(dir != nullptr)
  {
    DirectoryList ret;
    ret.valid = true;

    /* print all the files and directories within directory */
    while((ent = readdir(dir)) != nullptr)
    {
      const std::string name = ent->d_name;
      if(ent->d_type == DT_REG)
      {
        ret.files.emplace_back(name);
      }
      else if(ent->d_type == DT_DIR)
      {
        if(name == "." || name == "..")
        {
        }
        else
        {
          ret.directories.emplace_back(name);
        }
      }
    }

    closedir(dir);
    return ret;
  }
  else
  {
    DirectoryList ret;
    ret.valid = false;
    return ret;
  }
}
#endif

bool
EndsWith(const std::string& str, char c)
{
  const auto l = str.length();

  if(l == 0)
  {
    return false;
  }

  return str[l - 1] == c;
}

std::string
JoinPath(const std::string& left, const std::string& right)
{
  if(EndsWith(left, PATH_SEPARATOR))
  {
    return left + right;
  }
  else
  {
    return left + PATH_SEPARATOR + right;
  }
}

std::string
GetExtension(const std::string& path)
{
  return LastStrings(path, '.').second;
}

std::string
GetFileNameIncludingExtension(const std::string& path)
{
  const auto r = LastStrings(path, PATH_SEPARATOR).second;
  if(r.empty())
  {
    return r;
  }
  else
  {
    // skip leading path_separator
    return r.substr(1);
  }
}

std::string
GetFileNameWithoutExtension(const std::string& path)
{
  return LastStrings(GetFileNameIncludingExtension(path), '.').first;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017-2018 Baidu Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "openrasp_hook.h"
#include "openrasp_v8.h"

int include_handler(ZEND_OPCODE_HANDLER_ARGS);
int eval_handler(ZEND_OPCODE_HANDLER_ARGS);
int include_or_eval_handler(ZEND_OPCODE_HANDLER_ARGS)
{
    if (OPENRASP_INCLUDE_OR_EVAL_TYPE(execute_data->opline) == ZEND_EVAL)
    {
        return eval_handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
    }
    else
    {
        return include_handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
    }
}
int eval_handler(ZEND_OPCODE_HANDLER_ARGS)
{
    zend_op *opline = execute_data->opline;
    if (!openrasp_check_type_ignored(ZEND_STRL("webshell_eval") TSRMLS_CC) &&
        OPENRASP_OP1_TYPE(opline) == IS_VAR &&
        openrasp_zval_in_request(OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr TSRMLS_CC))
    {
        zval *attack_params;
        MAKE_STD_ZVAL(attack_params);
        array_init(attack_params);
        add_assoc_zval(attack_params, "eval", OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr);
        Z_ADDREF_P(OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr);
        zval *plugin_message = NULL;
        MAKE_STD_ZVAL(plugin_message);
        ZVAL_STRING(plugin_message, _("WebShell activity - Detected China Chopper webshell (eval method)"), 1);
        openrasp_buildin_php_risk_handle(1, "webshell_eval", 100, attack_params, plugin_message TSRMLS_CC);
    }
    return ZEND_USER_OPCODE_DISPATCH;
}
int include_handler(ZEND_OPCODE_HANDLER_ARGS)
{
    zend_op *opline = execute_data->opline;
    zval *op1 = nullptr;
    switch (OPENRASP_OP1_TYPE(opline))
    {
    case IS_TMP_VAR:
    {
        op1 = &OPENRASP_T(OPENRASP_OP1_VAR(opline)).tmp_var;
        break;
    }
    case IS_VAR:
    {
        // whether the parameter is the user input data
        if (!openrasp_check_type_ignored(ZEND_STRL("webshell_include") TSRMLS_CC)
        && openrasp_zval_in_request(OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr TSRMLS_CC))
        {
            zval *attack_params;
            MAKE_STD_ZVAL(attack_params);
            array_init(attack_params);
            add_assoc_zval(attack_params, "url", OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr);
            Z_ADDREF_P(OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr);
            zval *plugin_message = NULL;
            MAKE_STD_ZVAL(plugin_message);
            ZVAL_STRING(plugin_message, _("File inclusion - Detected include/require vulnerability or webshell based on the same mechanism"), 1);
            openrasp_buildin_php_risk_handle(1, "webshell_include", 100, attack_params, plugin_message TSRMLS_CC);
        }
        op1 = OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr;
        break;
    }
    case IS_CV:
    {
        zval **t = OPENRASP_CV(OPENRASP_OP1_VAR(opline));
        if (t && *t)
        {
            op1 = *t;
        }
        else if (EG(active_symbol_table))
        {
            zend_compiled_variable *cv = &EG(active_op_array)->vars[OPENRASP_OP1_VAR(opline)];
            if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len + 1, cv->hash_value, (void **)&t) == SUCCESS)
            {
                op1 = *t;
            }
        }
        break;
    }
    case IS_CONST:
    {
        op1 = OPENRASP_OP1_CONSTANT_PTR(opline);
        break;
    }
    default:
    {
        return ZEND_USER_OPCODE_DISPATCH;
    }
    }
    if (openrasp_check_type_ignored(ZEND_STRL("include") TSRMLS_CC))
    {
        return ZEND_USER_OPCODE_DISPATCH;
    }
    zval *path;
    MAKE_STD_ZVAL(path);
    MAKE_COPY_ZVAL(&op1, path);
    convert_to_string(path);
    char *real_path = nullptr;
    const char *scheme_end = nullptr;
    if ((Z_STRVAL_P(path) && (scheme_end = fetch_url_scheme(Z_STRVAL_P(path))) != nullptr) || (strlen(Z_STRVAL_P(path)) < 4 ||
    (strcmp(Z_STRVAL_P(path) + Z_STRLEN_P(path) - 4, ".php") && strcmp(Z_STRVAL_P(path) + Z_STRLEN_P(path) - 4, ".inc"))))
    {
        real_path = openrasp_real_path(Z_STRVAL_P(path), Z_STRLEN_P(path), 1, READING TSRMLS_CC);
    }

    if (!real_path)
    {
        zval_ptr_dtor(&path);
    }
    else
    {
        zval **doc_root;
        bool send_to_plugin = false;
        if (scheme_end || strstr(Z_STRVAL_P(path), "../") != nullptr)
        {
            send_to_plugin = true;
        }
        if (!PG(http_globals)[TRACK_VARS_SERVER] && !zend_is_auto_global(ZEND_STRL("_SERVER") TSRMLS_CC))
        {
            send_to_plugin = true;
        }
        if (Z_TYPE_P(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY)
        {
            if (zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), ZEND_STRS("DOCUMENT_ROOT"), (void **)&doc_root) == FAILURE)
            {
                send_to_plugin = true;
            }
            else
            {
                assert(Z_TYPE_PP(doc_root) == IS_STRING);
                if (0 == strncmp(real_path, Z_STRVAL_PP(doc_root), Z_STRLEN_PP(doc_root)))
                {
                    send_to_plugin = false;
                }
                else
                {
                    send_to_plugin = true;
                }
            }
        }
        if (send_to_plugin)
        {
            v8::Isolate *isolate = openrasp::get_isolate(TSRMLS_C);
            if (!isolate)
            {
                return ZEND_USER_OPCODE_DISPATCH;
            }
            const char *function = nullptr;
            switch (OPENRASP_INCLUDE_OR_EVAL_TYPE(execute_data->opline))
            {
            case ZEND_INCLUDE:
                function = "include";
                break;
            case ZEND_INCLUDE_ONCE:
                function = "include_once";
                break;
            case ZEND_REQUIRE:
                function = "require";
                break;
            case ZEND_REQUIRE_ONCE:
                function = "require_once";
                break;
            default:
                function = "";
                break;
            }
            bool is_block = false;
            {
                v8::HandleScope handle_scope(isolate);
                auto params = v8::Object::New(isolate);
                params->Set(openrasp::NewV8String(isolate, "path"), openrasp::NewV8String(isolate, Z_STRVAL_P(path), Z_STRLEN_P(path)));
                params->Set(openrasp::NewV8String(isolate, "url"), openrasp::NewV8String(isolate, Z_STRVAL_P(path), Z_STRLEN_P(path)));
                params->Set(openrasp::NewV8String(isolate, "realpath"), openrasp::NewV8String(isolate, real_path));
                efree(real_path);
                params->Set(openrasp::NewV8String(isolate, "function"), openrasp::NewV8String(isolate, function));
                is_block = openrasp::openrasp_check(isolate, openrasp::NewV8String(isolate, "include"), params TSRMLS_CC);
            }
            if (is_block)
            {
                handle_block(TSRMLS_C);
            }
        }
        else
        {
            //skip
            efree(real_path);
            zval_ptr_dtor(&path);
        }
    }
    return ZEND_USER_OPCODE_DISPATCH;
}<commit_msg>fix(php): memory leak<commit_after>/*
 * Copyright 2017-2018 Baidu Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "openrasp_hook.h"
#include "openrasp_v8.h"

int include_handler(ZEND_OPCODE_HANDLER_ARGS);
int eval_handler(ZEND_OPCODE_HANDLER_ARGS);
int include_or_eval_handler(ZEND_OPCODE_HANDLER_ARGS)
{
    if (OPENRASP_INCLUDE_OR_EVAL_TYPE(execute_data->opline) == ZEND_EVAL)
    {
        return eval_handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
    }
    else
    {
        return include_handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
    }
}
int eval_handler(ZEND_OPCODE_HANDLER_ARGS)
{
    zend_op *opline = execute_data->opline;
    if (!openrasp_check_type_ignored(ZEND_STRL("webshell_eval") TSRMLS_CC) &&
        OPENRASP_OP1_TYPE(opline) == IS_VAR &&
        openrasp_zval_in_request(OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr TSRMLS_CC))
    {
        zval *attack_params;
        MAKE_STD_ZVAL(attack_params);
        array_init(attack_params);
        add_assoc_zval(attack_params, "eval", OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr);
        Z_ADDREF_P(OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr);
        zval *plugin_message = NULL;
        MAKE_STD_ZVAL(plugin_message);
        ZVAL_STRING(plugin_message, _("WebShell activity - Detected China Chopper webshell (eval method)"), 1);
        openrasp_buildin_php_risk_handle(1, "webshell_eval", 100, attack_params, plugin_message TSRMLS_CC);
    }
    return ZEND_USER_OPCODE_DISPATCH;
}
int include_handler(ZEND_OPCODE_HANDLER_ARGS)
{
    zend_op *opline = execute_data->opline;
    zval *op1 = nullptr;
    switch (OPENRASP_OP1_TYPE(opline))
    {
    case IS_TMP_VAR:
    {
        op1 = &OPENRASP_T(OPENRASP_OP1_VAR(opline)).tmp_var;
        break;
    }
    case IS_VAR:
    {
        // whether the parameter is the user input data
        if (!openrasp_check_type_ignored(ZEND_STRL("webshell_include") TSRMLS_CC)
        && openrasp_zval_in_request(OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr TSRMLS_CC))
        {
            zval *attack_params;
            MAKE_STD_ZVAL(attack_params);
            array_init(attack_params);
            add_assoc_zval(attack_params, "url", OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr);
            Z_ADDREF_P(OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr);
            zval *plugin_message = NULL;
            MAKE_STD_ZVAL(plugin_message);
            ZVAL_STRING(plugin_message, _("File inclusion - Detected include/require vulnerability or webshell based on the same mechanism"), 1);
            openrasp_buildin_php_risk_handle(1, "webshell_include", 100, attack_params, plugin_message TSRMLS_CC);
        }
        op1 = OPENRASP_T(OPENRASP_OP1_VAR(opline)).var.ptr;
        break;
    }
    case IS_CV:
    {
        zval **t = OPENRASP_CV(OPENRASP_OP1_VAR(opline));
        if (t && *t)
        {
            op1 = *t;
        }
        else if (EG(active_symbol_table))
        {
            zend_compiled_variable *cv = &EG(active_op_array)->vars[OPENRASP_OP1_VAR(opline)];
            if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len + 1, cv->hash_value, (void **)&t) == SUCCESS)
            {
                op1 = *t;
            }
        }
        break;
    }
    case IS_CONST:
    {
        op1 = OPENRASP_OP1_CONSTANT_PTR(opline);
        break;
    }
    default:
    {
        return ZEND_USER_OPCODE_DISPATCH;
    }
    }
    if (openrasp_check_type_ignored(ZEND_STRL("include") TSRMLS_CC))
    {
        return ZEND_USER_OPCODE_DISPATCH;
    }
    zval *path;
    MAKE_STD_ZVAL(path);
    MAKE_COPY_ZVAL(&op1, path);
    convert_to_string(path);
    char *real_path = nullptr;
    const char *scheme_end = nullptr;
    if ((Z_STRVAL_P(path) && (scheme_end = fetch_url_scheme(Z_STRVAL_P(path))) != nullptr) || (strlen(Z_STRVAL_P(path)) < 4 ||
    (strcmp(Z_STRVAL_P(path) + Z_STRLEN_P(path) - 4, ".php") && strcmp(Z_STRVAL_P(path) + Z_STRLEN_P(path) - 4, ".inc"))))
    {
        real_path = openrasp_real_path(Z_STRVAL_P(path), Z_STRLEN_P(path), 1, READING TSRMLS_CC);
    }

    if (!real_path)
    {
        zval_ptr_dtor(&path);
    }
    else
    {
        zval **doc_root;
        bool send_to_plugin = false;
        if (scheme_end || strstr(Z_STRVAL_P(path), "../") != nullptr)
        {
            send_to_plugin = true;
        }
        if (!PG(http_globals)[TRACK_VARS_SERVER] && !zend_is_auto_global(ZEND_STRL("_SERVER") TSRMLS_CC))
        {
            send_to_plugin = true;
        }
        if (Z_TYPE_P(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY)
        {
            if (zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), ZEND_STRS("DOCUMENT_ROOT"), (void **)&doc_root) == FAILURE)
            {
                send_to_plugin = true;
            }
            else
            {
                assert(Z_TYPE_PP(doc_root) == IS_STRING);
                if (0 == strncmp(real_path, Z_STRVAL_PP(doc_root), Z_STRLEN_PP(doc_root)))
                {
                    send_to_plugin = false;
                }
                else
                {
                    send_to_plugin = true;
                }
            }
        }
        if (send_to_plugin)
        {
            v8::Isolate *isolate = openrasp::get_isolate(TSRMLS_C);
            if (!isolate)
            {
                return ZEND_USER_OPCODE_DISPATCH;
            }
            const char *function = nullptr;
            switch (OPENRASP_INCLUDE_OR_EVAL_TYPE(execute_data->opline))
            {
            case ZEND_INCLUDE:
                function = "include";
                break;
            case ZEND_INCLUDE_ONCE:
                function = "include_once";
                break;
            case ZEND_REQUIRE:
                function = "require";
                break;
            case ZEND_REQUIRE_ONCE:
                function = "require_once";
                break;
            default:
                function = "";
                break;
            }
            bool is_block = false;
            {
                v8::HandleScope handle_scope(isolate);
                auto params = v8::Object::New(isolate);
                params->Set(openrasp::NewV8String(isolate, "path"), openrasp::NewV8String(isolate, Z_STRVAL_P(path), Z_STRLEN_P(path)));
                params->Set(openrasp::NewV8String(isolate, "url"), openrasp::NewV8String(isolate, Z_STRVAL_P(path), Z_STRLEN_P(path)));
                zval_ptr_dtor(&path);
                params->Set(openrasp::NewV8String(isolate, "realpath"), openrasp::NewV8String(isolate, real_path));
                efree(real_path);
                params->Set(openrasp::NewV8String(isolate, "function"), openrasp::NewV8String(isolate, function));
                is_block = openrasp::openrasp_check(isolate, openrasp::NewV8String(isolate, "include"), params TSRMLS_CC);
            }
            if (is_block)
            {
                handle_block(TSRMLS_C);
            }
        }
        else
        {
            //skip
            efree(real_path);
            zval_ptr_dtor(&path);
        }
    }
    return ZEND_USER_OPCODE_DISPATCH;
}<|endoftext|>


<commit_before>/*
 NUI3 - C++ cross-platform GUI framework for OpenGL based applications
 Copyright (C) 2002-2003 Sebastien Metrot

 licence: see nui3/LICENCE.TXT
 */

#include "nui.h"
#include "nuiTCPClient.h"
#include "nuiNetworkHost.h"

#ifdef WIN32
#include <WinSock2.h>
#include <Ws2tcpip.h>
#undef GetAddrInfo
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/ioctl.h>
#endif

nuiTCPClient::nuiTCPClient()
{
  mReadConnected = false;
  mWriteConnected = false;
  mAutoDelete = false;
  mpAutoPool = NULL;
}

nuiTCPClient::nuiTCPClient(int sock)
: nuiSocket(sock)
{
  mReadConnected = true;
  mWriteConnected = true;
  mAutoDelete = false;
  mpAutoPool = NULL;
}

nuiTCPClient::~nuiTCPClient()
{
  Close();
}

bool nuiTCPClient::Connect(const nuiNetworkHost& rHost)
{
  if (!Init(AF_INET, SOCK_STREAM, 0))
    return false;

  struct addrinfo* addr = nuiSocket::GetAddrInfo(rHost);
  int res = connect(mSocket, addr->ai_addr, addr->ai_addrlen);
  if (res)
    DumpError(errno);

  freeaddrinfo(addr);

  mReadConnected = mWriteConnected = res == 0;

  return mReadConnected;
}

bool nuiTCPClient::Connect(const nglString& rHost, int16 port)
{
  return Connect(nuiNetworkHost(rHost, port, nuiNetworkHost::eTCP));
}

bool nuiTCPClient::Connect(uint32 ipaddress, int16 port)
{
  return Connect(nuiNetworkHost(ipaddress, port, nuiNetworkHost::eTCP));
}

int nuiTCPClient::Send(const nglString& rString)
{
  return Send((uint8*)rString.GetChars(), rString.GetLength());
}

int nuiTCPClient::Send(const std::vector<uint8>& rData)
{
  return Send(&rData[0], rData.size());
}

int nuiTCPClient::Send(const uint8* pData, int len)
{
#ifdef WIN32
  int res = send(mSocket, (const char*)pData, len, 0);
#else
  int res = send(mSocket, pData, len, 0);
#endif

  if (res < 0)
  {
    if (errno == EWOULDBLOCK && mNonBlocking)
      return res;

    DumpError(errno);
    mWriteConnected = false;
  }

  return res;
}

int nuiTCPClient::ReceiveAvailable(std::vector<uint8>& rData)
{
  int PendingBytes = 0;
#ifdef WIN32
  int result = WSAIoctl(mSocket, FIONREAD, NULL, 0, &PendingBytes, sizeof(PendingBytes), NULL, NULL, NULL);
#else
  int result = ioctl(mSocket, FIONREAD, &PendingBytes);
#endif

  if (!PendingBytes || result != 0)
    return 0;

  rData.resize(PendingBytes);
#ifdef WIN32
  int res = recv(mSocket, (char*)&rData[0], rData.size(), MSG_WAITALL);
#else
  int res = recv(mSocket, &rData[0], rData.size(), MSG_WAITALL);
#endif

  mReadConnected = res != 0;

  if (res < 0)
  {
    if (errno == EWOULDBLOCK && mNonBlocking)
      return res;

    rData.clear();
    return res;
  }

  rData.resize(res);

  return res;
}

int nuiTCPClient::Receive(uint8* pData, int32 len)
{
#ifdef WIN32
  int res = recv(mSocket, (char*)pData, len, MSG_WAITALL);
#else
  //int res = recv(mSocket, (char*)pData, len, MSG_WAITALL);
  int res = read(mSocket, pData, len);
  //printf("%p read returned %d\n", this, res);
#endif

  mReadConnected = res != 0;

  return res;
}

int nuiTCPClient::Receive(std::vector<uint8>& rData)
{
  int res = Receive(&rData[0], rData.size());

  if (res < 0)
  {
    // Error
    rData.clear();
    return res;
  }

  rData.resize(res);
  return res;
}

void nuiTCPClient::Close()
{
  nuiSocket::Close();
  mWriteConnected = false;
  mReadConnected = false;
}

bool nuiTCPClient::IsConnected() const
{
  return IsReadConnected() || IsWriteConnected();
}

bool nuiTCPClient::IsWriteConnected() const
{
  return mWriteConnected;
}

bool nuiTCPClient::IsReadConnected() const
{
  /*
  bool retval = false;
  int bytestoread = 0;
  timeval timeout;
  timeout.tv_sec = 0;
  timeout.tv_usec = 0;
  fd_set myfd;

  FD_ZERO(&myfd);
  FD_SET(mSocket, &myfd);
  int sio = select(FD_SETSIZE, &myfd, (fd_set *)0, (fd_set *)0, &timeout);
  //have to do select first for some reason
  int dio = ioctl(mSocket, FIONREAD, &bytestoread);//should do error checking on return value of this
  retval = ((bytestoread == 0) && (sio == 1));

  return retval;
   */
  return mReadConnected;
}

int32 nuiTCPClient::GetAvailable() const
{
  int PendingBytes = 0;
#ifdef WIN32
  int result = WSAIoctl(mSocket, FIONREAD, NULL, 0, &PendingBytes, sizeof(PendingBytes), NULL, NULL, NULL);
#else
  int result = ioctl(mSocket, FIONREAD, &PendingBytes);
#endif

  if (result != 0)
    PendingBytes = 0;

  return PendingBytes;
}

bool nuiTCPClient::CanWrite() const
{
  return IsWriteConnected();
}

// This is used by the client:
size_t nuiTCPClient::BufferedSend(const uint8* pBuffer, size_t size, bool BufferOnly)
{
  mOut.Write(pBuffer, size);
  if (!BufferOnly)
    SendWriteBuffer();
  return size;
}

size_t nuiTCPClient::BufferedSend(const nglString& rString, bool BufferOnly)
{
  return BufferedSend((uint8*)rString.GetChars(), rString.GetLength(), BufferOnly);
}

size_t nuiTCPClient::BufferedReceive(uint8* pBuffer, size_t size)
{
  return mIn.Read(pBuffer, size);
}

// This is only used by stream handlers
size_t nuiTCPClient::WriteToInputBuffer(const uint8* pBuffer, size_t size)
{
  return mIn.Write(pBuffer, size);
}

size_t nuiTCPClient::ReadFromOutputBuffer(uint8* pBuffer, size_t size)
{
  return mOut.Read(pBuffer, size);
}

void nuiTCPClient::SetAutoDelete(bool set)
{
  mAutoDelete = set;
}

void nuiTCPClient::SetAutoPool(nuiSocketPool* pPool)
{
  if (mpAutoPool != pPool)
    if (mpAutoPool)
      mpAutoPool->Del(this);

  if (mpPool != pPool && mpPool)
    mpPool->Del(this);

  mpAutoPool = pPool;
  if (mpAutoPool)
    mpAutoPool->Add(this, nuiSocketPool::eStateChange);
}

void nuiTCPClient::OnCanRead()
{
  if (mReadDelegate)
    mReadDelegate(*this);

  std::vector<uint8> Data;
  ReceiveAvailable(Data);

  WriteToInputBuffer(&Data[0], Data.size());
  //printf("%d write %d\n", GetSocket(), Data.size());
}

void nuiTCPClient::OnCanWrite()
{
  if (mWriteDelegate)
    mWriteDelegate(*this);

  SendWriteBuffer();
  if (mAutoDelete && mOut.GetSize() == 0)
    delete this;
}

void nuiTCPClient::SendWriteBuffer()
{
  const uint8* pBuffer = mOut.LockBuffer();
  size_t s = mOut.GetSize();

  if (!s)
  {
    mOut.UnlockBuffer();
    return;
  }

  int done = Send(pBuffer, s);
  if (done >= 0)
    mOut.Eat(done);

  NGL_LOG("socket", NGL_LOG_INFO, "sent %d of %d bytes", done, s);

  mOut.UnlockBuffer();

  if (mpAutoPool)
  {
    if (mOut.GetSize() > 0)
    {
      if (!mpPool)
        mpAutoPool->Add(this, nuiSocketPool::eStateChange);
    }
    else
    {
      if (mpPool)
        mpAutoPool->Del(this);
    }
  }

  //  NGL_LOG("socket", NGL_LOG_INFO, "%p %d eat %d\n", this, GetSocket(), done);
}

void nuiTCPClient::OnReadClosed()
{
  NGL_LOG("socket", NGL_LOG_INFO, "%d read closed\n", GetSocket());
  nuiSocket::OnReadClosed();
  mReadConnected = false;
}

void nuiTCPClient::OnWriteClosed()
{
  NGL_LOG("socket", NGL_LOG_INFO, "%d write closed\n", GetSocket());
  nuiSocket::OnWriteClosed();
  mWriteConnected = false;
}

//////////////////////////
//class nuiPipe
nuiPipe::nuiPipe()
{

}

nuiPipe::~nuiPipe()
{

}

size_t nuiPipe::Write(const uint8* pBuffer, size_t size)
{
  nglCriticalSectionGuard guard(mCS);
  size_t p = mBuffer.size();
  mBuffer.resize(size + p);
  memcpy(&mBuffer[p], pBuffer, size);

  return size;
}

size_t nuiPipe::Write(const nglString& rString)
{
  return Write((const uint8*)rString.GetChars(), rString.GetLength());
}

size_t nuiPipe::Read(uint8* pBuffer, size_t size)
{
  nglCriticalSectionGuard guard(mCS);
  size_t p = mBuffer.size();
  size_t todo = MIN(size, p);
  size_t remain = p - todo;

  memcpy(pBuffer, &mBuffer[0], todo);
  memmove(&mBuffer[0], &mBuffer[todo], remain);
  mBuffer.resize(remain);
  return todo;
}

size_t nuiPipe::GetSize() const
{
  nglCriticalSectionGuard guard(mCS);
  return mBuffer.size();
}

const uint8* nuiPipe::LockBuffer()
{
  mCS.Lock();
  return &mBuffer[0];
}

void nuiPipe::UnlockBuffer()
{
  mCS.Unlock();
}

void nuiPipe::Eat(size_t size)
{
  nglCriticalSectionGuard guard(mCS);
  size_t p = mBuffer.size();
  size_t todo = MIN(size, p);
  size_t remain = p - todo;

  memmove(&mBuffer[0], &mBuffer[todo], remain);
  mBuffer.resize(remain);
}

void nuiPipe::Clear()
{
  nglCriticalSectionGuard guard(mCS);
  mBuffer.clear();
}

<commit_msg>less logs<commit_after>/*
 NUI3 - C++ cross-platform GUI framework for OpenGL based applications
 Copyright (C) 2002-2003 Sebastien Metrot

 licence: see nui3/LICENCE.TXT
 */

#include "nui.h"
#include "nuiTCPClient.h"
#include "nuiNetworkHost.h"

#ifdef WIN32
#include <WinSock2.h>
#include <Ws2tcpip.h>
#undef GetAddrInfo
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/ioctl.h>
#endif

nuiTCPClient::nuiTCPClient()
{
  mReadConnected = false;
  mWriteConnected = false;
  mAutoDelete = false;
  mpAutoPool = NULL;
}

nuiTCPClient::nuiTCPClient(int sock)
: nuiSocket(sock)
{
  mReadConnected = true;
  mWriteConnected = true;
  mAutoDelete = false;
  mpAutoPool = NULL;
}

nuiTCPClient::~nuiTCPClient()
{
  Close();
}

bool nuiTCPClient::Connect(const nuiNetworkHost& rHost)
{
  if (!Init(AF_INET, SOCK_STREAM, 0))
    return false;

  struct addrinfo* addr = nuiSocket::GetAddrInfo(rHost);
  int res = connect(mSocket, addr->ai_addr, addr->ai_addrlen);
  if (res)
    DumpError(errno);

  freeaddrinfo(addr);

  mReadConnected = mWriteConnected = res == 0;

  return mReadConnected;
}

bool nuiTCPClient::Connect(const nglString& rHost, int16 port)
{
  return Connect(nuiNetworkHost(rHost, port, nuiNetworkHost::eTCP));
}

bool nuiTCPClient::Connect(uint32 ipaddress, int16 port)
{
  return Connect(nuiNetworkHost(ipaddress, port, nuiNetworkHost::eTCP));
}

int nuiTCPClient::Send(const nglString& rString)
{
  return Send((uint8*)rString.GetChars(), rString.GetLength());
}

int nuiTCPClient::Send(const std::vector<uint8>& rData)
{
  return Send(&rData[0], rData.size());
}

int nuiTCPClient::Send(const uint8* pData, int len)
{
#ifdef WIN32
  int res = send(mSocket, (const char*)pData, len, 0);
#else
  int res = send(mSocket, pData, len, 0);
#endif

  if (res < 0)
  {
    if (errno == EWOULDBLOCK && mNonBlocking)
      return res;

    DumpError(errno);
    mWriteConnected = false;
  }

  return res;
}

int nuiTCPClient::ReceiveAvailable(std::vector<uint8>& rData)
{
  int PendingBytes = 0;
#ifdef WIN32
  int result = WSAIoctl(mSocket, FIONREAD, NULL, 0, &PendingBytes, sizeof(PendingBytes), NULL, NULL, NULL);
#else
  int result = ioctl(mSocket, FIONREAD, &PendingBytes);
#endif

  if (!PendingBytes || result != 0)
    return 0;

  rData.resize(PendingBytes);
#ifdef WIN32
  int res = recv(mSocket, (char*)&rData[0], rData.size(), MSG_WAITALL);
#else
  int res = recv(mSocket, &rData[0], rData.size(), MSG_WAITALL);
#endif

  mReadConnected = res != 0;

  if (res < 0)
  {
    if (errno == EWOULDBLOCK && mNonBlocking)
      return res;

    rData.clear();
    return res;
  }

  rData.resize(res);

  return res;
}

int nuiTCPClient::Receive(uint8* pData, int32 len)
{
#ifdef WIN32
  int res = recv(mSocket, (char*)pData, len, MSG_WAITALL);
#else
  //int res = recv(mSocket, (char*)pData, len, MSG_WAITALL);
  int res = read(mSocket, pData, len);
  //printf("%p read returned %d\n", this, res);
#endif

  mReadConnected = res != 0;

  return res;
}

int nuiTCPClient::Receive(std::vector<uint8>& rData)
{
  int res = Receive(&rData[0], rData.size());

  if (res < 0)
  {
    // Error
    rData.clear();
    return res;
  }

  rData.resize(res);
  return res;
}

void nuiTCPClient::Close()
{
  nuiSocket::Close();
  mWriteConnected = false;
  mReadConnected = false;
}

bool nuiTCPClient::IsConnected() const
{
  return IsReadConnected() || IsWriteConnected();
}

bool nuiTCPClient::IsWriteConnected() const
{
  return mWriteConnected;
}

bool nuiTCPClient::IsReadConnected() const
{
  /*
  bool retval = false;
  int bytestoread = 0;
  timeval timeout;
  timeout.tv_sec = 0;
  timeout.tv_usec = 0;
  fd_set myfd;

  FD_ZERO(&myfd);
  FD_SET(mSocket, &myfd);
  int sio = select(FD_SETSIZE, &myfd, (fd_set *)0, (fd_set *)0, &timeout);
  //have to do select first for some reason
  int dio = ioctl(mSocket, FIONREAD, &bytestoread);//should do error checking on return value of this
  retval = ((bytestoread == 0) && (sio == 1));

  return retval;
   */
  return mReadConnected;
}

int32 nuiTCPClient::GetAvailable() const
{
  int PendingBytes = 0;
#ifdef WIN32
  int result = WSAIoctl(mSocket, FIONREAD, NULL, 0, &PendingBytes, sizeof(PendingBytes), NULL, NULL, NULL);
#else
  int result = ioctl(mSocket, FIONREAD, &PendingBytes);
#endif

  if (result != 0)
    PendingBytes = 0;

  return PendingBytes;
}

bool nuiTCPClient::CanWrite() const
{
  return IsWriteConnected();
}

// This is used by the client:
size_t nuiTCPClient::BufferedSend(const uint8* pBuffer, size_t size, bool BufferOnly)
{
  mOut.Write(pBuffer, size);
  if (!BufferOnly)
    SendWriteBuffer();
  return size;
}

size_t nuiTCPClient::BufferedSend(const nglString& rString, bool BufferOnly)
{
  return BufferedSend((uint8*)rString.GetChars(), rString.GetLength(), BufferOnly);
}

size_t nuiTCPClient::BufferedReceive(uint8* pBuffer, size_t size)
{
  return mIn.Read(pBuffer, size);
}

// This is only used by stream handlers
size_t nuiTCPClient::WriteToInputBuffer(const uint8* pBuffer, size_t size)
{
  return mIn.Write(pBuffer, size);
}

size_t nuiTCPClient::ReadFromOutputBuffer(uint8* pBuffer, size_t size)
{
  return mOut.Read(pBuffer, size);
}

void nuiTCPClient::SetAutoDelete(bool set)
{
  mAutoDelete = set;
}

void nuiTCPClient::SetAutoPool(nuiSocketPool* pPool)
{
  if (mpAutoPool != pPool)
    if (mpAutoPool)
      mpAutoPool->Del(this);

  if (mpPool != pPool && mpPool)
    mpPool->Del(this);

  mpAutoPool = pPool;
  if (mpAutoPool)
    mpAutoPool->Add(this, nuiSocketPool::eStateChange);
}

void nuiTCPClient::OnCanRead()
{
  if (mReadDelegate)
    mReadDelegate(*this);

  std::vector<uint8> Data;
  ReceiveAvailable(Data);

  WriteToInputBuffer(&Data[0], Data.size());
  //printf("%d write %d\n", GetSocket(), Data.size());
}

void nuiTCPClient::OnCanWrite()
{
  if (mWriteDelegate)
    mWriteDelegate(*this);

  SendWriteBuffer();
  if (mAutoDelete && mOut.GetSize() == 0)
    delete this;
}

void nuiTCPClient::SendWriteBuffer()
{
  const uint8* pBuffer = mOut.LockBuffer();
  size_t s = mOut.GetSize();

  if (!s)
  {
    mOut.UnlockBuffer();
    return;
  }

  int done = Send(pBuffer, s);
  if (done >= 0)
    mOut.Eat(done);

  //NGL_LOG("socket", NGL_LOG_INFO, "sent %d of %d bytes", done, s);

  mOut.UnlockBuffer();

  if (mpAutoPool)
  {
    if (mOut.GetSize() > 0)
    {
      if (!mpPool)
        mpAutoPool->Add(this, nuiSocketPool::eStateChange);
    }
    else
    {
      if (mpPool)
        mpAutoPool->Del(this);
    }
  }

  //  NGL_LOG("socket", NGL_LOG_INFO, "%p %d eat %d\n", this, GetSocket(), done);
}

void nuiTCPClient::OnReadClosed()
{
  NGL_LOG("socket", NGL_LOG_INFO, "%d read closed\n", GetSocket());
  nuiSocket::OnReadClosed();
  mReadConnected = false;
}

void nuiTCPClient::OnWriteClosed()
{
  NGL_LOG("socket", NGL_LOG_INFO, "%d write closed\n", GetSocket());
  nuiSocket::OnWriteClosed();
  mWriteConnected = false;
}

//////////////////////////
//class nuiPipe
nuiPipe::nuiPipe()
{

}

nuiPipe::~nuiPipe()
{

}

size_t nuiPipe::Write(const uint8* pBuffer, size_t size)
{
  nglCriticalSectionGuard guard(mCS);
  size_t p = mBuffer.size();
  mBuffer.resize(size + p);
  memcpy(&mBuffer[p], pBuffer, size);

  return size;
}

size_t nuiPipe::Write(const nglString& rString)
{
  return Write((const uint8*)rString.GetChars(), rString.GetLength());
}

size_t nuiPipe::Read(uint8* pBuffer, size_t size)
{
  nglCriticalSectionGuard guard(mCS);
  size_t p = mBuffer.size();
  size_t todo = MIN(size, p);
  size_t remain = p - todo;

  memcpy(pBuffer, &mBuffer[0], todo);
  memmove(&mBuffer[0], &mBuffer[todo], remain);
  mBuffer.resize(remain);
  return todo;
}

size_t nuiPipe::GetSize() const
{
  nglCriticalSectionGuard guard(mCS);
  return mBuffer.size();
}

const uint8* nuiPipe::LockBuffer()
{
  mCS.Lock();
  return &mBuffer[0];
}

void nuiPipe::UnlockBuffer()
{
  mCS.Unlock();
}

void nuiPipe::Eat(size_t size)
{
  nglCriticalSectionGuard guard(mCS);
  size_t p = mBuffer.size();
  size_t todo = MIN(size, p);
  size_t remain = p - todo;

  memmove(&mBuffer[0], &mBuffer[todo], remain);
  mBuffer.resize(remain);
}

void nuiPipe::Clear()
{
  nglCriticalSectionGuard guard(mCS);
  mBuffer.clear();
}

<|endoftext|>


<commit_before>#include <http_server.hpp>
#include <zlib_compression.hpp>
#include <exception>
#include <cstring>

#include <list>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>

namespace {
const std::string CRLF = "\r\n";
const std::string KV_SEPARATOR = ": ";
const std::string STATUS_CODE_STR[] = {
    "HTTP/1.1 200 OK\r\n",
    "HTTP/1.1 404 Not Found\r\n",
    "HTTP/1.1 503 Service Unavailable\r\n",
    "HTTP/1.1 508 Bad Handler ( Recurive threaded switch)\r\n",
    "HTTP/1.1 501 Not Implemented\r\n",
};

}

namespace tws {

class ServerException: public std::exception {
    char msg_[64];
public:
    ServerException(const char *msg) { strncpy(msg_, msg, 64); }
    const char *what() const noexcept { return msg_; }
};

class HttpConnection
    : public boost::enable_shared_from_this<HttpConnection>
{
    enum State {
        kReadingHeader,
        kReadingPost,
        kProcessing,
        kWriteHeader,
        kWriteBody,
    };

    boost::asio::ip::tcp::socket socket_;
    HttpServerInter *http_server_;

    int state_;
    int http_ret_;

    unsigned int postsize_;
    std::string request_;

    std::array<char, 8192> buffer_;

    Request req_;
    Response resp_;

public:
    HttpConnection(HttpServerInter* http_server);

    void start();
    void handle_read(const boost::system::error_code& e,
                std::size_t bytes_transferred);

    void process_request();
    int parse_request_header();
    int try_parse_request(std::size_t bytes_transferred);
    void begin_response();
    void handle_write(const boost::system::error_code& e);
    boost::asio::ip::tcp::socket& socket()  { return socket_;}
};

typedef boost::shared_ptr<HttpConnection> HttpConnPtr;
class HttpServerInter
{
    //friend class HttpServer;
    friend class HttpConnection;

private:
    boost::asio::io_service io_;
    boost::asio::ip::tcp::acceptor acceptor_;

    std::thread **threads_;
    int threadnum_;
    RequestHandler req_handler_;

    std::list<HttpConnPtr> threadpool_q_;
    std::condition_variable threadpool_cv_;
    std::mutex threadpool_m_;

    void start_accept();
    void handle_accept(HttpConnPtr new_conn,
        const boost::system::error_code& error);

    void push_to_threadpool(HttpConnPtr conn);

    void thread_proc(int id);
public:
    HttpServerInter(unsigned short port,
            RequestHandler main_handler, int threadnum);
    void set_handler(RequestHandler handler);
    void run();
    void stop();
};

void Request::clear()
{
    type_ = HTTP_INVALID;
    path_.clear();
    postdata_.clear();
    threaded_ = false;
}

void Response::clear()
{
    headers_.clear();
    body_.clear();
#ifdef HTTP_COMPRESSION
    compression_ = 5;
#endif

}

#ifdef HTTP_COMPRESSION
void Response::set_compression(int level)
{
    compression_ = level;
    if (compression_ < 0)
        compression_ = 0;
    if (compression_ > 9)
        compression_ = 9;
}
#endif

HttpConnection::HttpConnection(HttpServerInter* http_server)
        : socket_(http_server->io_),
        http_server_(http_server)
{
    req_.clear();
    resp_.clear();
}

void HttpConnection::start()
{
    state_ = kReadingHeader;
    socket_.async_read_some(boost::asio::buffer(buffer_),
        boost::bind(&HttpConnection::handle_read, shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
}

void HttpConnection::handle_read(const boost::system::error_code& e,
    std::size_t bytes_transferred)
{
    if (!e) {
        int ret = try_parse_request(bytes_transferred);
        if (ret == 0) {
            //pasre succeed
            state_ = kProcessing;
            process_request();
        } else if (ret > 0) {
            //go on read
            socket_.async_read_some(boost::asio::buffer(buffer_),
                boost::bind(&HttpConnection::handle_read, shared_from_this(),
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
        } else {
            // < 0 parse failed
            socket_.close();
        }
    } else {
        socket_.close();
    }
}

void HttpConnection::process_request()
{
    http_ret_ = http_server_->req_handler_(resp_, req_);
    if (http_ret_ == HTTP_SWITCH_THREAD) {
        if (req_.threaded_ == true) {
            http_ret_ = HTTP_508;
            resp_.set_body("");
            begin_response();
        } else {
            /* push to http_server's thread pool */
            req_.threaded_ = true;
            if (http_server_->threadnum_ == 0) {
                ServerException e("Useing threadpool in callback handler"
                        "but thread num set to zero");
                throw e;
            }
            http_server_->push_to_threadpool(shared_from_this());
        }
    } else {
        begin_response();
    }
}

int HttpConnection::parse_request_header()
{
    size_t spos;
    const char *h;
    if ((spos = request_.find("\r\n\r\n")) == std::string::npos)
        return -1;
    spos += 4; //after \r\n\r\n
    h = request_.c_str();

#define PREFIX_EQ(haystack, needle) (::strncmp(haystack, needle, ::strlen(needle)) == 0)
    if (PREFIX_EQ(h, "GET ")) {
        req_.type_ = HTTP_GET;
        h += ::strlen("GET ");
    } else if (PREFIX_EQ(h, "POST ")) {
        req_.type_ = HTTP_POST;
        state_ = kReadingPost;
        req_.postdata_ = request_.substr(spos);
        h += ::strlen("POST ");
    } else if (PREFIX_EQ(h, "HEAD ")) {
        req_.type_ = HTTP_HEAD;
        h += ::strlen("HEAD ");
    } else if (PREFIX_EQ(h, "PUT ")) {
        req_.type_ = HTTP_PUT;
        state_ = kReadingPost;
        req_.postdata_ = request_.substr(spos);
        h += ::strlen("PUT ");
    } else {
        // not begin with GET/POST/PUT/HEAD..
        return  -1;
    }

    while(!PREFIX_EQ(h, " HTTP/1.")) {
        req_.path_.append(1, *h);
        h++;
    }

    h += ::strlen(" HTTP/1.1\r\n");

    auto split_kv = [this](const char *s) {
        const char *p = s;
        std::string key, value;
        bool inkey = true;
        for( ; *p != '\0'; ) {
            if (PREFIX_EQ(p, "\r\n")) {
                // \r\n found
                if (!key.empty())
                    this->req_.headers_[key] = value;
                return p + 2;
            }
            if (inkey) {
                // before  " : "
                if (PREFIX_EQ(p, ": ")) {
                    p += 2;
                    inkey = false;
                    continue;
                } else if (PREFIX_EQ(p, " : ")) {
                    p += 3;
                    inkey = false;
                    continue;
                } else {
                    key.append(1, *p);
                }
            } else {
                // after " : "
                value.append(1, *p);
            }
            p++;
        }
        return p;
    };
    while(!PREFIX_EQ(h, "\r\n")) {
        h = split_kv(h);
    }
#undef PREFIX_EQ
    auto it = req_.headers_.find("Content-Length");
    if (it != req_.headers_.end()) {
        postsize_ = ::atol(it->second.c_str());
    }
    return 0;
}

int HttpConnection::try_parse_request(std::size_t bytes_transferred)
{
    if (state_ == kReadingHeader) {
        request_.append(buffer_.data(), bytes_transferred);
        if (parse_request_header() < 0) {
            if (request_.size() > 8192)
                // header size less than 8K count as valid
                return -1;
            else
                return 1;
        }
        if (state_ == kReadingPost && req_.postdata_.size() < postsize_)
            return 1;
        else
            return 0;
    } else if (state_ == kReadingPost) {
        req_.postdata_.append(buffer_.data(), bytes_transferred);
        if (req_.postdata_.size() >= postsize_)
            return 0;
        else
            return 1;
    }
    // impossible to reach
    return -1;
}

void HttpConnection::begin_response()
{
    if (http_ret_ < 0 || http_ret_ >= HTTP_END) {
        http_ret_ = HTTP_503;
    }
    if (http_ret_ != HTTP_200 && resp_.body_.empty()) {
        resp_.set_body("
" + STATUS_CODE_STR[http_ret_] + "
");
        resp_.set_header("Content-Type", "text/html");
    }
    if (resp_.headers_.count("Server") == 0)
        resp_.set_header("Server", "SimpleWebSvr/1.0");
    if (resp_.headers_.count("Connection") == 0)
        resp_.set_header("Connection", "Keep-Alive");
#ifdef HTTP_COMPRESSION
    auto it = req_.headers_.find("Accept-Encoding");
    if (it != req_.headers_.end()
            && it->second.find("deflate") != std::string::npos) {
        // client support deflate compression
        if (!resp_.body_.empty()) {
            std::string deflated = zlib_compress(resp_.body_);
            if (!deflated.empty()) {
                resp_.body_ = deflated;
                resp_.set_header("Content-Encoding", "deflate");
            }
        }
    }
#endif
    if (resp_.headers_.count("Content-Length") == 0)
        resp_.set_header("Content-Length", resp_.body_.size());

    std::vector<boost::asio::const_buffer> buffers;
    if (http_ret_ >= 0 && http_ret_ < HTTP_END) {
        buffers.push_back(boost::asio::buffer(STATUS_CODE_STR[http_ret_]));
        for(auto it = resp_.headers_.begin();
                it != resp_.headers_.end(); it++) {
            buffers.push_back(boost::asio::buffer(it->first));
            buffers.push_back(boost::asio::buffer(KV_SEPARATOR));
            buffers.push_back(boost::asio::buffer(it->second));
            buffers.push_back(boost::asio::buffer(CRLF));
        }
        buffers.push_back(boost::asio::buffer(std::string(CRLF)));
        buffers.push_back(boost::asio::buffer(resp_.body_));
        boost::asio::async_write(socket_, buffers,
            boost::bind(&HttpConnection::handle_write, shared_from_this(),
            boost::asio::placeholders::error));
    } else {
        socket_.close();
    }
}

void HttpConnection::handle_write(const boost::system::error_code& e)
{
    if (!e) {
        boost::system::error_code ignored_ec;
        socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
    }
    //socket_.close();
    request_.clear();
    req_.clear();
    resp_.clear();
    postsize_ = 0;
    start();
}

void Response::set_header(const std::string& key, const std::string &value)
{
    headers_[key] = value;
}

void Response::set_header(const std::string& key, long value)
{
    char strvalue[24];
    snprintf(strvalue, 24, "%ld", value);
    set_header(key, std::string(strvalue));
}

void HttpServerInter::start_accept()
{
    HttpConnPtr new_conn =
        HttpConnPtr(new HttpConnection(this));

    acceptor_.async_accept(new_conn->socket(),
        boost::bind(&HttpServerInter::handle_accept, this, new_conn,
        boost::asio::placeholders::error));
}

void HttpServerInter::handle_accept(HttpConnPtr new_conn,
  const boost::system::error_code& error)
{
    if (!error) {
        new_conn->start();
    }
    start_accept();
}

void HttpServerInter::push_to_threadpool(HttpConnPtr conn)
{
    /* push conn to queue belongs to least busy thread */
    std::lock_guard<std::mutex> lk(threadpool_m_);
    threadpool_q_.push_back(conn);
    threadpool_cv_.notify_one();
}

void HttpServerInter::thread_proc(int id)
{
    std::list<HttpConnPtr> &q = threadpool_q_; //alias

    (void)(id);
    for(;;) {
        HttpConnPtr conn;
        std::unique_lock<std::mutex> lk(threadpool_m_);
        if (q.size() > 0) {
            conn = q.front();
            q.pop_front();
            lk.unlock();
        } else {
            threadpool_cv_.wait(lk);
            continue;
        }
        conn->process_request();
    }
}

void HttpServerInter::set_handler(RequestHandler handler)
{
    req_handler_ = handler;
}

void HttpServerInter::run()
{
    io_.run();
}

void HttpServerInter::stop()
{
    //not implemented yet
}

HttpServerInter::HttpServerInter(unsigned short port,
        RequestHandler main_handler, int threadnum)
    : io_(),
      acceptor_(io_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
      threadnum_(threadnum)
{
    threads_ = new std::thread*[threadnum_];
    for(int i = 0; i < threadnum_; i++)
        threads_[i] = new std::thread(
                &HttpServerInter::thread_proc, this, i);
    req_handler_ = main_handler;
    start_accept();
}

HttpServer::HttpServer(unsigned short port,
        RequestHandler main_handler, int threadnum)
    : inter_(new HttpServerInter(port, main_handler, threadnum))
{
}

void HttpServer::set_handler(RequestHandler handler)
{
    inter_->set_handler(handler);
}

void HttpServer::run()
{
    inter_->run();
}

void HttpServer::stop()
{
    inter_->stop();
}

void HttpServer::stat()
{
    //not implement
}

}
<commit_msg>fix a bug of infinite loop in HTTP Header Parser<commit_after>#include <http_server.hpp>
#include <zlib_compression.hpp>
#include <exception>
#include <cstring>

#include <list>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>

namespace {
const std::string CRLF = "\r\n";
const std::string KV_SEPARATOR = ": ";
const std::string STATUS_CODE_STR[] = {
    "HTTP/1.1 200 OK\r\n",
    "HTTP/1.1 404 Not Found\r\n",
    "HTTP/1.1 503 Service Unavailable\r\n",
    "HTTP/1.1 508 Bad Handler ( Recurive threaded switch)\r\n",
    "HTTP/1.1 501 Not Implemented\r\n",
};

}

namespace tws {

class ServerException: public std::exception {
    char msg_[64];
public:
    ServerException(const char *msg) { strncpy(msg_, msg, 64); }
    const char *what() const noexcept { return msg_; }
};

class HttpConnection
    : public boost::enable_shared_from_this<HttpConnection>
{
    enum State {
        kReadingHeader,
        kReadingPost,
        kProcessing,
        kWriteHeader,
        kWriteBody,
    };

    boost::asio::ip::tcp::socket socket_;
    HttpServerInter *http_server_;

    int state_;
    int http_ret_;

    unsigned int postsize_;
    std::string request_;

    std::array<char, 8192> buffer_;

    Request req_;
    Response resp_;

public:
    HttpConnection(HttpServerInter* http_server);

    void start();
    void handle_read(const boost::system::error_code& e,
                std::size_t bytes_transferred);

    void process_request();
    int parse_request_header();
    int try_parse_request(std::size_t bytes_transferred);
    void begin_response();
    void handle_write(const boost::system::error_code& e);
    boost::asio::ip::tcp::socket& socket()  { return socket_;}
};

typedef boost::shared_ptr<HttpConnection> HttpConnPtr;
class HttpServerInter
{
    //friend class HttpServer;
    friend class HttpConnection;

private:
    boost::asio::io_service io_;
    boost::asio::ip::tcp::acceptor acceptor_;

    std::thread **threads_;
    int threadnum_;
    RequestHandler req_handler_;

    std::list<HttpConnPtr> threadpool_q_;
    std::condition_variable threadpool_cv_;
    std::mutex threadpool_m_;

    void start_accept();
    void handle_accept(HttpConnPtr new_conn,
        const boost::system::error_code& error);

    void push_to_threadpool(HttpConnPtr conn);

    void thread_proc(int id);
public:
    HttpServerInter(unsigned short port,
            RequestHandler main_handler, int threadnum);
    void set_handler(RequestHandler handler);
    void run();
    void stop();
};

void Request::clear()
{
    type_ = HTTP_INVALID;
    path_.clear();
    postdata_.clear();
    threaded_ = false;
}

void Response::clear()
{
    headers_.clear();
    body_.clear();
#ifdef HTTP_COMPRESSION
    compression_ = 5;
#endif

}

#ifdef HTTP_COMPRESSION
void Response::set_compression(int level)
{
    compression_ = level;
    if (compression_ < 0)
        compression_ = 0;
    if (compression_ > 9)
        compression_ = 9;
}
#endif

HttpConnection::HttpConnection(HttpServerInter* http_server)
        : socket_(http_server->io_),
        http_server_(http_server)
{
    req_.clear();
    resp_.clear();
}

void HttpConnection::start()
{
    state_ = kReadingHeader;
    socket_.async_read_some(boost::asio::buffer(buffer_),
        boost::bind(&HttpConnection::handle_read, shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
}

void HttpConnection::handle_read(const boost::system::error_code& e,
    std::size_t bytes_transferred)
{
    if (!e) {
        int ret = try_parse_request(bytes_transferred);
        if (ret == 0) {
            //pasre succeed
            state_ = kProcessing;
            process_request();
        } else if (ret > 0) {
            //go on read
            socket_.async_read_some(boost::asio::buffer(buffer_),
                boost::bind(&HttpConnection::handle_read, shared_from_this(),
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
        } else {
            // < 0 parse failed
            socket_.close();
        }
    } else {
        socket_.close();
    }
}

void HttpConnection::process_request()
{
    http_ret_ = http_server_->req_handler_(resp_, req_);
    if (http_ret_ == HTTP_SWITCH_THREAD) {
        if (req_.threaded_ == true) {
            http_ret_ = HTTP_508;
            resp_.set_body("");
            begin_response();
        } else {
            /* push to http_server's thread pool */
            req_.threaded_ = true;
            if (http_server_->threadnum_ == 0) {
                ServerException e("Useing threadpool in callback handler"
                        "but thread num set to zero");
                throw e;
            }
            http_server_->push_to_threadpool(shared_from_this());
        }
    } else {
        begin_response();
    }
}

int HttpConnection::parse_request_header()
{
    size_t spos;
    const char *h;
    if ((spos = request_.find("\r\n\r\n")) == std::string::npos)
        return -1;
    spos += 4; //after \r\n\r\n
    h = request_.c_str();

#define PREFIX_EQ(haystack, needle) (::strncmp(haystack, needle, ::strlen(needle)) == 0)
    if (PREFIX_EQ(h, "GET ")) {
        req_.type_ = HTTP_GET;
        h += ::strlen("GET ");
    } else if (PREFIX_EQ(h, "POST ")) {
        req_.type_ = HTTP_POST;
        state_ = kReadingPost;
        req_.postdata_ = request_.substr(spos);
        h += ::strlen("POST ");
    } else if (PREFIX_EQ(h, "HEAD ")) {
        req_.type_ = HTTP_HEAD;
        h += ::strlen("HEAD ");
    } else if (PREFIX_EQ(h, "PUT ")) {
        req_.type_ = HTTP_PUT;
        state_ = kReadingPost;
        req_.postdata_ = request_.substr(spos);
        h += ::strlen("PUT ");
    } else {
        // not begin with GET/POST/PUT/HEAD..
        return  -1;
    }

    while(!PREFIX_EQ(h, " HTTP/1.")) {
        req_.path_.append(1, *h);
        h++;
    }

    h += ::strlen(" HTTP/1.1\r\n");

    auto split_kv = [this](const char *s) {
        const char *p = s;
        std::string key, value;
        bool inkey = true;
        for( ; *p != '\0'; ) {
            if (PREFIX_EQ(p, "\r\n")) {
                // \r\n found
                if (!key.empty())
                    this->req_.headers_[key] = value;
                return p + 2;
            }
            if (inkey) {
                // before  " : "
                if (PREFIX_EQ(p, ": ")) {
                    p += 2;
                    inkey = false;
                    continue;
                } else if (PREFIX_EQ(p, " : ")) {
                    p += 3;
                    inkey = false;
                    continue;
                } else {
                    key.append(1, *p);
                }
            } else {
                // after " : "
                value.append(1, *p);
            }
            p++;
        }
        return p;
    };
    while(*h && !PREFIX_EQ(h, "\r\n")) {
        h = split_kv(h);
    }
#undef PREFIX_EQ
    auto it = req_.headers_.find("Content-Length");
    if (it != req_.headers_.end()) {
        postsize_ = ::atol(it->second.c_str());
    }
    return 0;
}

int HttpConnection::try_parse_request(std::size_t bytes_transferred)
{
    if (state_ == kReadingHeader) {
        request_.append(buffer_.data(), bytes_transferred);
        if (parse_request_header() < 0) {
            if (request_.size() > 8192)
                // header size less than 8K count as valid
                return -1;
            else
                return 1;
        }
        if (state_ == kReadingPost && req_.postdata_.size() < postsize_)
            return 1;
        else
            return 0;
    } else if (state_ == kReadingPost) {
        req_.postdata_.append(buffer_.data(), bytes_transferred);
        if (req_.postdata_.size() >= postsize_)
            return 0;
        else
            return 1;
    }
    // impossible to reach
    return -1;
}

void HttpConnection::begin_response()
{
    if (http_ret_ < 0 || http_ret_ >= HTTP_END) {
        http_ret_ = HTTP_503;
    }
    if (http_ret_ != HTTP_200 && resp_.body_.empty()) {
        resp_.set_body("
" + STATUS_CODE_STR[http_ret_] + "
");
        resp_.set_header("Content-Type", "text/html");
    }
    if (resp_.headers_.count("Server") == 0)
        resp_.set_header("Server", "SimpleWebSvr/1.0");
    if (resp_.headers_.count("Connection") == 0)
        resp_.set_header("Connection", "Keep-Alive");
#ifdef HTTP_COMPRESSION
    auto it = req_.headers_.find("Accept-Encoding");
    if (it != req_.headers_.end()
            && it->second.find("deflate") != std::string::npos) {
        // client support deflate compression
        if (!resp_.body_.empty()) {
            std::string deflated = zlib_compress(resp_.body_);
            if (!deflated.empty()) {
                resp_.body_ = deflated;
                resp_.set_header("Content-Encoding", "deflate");
            }
        }
    }
#endif
    if (resp_.headers_.count("Content-Length") == 0)
        resp_.set_header("Content-Length", resp_.body_.size());

    std::vector<boost::asio::const_buffer> buffers;
    if (http_ret_ >= 0 && http_ret_ < HTTP_END) {
        buffers.push_back(boost::asio::buffer(STATUS_CODE_STR[http_ret_]));
        for(auto it = resp_.headers_.begin();
                it != resp_.headers_.end(); it++) {
            buffers.push_back(boost::asio::buffer(it->first));
            buffers.push_back(boost::asio::buffer(KV_SEPARATOR));
            buffers.push_back(boost::asio::buffer(it->second));
            buffers.push_back(boost::asio::buffer(CRLF));
        }
        buffers.push_back(boost::asio::buffer(std::string(CRLF)));
        buffers.push_back(boost::asio::buffer(resp_.body_));
        boost::asio::async_write(socket_, buffers,
            boost::bind(&HttpConnection::handle_write, shared_from_this(),
            boost::asio::placeholders::error));
    } else {
        socket_.close();
    }
}

void HttpConnection::handle_write(const boost::system::error_code& e)
{
    if (!e) {
        boost::system::error_code ignored_ec;
        socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
    }
    //socket_.close();
    request_.clear();
    req_.clear();
    resp_.clear();
    postsize_ = 0;
    start();
}

void Response::set_header(const std::string& key, const std::string &value)
{
    headers_[key] = value;
}

void Response::set_header(const std::string& key, long value)
{
    char strvalue[24];
    snprintf(strvalue, 24, "%ld", value);
    set_header(key, std::string(strvalue));
}

void HttpServerInter::start_accept()
{
    HttpConnPtr new_conn =
        HttpConnPtr(new HttpConnection(this));

    acceptor_.async_accept(new_conn->socket(),
        boost::bind(&HttpServerInter::handle_accept, this, new_conn,
        boost::asio::placeholders::error));
}

void HttpServerInter::handle_accept(HttpConnPtr new_conn,
  const boost::system::error_code& error)
{
    if (!error) {
        new_conn->start();
    }
    start_accept();
}

void HttpServerInter::push_to_threadpool(HttpConnPtr conn)
{
    /* push conn to queue belongs to least busy thread */
    std::lock_guard<std::mutex> lk(threadpool_m_);
    threadpool_q_.push_back(conn);
    threadpool_cv_.notify_one();
}

void HttpServerInter::thread_proc(int id)
{
    std::list<HttpConnPtr> &q = threadpool_q_; //alias

    (void)(id);
    for(;;) {
        HttpConnPtr conn;
        std::unique_lock<std::mutex> lk(threadpool_m_);
        if (q.size() > 0) {
            conn = q.front();
            q.pop_front();
            lk.unlock();
        } else {
            threadpool_cv_.wait(lk);
            continue;
        }
        conn->process_request();
    }
}

void HttpServerInter::set_handler(RequestHandler handler)
{
    req_handler_ = handler;
}

void HttpServerInter::run()
{
    io_.run();
}

void HttpServerInter::stop()
{
    //not implemented yet
}

HttpServerInter::HttpServerInter(unsigned short port,
        RequestHandler main_handler, int threadnum)
    : io_(),
      acceptor_(io_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
      threadnum_(threadnum)
{
    threads_ = new std::thread*[threadnum_];
    for(int i = 0; i < threadnum_; i++)
        threads_[i] = new std::thread(
                &HttpServerInter::thread_proc, this, i);
    req_handler_ = main_handler;
    start_accept();
}

HttpServer::HttpServer(unsigned short port,
        RequestHandler main_handler, int threadnum)
    : inter_(new HttpServerInter(port, main_handler, threadnum))
{
}

void HttpServer::set_handler(RequestHandler handler)
{
    inter_->set_handler(handler);
}

void HttpServer::run()
{
    inter_->run();
}

void HttpServer::stop()
{
    inter_->stop();
}

void HttpServer::stat()
{
    //not implement
}

}
<|endoftext|>


<commit_before>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2008 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//
//  Original author of this file is Nick Hebner (hebnern@gmail.com).
//

#include "ArgList.h"

#include "Node.h"

#include "../base/Logger.h"
#include "../base/Exception.h"

#include <sstream>

using namespace std;

namespace avg {

ArgList::ArgList()
{
}

ArgList::ArgList(const ArgList& ArgTemplates, const xmlNodePtr xmlNode)
{
    copyArgsFrom(ArgTemplates);

    for(xmlAttrPtr prop = xmlNode->properties; prop; prop = prop->next)
    {
        string name = (char*)prop->name;
        string value = (char*)prop->children->content;
        setArgValue(name, value);
    }
}

ArgList::ArgList(const ArgList& ArgTemplates, const boost::python::dict& PyDict)
{
    // TODO: Check if all required args are being set.
    copyArgsFrom(ArgTemplates);
    boost::python::list keys = PyDict.keys();
    int nKeys = boost::python::len(keys);
// Workaround for older boost::python
//    int nKeys = boost::python::extract<int>(keys.attr("__len__"))();
    for(int i = 0; i < nKeys; i++)
    {
        boost::python::object keyObj = keys[i];
        boost::python::object valObj = PyDict[keyObj];

        boost::python::extract<string> keyStrProxy(keyObj);
        if (!keyStrProxy.check()) {
            throw Exception(AVG_ERR_INVALID_ARGS, "Argument name must be a string.");
        }
        string keyStr = keyStrProxy();

        setArgValue(keyStr, valObj);
    }
}

ArgList::~ArgList()
{
}

const ArgBasePtr ArgList::getArg(const string& sName) const
{
    ArgMap::const_iterator valIt = m_Args.find(sName);
    if (valIt == m_Args.end()) {
        // TODO: The error message should mention line number and node type.
        throw Exception(AVG_ERR_INVALID_ARGS, string("Argument ")+sName+" is not valid.");
    }
    return valIt->second;
}

const ArgMap& ArgList::getArgMap() const
{
    return m_Args;
}

void ArgList::setArg(const ArgBase& newArg)
{
    m_Args.insert(ArgMap::value_type(newArg.getName(), ArgBasePtr(newArg.createCopy())));
}

void ArgList::setArgs(const ArgList& Args)
{
    for(ArgMap::const_iterator it = Args.m_Args.begin();
            it != Args.m_Args.end(); it++)
    {
        m_Args.insert(*it);
    }
}

void ArgList::setMembers(Node * pNode) const
{
    for(ArgMap::const_iterator it = m_Args.begin(); it != m_Args.end(); it++)
    {
        const ArgBasePtr pCurArg = it->second;
        pCurArg->setMember(pNode);
    }
    pNode->setArgs(*this);
}

template<class T>
void setArgValue(Arg<T>* pArg, const std::string & sName, const boost::python::object& Value)
{
    boost::python::extract<T> valProxy(Value);
    if (!valProxy.check()) {
        throw Exception(AVG_ERR_INVALID_ARGS, "Type error in argument "+sName+": "+typeid(T).name()+" expected.");
    }
    pArg->setValue(valProxy());
}

void ArgList::setArgValue(const std::string & sName, const boost::python::object& Value)
{
    ArgBasePtr pArg = getArg(sName);
    Arg<string>* pStringArg = dynamic_cast<Arg<string>* >(&*pArg);
    Arg<int>* pIntArg = dynamic_cast<Arg<int>* >(&*pArg);
    Arg<double>* pDoubleArg = dynamic_cast<Arg<double>* >(&*pArg);
    Arg<bool>* pBoolArg = dynamic_cast<Arg<bool>* >(&*pArg);
    if(pStringArg) {
        avg::setArgValue(pStringArg, sName, Value);
    } else if (pIntArg) {
        avg::setArgValue(pIntArg, sName, Value);
    } else if (pDoubleArg) {
        avg::setArgValue(pDoubleArg, sName, Value);
    } else if (pBoolArg) {
        avg::setArgValue(pBoolArg, sName, Value);
    }
}

void ArgList::setArgValue(const std::string & sName, const std::string & sValue)
{
    ArgBasePtr pArg = getArg(sName);
    Arg<string>* pStringArg = dynamic_cast<Arg<string>* >(&*pArg);
    Arg<int>* pIntArg = dynamic_cast<Arg<int>* >(&*pArg);
    Arg<double>* pDoubleArg = dynamic_cast<Arg<double>* >(&*pArg);
    Arg<bool>* pBoolArg = dynamic_cast<Arg<bool>* >(&*pArg);
    if (pStringArg) {
        pStringArg->setValue(sValue);
    } else if (pIntArg) {
        char * errStr;
        const char * valStr = sValue.c_str();
        int ret = strtol(valStr, &errStr, 10);
        if (ret == 0 && errStr == valStr) {
            throw Exception(AVG_ERR_NO_ARG,
                    string("Error in conversion of '")+sValue+"' to int");
        }
        pIntArg->setValue(ret);
    } else if (pDoubleArg) {
        char * errStr;
        const char * valStr = sValue.c_str();
        double ret = strtod(valStr, &errStr);
        if (ret == 0 && errStr == valStr) {
            throw Exception(AVG_ERR_NO_ARG,
                    string("Error in conversion of '")+sValue+"' to double");
        }
        pDoubleArg->setValue(ret);
    } else if (pBoolArg) {
        pBoolArg->setValue(sValue == "True" || sValue == "true" || sValue == "1");
    }
}

void ArgList::copyArgsFrom(const ArgList& ArgTemplates)
{
    for(ArgMap::const_iterator it = ArgTemplates.m_Args.begin();
            it != ArgTemplates.m_Args.end(); it++)
    {
        string sKey = it->first;
        ArgBasePtr pArg = ArgBasePtr(it->second->createCopy());
        m_Args[sKey] = pArg;
    }
}

}

<commit_msg>applied ArgList.cpp patch to check for typeinfo problems in the plugin architecture<commit_after>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2008 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//
//  Original author of this file is Nick Hebner (hebnern@gmail.com).
//

#include "ArgList.h"

#include "Node.h"

#include "../base/Logger.h"
#include "../base/Exception.h"

#include <sstream>

using namespace std;

namespace avg {

ArgList::ArgList()
{
}

ArgList::ArgList(const ArgList& ArgTemplates, const xmlNodePtr xmlNode)
{
    copyArgsFrom(ArgTemplates);

    for(xmlAttrPtr prop = xmlNode->properties; prop; prop = prop->next)
    {
        string name = (char*)prop->name;
        string value = (char*)prop->children->content;
        setArgValue(name, value);
    }
}

ArgList::ArgList(const ArgList& ArgTemplates, const boost::python::dict& PyDict)
{
    // TODO: Check if all required args are being set.
    copyArgsFrom(ArgTemplates);
    boost::python::list keys = PyDict.keys();
    int nKeys = boost::python::len(keys);
// Workaround for older boost::python
//    int nKeys = boost::python::extract<int>(keys.attr("__len__"))();
    for(int i = 0; i < nKeys; i++)
    {
        boost::python::object keyObj = keys[i];
        boost::python::object valObj = PyDict[keyObj];

        boost::python::extract<string> keyStrProxy(keyObj);
        if (!keyStrProxy.check()) {
            throw Exception(AVG_ERR_INVALID_ARGS, "Argument name must be a string.");
        }
        string keyStr = keyStrProxy();

        setArgValue(keyStr, valObj);
    }
}

ArgList::~ArgList()
{
}

const ArgBasePtr ArgList::getArg(const string& sName) const
{
    ArgMap::const_iterator valIt = m_Args.find(sName);
    if (valIt == m_Args.end()) {
        // TODO: The error message should mention line number and node type.
        throw Exception(AVG_ERR_INVALID_ARGS, string("Argument ")+sName+" is not valid.");
    }
    return valIt->second;
}

const ArgMap& ArgList::getArgMap() const
{
    return m_Args;
}

void ArgList::setArg(const ArgBase& newArg)
{
    m_Args.insert(ArgMap::value_type(newArg.getName(), ArgBasePtr(newArg.createCopy())));
}

void ArgList::setArgs(const ArgList& Args)
{
    for(ArgMap::const_iterator it = Args.m_Args.begin();
            it != Args.m_Args.end(); it++)
    {
        m_Args.insert(*it);
    }
}

void ArgList::setMembers(Node * pNode) const
{
    for(ArgMap::const_iterator it = m_Args.begin(); it != m_Args.end(); it++)
    {
        const ArgBasePtr pCurArg = it->second;
        pCurArg->setMember(pNode);
    }
    pNode->setArgs(*this);
}

template<class T>
void setArgValue(Arg<T>* pArg, const std::string & sName, const boost::python::object& Value)
{
    boost::python::extract<T> valProxy(Value);
    if (!valProxy.check()) {
        throw Exception(AVG_ERR_INVALID_ARGS, "Type error in argument "+sName+": "+typeid(T).name()+" expected.");
    }
    pArg->setValue(valProxy());
}

void ArgList::setArgValue(const std::string & sName, const boost::python::object& Value)
{
    ArgBasePtr pArg = getArg(sName);
    Arg<string>* pStringArg = dynamic_cast<Arg<string>* >(&*pArg);
    Arg<int>* pIntArg = dynamic_cast<Arg<int>* >(&*pArg);
    Arg<double>* pDoubleArg = dynamic_cast<Arg<double>* >(&*pArg);
    Arg<bool>* pBoolArg = dynamic_cast<Arg<bool>* >(&*pArg);
    if(pStringArg) {
        avg::setArgValue(pStringArg, sName, Value);
    } else if (pIntArg) {
        avg::setArgValue(pIntArg, sName, Value);
    } else if (pDoubleArg) {
        avg::setArgValue(pDoubleArg, sName, Value);
    } else if (pBoolArg) {
        avg::setArgValue(pBoolArg, sName, Value);
    }
}

void ArgList::setArgValue(const std::string & sName, const std::string & sValue)
{
    ArgBasePtr pArg = getArg(sName);
    Arg<string>* pStringArg = dynamic_cast<Arg<string>* >(&*pArg);
    Arg<int>* pIntArg = dynamic_cast<Arg<int>* >(&*pArg);
    Arg<double>* pDoubleArg = dynamic_cast<Arg<double>* >(&*pArg);
    Arg<bool>* pBoolArg = dynamic_cast<Arg<bool>* >(&*pArg);
    if (pStringArg) {
        pStringArg->setValue(sValue);
    } else if (pIntArg) {
        char * errStr;
        const char * valStr = sValue.c_str();
        int ret = strtol(valStr, &errStr, 10);
        if (ret == 0 && errStr == valStr) {
            throw Exception(AVG_ERR_NO_ARG,
                    string("Error in conversion of '")+sValue+"' to int");
        }
        pIntArg->setValue(ret);
    } else if (pDoubleArg) {
        char * errStr;
        const char * valStr = sValue.c_str();
        double ret = strtod(valStr, &errStr);
        if (ret == 0 && errStr == valStr) {
            throw Exception(AVG_ERR_NO_ARG,
                    string("Error in conversion of '")+sValue+"' to double");
        }
        pDoubleArg->setValue(ret);
    } else if (pBoolArg) {
        pBoolArg->setValue(sValue == "True" || sValue == "true" || sValue == "1");
    } else {
		assert(false);
	}
}

void ArgList::copyArgsFrom(const ArgList& ArgTemplates)
{
    for(ArgMap::const_iterator it = ArgTemplates.m_Args.begin();
            it != ArgTemplates.m_Args.end(); it++)
    {
        string sKey = it->first;
        ArgBasePtr pArg = ArgBasePtr(it->second->createCopy());
        m_Args[sKey] = pArg;
    }
}

}

<|endoftext|>


<commit_before>/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
   Copyright (C) 2009 Red Hat, Inc.

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

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/

#include "common.h"
#include "glz_decoder_config.h"
#include "glz_decoder.h"

static void op_decode (SpiceGlzDecoder *decoder,
                       uint8_t *data,
                       SpicePalette *plt,
                       void *usr_data)
{
    GlzDecoder* _decoder = static_cast<GlzDecoder*>(decoder);
    _decoder->decode(data, plt, usr_data);
}

GlzDecoder::GlzDecoder(GlzDecoderWindow &images_window,
                       GlzDecodeHandler &usr_handler, GlzDecoderDebug &debug_calls)
    : _images_window (images_window)
    , _usr_handler (usr_handler)
    , _debug_calls (debug_calls)
{
    static SpiceGlzDecoderOps decoder_ops = {
        op_decode,
    };
    ops = &decoder_ops;
}

GlzDecoder::~GlzDecoder()
{
}

void GlzDecoder::decode_header()
{
    int magic;
    int version;
    uint8_t tmp;
    int stride;

    magic = decode_32();
    if (magic != LZ_MAGIC) {
        _debug_calls.warn(std::string("bad magic\n"));
    }

    version = decode_32();
    if (version != LZ_VERSION) {
        _debug_calls.warn(std::string("bad version\n"));
    }

    tmp = *(_in_now++);

    _image.type = (LzImageType)(tmp & LZ_IMAGE_TYPE_MASK);
    _image.top_down = (tmp >> LZ_IMAGE_TYPE_LOG) ? true : false;
    _image.width = decode_32();
    _image.height = decode_32();
    stride = decode_32();

    if (IS_IMAGE_TYPE_PLT[_image.type]) {
        _image.gross_pixels = stride * PLT_PIXELS_PER_BYTE[_image.type] * _image.height;
    } else {
        _image.gross_pixels = _image.width * _image.height;
    }

    _image.id = decode_64();
    _image.win_head_dist = decode_32();
}

inline uint32_t GlzDecoder::decode_32()
{
    uint32_t word = 0;
    word |= *(_in_now++);
    word <<= 8;
    word |= *(_in_now++);
    word <<= 8;
    word |= *(_in_now++);
    word <<= 8;
    word |= *(_in_now++);
    return word;
}

inline uint64_t GlzDecoder::decode_64()
{
    uint64_t long_word = decode_32();
    long_word <<= 32;
    long_word |= decode_32();
    return long_word;
}

// TODO: the code is historically c based. Consider transforming to c++ and use templates
//      - but be sure it won't make it slower!

/*
 * Give hints to the compiler for branch prediction optimization.
 */
#if defined(__GNUC__) && (__GNUC__ > 2)
#define LZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
#define LZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
#else
#define LZ_EXPECT_CONDITIONAL(c) (c)
#define LZ_UNEXPECT_CONDITIONAL(c) (c)
#endif

#ifdef __GNUC__
#define ATTR_PACKED __attribute__ ((__packed__))
#else
#define ATTR_PACKED
#pragma pack(push)
#pragma pack(1)
#endif

/* the palette images will be treated as one byte pixels. Their width should be transformed
   accordingly.
*/
typedef struct ATTR_PACKED one_byte_pixel_t {
    uint8_t a;
} one_byte_pixel_t;

typedef struct ATTR_PACKED rgb32_pixel_t {
    uint8_t b;
    uint8_t g;
    uint8_t r;
    uint8_t pad;
} rgb32_pixel_t;

typedef struct ATTR_PACKED rgb24_pixel_t {
    uint8_t b;
    uint8_t g;
    uint8_t r;
} rgb24_pixel_t;

typedef uint16_t rgb16_pixel_t;

#ifndef __GNUC__
#pragma pack(pop)
#endif

#undef ATTR_PACKED

#define LZ_PLT
#include "glz_decode_tmpl.c"

#define LZ_PLT
#define PLT8
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_PLT
#define PLT4_BE
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_PLT
#define PLT4_LE
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_PLT
#define PLT1_BE
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_PLT
#define PLT1_LE
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_RGB16
#include "glz_decode_tmpl.c"
#define LZ_RGB16
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_RGB24
#include "glz_decode_tmpl.c"

#define LZ_RGB32
#include "glz_decode_tmpl.c"

#define LZ_RGB_ALPHA
#include "glz_decode_tmpl.c"

#undef LZ_UNEXPECT_CONDITIONAL
#undef LZ_EXPECT_CONDITIONAL

typedef size_t (*decode_function)(GlzDecoderWindow &window, uint8_t* in_buf,
                                  uint8_t *out_buf, int size,
                                  DecodedImageWinId image_win_id, SpicePalette *plt,
                                  GlzDecoderDebug  &debug_calls);

// ordered according to LZ_IMAGE_TYPE
const decode_function DECODE_TO_RGB32[] = {
    NULL,
    glz_plt1_le_to_rgb32_decode,
    glz_plt1_be_to_rgb32_decode,
    glz_plt4_le_to_rgb32_decode,
    glz_plt4_be_to_rgb32_decode,
    glz_plt8_to_rgb32_decode,
    glz_rgb16_to_rgb32_decode,
    glz_rgb32_decode,
    glz_rgb32_decode,
    glz_rgb32_decode
};

const decode_function DECODE_TO_SAME[] = {
    NULL,
    glz_plt_decode,
    glz_plt_decode,
    glz_plt_decode,
    glz_plt_decode,
    glz_plt_decode,
    glz_rgb16_decode,
    glz_rgb24_decode,
    glz_rgb32_decode,
    glz_rgb32_decode
};

void GlzDecoder::decode(uint8_t *data, SpicePalette *palette, void *opaque_usr_info)
{
    int out_size;
    DecodedImageWinId image_window_id;
    GlzDecodedImage *decoded_image;
    size_t n_in_bytes_decoded;
    int bytes_per_pixel;
    LzImageType decoded_type;

    _in_start = data;
    _in_now = data;

    decode_header();

#ifdef GLZ_DECODE_TO_RGB32
    out_size = _image.gross_pixels << 2;
    bytes_per_pixel = 4;

    if (_image.type == LZ_IMAGE_TYPE_RGBA) {
        decoded_type = LZ_IMAGE_TYPE_RGBA;
    } else {
        decoded_type = LZ_IMAGE_TYPE_RGB32;
    }

#else
    if (IS_IMAGE_TYPE_PLT[_image.type]) {
        GLZ_ASSERT(_debug_calls, !(_image.gross_pixels % PLT_PIXELS_PER_BYTE[_image.type]));
        out_size = _image.gross_pixels / PLT_PIXELS_PER_BYTE[_image.type];
    } else {
        out_size = _image.gross_pixels * RGB_BYTES_PER_PIXEL[_image.type];
    }
    bytes_per_pixel = RGB_BYTES_PER_PIXEL[_image.type];
    decoded_type = _image.type;
#endif

    image_window_id = _images_window.pre_decode(_image.id, _image.id - _image.win_head_dist);

    decoded_image = _usr_handler.alloc_image(opaque_usr_info, _image.id,
                                             _image.id - _image.win_head_dist,
                                             decoded_type, _image.width, _image.height,
                                             _image.gross_pixels, bytes_per_pixel,
                                             _image.top_down);

    _image.data = decoded_image->get_data();

    // decode_by_type
#ifdef GLZ_DECODE_TO_RGB32
    n_in_bytes_decoded = DECODE_TO_RGB32[_image.type](_images_window, _in_now, _image.data,
                                                      _image.gross_pixels, image_window_id,
                                                      palette, _debug_calls);
#else
    n_in_bytes_decoded = DECODE_TO_SAME[_image.type](_images_window, _in_now, _image.data,
                                                     IS_IMAGE_TYPE_PLT[_image.type] ?
                                                     _image.gross_pixels /
                                                     PLT_PIXELS_PER_BYTE[_image.type] :
                                                     _image.gross_pixels,
                                                     image_window_id, palette, _debug_calls);
#endif

    _in_now += n_in_bytes_decoded;

    if (_image.type == LZ_IMAGE_TYPE_RGBA) {
        glz_rgb_alpha_decode(_images_window, _in_now, _image.data,
                             _image.gross_pixels, image_window_id, palette, _debug_calls);
    }

    _images_window.post_decode(decoded_image);
}

<commit_msg>client/glz_decoder.cpp: gcc 4.6.0 unused fixes<commit_after>/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
   Copyright (C) 2009 Red Hat, Inc.

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

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/

#include "common.h"
#include "glz_decoder_config.h"
#include "glz_decoder.h"

static void op_decode (SpiceGlzDecoder *decoder,
                       uint8_t *data,
                       SpicePalette *plt,
                       void *usr_data)
{
    GlzDecoder* _decoder = static_cast<GlzDecoder*>(decoder);
    _decoder->decode(data, plt, usr_data);
}

GlzDecoder::GlzDecoder(GlzDecoderWindow &images_window,
                       GlzDecodeHandler &usr_handler, GlzDecoderDebug &debug_calls)
    : _images_window (images_window)
    , _usr_handler (usr_handler)
    , _debug_calls (debug_calls)
{
    static SpiceGlzDecoderOps decoder_ops = {
        op_decode,
    };
    ops = &decoder_ops;
}

GlzDecoder::~GlzDecoder()
{
}

void GlzDecoder::decode_header()
{
    int magic;
    int version;
    uint8_t tmp;
    int stride;

    magic = decode_32();
    if (magic != LZ_MAGIC) {
        _debug_calls.warn(std::string("bad magic\n"));
    }

    version = decode_32();
    if (version != LZ_VERSION) {
        _debug_calls.warn(std::string("bad version\n"));
    }

    tmp = *(_in_now++);

    _image.type = (LzImageType)(tmp & LZ_IMAGE_TYPE_MASK);
    _image.top_down = (tmp >> LZ_IMAGE_TYPE_LOG) ? true : false;
    _image.width = decode_32();
    _image.height = decode_32();
    stride = decode_32();

    if (IS_IMAGE_TYPE_PLT[_image.type]) {
        _image.gross_pixels = stride * PLT_PIXELS_PER_BYTE[_image.type] * _image.height;
    } else {
        _image.gross_pixels = _image.width * _image.height;
    }

    _image.id = decode_64();
    _image.win_head_dist = decode_32();
}

inline uint32_t GlzDecoder::decode_32()
{
    uint32_t word = 0;
    word |= *(_in_now++);
    word <<= 8;
    word |= *(_in_now++);
    word <<= 8;
    word |= *(_in_now++);
    word <<= 8;
    word |= *(_in_now++);
    return word;
}

inline uint64_t GlzDecoder::decode_64()
{
    uint64_t long_word = decode_32();
    long_word <<= 32;
    long_word |= decode_32();
    return long_word;
}

// TODO: the code is historically c based. Consider transforming to c++ and use templates
//      - but be sure it won't make it slower!

/*
 * Give hints to the compiler for branch prediction optimization.
 */
#if defined(__GNUC__) && (__GNUC__ > 2)
#define LZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
#define LZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
#else
#define LZ_EXPECT_CONDITIONAL(c) (c)
#define LZ_UNEXPECT_CONDITIONAL(c) (c)
#endif

#ifdef __GNUC__
#define ATTR_PACKED __attribute__ ((__packed__))
#else
#define ATTR_PACKED
#pragma pack(push)
#pragma pack(1)
#endif

/* the palette images will be treated as one byte pixels. Their width should be transformed
   accordingly.
*/
typedef struct ATTR_PACKED one_byte_pixel_t {
    uint8_t a;
} one_byte_pixel_t;

typedef struct ATTR_PACKED rgb32_pixel_t {
    uint8_t b;
    uint8_t g;
    uint8_t r;
    uint8_t pad;
} rgb32_pixel_t;

typedef struct ATTR_PACKED rgb24_pixel_t {
    uint8_t b;
    uint8_t g;
    uint8_t r;
} rgb24_pixel_t;

typedef uint16_t rgb16_pixel_t;

#ifndef __GNUC__
#pragma pack(pop)
#endif

#undef ATTR_PACKED

#define LZ_PLT
#include "glz_decode_tmpl.c"

#define LZ_PLT
#define PLT8
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_PLT
#define PLT4_BE
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_PLT
#define PLT4_LE
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_PLT
#define PLT1_BE
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_PLT
#define PLT1_LE
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_RGB16
#include "glz_decode_tmpl.c"
#define LZ_RGB16
#define TO_RGB32
#include "glz_decode_tmpl.c"

#define LZ_RGB24
#include "glz_decode_tmpl.c"

#define LZ_RGB32
#include "glz_decode_tmpl.c"

#define LZ_RGB_ALPHA
#include "glz_decode_tmpl.c"

#undef LZ_UNEXPECT_CONDITIONAL
#undef LZ_EXPECT_CONDITIONAL

typedef size_t (*decode_function)(GlzDecoderWindow &window, uint8_t* in_buf,
                                  uint8_t *out_buf, int size,
                                  DecodedImageWinId image_win_id, SpicePalette *plt,
                                  GlzDecoderDebug  &debug_calls);

// ordered according to LZ_IMAGE_TYPE
const decode_function DECODE_TO_RGB32[] = {
    NULL,
    glz_plt1_le_to_rgb32_decode,
    glz_plt1_be_to_rgb32_decode,
    glz_plt4_le_to_rgb32_decode,
    glz_plt4_be_to_rgb32_decode,
    glz_plt8_to_rgb32_decode,
    glz_rgb16_to_rgb32_decode,
    glz_rgb32_decode,
    glz_rgb32_decode,
    glz_rgb32_decode
};

const decode_function DECODE_TO_SAME[] = {
    NULL,
    glz_plt_decode,
    glz_plt_decode,
    glz_plt_decode,
    glz_plt_decode,
    glz_plt_decode,
    glz_rgb16_decode,
    glz_rgb24_decode,
    glz_rgb32_decode,
    glz_rgb32_decode
};

void GlzDecoder::decode(uint8_t *data, SpicePalette *palette, void *opaque_usr_info)
{
    DecodedImageWinId image_window_id;
    GlzDecodedImage *decoded_image;
    size_t n_in_bytes_decoded;
    int bytes_per_pixel;
    LzImageType decoded_type;

    _in_start = data;
    _in_now = data;

    decode_header();

#ifdef GLZ_DECODE_TO_RGB32
    bytes_per_pixel = 4;

    if (_image.type == LZ_IMAGE_TYPE_RGBA) {
        decoded_type = LZ_IMAGE_TYPE_RGBA;
    } else {
        decoded_type = LZ_IMAGE_TYPE_RGB32;
    }

#else
    if (IS_IMAGE_TYPE_PLT[_image.type]) {
        GLZ_ASSERT(_debug_calls, !(_image.gross_pixels % PLT_PIXELS_PER_BYTE[_image.type]));
    }
    bytes_per_pixel = RGB_BYTES_PER_PIXEL[_image.type];
    decoded_type = _image.type;
#endif

    image_window_id = _images_window.pre_decode(_image.id, _image.id - _image.win_head_dist);

    decoded_image = _usr_handler.alloc_image(opaque_usr_info, _image.id,
                                             _image.id - _image.win_head_dist,
                                             decoded_type, _image.width, _image.height,
                                             _image.gross_pixels, bytes_per_pixel,
                                             _image.top_down);

    _image.data = decoded_image->get_data();

    // decode_by_type
#ifdef GLZ_DECODE_TO_RGB32
    n_in_bytes_decoded = DECODE_TO_RGB32[_image.type](_images_window, _in_now, _image.data,
                                                      _image.gross_pixels, image_window_id,
                                                      palette, _debug_calls);
#else
    n_in_bytes_decoded = DECODE_TO_SAME[_image.type](_images_window, _in_now, _image.data,
                                                     IS_IMAGE_TYPE_PLT[_image.type] ?
                                                     _image.gross_pixels /
                                                     PLT_PIXELS_PER_BYTE[_image.type] :
                                                     _image.gross_pixels,
                                                     image_window_id, palette, _debug_calls);
#endif

    _in_now += n_in_bytes_decoded;

    if (_image.type == LZ_IMAGE_TYPE_RGBA) {
        glz_rgb_alpha_decode(_images_window, _in_now, _image.data,
                             _image.gross_pixels, image_window_id, palette, _debug_calls);
    }

    _images_window.post_decode(decoded_image);
}

<|endoftext|>