<commit_before>#include "./compile.h"
#include "tree_sitter/compiler.h"
#include <utility>
#include <vector>

namespace node_tree_sitter_compiler {

using namespace v8;
using namespace tree_sitter;
using std::string;
using std::get;
using std::pair;
using std::vector;

static std::string StringFromJsString(Handle<String> js_string) {
  String::Utf8Value utf8_string(js_string);
  return std::string(*utf8_string);
}

template<typename T>
Handle<T> ObjectGet(Handle<Object> object, const char *key) {
  return Handle<T>::Cast(object->Get(Nan::New(key).ToLocalChecked()));
}

template<typename T>
Handle<T> ArrayGet(Handle<Array> array, uint32_t i) {
  return Handle<T>::Cast(array->Get(i));
}

rule_ptr RuleFromJsRule(Handle<Object> js_rule) {
  if (!js_rule->IsObject()) {
    Nan::ThrowTypeError("Expected rule to be an object");
    return rule_ptr();
  }

  Handle<String> js_type = ObjectGet<String>(js_rule, "type");
  if (!js_type->IsString()) {
    Nan::ThrowTypeError("Expected rule type to be a string");
    return rule_ptr();
  }

  string type = StringFromJsString(js_type);
  if (type == "BLANK")
    return blank();

  if (type == "CHOICE") {
    Handle<Array> js_members = ObjectGet<Array>(js_rule, "members");
    vector<rule_ptr> members;
    uint32_t length = js_members->Length();
    for (uint32_t i = 0; i < length; i++) {
      Handle<Object> js_member = ArrayGet<Object>(js_members, i);
      rule_ptr member = RuleFromJsRule(js_member);
      if (member.get())
        members.push_back(member);
      else
        return rule_ptr();
    }
    return choice(members);
  }

  if (type == "ERROR")
    return err(RuleFromJsRule(ObjectGet<Object>(js_rule, "value")));

  if (type == "PATTERN")
    return pattern(StringFromJsString(ObjectGet<String>(js_rule, "value")));

  if (type == "REPEAT")
    return repeat(RuleFromJsRule(ObjectGet<Object>(js_rule, "value")));

  if (type == "REPEAT1")
    return repeat1(RuleFromJsRule(ObjectGet<Object>(js_rule, "value")));

  if (type == "SEQ") {
    Handle<Array> js_members = ObjectGet<Array>(js_rule, "members");
    vector<rule_ptr> members;
    uint32_t length = js_members->Length();
    for (uint32_t i = 0; i < length; i++) {
      Handle<Object> js_member = ArrayGet<Object>(js_members, i);
      rule_ptr member = RuleFromJsRule(js_member);
      if (member.get())
        members.push_back(member);
      else
        return rule_ptr();
    }
    return seq(members);
  }

  if (type == "STRING")
    return str(StringFromJsString(ObjectGet<String>(js_rule, "value")));

  if (type == "PREC") {
    rule_ptr rule = RuleFromJsRule(ObjectGet<Object>(js_rule, "rule"));
    if (rule.get())
      return prec(ObjectGet<Integer>(js_rule, "value")->IntegerValue(), rule);
    else
      return rule_ptr();
  }

  if (type == "PREC_LEFT") {
    rule_ptr rule = RuleFromJsRule(ObjectGet<Object>(js_rule, "rule"));
    if (rule.get())
      return prec_left(ObjectGet<Integer>(js_rule, "value")->IntegerValue(), rule);
    else
      return rule_ptr();
  }

  if (type == "PREC_RIGHT") {
    rule_ptr rule = RuleFromJsRule(ObjectGet<Object>(js_rule, "rule"));
    if (rule.get())
      return prec_right(ObjectGet<Integer>(js_rule, "value")->IntegerValue(), rule);
    else
      return rule_ptr();
  }

  if (type == "TOKEN") {
    rule_ptr value = RuleFromJsRule(ObjectGet<Object>(js_rule, "value"));
    if (value.get())
      return token(value);
    else
      return rule_ptr();
  }

  if (type == "SYMBOL")
    return sym(StringFromJsString(ObjectGet<String>(js_rule, "name")));

  Nan::ThrowError((string("Unexpected rule type: ") + type).c_str());
  return rule_ptr();
}

pair<Grammar, bool> GrammarFromJsGrammar(Handle<Object> js_grammar) {
  Handle<Object> js_rules = ObjectGet<Object>(js_grammar, "rules");
  if (!js_rules->IsObject()) {
    Nan::ThrowTypeError("Expected rules to be an object");
    return { Grammar({}), false };
  }

  vector<pair<string, rule_ptr>> rules;
  Local<Array> rule_names = js_rules->GetOwnPropertyNames();
  uint32_t length = rule_names->Length();
  for (uint32_t i = 0; i < length; i++) {
    Local<String> js_rule_name = Local<String>::Cast(rule_names->Get(i));
    string rule_name = StringFromJsString(js_rule_name);
    rule_ptr rule = RuleFromJsRule(Handle<Object>::Cast(js_rules->Get(js_rule_name)));
    if (rule.get()) {
      rules.push_back({ rule_name, rule });
    } else {
      return { Grammar({}), false };
    }
  }

  Grammar result(rules);

  Handle<Array> js_ubiquitous_tokens = ObjectGet<Array>(js_grammar, "ubiquitous");
  if (!js_ubiquitous_tokens->IsUndefined()) {
    if (!js_ubiquitous_tokens->IsArray()) {
      Nan::ThrowTypeError("Expected ubiquitous_tokens to be an array");
      return { Grammar({}), false };
    }

    vector<rule_ptr> ubiquitous_tokens;
    const uint32_t length = js_ubiquitous_tokens->Length();
    for (uint32_t i = 0; i < length; i++)
      ubiquitous_tokens.push_back(RuleFromJsRule(ArrayGet<Object>(js_ubiquitous_tokens, i)));

    result.ubiquitous_tokens(ubiquitous_tokens);
  }

  Handle<Array> js_expected_conflicts = ObjectGet<Array>(js_grammar, "expectedConflicts");
  if (!js_expected_conflicts->IsUndefined()) {
    if (!js_expected_conflicts->IsArray()) {
      Nan::ThrowTypeError("Expected expectedConflicts to be an array");
      return { Grammar({}), false };
    }

    vector<vector<string>> expected_conflicts;
    const uint32_t length = js_ubiquitous_tokens->Length();
    for (uint32_t i = 0; i < length; i++) {
      vector<string> conflict_set;
      Handle<Array> js_conflict_set = ArrayGet<Array>(js_expected_conflicts, i);
      const uint32_t conflict_set_length = js_conflict_set->Length();
      if (!js_conflict_set->IsArray()) {
        Nan::ThrowTypeError("Expected each expectedConflicts entry to be an array");
        return { Grammar({}), false };
      }

      for (uint32_t j = 0; j < conflict_set_length; j++) {
        Handle<String> conflict_symbol_name = ArrayGet<String>(js_conflict_set, j);
        if (!conflict_symbol_name->IsString()) {
          Nan::ThrowTypeError("Expected each item within each expectedConflicts entry to be a string");
          return { Grammar({}), false };
        }
        conflict_set.push_back(StringFromJsString(conflict_symbol_name));
      }

      expected_conflicts.push_back(conflict_set);
    }

    result.expected_conflicts(expected_conflicts);
  }

  return { result, true };
}

NAN_METHOD(Compile) {
  Handle<Object> js_grammar = Handle<Object>::Cast(info[0]);
  if (!js_grammar->IsObject()) {
    Nan::ThrowTypeError("Expected grammar to be an object");
    return;
  }

  Handle<String> js_name = ObjectGet<String>(js_grammar, "name");
  if (!js_name->IsString()) {
    Nan::ThrowTypeError("Expected grammar name to be a string");
    return;
  }

  string name = StringFromJsString(js_name);

  pair<Grammar, bool> grammarResult = GrammarFromJsGrammar(js_grammar);
  if (!grammarResult.second) {
    return;
  }

  pair<string, const GrammarError *> result = tree_sitter::compile(grammarResult.first, name);
  if (result.second) {
    Local<Value> error = Nan::Error(result.second->message.c_str());
    Local<Object>::Cast(error)->Set(Nan::New("isGrammarError").ToLocalChecked(), Nan::True());
    Nan::ThrowError(error);
    return;
  }

  info.GetReturnValue().Set(Nan::New(result.first).ToLocalChecked());
}

}  // namespace node_tree_sitter_compiler
<commit_msg>v8::Handle -> v8::Local<commit_after>#include "./compile.h"
#include "tree_sitter/compiler.h"
#include <utility>
#include <vector>

namespace node_tree_sitter_compiler {

using namespace v8;
using namespace tree_sitter;
using std::string;
using std::get;
using std::pair;
using std::vector;

static std::string StringFromJsString(Local<String> js_string) {
  String::Utf8Value utf8_string(js_string);
  return std::string(*utf8_string);
}

template<typename T>
Local<T> ObjectGet(Local<Object> object, const char *key) {
  return Local<T>::Cast(object->Get(Nan::New(key).ToLocalChecked()));
}

template<typename T>
Local<T> ArrayGet(Local<Array> array, uint32_t i) {
  return Local<T>::Cast(array->Get(i));
}

rule_ptr RuleFromJsRule(Local<Object> js_rule) {
  if (!js_rule->IsObject()) {
    Nan::ThrowTypeError("Expected rule to be an object");
    return rule_ptr();
  }

  Local<String> js_type = ObjectGet<String>(js_rule, "type");
  if (!js_type->IsString()) {
    Nan::ThrowTypeError("Expected rule type to be a string");
    return rule_ptr();
  }

  string type = StringFromJsString(js_type);
  if (type == "BLANK")
    return blank();

  if (type == "CHOICE") {
    Local<Array> js_members = ObjectGet<Array>(js_rule, "members");
    vector<rule_ptr> members;
    uint32_t length = js_members->Length();
    for (uint32_t i = 0; i < length; i++) {
      Local<Object> js_member = ArrayGet<Object>(js_members, i);
      rule_ptr member = RuleFromJsRule(js_member);
      if (member.get())
        members.push_back(member);
      else
        return rule_ptr();
    }
    return choice(members);
  }

  if (type == "ERROR")
    return err(RuleFromJsRule(ObjectGet<Object>(js_rule, "value")));

  if (type == "PATTERN")
    return pattern(StringFromJsString(ObjectGet<String>(js_rule, "value")));

  if (type == "REPEAT")
    return repeat(RuleFromJsRule(ObjectGet<Object>(js_rule, "value")));

  if (type == "REPEAT1")
    return repeat1(RuleFromJsRule(ObjectGet<Object>(js_rule, "value")));

  if (type == "SEQ") {
    Local<Array> js_members = ObjectGet<Array>(js_rule, "members");
    vector<rule_ptr> members;
    uint32_t length = js_members->Length();
    for (uint32_t i = 0; i < length; i++) {
      Local<Object> js_member = ArrayGet<Object>(js_members, i);
      rule_ptr member = RuleFromJsRule(js_member);
      if (member.get())
        members.push_back(member);
      else
        return rule_ptr();
    }
    return seq(members);
  }

  if (type == "STRING")
    return str(StringFromJsString(ObjectGet<String>(js_rule, "value")));

  if (type == "PREC") {
    rule_ptr rule = RuleFromJsRule(ObjectGet<Object>(js_rule, "rule"));
    if (rule.get())
      return prec(ObjectGet<Integer>(js_rule, "value")->IntegerValue(), rule);
    else
      return rule_ptr();
  }

  if (type == "PREC_LEFT") {
    rule_ptr rule = RuleFromJsRule(ObjectGet<Object>(js_rule, "rule"));
    if (rule.get())
      return prec_left(ObjectGet<Integer>(js_rule, "value")->IntegerValue(), rule);
    else
      return rule_ptr();
  }

  if (type == "PREC_RIGHT") {
    rule_ptr rule = RuleFromJsRule(ObjectGet<Object>(js_rule, "rule"));
    if (rule.get())
      return prec_right(ObjectGet<Integer>(js_rule, "value")->IntegerValue(), rule);
    else
      return rule_ptr();
  }

  if (type == "TOKEN") {
    rule_ptr value = RuleFromJsRule(ObjectGet<Object>(js_rule, "value"));
    if (value.get())
      return token(value);
    else
      return rule_ptr();
  }

  if (type == "SYMBOL")
    return sym(StringFromJsString(ObjectGet<String>(js_rule, "name")));

  Nan::ThrowError((string("Unexpected rule type: ") + type).c_str());
  return rule_ptr();
}

pair<Grammar, bool> GrammarFromJsGrammar(Local<Object> js_grammar) {
  Local<Object> js_rules = ObjectGet<Object>(js_grammar, "rules");
  if (!js_rules->IsObject()) {
    Nan::ThrowTypeError("Expected rules to be an object");
    return { Grammar({}), false };
  }

  vector<pair<string, rule_ptr>> rules;
  Local<Array> rule_names = js_rules->GetOwnPropertyNames();
  uint32_t length = rule_names->Length();
  for (uint32_t i = 0; i < length; i++) {
    Local<String> js_rule_name = Local<String>::Cast(rule_names->Get(i));
    string rule_name = StringFromJsString(js_rule_name);
    rule_ptr rule = RuleFromJsRule(Local<Object>::Cast(js_rules->Get(js_rule_name)));
    if (rule.get()) {
      rules.push_back({ rule_name, rule });
    } else {
      return { Grammar({}), false };
    }
  }

  Grammar result(rules);

  Local<Array> js_ubiquitous_tokens = ObjectGet<Array>(js_grammar, "ubiquitous");
  if (!js_ubiquitous_tokens->IsUndefined()) {
    if (!js_ubiquitous_tokens->IsArray()) {
      Nan::ThrowTypeError("Expected ubiquitous_tokens to be an array");
      return { Grammar({}), false };
    }

    vector<rule_ptr> ubiquitous_tokens;
    for (uint32_t i = 0, length = js_ubiquitous_tokens->Length(); i < length; i++)
      ubiquitous_tokens.push_back(RuleFromJsRule(ArrayGet<Object>(js_ubiquitous_tokens, i)));

    result.ubiquitous_tokens(ubiquitous_tokens);
  }

  Local<Array> js_expected_conflicts = ObjectGet<Array>(js_grammar, "expectedConflicts");
  if (!js_expected_conflicts->IsUndefined()) {
    if (!js_expected_conflicts->IsArray()) {
      Nan::ThrowTypeError("Expected expectedConflicts to be an array");
      return { Grammar({}), false };
    }

    vector<vector<string>> expected_conflicts;
    for (uint32_t i = 0, length = js_expected_conflicts->Length(); i < length; i++) {
      vector<string> conflict_set;
      Local<Array> js_conflict_set = ArrayGet<Array>(js_expected_conflicts, i);
      if (!js_conflict_set->IsArray()) {
        Nan::ThrowTypeError("Expected each expectedConflicts entry to be an array");
        return { Grammar({}), false };
      }

      for (uint32_t j = 0, conflict_set_length = js_conflict_set->Length(); j < conflict_set_length; j++) {
        Local<String> conflict_symbol_name = ArrayGet<String>(js_conflict_set, j);
        if (!conflict_symbol_name->IsString()) {
          Nan::ThrowTypeError("Expected each item within each expectedConflicts entry to be a string");
          return { Grammar({}), false };
        }
        conflict_set.push_back(StringFromJsString(conflict_symbol_name));
      }

      expected_conflicts.push_back(conflict_set);
    }

    result.expected_conflicts(expected_conflicts);
  }

  return { result, true };
}

NAN_METHOD(Compile) {
  Local<Object> js_grammar = Local<Object>::Cast(info[0]);
  if (!js_grammar->IsObject()) {
    Nan::ThrowTypeError("Expected grammar to be an object");
    return;
  }

  Local<String> js_name = ObjectGet<String>(js_grammar, "name");
  if (!js_name->IsString()) {
    Nan::ThrowTypeError("Expected grammar name to be a string");
    return;
  }

  string name = StringFromJsString(js_name);

  pair<Grammar, bool> grammarResult = GrammarFromJsGrammar(js_grammar);
  if (!grammarResult.second) {
    return;
  }

  pair<string, const GrammarError *> result = tree_sitter::compile(grammarResult.first, name);
  if (result.second) {
    Local<Value> error = Nan::Error(result.second->message.c_str());
    Local<Object>::Cast(error)->Set(Nan::New("isGrammarError").ToLocalChecked(), Nan::True());
    Nan::ThrowError(error);
    return;
  }

  info.GetReturnValue().Set(Nan::New(result.first).ToLocalChecked());
}

}  // namespace node_tree_sitter_compiler
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2004-2007  See the AUTHORS file for details.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 */

#include "HTTPSock.h"
#include "znc.h"

CHTTPSock::CHTTPSock() : Csock() {
	m_bSentHeader = false;
	m_bGotHeader = false;
	m_bLoggedIn = false;
	m_bPost = false;
	m_bDone = false;
	m_uPostLen = 0;
	EnableReadLine();
}

CHTTPSock::CHTTPSock(const CString& sHostname, unsigned short uPort, int iTimeout) : Csock(sHostname, uPort, iTimeout) {
	m_bSentHeader = false;
	m_bGotHeader = false;
	m_bLoggedIn = false;
	m_bPost = false;
	m_bDone = false;
	m_uPostLen = 0;
	EnableReadLine();
}

CHTTPSock::~CHTTPSock() {}

void CHTTPSock::ReadData(const char* data, int len) {
	string s;
	s.append(data, len);

	if (!m_bDone && m_bGotHeader && m_bPost) {
		m_sPostData.append(data, len);
		CheckPost();
	}
}

void CHTTPSock::CheckPost() {
	if (m_sPostData.size() >= m_uPostLen) {
		ParseParams(m_sPostData.Left(m_uPostLen));
		GetPage();
		m_sPostData.clear();
		m_bDone = true;
		Close(Csock::CLT_AFTERWRITE);
	}
}

void CHTTPSock::ReadLine(const CString& sData) {
	CString sLine = sData;
	sLine.TrimRight("\r\n");

	if (m_bGotHeader) {
		return;
	}

	CString sName = sLine.Token(0);

	if (sName.CaseCmp("GET") == 0) {
		m_bPost = false;
		m_sURI = sLine.Token(1);
		ParseURI();
	} else if (sName.CaseCmp("POST") == 0) {
		m_bPost = true;
		m_sURI = sLine.Token(1);
		ParseURI();
	} else if (sName.CaseCmp("Authorization:") == 0) {
		CString sUnhashed;
		sLine.Token(2).Base64Decode(sUnhashed);
		m_sUser = sUnhashed.Token(0, false, ":");
		m_sPass = sUnhashed.Token(1, true, ":");
		m_bLoggedIn = OnLogin(m_sUser, m_sPass);
	} else if (sName.CaseCmp("Content-Length:") == 0) {
		m_uPostLen = sLine.Token(1).ToULong();
	} else if (sLine.empty()) {
		m_bGotHeader = true;
		DisableReadLine();

		if (m_bPost) {
			m_sPostData = GetInternalBuffer();
			CheckPost();
		} else {
			GetPage();
		}
	}
}

void CHTTPSock::GetPage() {
	CString sPage;

	DEBUG_ONLY(cout << "Page Request [" << m_sURI << "] ");

	if (!OnPageRequest(m_sURI, sPage)) {
		PrintNotFound();
		return;
	}

	if (PrintHeader(sPage.length())) {
		Write(sPage);
		Close(Csock::CLT_AFTERWRITE);
	}
}

bool CHTTPSock::PrintFile(const CString& sFileName, CString sContentType) {
	CString sFilePath = sFileName;

	if (!m_sDocRoot.empty()) {
		while (sFilePath.Left(1) == "/") {
			sFilePath.LeftChomp(1);
		}

		sFilePath = CUtils::ChangeDir(m_sDocRoot, sFilePath, m_sDocRoot);

		if (sFilePath.Left(m_sDocRoot.size()) != m_sDocRoot) {
			PrintErrorPage(403, "Forbidden", "You don't have permission to access that file on this server.");
			DEBUG_ONLY(cout << "THIS FILE:     [" << sFilePath << "] does not live in ..." << endl);
			DEBUG_ONLY(cout << "DOCUMENT ROOT: [" << m_sDocRoot << "]" << endl);
			return false;
		}
	}

	CFile File(sFilePath);

	if (!File.Open(CFile::F_Read)) {
		PrintNotFound();
		return false;
	}

	if (sContentType.empty()) {
		if (sFileName.Right(5).CaseCmp(".html") == 0 || sFileName.Right(4).CaseCmp(".htm") == 0) {
			sContentType = "text/html";
		} else if (sFileName.Right(4).CaseCmp(".css") == 0) {
			sContentType = "text/css";
		} else if (sFileName.Right(3).CaseCmp(".js") == 0) {
			sContentType = "application/x-javascript";
		} else if (sFileName.Right(4).CaseCmp(".jpg") == 0) {
			sContentType = "image/jpeg";
		} else if (sFileName.Right(4).CaseCmp(".gif") == 0) {
			sContentType = "image/gif";
		} else if (sFileName.Right(4).CaseCmp(".ico") == 0) {
			sContentType = "image/x-icon";
		} else if (sFileName.Right(4).CaseCmp(".png") == 0) {
			sContentType = "image/png";
		} else if (sFileName.Right(4).CaseCmp(".bmp") == 0) {
			sContentType = "image/bmp";
		} else {
			sContentType = "text/plain";
		}
	}

	PrintHeader(File.GetSize(), sContentType);

	char szBuf[4096];
	int iLen = 0;

	while((iLen = File.Read(szBuf, 4096)) > 0) {
		Write(szBuf, iLen);
	}

	Close(Csock::CLT_AFTERWRITE);

	return true;
}

void CHTTPSock::ParseURI() {
	ParseParams(m_sURI.Token(1, true, "?"));
	m_sURI = m_sURI.Token(0, false, "?");
}

void CHTTPSock::ParseParams(const CString& sParams) {
	m_msvsParams.clear();

	VCString vsPairs;
	sParams.Split("&", vsPairs, true);

	for (unsigned int a = 0; a < vsPairs.size(); a++) {
		const CString& sPair = vsPairs[a];
		CString sName = sPair.Token(0, false, "=").Escape_n(CString::EURL, CString::EASCII);
		CString sValue = sPair.Token(1, true, "=").Escape_n(CString::EURL, CString::EASCII);

		m_msvsParams[sName].push_back(sValue);
	}
}

void CHTTPSock::SetDocRoot(const CString& s) {
	m_sDocRoot = s + "/";
	m_sDocRoot.Replace("//", "/");
}

const CString& CHTTPSock::GetDocRoot() const {
	return m_sDocRoot;
}

const CString& CHTTPSock::GetUser() const {
	return m_sUser;
}

const CString& CHTTPSock::GetPass() const {
	return m_sPass;
}

const CString& CHTTPSock::GetContentType() const {
	return m_sContentType;
}

const CString& CHTTPSock::GetParamString() const {
	return m_sPostData;
}

bool CHTTPSock::HasParam(const CString& sName) const {
	return (m_msvsParams.find(sName) != m_msvsParams.end());
}

CString CHTTPSock::GetParam(const CString& sName) const {
	CString sRet;

	VCString vsParams;
	if (GetParamValues(sName, vsParams)) {
		sRet = vsParams[0];
	}

	return sRet;
}

unsigned int CHTTPSock::GetParamValues(const CString& sName, set<CString>& ssRet) const {
	ssRet.clear();

	map<CString, VCString>::const_iterator it = m_msvsParams.find(sName);

	if (it != m_msvsParams.end()) {
		for (unsigned int a = 0; a < it->second.size(); a++) {
			ssRet.insert(it->second[a]);
		}
	}

	return ssRet.size();
}

unsigned int CHTTPSock::GetParamValues(const CString& sName, VCString& vsRet) const {
	vsRet.clear();

	map<CString, VCString>::const_iterator it = m_msvsParams.find(sName);

	if (it != m_msvsParams.end()) {
		vsRet = it->second;
	}

	return vsRet.size();
}

const map<CString, VCString>& CHTTPSock::GetParams() const {
	return m_msvsParams;
}

bool CHTTPSock::OnPageRequest(const CString& sURI, CString& sPageRet) {
	return false;
}

bool CHTTPSock::PrintNotFound() {
	return PrintErrorPage(404, "Not Found", "The requested URL was not found on this server.");
}

bool CHTTPSock::PrintErrorPage(unsigned int uStatusId, const CString& sStatusMsg, const CString& sMessage) {
	if (SentHeader()) {
		return false;
	}

	CString sPage = GetErrorPage(uStatusId, sStatusMsg, sMessage);
	PrintHeader(sPage.length(), "text/html", uStatusId, sStatusMsg);
	Write(sPage);
	Close(Csock::CLT_AFTERWRITE);

	return true;
}

CString CHTTPSock::GetErrorPage(unsigned int uStatusId, const CString& sStatusMsg, const CString& sMessage) {
	return "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
		"\r\n<title>" + CString(uStatusId) + " " + sStatusMsg.Escape_n(CString::EHTML) + "</title>\r\n"
		"\r\n
" + sStatusMsg.Escape_n(CString::EHTML) + "
\r\n"
		"
" + sMessage.Escape_n(CString::EHTML) + "
\r\n"
		"
\r\n<address>" + CZNC::GetTag().Escape_n(CString::EHTML) + " at " + GetLocalIP().Escape_n(CString::EHTML) + " Port " + CString(GetLocalPort()) + "</address>\r\n"
		"\r\n";
}

bool CHTTPSock::ForceLogin() {
	if (m_bLoggedIn) {
		return true;
	}

	if (SentHeader()) {
		return false;
	}

	CString sPage = GetErrorPage(401, "Unauthorized", "You need to login to view this page.");
	AddHeader("WWW-Authenticate", "Basic realm=\"" + CZNC::GetTag() + "\"");
	PrintHeader(sPage.length(), "text/html", 401, "Unauthorized");
	Write(sPage);
	Close(Csock::CLT_AFTERWRITE);

	return false;
}

bool CHTTPSock::OnLogin(const CString& sUser, const CString& sPass) {
	return false;
}

bool CHTTPSock::SentHeader() const {
	return m_bSentHeader;
}

bool CHTTPSock::PrintHeader(unsigned long uContentLength, const CString& sContentType, unsigned int uStatusId, const CString& sStatusMsg) {
	if (SentHeader()) {
		DEBUG_ONLY(cout << "- Header already sent!" << endl);
		return false;
	}

	if (!sContentType.empty()) {
		m_sContentType = sContentType;
	}

	if (m_sContentType.empty()) {
		m_sContentType = "text/html";
	}

	DEBUG_ONLY(cout << "- " << uStatusId << " (" << sStatusMsg << ") [" << m_sContentType << "]" << endl);

	Write("HTTP/1.0 " + CString(uStatusId) + " " + sStatusMsg + "\r\n");
	//Write("Date: Tue, 28 Jun 2005 20:45:36 GMT\r\n");
	Write("Server: ZNC " + CZNC::GetTag() + "\r\n");
	Write("Content-Length: " + CString(uContentLength) + "\r\n");
	Write("Connection: Close\r\n");
	Write("Content-Type: " + m_sContentType + "\r\n");

	for (MCString::iterator it = m_msHeaders.begin(); it != m_msHeaders.end(); it++) {
		Write(it->first + ": " + it->second + "\r\n");
	}

	Write("\r\n");
	m_bSentHeader = true;

	return true;
}

void CHTTPSock::SetContentType(const CString& sContentType) {
	m_sContentType = sContentType;
}

void CHTTPSock::AddHeader(const CString& sName, const CString& sValue) {
	m_msHeaders[sName] = sValue;
}

bool CHTTPSock::Redirect(const CString& sURL) {
	if (SentHeader()) {
		return false;
	}

	DEBUG_ONLY(cout << "- Redirect to [" << sURL << "]" << endl);
	CString sPage = GetErrorPage(302, "Found", "The document has moved here.");
	AddHeader("Location", sURL);
	PrintHeader(sPage.length(), "text/html", 302, "Found");
	Write(sPage);
	Close(Csock::CLT_AFTERWRITE);

	return true;
}

void CHTTPSock::Timeout() {
}

void CHTTPSock::SockError(int iErrno) {
}

void CHTTPSock::Connected() {
	SetTimeout(120);
}

void CHTTPSock::Disconnected() {
}

Csock* CHTTPSock::GetSockObj(const CString& sHost, unsigned short uPort) {
	CHTTPSock* pSock = new CHTTPSock;
	pSock->SetSockName("HTTP::CLIENT");
	pSock->SetTimeout(120);

	return pSock;
}
<commit_msg>Webadmin sent 'Server: ZNC ZNC 0.051', remove one 'ZNC'<commit_after>/*
 * Copyright (C) 2004-2007  See the AUTHORS file for details.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 */

#include "HTTPSock.h"
#include "znc.h"

CHTTPSock::CHTTPSock() : Csock() {
	m_bSentHeader = false;
	m_bGotHeader = false;
	m_bLoggedIn = false;
	m_bPost = false;
	m_bDone = false;
	m_uPostLen = 0;
	EnableReadLine();
}

CHTTPSock::CHTTPSock(const CString& sHostname, unsigned short uPort, int iTimeout) : Csock(sHostname, uPort, iTimeout) {
	m_bSentHeader = false;
	m_bGotHeader = false;
	m_bLoggedIn = false;
	m_bPost = false;
	m_bDone = false;
	m_uPostLen = 0;
	EnableReadLine();
}

CHTTPSock::~CHTTPSock() {}

void CHTTPSock::ReadData(const char* data, int len) {
	string s;
	s.append(data, len);

	if (!m_bDone && m_bGotHeader && m_bPost) {
		m_sPostData.append(data, len);
		CheckPost();
	}
}

void CHTTPSock::CheckPost() {
	if (m_sPostData.size() >= m_uPostLen) {
		ParseParams(m_sPostData.Left(m_uPostLen));
		GetPage();
		m_sPostData.clear();
		m_bDone = true;
		Close(Csock::CLT_AFTERWRITE);
	}
}

void CHTTPSock::ReadLine(const CString& sData) {
	CString sLine = sData;
	sLine.TrimRight("\r\n");

	if (m_bGotHeader) {
		return;
	}

	CString sName = sLine.Token(0);

	if (sName.CaseCmp("GET") == 0) {
		m_bPost = false;
		m_sURI = sLine.Token(1);
		ParseURI();
	} else if (sName.CaseCmp("POST") == 0) {
		m_bPost = true;
		m_sURI = sLine.Token(1);
		ParseURI();
	} else if (sName.CaseCmp("Authorization:") == 0) {
		CString sUnhashed;
		sLine.Token(2).Base64Decode(sUnhashed);
		m_sUser = sUnhashed.Token(0, false, ":");
		m_sPass = sUnhashed.Token(1, true, ":");
		m_bLoggedIn = OnLogin(m_sUser, m_sPass);
	} else if (sName.CaseCmp("Content-Length:") == 0) {
		m_uPostLen = sLine.Token(1).ToULong();
	} else if (sLine.empty()) {
		m_bGotHeader = true;
		DisableReadLine();

		if (m_bPost) {
			m_sPostData = GetInternalBuffer();
			CheckPost();
		} else {
			GetPage();
		}
	}
}

void CHTTPSock::GetPage() {
	CString sPage;

	DEBUG_ONLY(cout << "Page Request [" << m_sURI << "] ");

	if (!OnPageRequest(m_sURI, sPage)) {
		PrintNotFound();
		return;
	}

	if (PrintHeader(sPage.length())) {
		Write(sPage);
		Close(Csock::CLT_AFTERWRITE);
	}
}

bool CHTTPSock::PrintFile(const CString& sFileName, CString sContentType) {
	CString sFilePath = sFileName;

	if (!m_sDocRoot.empty()) {
		while (sFilePath.Left(1) == "/") {
			sFilePath.LeftChomp(1);
		}

		sFilePath = CUtils::ChangeDir(m_sDocRoot, sFilePath, m_sDocRoot);

		if (sFilePath.Left(m_sDocRoot.size()) != m_sDocRoot) {
			PrintErrorPage(403, "Forbidden", "You don't have permission to access that file on this server.");
			DEBUG_ONLY(cout << "THIS FILE:     [" << sFilePath << "] does not live in ..." << endl);
			DEBUG_ONLY(cout << "DOCUMENT ROOT: [" << m_sDocRoot << "]" << endl);
			return false;
		}
	}

	CFile File(sFilePath);

	if (!File.Open(CFile::F_Read)) {
		PrintNotFound();
		return false;
	}

	if (sContentType.empty()) {
		if (sFileName.Right(5).CaseCmp(".html") == 0 || sFileName.Right(4).CaseCmp(".htm") == 0) {
			sContentType = "text/html";
		} else if (sFileName.Right(4).CaseCmp(".css") == 0) {
			sContentType = "text/css";
		} else if (sFileName.Right(3).CaseCmp(".js") == 0) {
			sContentType = "application/x-javascript";
		} else if (sFileName.Right(4).CaseCmp(".jpg") == 0) {
			sContentType = "image/jpeg";
		} else if (sFileName.Right(4).CaseCmp(".gif") == 0) {
			sContentType = "image/gif";
		} else if (sFileName.Right(4).CaseCmp(".ico") == 0) {
			sContentType = "image/x-icon";
		} else if (sFileName.Right(4).CaseCmp(".png") == 0) {
			sContentType = "image/png";
		} else if (sFileName.Right(4).CaseCmp(".bmp") == 0) {
			sContentType = "image/bmp";
		} else {
			sContentType = "text/plain";
		}
	}

	PrintHeader(File.GetSize(), sContentType);

	char szBuf[4096];
	int iLen = 0;

	while((iLen = File.Read(szBuf, 4096)) > 0) {
		Write(szBuf, iLen);
	}

	Close(Csock::CLT_AFTERWRITE);

	return true;
}

void CHTTPSock::ParseURI() {
	ParseParams(m_sURI.Token(1, true, "?"));
	m_sURI = m_sURI.Token(0, false, "?");
}

void CHTTPSock::ParseParams(const CString& sParams) {
	m_msvsParams.clear();

	VCString vsPairs;
	sParams.Split("&", vsPairs, true);

	for (unsigned int a = 0; a < vsPairs.size(); a++) {
		const CString& sPair = vsPairs[a];
		CString sName = sPair.Token(0, false, "=").Escape_n(CString::EURL, CString::EASCII);
		CString sValue = sPair.Token(1, true, "=").Escape_n(CString::EURL, CString::EASCII);

		m_msvsParams[sName].push_back(sValue);
	}
}

void CHTTPSock::SetDocRoot(const CString& s) {
	m_sDocRoot = s + "/";
	m_sDocRoot.Replace("//", "/");
}

const CString& CHTTPSock::GetDocRoot() const {
	return m_sDocRoot;
}

const CString& CHTTPSock::GetUser() const {
	return m_sUser;
}

const CString& CHTTPSock::GetPass() const {
	return m_sPass;
}

const CString& CHTTPSock::GetContentType() const {
	return m_sContentType;
}

const CString& CHTTPSock::GetParamString() const {
	return m_sPostData;
}

bool CHTTPSock::HasParam(const CString& sName) const {
	return (m_msvsParams.find(sName) != m_msvsParams.end());
}

CString CHTTPSock::GetParam(const CString& sName) const {
	CString sRet;

	VCString vsParams;
	if (GetParamValues(sName, vsParams)) {
		sRet = vsParams[0];
	}

	return sRet;
}

unsigned int CHTTPSock::GetParamValues(const CString& sName, set<CString>& ssRet) const {
	ssRet.clear();

	map<CString, VCString>::const_iterator it = m_msvsParams.find(sName);

	if (it != m_msvsParams.end()) {
		for (unsigned int a = 0; a < it->second.size(); a++) {
			ssRet.insert(it->second[a]);
		}
	}

	return ssRet.size();
}

unsigned int CHTTPSock::GetParamValues(const CString& sName, VCString& vsRet) const {
	vsRet.clear();

	map<CString, VCString>::const_iterator it = m_msvsParams.find(sName);

	if (it != m_msvsParams.end()) {
		vsRet = it->second;
	}

	return vsRet.size();
}

const map<CString, VCString>& CHTTPSock::GetParams() const {
	return m_msvsParams;
}

bool CHTTPSock::OnPageRequest(const CString& sURI, CString& sPageRet) {
	return false;
}

bool CHTTPSock::PrintNotFound() {
	return PrintErrorPage(404, "Not Found", "The requested URL was not found on this server.");
}

bool CHTTPSock::PrintErrorPage(unsigned int uStatusId, const CString& sStatusMsg, const CString& sMessage) {
	if (SentHeader()) {
		return false;
	}

	CString sPage = GetErrorPage(uStatusId, sStatusMsg, sMessage);
	PrintHeader(sPage.length(), "text/html", uStatusId, sStatusMsg);
	Write(sPage);
	Close(Csock::CLT_AFTERWRITE);

	return true;
}

CString CHTTPSock::GetErrorPage(unsigned int uStatusId, const CString& sStatusMsg, const CString& sMessage) {
	return "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
		"\r\n<title>" + CString(uStatusId) + " " + sStatusMsg.Escape_n(CString::EHTML) + "</title>\r\n"
		"\r\n
" + sStatusMsg.Escape_n(CString::EHTML) + "
\r\n"
		"
" + sMessage.Escape_n(CString::EHTML) + "
\r\n"
		"
\r\n<address>" + CZNC::GetTag().Escape_n(CString::EHTML) + " at " + GetLocalIP().Escape_n(CString::EHTML) + " Port " + CString(GetLocalPort()) + "</address>\r\n"
		"\r\n";
}

bool CHTTPSock::ForceLogin() {
	if (m_bLoggedIn) {
		return true;
	}

	if (SentHeader()) {
		return false;
	}

	CString sPage = GetErrorPage(401, "Unauthorized", "You need to login to view this page.");
	AddHeader("WWW-Authenticate", "Basic realm=\"" + CZNC::GetTag() + "\"");
	PrintHeader(sPage.length(), "text/html", 401, "Unauthorized");
	Write(sPage);
	Close(Csock::CLT_AFTERWRITE);

	return false;
}

bool CHTTPSock::OnLogin(const CString& sUser, const CString& sPass) {
	return false;
}

bool CHTTPSock::SentHeader() const {
	return m_bSentHeader;
}

bool CHTTPSock::PrintHeader(unsigned long uContentLength, const CString& sContentType, unsigned int uStatusId, const CString& sStatusMsg) {
	if (SentHeader()) {
		DEBUG_ONLY(cout << "- Header already sent!" << endl);
		return false;
	}

	if (!sContentType.empty()) {
		m_sContentType = sContentType;
	}

	if (m_sContentType.empty()) {
		m_sContentType = "text/html";
	}

	DEBUG_ONLY(cout << "- " << uStatusId << " (" << sStatusMsg << ") [" << m_sContentType << "]" << endl);

	Write("HTTP/1.0 " + CString(uStatusId) + " " + sStatusMsg + "\r\n");
	//Write("Date: Tue, 28 Jun 2005 20:45:36 GMT\r\n");
	Write("Server: " + CZNC::GetTag() + "\r\n");
	Write("Content-Length: " + CString(uContentLength) + "\r\n");
	Write("Connection: Close\r\n");
	Write("Content-Type: " + m_sContentType + "\r\n");

	for (MCString::iterator it = m_msHeaders.begin(); it != m_msHeaders.end(); it++) {
		Write(it->first + ": " + it->second + "\r\n");
	}

	Write("\r\n");
	m_bSentHeader = true;

	return true;
}

void CHTTPSock::SetContentType(const CString& sContentType) {
	m_sContentType = sContentType;
}

void CHTTPSock::AddHeader(const CString& sName, const CString& sValue) {
	m_msHeaders[sName] = sValue;
}

bool CHTTPSock::Redirect(const CString& sURL) {
	if (SentHeader()) {
		return false;
	}

	DEBUG_ONLY(cout << "- Redirect to [" << sURL << "]" << endl);
	CString sPage = GetErrorPage(302, "Found", "The document has moved here.");
	AddHeader("Location", sURL);
	PrintHeader(sPage.length(), "text/html", 302, "Found");
	Write(sPage);
	Close(Csock::CLT_AFTERWRITE);

	return true;
}

void CHTTPSock::Timeout() {
}

void CHTTPSock::SockError(int iErrno) {
}

void CHTTPSock::Connected() {
	SetTimeout(120);
}

void CHTTPSock::Disconnected() {
}

Csock* CHTTPSock::GetSockObj(const CString& sHost, unsigned short uPort) {
	CHTTPSock* pSock = new CHTTPSock;
	pSock->SetSockName("HTTP::CLIENT");
	pSock->SetTimeout(120);

	return pSock;
}
<|endoftext|>


<commit_before>#pragma once
#include <string>
#include <map>
#include <sstream>
#include <fstream>
#include <functional>
#include <vector>
#include <list>
#ifndef OUT
#define OUT
#endif
#define JSON_CLASS(name) class name final : public cppson::Parsable<name>
#define FIELD(type, name) \
struct Init_ ## name \
{\
	Init_ ## name ## () \
	{ \
		meta[ #name ] = [](Type* t, cppson::JsonValue value) -> bool \
		{ \
			return value.parse(t->name); \
		}; \
	} \
};\
struct Meta_ ## name\
{\
	Meta_ ## name ## ()\
	{\
		static Init_ ## name init;\
	}\
};\
Meta_ ## name meta_ ## name;\
type name;

namespace cppson
{

class Single
{
public:
	template<typename R>
	R getValue()
	{
		static_assert(false, "invalid call.");
	}

	template<>
	int getValue<int>()
	{
		return atoi(str.c_str());
	}

	template<>
	bool getValue<bool>()
	{
		if (str == "true")
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	template<>
	double getValue<double>()
	{
		return atof(str.c_str());
	}

	template<>
	std::string getValue<std::string>()
	{
		return str;
	}

	std::string& getStr()
	{
		return str;
	}

private:
	std::string str;
};

class JsonValue
{
public:

	//if failed, return -1
	int init(std::vector<std::string>& tokens, int offset)
	{
		//object
		if (tokens[offset] == "{")
		{
			type = OBJECT;
			offset++;

			while (offset < tokens.size())
			{
				if (tokens[offset] == "\"")
				{
					std::string& key = tokens[offset + 1];

					if (tokens[offset + 2] != "\"" || tokens[offset + 3] != ":")
						return -1;

					offset = obj[key].init(tokens, offset + 4);

					if (offset == -1)
						return -1;
				}
				else if (tokens[offset] == ",")
				{
					offset++;
				}
				else if (tokens[offset] == "}")
				{
					return offset + 1;
				}
				else
				{
					return -1;
				}
			}
			return -1;
		}
		else if (tokens[offset] == "[")
		{
			type = ARRAY;
			offset++;
			while (offset < tokens.size())
			{
				if (tokens[offset] == ",")
				{
					offset++;
				}
				else if (tokens[offset] == "]")
				{
					return offset + 1;
				}
				else
				{
					JsonValue value;
					offset = value.init(tokens, offset);
					if (offset == -1)
						return -1;

					arr.push_back(value);
				}
			}
			return -1;
		}
		else
		{
			type = SINGLE;
			if (tokens[offset] == "\"")
			{
				single.getStr() = tokens[offset + 1];
				return offset + 3;
			}
			else
			{
				single.getStr() = tokens[offset];
				return offset + 1;
			}
		}
	}

	template<typename T>
	bool parse(T& val)
	{
		static_assert(std::is_base_of<Parsable<T>, T>::value, "T must be derived from Parsable<T>");

		if (type != OBJECT)
			return false;

		for (auto m : obj)
		{
			if (!T::getMeta().at(m.first)(&val, m.second))
			{
				return false;
			}
		}

		return true;
	}

	template<typename T>
	bool parse(std::list<T>& val)
	{
		static_assert(std::is_base_of<Parsable<T>, T>::value ||
			std::is_same<int, T>::value ||
			std::is_same<float, T>::value ||
			std::is_same<double, T>::value ||
			std::is_same<std::string, T>::value ||
			std::is_same<bool, T>::value, "T must be derived from Parsable<T>, or T must be int or float or double or std::string or bool.");

		if (type != ARRAY)
			return false;

		for (auto v : arr)
		{
			T p;

			if (v.parse(p))
				val.push_back(p);
			else
				return false;
		}

		return true;
	}

	template<typename T>
	bool parse(std::vector<T>& val)
	{
		static_assert(std::is_base_of<Parsable<T>, T>::value ||
					std::is_same<int, T>::value ||
					std::is_same<float, T>::value ||
					std::is_same<double, T>::value ||
					std::is_same<std::string, T>::value ||
					std::is_same<bool, T>::value, "T must be derived from Parsable<T>, or T must be int or float or double or std::string or bool.");

		if (type != ARRAY)
			return false;

		for (auto v : arr)
		{
			T p;

			if (v.parse(p))
				val.push_back(p);
			else
				return false;
		}

		return true;
	}

	template<>
	bool parse<int>(int& val)
	{
		if (type != SINGLE)
			return false;

		val = single.getValue<int>();

		return true;
	}

	template<>
	bool parse<double>(double& val)
	{
		if (type != SINGLE)
			return false;

		val = single.getValue<double>();

		return true;
	}

	template<>
	bool parse<float>(float& val)
	{
		if (type != SINGLE)
			return false;

		val = single.getValue<double>();

		return true;
	}

	template<>
	bool parse<bool>(bool& val)
	{
		if (type != SINGLE)
			return false;

		val = single.getValue<bool>();

		return true;
	}

	template<>
	bool parse<std::string>(std::string& val)
	{
		if (type != SINGLE)
			return false;

		val = single.getValue<std::string>();

		return true;
	}

private:
	enum Type
	{
		NONE,
		SINGLE,
		OBJECT,
		ARRAY
	};

	Type type;
	Single single;
	std::map<std::string, JsonValue> obj;
	std::vector<JsonValue> arr;
};

bool tokenize(const std::string& str, OUT std::vector<std::string>& tokens)
{
	std::string token;
	std::string space = " \t\r\n";
	std::string delim = ":{}[],";
	std::string number = "0123456789.";
	int state = 0;

	for (size_t i = 0; i < str.size(); i++)
	{
		switch (state)
		{
		case 0:
			//ignore space
			if (space.find(str[i]) != std::string::npos)
			{
				if (token.size() != 0)
				{
					tokens.push_back(token);
					token.clear();
				}

				continue;
			}
			//go string
			else if (str[i] == '"')
			{
				if (token.size() != 0)
					return false;

				tokens.push_back({ str[i] });
				state = 1;
			}
			//one char token
			else if (delim.find(str[i]) != std::string::npos)
			{
				if (token.size() != 0)
				{
					tokens.push_back(token);
					token.clear();
				}
				tokens.push_back({ str[i] });
			}
			//number
			else if (number.find(str[i]) != std::string::npos)
			{
				token.push_back({ str[i] });
			}
			//true or false
			else if (str.substr(i, 4) == "true")
			{
				if (token.size() != 0)
					return false;

				tokens.push_back(str.substr(i, 4));
				i += 3;
			}
			else if (str.substr(i, 5) == "false")
			{
				if (token.size() != 0)
					return false;

				tokens.push_back(str.substr(i, 5));
				i += 4;
			}
			else
			{
				return false;
			}
			break;
		case 1:
			if (str[i] == '"')
			{
				tokens.push_back(token);
				tokens.push_back({ str[i] });
				token.clear();
				state = 0;
			}
			else
			{
				token.push_back({ str[i] });
			}
			break;
		}
	}

	return true;
}

template<typename T>
class Parsable
{
public:
	using Type = T;

	bool loadFile(const std::string& fileName)
	{
		Type& t = static_cast<Type&>(*this);
		return cppson::loadFile(t, fileName);
	}

	using Meta = std::map<std::string, std::function<bool(Type*, JsonValue)> >;

	static const Meta& getMeta()
	{
		return meta;
	}

protected:
	static Meta meta;
};

template<typename T>
bool loadFile(T& value, const std::string& fileName)
{
	std::ifstream file(fileName);

	if (!file.is_open())
		return false;

	std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());

	return loadString(value, str);
}

template<typename T>
bool loadString(T& value, const std::string& str)
{
	std::vector<std::string> tokens;

	if (!tokenize(str, tokens))
	{
		return false;
	}

	JsonValue json;

	if (json.init(tokens, 0) == -1)
		return false;

	json.parse(value);
	return true;
}

template<typename T>
typename Parsable<T>::Meta Parsable<T>::meta;

}<commit_msg>fixed #7<commit_after>#pragma once
#include <string>
#include <map>
#include <sstream>
#include <fstream>
#include <functional>
#include <vector>
#include <list>
#ifndef OUT
#define OUT
#endif
#define JSON_CLASS(name) class name final : public cppson::Parsable<name>
#define FIELD(type, name) \
struct Init_ ## name \
{\
	Init_ ## name ## () \
	{ \
		meta[ #name ] = [](Type* t, cppson::JsonValue& value) -> bool \
		{ \
			return value.parse(t->name); \
		}; \
	} \
};\
struct Meta_ ## name\
{\
	Meta_ ## name ## ()\
	{\
		static Init_ ## name init;\
	}\
};\
Meta_ ## name meta_ ## name;\
type name;

namespace cppson
{

class Single
{
public:
	template<typename R>
	R getValue()
	{
		static_assert(false, "invalid call.");
	}

	template<>
	int getValue<int>()
	{
		return atoi(str.c_str());
	}

	template<>
	bool getValue<bool>()
	{
		if (str == "true")
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	template<>
	double getValue<double>()
	{
		return atof(str.c_str());
	}

	template<>
	std::string getValue<std::string>()
	{
		return str;
	}

	std::string& getStr()
	{
		return str;
	}

private:
	std::string str;
};

class JsonValue
{
public:

	//if failed, return -1
	int init(std::vector<std::string>& tokens, int offset)
	{
		//object
		if (tokens[offset] == "{")
		{
			type = OBJECT;
			offset++;

			while (offset < tokens.size())
			{
				if (tokens[offset] == "\"")
				{
					std::string& key = tokens[offset + 1];

					if (tokens[offset + 2] != "\"" || tokens[offset + 3] != ":")
						return -1;

					offset = obj[key].init(tokens, offset + 4);

					if (offset == -1)
						return -1;
				}
				else if (tokens[offset] == ",")
				{
					offset++;
				}
				else if (tokens[offset] == "}")
				{
					return offset + 1;
				}
				else
				{
					return -1;
				}
			}
			return -1;
		}
		else if (tokens[offset] == "[")
		{
			type = ARRAY;
			offset++;
			while (offset < tokens.size())
			{
				if (tokens[offset] == ",")
				{
					offset++;
				}
				else if (tokens[offset] == "]")
				{
					return offset + 1;
				}
				else
				{
					JsonValue value;
					offset = value.init(tokens, offset);
					if (offset == -1)
						return -1;

					arr.push_back(value);
				}
			}
			return -1;
		}
		else
		{
			type = SINGLE;
			if (tokens[offset] == "\"")
			{
				single.getStr() = tokens[offset + 1];
				return offset + 3;
			}
			else
			{
				single.getStr() = tokens[offset];
				return offset + 1;
			}
		}
	}

	template<typename T>
	bool parse(T& val)
	{
		static_assert(std::is_base_of<Parsable<T>, T>::value, "T must be derived from Parsable<T>");

		if (type != OBJECT)
			return false;

		for (auto& m : obj)
		{
			if (!T::getMeta().at(m.first)(&val, m.second))
			{
				return false;
			}
		}

		return true;
	}

	template<typename T>
	bool parse(std::list<T>& val)
	{
		static_assert(std::is_base_of<Parsable<T>, T>::value ||
			std::is_same<int, T>::value ||
			std::is_same<float, T>::value ||
			std::is_same<double, T>::value ||
			std::is_same<std::string, T>::value ||
			std::is_same<bool, T>::value, "T must be derived from Parsable<T>, or T must be int or float or double or std::string or bool.");

		if (type != ARRAY)
			return false;

		for (auto& v : arr)
		{
			T p;

			if (v.parse(p))
				val.push_back(std::move(p));
			else
				return false;
		}

		return true;
	}

	template<typename T>
	bool parse(std::vector<T>& val)
	{
		static_assert(std::is_base_of<Parsable<T>, T>::value ||
					std::is_same<int, T>::value ||
					std::is_same<float, T>::value ||
					std::is_same<double, T>::value ||
					std::is_same<std::string, T>::value ||
					std::is_same<bool, T>::value, "T must be derived from Parsable<T>, or T must be int or float or double or std::string or bool.");

		if (type != ARRAY)
			return false;

		for (auto v : arr)
		{
			T p;

			if (v.parse(p))
				val.push_back(std::move(p));
			else
				return false;
		}

		return true;
	}

	template<>
	bool parse<int>(int& val)
	{
		if (type != SINGLE)
			return false;

		val = single.getValue<int>();

		return true;
	}

	template<>
	bool parse<double>(double& val)
	{
		if (type != SINGLE)
			return false;

		val = single.getValue<double>();

		return true;
	}

	template<>
	bool parse<float>(float& val)
	{
		if (type != SINGLE)
			return false;

		val = single.getValue<double>();

		return true;
	}

	template<>
	bool parse<bool>(bool& val)
	{
		if (type != SINGLE)
			return false;

		val = single.getValue<bool>();

		return true;
	}

	template<>
	bool parse<std::string>(std::string& val)
	{
		if (type != SINGLE)
			return false;

		val = single.getValue<std::string>();

		return true;
	}

private:
	enum Type
	{
		NONE,
		SINGLE,
		OBJECT,
		ARRAY
	};

	Type type;
	Single single;
	std::map<std::string, JsonValue> obj;
	std::vector<JsonValue> arr;
};

bool tokenize(const std::string& str, OUT std::vector<std::string>& tokens)
{
	std::string token;
	std::string space = " \t\r\n";
	std::string delim = ":{}[],";
	std::string number = "0123456789.";
	int state = 0;

	for (size_t i = 0; i < str.size(); i++)
	{
		switch (state)
		{
		case 0:
			//ignore space
			if (space.find(str[i]) != std::string::npos)
			{
				if (token.size() != 0)
				{
					tokens.push_back(token);
					token.clear();
				}

				continue;
			}
			//go string
			else if (str[i] == '"')
			{
				if (token.size() != 0)
					return false;

				tokens.push_back({ str[i] });
				state = 1;
			}
			//one char token
			else if (delim.find(str[i]) != std::string::npos)
			{
				if (token.size() != 0)
				{
					tokens.push_back(token);
					token.clear();
				}
				tokens.push_back({ str[i] });
			}
			//number
			else if (number.find(str[i]) != std::string::npos)
			{
				token.push_back({ str[i] });
			}
			//true or false
			else if (str.substr(i, 4) == "true")
			{
				if (token.size() != 0)
					return false;

				tokens.push_back(str.substr(i, 4));
				i += 3;
			}
			else if (str.substr(i, 5) == "false")
			{
				if (token.size() != 0)
					return false;

				tokens.push_back(str.substr(i, 5));
				i += 4;
			}
			else
			{
				return false;
			}
			break;
		case 1:
			if (str[i] == '"')
			{
				tokens.push_back(token);
				tokens.push_back({ str[i] });
				token.clear();
				state = 0;
			}
			else
			{
				token.push_back({ str[i] });
			}
			break;
		}
	}

	return true;
}

template<typename T>
class Parsable
{
public:
	using Type = T;

	bool loadFile(const std::string& fileName)
	{
		Type& t = static_cast<Type&>(*this);
		return cppson::loadFile(t, fileName);
	}

	using Meta = std::map<std::string, std::function<bool(Type*, JsonValue&)> >;

	static const Meta& getMeta()
	{
		return meta;
	}

protected:
	static Meta meta;
};

template<typename T>
bool loadFile(T& value, const std::string& fileName)
{
	std::ifstream file(fileName);

	if (!file.is_open())
		return false;

	std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());

	return loadString(value, str);
}

template<typename T>
bool loadString(T& value, const std::string& str)
{
	std::vector<std::string> tokens;

	if (!tokenize(str, tokens))
	{
		return false;
	}

	JsonValue json;

	if (json.init(tokens, 0) == -1)
		return false;

	json.parse(value);
	return true;
}

template<typename T>
typename Parsable<T>::Meta Parsable<T>::meta;

}<|endoftext|>


<commit_before>/**
 * @file crc32c.cpp
 * @brief Node.js bindings for CRC-32C calculation
 *
 * The code below provides the bindings for the node-addon allowing for
 * interfacing of C/C++ code with JavaScript. It chooses between two versions of
 * the CRC-32C calculator:
 * - The hardware-accelerated version that uses Intel's SSE 4.2 instructions
 *   implemented in sse42.cpp
 * - A table-lookup based CRC calculated implemented in software for non-Nehalam
 *   architectures implemented in table.cpp
 *
 * NOTES:
 * - This code, though originally designed for little-endian hardware, should
 *   work for all platforms.
 * - Table-based CRC-32C implementation based on code by Mark Adler at
 *   http://stackoverflow.com/a/17646775.
 *
 * @author Anand Suresh <anandsuresh@gmail.com>
 * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved.
 */

#include "sse42.h"
#include "table.h"
#include <napi.h>
#include <stdio.h>

/**
 * Calculates CRC-32C for the specified string/buffer using SSE 4.2 extensions
 */
Napi::Value sse42_crc(const Napi::CallbackInfo &info) {
  Napi::Env env = info.Env();
  uint32_t crc = 0;
  const char *buf;
  size_t len;

  // validate arguments
  if (info.Length() < 1) {
    return Napi::Number::New(env, 0);
  } else if (info.Length() > 2) {
    throw Napi::TypeError::New(env, "Invalid number of arguments!");
  }

  // use the specified initial CRC, if any
  if (info.Length() > 1) {
    if (!(info[1].IsNumber())) {
      throw Napi::TypeError::New(env, "Initial CRC-32C is not an integer!");
    }
    crc = info[1].As<Napi::Number>().Uint32Value();
  }

  // Ensure the argument is a buffer or a string
  if (info[0].IsBuffer()) {
    Napi::Buffer<char> data = info[0].As<Napi::Buffer<char>>();
    buf = (const char *)data.Data();
    len = (size_t)data.Length();
  } else if (info[0].IsString()) {
    std::string strInput = info[0].As<Napi::String>().Utf8Value();
    buf = (const char *)strInput.c_str();
    len = (size_t)strInput.length();
  } else {
    throw Napi::TypeError::New(env, "input is not a string/buffer!");
  }

  return Napi::Number::New(env, sse42_calculate(crc, buf, len));
}

/**
 * Calculates CRC-32C for the specified string/buffer using table-lookups
 */
Napi::Value table_crc(const Napi::CallbackInfo &info) {
  Napi::Env env = info.Env();
  uint32_t crc = 0;
  const char *buf;
  size_t len;

  // validate arguments
  if (info.Length() < 1) {
    return Napi::Number::New(env, 0);
  } else if (info.Length() > 2) {
    throw Napi::TypeError::New(env, "Invalid number of arguments!");
  }

  // use the specified initial CRC, if any
  if (info.Length() > 1) {
    if (!(info[1].IsNumber())) {
      throw Napi::TypeError::New(env, "Initial CRC-32C is not an integer!");
    }
    crc = info[1].As<Napi::Number>().Uint32Value();
  }

  // Ensure the argument is a buffer or a string
  if (info[0].IsBuffer()) {
    Napi::Buffer<char> data = info[0].As<Napi::Buffer<char>>();
    buf = (const char *)data.Data();
    len = (size_t)data.Length();
  } else if (info[0].IsString()) {
    std::string strInput = info[0].As<Napi::String>().Utf8Value();
    buf = (const char *)strInput.c_str();
    len = (size_t)strInput.length();
  } else {
    throw Napi::TypeError::New(env, "input is not a string/buffer!");
  }

  return Napi::Number::New(env, table_calculate(crc, buf, len));
}

/**
 * Initialize the module
 */
Napi::Object init(Napi::Env env, Napi::Object exports) {
  // initialize the required pieces
  table_init();

  // export the binding interface
  exports["hardware_support"] = Napi::Boolean::New(env, sse42_is_available());
  exports["sse42_crc"] = Napi::Function::New(env, sse42_crc);
  exports["table_crc"] = Napi::Function::New(env, table_crc);

  return exports;
};

NODE_API_MODULE(crc32c, init);
<commit_msg>Fix UAF caused by `strInput` being deallocated at the end of the `if` block<commit_after>/**
 * @file crc32c.cpp
 * @brief Node.js bindings for CRC-32C calculation
 *
 * The code below provides the bindings for the node-addon allowing for
 * interfacing of C/C++ code with JavaScript. It chooses between two versions of
 * the CRC-32C calculator:
 * - The hardware-accelerated version that uses Intel's SSE 4.2 instructions
 *   implemented in sse42.cpp
 * - A table-lookup based CRC calculated implemented in software for non-Nehalam
 *   architectures implemented in table.cpp
 *
 * NOTES:
 * - This code, though originally designed for little-endian hardware, should
 *   work for all platforms.
 * - Table-based CRC-32C implementation based on code by Mark Adler at
 *   http://stackoverflow.com/a/17646775.
 *
 * @author Anand Suresh <anandsuresh@gmail.com>
 * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved.
 */

#include "sse42.h"
#include "table.h"
#include <napi.h>
#include <stdio.h>

/**
 * Calculates CRC-32C for the specified string/buffer using SSE 4.2 extensions
 */
Napi::Value sse42_crc(const Napi::CallbackInfo &info) {
  Napi::Env env = info.Env();
  uint32_t crc = 0;
  const char *buf;
  size_t len;

  // validate arguments
  if (info.Length() < 1) {
    return Napi::Number::New(env, 0);
  } else if (info.Length() > 2) {
    throw Napi::TypeError::New(env, "Invalid number of arguments!");
  }

  // use the specified initial CRC, if any
  if (info.Length() > 1) {
    if (!(info[1].IsNumber())) {
      throw Napi::TypeError::New(env, "Initial CRC-32C is not an integer!");
    }
    crc = info[1].As<Napi::Number>().Uint32Value();
  }

  // Ensure the argument is a buffer or a string
  if (info[0].IsBuffer()) {
    Napi::Buffer<char> data = info[0].As<Napi::Buffer<char>>();
    buf = (const char *)data.Data();
    len = (size_t)data.Length();
    return Napi::Number::New(env, sse42_calculate(crc, buf, len));
  } else if (info[0].IsString()) {
    std::string strInput = info[0].As<Napi::String>().Utf8Value();
    buf = (const char *)strInput.c_str();
    len = (size_t)strInput.length();
    return Napi::Number::New(env, sse42_calculate(crc, buf, len));
  } else {
    throw Napi::TypeError::New(env, "input is not a string/buffer!");
  }
}

/**
 * Calculates CRC-32C for the specified string/buffer using table-lookups
 */
Napi::Value table_crc(const Napi::CallbackInfo &info) {
  Napi::Env env = info.Env();
  uint32_t crc = 0;
  const char *buf;
  size_t len;

  // validate arguments
  if (info.Length() < 1) {
    return Napi::Number::New(env, 0);
  } else if (info.Length() > 2) {
    throw Napi::TypeError::New(env, "Invalid number of arguments!");
  }

  // use the specified initial CRC, if any
  if (info.Length() > 1) {
    if (!(info[1].IsNumber())) {
      throw Napi::TypeError::New(env, "Initial CRC-32C is not an integer!");
    }
    crc = info[1].As<Napi::Number>().Uint32Value();
  }

  // Ensure the argument is a buffer or a string
  if (info[0].IsBuffer()) {
    Napi::Buffer<char> data = info[0].As<Napi::Buffer<char>>();
    buf = (const char *)data.Data();
    len = (size_t)data.Length();
    return Napi::Number::New(env, table_calculate(crc, buf, len));
  } else if (info[0].IsString()) {
    std::string strInput = info[0].As<Napi::String>().Utf8Value();
    buf = (const char *)strInput.c_str();
    len = (size_t)strInput.length();
    return Napi::Number::New(env, table_calculate(crc, buf, len));
  } else {
    throw Napi::TypeError::New(env, "input is not a string/buffer!");
  }
}

/**
 * Initialize the module
 */
Napi::Object init(Napi::Env env, Napi::Object exports) {
  // initialize the required pieces
  table_init();

  // export the binding interface
  exports["hardware_support"] = Napi::Boolean::New(env, sse42_is_available());
  exports["sse42_crc"] = Napi::Function::New(env, sse42_crc);
  exports["table_crc"] = Napi::Function::New(env, table_crc);

  return exports;
};

NODE_API_MODULE(crc32c, init);
<|endoftext|>


<commit_before>#include "dialogs.h"
#include "juci.h"
#include "singletons.h"
#include <cmath>

namespace sigc {
#ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
  template <typename Functor>
  struct functor_trait<Functor, false> {
    typedef decltype (::sigc::mem_fun(std::declval<Functor&>(),
                                      &Functor::operator())) _intermediate;
    typedef typename _intermediate::result_type result_type;
    typedef Functor functor_type;
  };
#else
  SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
#endif
}

Dialog::Message::Message(const std::string &text): Gtk::Window(Gtk::WindowType::WINDOW_POPUP), label(text) {
  auto font_desc=label.get_pango_context()->get_font_description();
  font_desc.set_size(static_cast<int>(round(static_cast<double>(font_desc.get_size())*1.25)));
  label.get_pango_context()->set_font_description(font_desc);
  add(label);

  property_decorated()=false;
  set_accept_focus(false);
  set_skip_taskbar_hint(true);

  auto g_application=g_application_get_default();
  auto gio_application=Glib::wrap(g_application, true);
  auto application=Glib::RefPtr<Application>::cast_static(gio_application);
  set_transient_for(*application->window);
  set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
  label.signal_draw().connect([this](const Cairo::RefPtr<Cairo::Context>& cr) {
    label_drawn=true;
    return false;
  });
  show_all();
}

void Dialog::Message::wait_until_drawn() {
  while(!label_drawn)
    g_main_context_iteration(NULL, false);
}

std::string Dialog::gtk_dialog(const std::string &title,
                        const std::vector<std::pair<std::string, Gtk::ResponseType>> &buttons,
                        Gtk::FileChooserAction gtk_options,
                        const std::string &file_name) {
  Gtk::FileChooserDialog dialog(title, gtk_options);

  auto g_application=g_application_get_default(); //TODO: Post issue that Gio::Application::get_default should return pointer and not Glib::RefPtr
  auto gio_application=Glib::wrap(g_application, true);
  auto application=Glib::RefPtr<Application>::cast_static(gio_application);
  dialog.set_transient_for(*application->window);

  auto current_path=application->window->notebook.get_current_folder();
  if(current_path.empty())
    current_path=boost::filesystem::current_path();
  gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), current_path.string().c_str());

  if (!file_name.empty())
    gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), file_name.c_str());
  dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);

  for (auto &button : buttons)
    dialog.add_button(button.first, button.second);
  return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : "";
}
<commit_msg>Dialogs now is centered with respect to the juci application.<commit_after>#include "dialogs.h"
#include "juci.h"
#include "singletons.h"
#include <cmath>

namespace sigc {
#ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
  template <typename Functor>
  struct functor_trait<Functor, false> {
    typedef decltype (::sigc::mem_fun(std::declval<Functor&>(),
                                      &Functor::operator())) _intermediate;
    typedef typename _intermediate::result_type result_type;
    typedef Functor functor_type;
  };
#else
  SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
#endif
}

Dialog::Message::Message(const std::string &text): Gtk::Window(Gtk::WindowType::WINDOW_POPUP), label(text) {
  auto font_desc=label.get_pango_context()->get_font_description();
  font_desc.set_size(static_cast<int>(round(static_cast<double>(font_desc.get_size())*1.25)));
  label.get_pango_context()->set_font_description(font_desc);
  add(label);

  property_decorated()=false;
  set_accept_focus(false);
  set_skip_taskbar_hint(true);

  auto g_application=g_application_get_default();
  auto gio_application=Glib::wrap(g_application, true);
  auto application=Glib::RefPtr<Application>::cast_static(gio_application);
  set_transient_for(*application->window);
  set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
  label.signal_draw().connect([this](const Cairo::RefPtr<Cairo::Context>& cr) {
    label_drawn=true;
    return false;
  });
  show_all();
}

void Dialog::Message::wait_until_drawn() {
  while(!label_drawn)
    g_main_context_iteration(NULL, false);
}

std::string Dialog::gtk_dialog(const std::string &title,
                        const std::vector<std::pair<std::string, Gtk::ResponseType>> &buttons,
                        Gtk::FileChooserAction gtk_options,
                        const std::string &file_name) {
  Gtk::FileChooserDialog dialog(title, gtk_options);

  auto g_application=g_application_get_default(); //TODO: Post issue that Gio::Application::get_default should return pointer and not Glib::RefPtr
  auto gio_application=Glib::wrap(g_application, true);
  auto application=Glib::RefPtr<Application>::cast_static(gio_application);
  dialog.set_transient_for(*application->window);

  auto current_path=application->window->notebook.get_current_folder();
  if(current_path.empty())
    current_path=boost::filesystem::current_path();
  gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), current_path.string().c_str());

  if (!file_name.empty())
    gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), file_name.c_str());
  dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);

  for (auto &button : buttons)
    dialog.add_button(button.first, button.second);
  return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : "";
}
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "duplex.hxx"
#include "event/SocketEvent.hxx"
#include "event/DeferEvent.hxx"
#include "system/Error.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "net/Buffered.hxx"
#include "io/Buffered.hxx"
#include "io/Logger.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "pool/pool.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"

#include <sys/socket.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

class FallbackEvent {
    SocketEvent socket_event;
    DeferEvent defer_event;

    const BoundMethod<void()> callback;

public:
    FallbackEvent(EventLoop &event_loop, FileDescriptor fd, short events,
                  BoundMethod<void()> _callback)
        :socket_event(event_loop, fd.Get(), events,
                      BIND_THIS_METHOD(OnSocket)),
         defer_event(event_loop, _callback),
         callback(_callback) {}

    void Add() {
        if (!socket_event.Add())
            /* if "fd" is a regular file, trigger the event repeatedly
               using DeferEvent, because we can't use SocketEvent::READ on
               regular files */
            defer_event.Schedule();
    }

    void Delete() {
        socket_event.Delete();
        defer_event.Cancel();
    }

private:
    void OnSocket(unsigned) {
        callback();
    }
};

class Duplex {
    UniqueFileDescriptor read_fd;
    UniqueFileDescriptor write_fd;
    UniqueSocketDescriptor sock_fd;
    bool sock_eof = false;

    SliceFifoBuffer from_read, to_write;

    FallbackEvent read_event, write_event;
    SocketEvent socket_read_event, socket_write_event;

public:
    Duplex(EventLoop &event_loop,
           UniqueFileDescriptor &&_read_fd, UniqueFileDescriptor &&_write_fd,
           UniqueSocketDescriptor &&_sock_fd)
        :read_fd(std::move(_read_fd)), write_fd(std::move(_write_fd)),
         sock_fd(std::move(_sock_fd)),
         read_event(event_loop, read_fd, SocketEvent::READ,
                    BIND_THIS_METHOD(ReadEventCallback)),
         write_event(event_loop, write_fd, SocketEvent::WRITE,
                     BIND_THIS_METHOD(WriteEventCallback)),
         socket_read_event(event_loop, sock_fd.Get(), SocketEvent::READ,
                           BIND_THIS_METHOD(SocketReadEventCallback)),
         socket_write_event(event_loop, sock_fd.Get(), SocketEvent::WRITE,
                            BIND_THIS_METHOD(SocketWriteEventCallback))
    {
        from_read.Allocate(fb_pool_get());
        to_write.Allocate(fb_pool_get());

        read_event.Add();
        socket_read_event.Add();
    }

private:
    void CloseRead() {
        assert(read_fd.IsDefined());

        read_event.Delete();

        if (read_fd.Get() > 2)
            read_fd.Close();
        else
            read_fd.Steal();
    }

    void CloseWrite() {
        assert(write_fd.IsDefined());

        write_event.Delete();

        if (write_fd.Get() > 2)
            write_fd.Close();
        else
            write_fd.Steal();
    }

    void CloseSocket() {
        assert(sock_fd.IsDefined());

        socket_read_event.Delete();
        socket_write_event.Delete();

        sock_fd.Close();
    }

    void Destroy();
    bool CheckDestroy();

    void ReadEventCallback();
    void WriteEventCallback();
    void SocketReadEventCallback(unsigned);
    void SocketWriteEventCallback(unsigned);
};

void
Duplex::Destroy()
{
    if (read_fd.IsDefined())
        CloseRead();

    if (write_fd.IsDefined())
        CloseWrite();

    if (sock_fd.IsDefined())
        CloseSocket();

    from_read.Free(fb_pool_get());
    to_write.Free(fb_pool_get());

    this->~Duplex();
}

bool
Duplex::CheckDestroy()
{
    if (!read_fd.IsDefined() && sock_eof &&
        from_read.empty() && to_write.empty()) {
        Destroy();
        return true;
    } else
        return false;
}

inline void
Duplex::ReadEventCallback()
{
    ssize_t nbytes = read_to_buffer(read_fd.Get(), from_read, INT_MAX);
    if (nbytes == -1) {
        LogConcat(1, "Duplex", "failed to read: ", strerror(errno));
        Destroy();
        return;
    }

    if (nbytes == 0) {
        CloseRead();
        CheckDestroy();
        return;
    }

    socket_write_event.Add();

    if (!from_read.IsFull())
        read_event.Add();
}

inline void
Duplex::WriteEventCallback()
{
    ssize_t nbytes = write_from_buffer(write_fd.Get(), to_write);
    if (nbytes == -1) {
        Destroy();
        return;
    }

    if (nbytes > 0 && !sock_eof)
        socket_read_event.Add();

    if (!to_write.empty())
        write_event.Add();
    else
        CheckDestroy();
}

inline void
Duplex::SocketReadEventCallback(unsigned)
{
    ssize_t nbytes = ReceiveToBuffer(sock_fd.Get(), to_write);
    if (nbytes == -1) {
        LogConcat(1, "Duplex", "failed to read: ", strerror(errno));
        Destroy();
        return;
    }

    if (gcc_likely(nbytes > 0)) {
        write_event.Add();
        if (!to_write.IsFull())
            socket_read_event.Add();
    } else {
        sock_eof = true;
        CheckDestroy();
    }
}

inline void
Duplex::SocketWriteEventCallback(unsigned)
{
    ssize_t nbytes = SendFromBuffer(sock_fd.Get(), from_read);
    if (nbytes == -1) {
        Destroy();
        return;
    }

    if (nbytes > 0 && read_fd.IsDefined())
        read_event.Add();

    if (!from_read.empty())
        socket_write_event.Add();
}

UniqueSocketDescriptor
duplex_new(EventLoop &event_loop, struct pool *pool,
           UniqueFileDescriptor read_fd, UniqueFileDescriptor write_fd)
{
    assert(pool != nullptr);
    assert(read_fd.IsDefined());
    assert(write_fd.IsDefined());

    UniqueSocketDescriptor result_fd, duplex_fd;
    if (!UniqueSocketDescriptor::CreateSocketPairNonBlock(AF_LOCAL, SOCK_STREAM, 0,
                                                          result_fd, duplex_fd))
        throw MakeErrno("socketpair() failed");

    NewFromPool<Duplex>(*pool, event_loop,
                        std::move(read_fd), std::move(write_fd),
                        std::move(duplex_fd));
    return result_fd;
}
<commit_msg>duplex: migrate to class NewSocketEvent<commit_after>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "duplex.hxx"
#include "event/NewSocketEvent.hxx"
#include "event/DeferEvent.hxx"
#include "system/Error.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "net/Buffered.hxx"
#include "io/Buffered.hxx"
#include "io/Logger.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "pool/pool.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"

#include <sys/socket.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

class FallbackEvent {
    NewSocketEvent socket_event;
    DeferEvent defer_event;

    const unsigned events;

    const BoundMethod<void()> callback;

public:
    FallbackEvent(EventLoop &event_loop, FileDescriptor fd, unsigned _events,
                  BoundMethod<void()> _callback)
        :socket_event(event_loop, BIND_THIS_METHOD(OnSocket),
                      SocketDescriptor::FromFileDescriptor(fd)),
         defer_event(event_loop, _callback),
         events(fd.IsRegularFile() ? 0 : _events),
         callback(_callback) {}

    void Add() {
        if (events == 0)
            /* if "fd" is a regular file, trigger the event repeatedly
               using DeferEvent, because we can't use SocketEvent::READ on
               regular files */
            defer_event.Schedule();
        else
            socket_event.Schedule(events);
    }

    void Delete() {
        socket_event.Cancel();
        defer_event.Cancel();
    }

private:
    void OnSocket(unsigned) {
        callback();
    }
};

class Duplex {
    UniqueFileDescriptor read_fd;
    UniqueFileDescriptor write_fd;
    UniqueSocketDescriptor sock_fd;
    bool sock_eof = false;

    SliceFifoBuffer from_read, to_write;

    FallbackEvent read_event, write_event;
    NewSocketEvent socket_event;

public:
    Duplex(EventLoop &event_loop,
           UniqueFileDescriptor &&_read_fd, UniqueFileDescriptor &&_write_fd,
           UniqueSocketDescriptor &&_sock_fd)
        :read_fd(std::move(_read_fd)), write_fd(std::move(_write_fd)),
         sock_fd(std::move(_sock_fd)),
         read_event(event_loop, read_fd, SocketEvent::READ,
                    BIND_THIS_METHOD(ReadEventCallback)),
         write_event(event_loop, write_fd, SocketEvent::WRITE,
                     BIND_THIS_METHOD(WriteEventCallback)),
         socket_event(event_loop, BIND_THIS_METHOD(OnSocketReady), sock_fd)
    {
        from_read.Allocate(fb_pool_get());
        to_write.Allocate(fb_pool_get());

        read_event.Add();
        socket_event.ScheduleRead();
    }

private:
    void CloseRead() {
        assert(read_fd.IsDefined());

        read_event.Delete();

        if (read_fd.Get() > 2)
            read_fd.Close();
        else
            read_fd.Steal();
    }

    void CloseWrite() {
        assert(write_fd.IsDefined());

        write_event.Delete();

        if (write_fd.Get() > 2)
            write_fd.Close();
        else
            write_fd.Steal();
    }

    void CloseSocket() {
        assert(sock_fd.IsDefined());

        socket_event.Cancel();

        sock_fd.Close();
    }

    void Destroy();
    bool CheckDestroy();

    void ReadEventCallback();
    void WriteEventCallback();

    bool TryReadSocket() noexcept;
    bool TryWriteSocket() noexcept;
    void OnSocketReady(unsigned events) noexcept;
};

void
Duplex::Destroy()
{
    if (read_fd.IsDefined())
        CloseRead();

    if (write_fd.IsDefined())
        CloseWrite();

    if (sock_fd.IsDefined())
        CloseSocket();

    from_read.Free(fb_pool_get());
    to_write.Free(fb_pool_get());

    this->~Duplex();
}

bool
Duplex::CheckDestroy()
{
    if (!read_fd.IsDefined() && sock_eof &&
        from_read.empty() && to_write.empty()) {
        Destroy();
        return true;
    } else
        return false;
}

inline void
Duplex::ReadEventCallback()
{
    ssize_t nbytes = read_to_buffer(read_fd.Get(), from_read, INT_MAX);
    if (nbytes == -1) {
        LogConcat(1, "Duplex", "failed to read: ", strerror(errno));
        Destroy();
        return;
    }

    if (nbytes == 0) {
        CloseRead();
        CheckDestroy();
        return;
    }

    socket_event.ScheduleWrite();

    if (from_read.IsFull())
        read_event.Delete();
}

inline void
Duplex::WriteEventCallback()
{
    ssize_t nbytes = write_from_buffer(write_fd.Get(), to_write);
    if (nbytes == -1) {
        Destroy();
        return;
    }

    if (nbytes > 0 && !sock_eof)
        socket_event.ScheduleRead();

    if (to_write.empty()) {
        write_event.Delete();
        CheckDestroy();
    }
}

inline bool
Duplex::TryReadSocket() noexcept
{
    ssize_t nbytes = ReceiveToBuffer(sock_fd.Get(), to_write);
    if (nbytes == -1) {
        LogConcat(1, "Duplex", "failed to read: ", strerror(errno));
        Destroy();
        return false;
    }

    if (gcc_likely(nbytes > 0)) {
        write_event.Add();
        if (to_write.IsFull())
            socket_event.CancelRead();
        return true;
    } else {
        socket_event.CancelRead();
        sock_eof = true;
        return !CheckDestroy();
    }
}

inline bool
Duplex::TryWriteSocket() noexcept
{
    ssize_t nbytes = SendFromBuffer(sock_fd.Get(), from_read);
    if (nbytes == -1) {
        Destroy();
        return false;
    }

    if (nbytes > 0 && read_fd.IsDefined())
        read_event.Add();

    if (!from_read.empty())
        socket_event.ScheduleWrite();

    return true;
}

inline void
Duplex::OnSocketReady(unsigned events) noexcept
{
    if (events & SocketEvent::READ) {
        if (!TryReadSocket())
            return;
    }

    if (events & SocketEvent::WRITE)
        TryWriteSocket();
}

UniqueSocketDescriptor
duplex_new(EventLoop &event_loop, struct pool *pool,
           UniqueFileDescriptor read_fd, UniqueFileDescriptor write_fd)
{
    assert(pool != nullptr);
    assert(read_fd.IsDefined());
    assert(write_fd.IsDefined());

    UniqueSocketDescriptor result_fd, duplex_fd;
    if (!UniqueSocketDescriptor::CreateSocketPairNonBlock(AF_LOCAL, SOCK_STREAM, 0,
                                                          result_fd, duplex_fd))
        throw MakeErrno("socketpair() failed");

    NewFromPool<Duplex>(*pool, event_loop,
                        std::move(read_fd), std::move(write_fd),
                        std::move(duplex_fd));
    return result_fd;
}
<|endoftext|>


<commit_before>#include <v8.h>
#include <node.h>
#include <cstring>
#include <stdlib.h>
#include <dbus/dbus.h>
#include <iostream>
#include <nan.h>

#include "encoder.h"

namespace Encoder {

	using namespace node;
	using namespace v8;
	using namespace std;

	bool IsByte(Local<Value> value)
	{
		if(value->IsUint32()) {
			int number = value->Int32Value();
			if(number >= 0 && number <= 255) {
				return true;
			}
		}
		return false;
	}

	const char *GetSignatureFromV8Type(Local<Value>& value)
	{
		if (value->IsTrue() || value->IsFalse() || value->IsBoolean() ) {
			return const_cast<char*>(DBUS_TYPE_BOOLEAN_AS_STRING);
		} else if (IsByte(value)) {
			return const_cast<char*>(DBUS_TYPE_BYTE_AS_STRING);
		} else if (value->IsUint32()) {
			return const_cast<char*>(DBUS_TYPE_UINT32_AS_STRING);
		} else if (value->IsInt32()) {
			return const_cast<char*>(DBUS_TYPE_INT32_AS_STRING);
		} else if (value->IsNumber()) {
			return const_cast<char*>(DBUS_TYPE_DOUBLE_AS_STRING);
		} else if (value->IsString()) {
			return const_cast<char*>(DBUS_TYPE_STRING_AS_STRING);
		} else if (value->IsArray()) {
			return const_cast<char*>(DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_VARIANT_AS_STRING);
		} else if (value->IsObject()) {
			return const_cast<char*>(DBUS_TYPE_ARRAY_AS_STRING
				DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
				DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
				DBUS_DICT_ENTRY_END_CHAR_AS_STRING);
		} else {
			return NULL;
		}
	}

	bool EncodeObject(Local<Value> value, DBusMessageIter *iter, const char *signature)
	{
		// printf("EncodeObject %s\n",signature);
		// printf("%p", value);
		Nan::HandleScope scope;
		DBusSignatureIter siter;
		int type;

		// Get type of current value
		dbus_signature_iter_init(&siter, signature);
		type = dbus_signature_iter_get_current_type(&siter);

		switch(type) {
		case DBUS_TYPE_INVALID:
		// case DBUS_TYPE_INVALID_AS_STRING:
		{
			printf("Invalid type\n");
		}
		break;
		case DBUS_TYPE_BOOLEAN:
		{
			dbus_bool_t data = value->BooleanValue();

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode boolean value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_INT16:
		{
			dbus_int16_t data = value->IntegerValue();
			// printf("value: %lu\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_INT64:
		{
			dbus_int64_t data = value->IntegerValue();
			// printf("value: %lu\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_UINT16:
		{
			dbus_uint16_t data = value->IntegerValue();
			// printf("value: %lu\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_UINT64:
		{
			dbus_uint64_t data = value->IntegerValue();
			// printf("value: %lu\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_BYTE:
		{
			unsigned char data = value->IntegerValue();
			// printf("DBUS_TYPE_BYTE: ");
			// printf("value: %u\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_UINT32:
		{
			dbus_uint32_t data = value->Uint32Value();
			// printf("DBUS_TYPE_UINT32: ");
			// printf("value: %u\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_INT32:
		{
			dbus_int32_t data = value->Int32Value();
			// printf("DBUS_TYPE_INT32: ");
			// printf("value: %u\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_STRING:
		case DBUS_TYPE_OBJECT_PATH:
		case DBUS_TYPE_SIGNATURE:
		{
			char *data = strdup(*String::Utf8Value(value->ToString()));
			// printf("value: %s\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				dbus_free(data);
				printf("Failed to encode string value\n");
				return false;
			}

			dbus_free(data);

			break;
		}

		case DBUS_TYPE_DOUBLE:
		{
			double data = value->NumberValue();
			// printf("value: %f\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode double value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_ARRAY:
		{
			if (!value->IsObject()) {
				printf("Failed to encode dictionary\n");
				return false;
			}

			DBusMessageIter subIter;
			DBusSignatureIter arraySiter;
			char *array_sig = NULL;

			// Getting signature of array object
			dbus_signature_iter_recurse(&siter, &arraySiter);
			array_sig = dbus_signature_iter_get_signature(&arraySiter);

			// Open array container to process elements in there
			if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, array_sig, &subIter)) {
				dbus_free(array_sig);
				printf("Can't open container for Array type\n");
				return false;
			}

			// It's a dictionary
			if (dbus_signature_iter_get_element_type(&siter) == DBUS_TYPE_DICT_ENTRY) {

				dbus_free(array_sig);

				Local<Object> value_object = value->ToObject();
				DBusSignatureIter dictSubSiter;

				// Getting sub-signature object
				dbus_signature_iter_recurse(&arraySiter, &dictSubSiter);
				dbus_signature_iter_next(&dictSubSiter);
				char *sig = dbus_signature_iter_get_signature(&dictSubSiter);

				// process each elements
				Local<Array> prop_names = value_object->GetPropertyNames();
				unsigned int len = prop_names->Length();

				bool failed = false;
				for (unsigned int i = 0; i < len; ++i) {
					DBusMessageIter dict_iter;

					// Open dict entry container
					if (!dbus_message_iter_open_container(&subIter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_iter)) {
						printf("Can't open container for DICT-ENTRY\n");
						failed = true;
						break;
					}

					// Getting the key and value
					Local<Value> prop_key = prop_names->Get(i);
					Local<Value> prop_value = value_object->Get(prop_key);

					// Append the key
					char *prop_key_str = strdup(*String::Utf8Value(prop_key->ToString()));
					// printf("key: %s\n", prop_key_str);
					dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &prop_key_str);
					dbus_free(prop_key_str);

					// Append the value
					if (!EncodeObject(prop_value, &dict_iter, sig)) {
						dbus_message_iter_close_container(&subIter, &dict_iter);
						printf("Failed to encode element of dictionary\n");
						failed = true;
						break;
					}

					dbus_message_iter_close_container(&subIter, &dict_iter);
				}

				dbus_free(sig);
				dbus_message_iter_close_container(iter, &subIter);

				if (failed)
					return false;

				break;
			}

			if (!value->IsArray()) {
				printf("Failed to encode array object\n");
				return false;
			}

			// process each elements
			Local<Array> arrayData = Local<Array>::Cast(value);
			for (unsigned int i = 0; i < arrayData->Length(); ++i) {
				Local<Value> arrayItem = arrayData->Get(i);
				if (!EncodeObject(arrayItem, &subIter, array_sig))
					break;
			}

			dbus_message_iter_close_container(iter, &subIter);
			dbus_free(array_sig);

			break;
		}

		case DBUS_TYPE_VARIANT:
		{
			DBusMessageIter subIter;

			const char *var_sig = GetSignatureFromV8Type(value);

			if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, var_sig, &subIter)) {
				printf("Can't open container for VARIANT type\n");
				return false;
			}

			if (!EncodeObject(value, &subIter, var_sig)) {
				dbus_message_iter_close_container(iter, &subIter);
				return false;
			}

			dbus_message_iter_close_container(iter, &subIter);

			break;
		}
		case DBUS_TYPE_STRUCT:
		{
			// printf("struct\n");
			DBusMessageIter subIter;
			DBusSignatureIter structSiter;

			// Open array container to process elements in there
			if (!dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &subIter)) {
				printf("Can't open container for Struct type\n");
				return false;
			}

			Local<Object> value_object = value->ToObject();

			// Getting sub-signature object
			dbus_signature_iter_recurse(&siter, &structSiter);

			// process each elements
			Local<Array> prop_names = value_object->GetPropertyNames();
			unsigned int len = prop_names->Length();

			for (unsigned int i = 0; i < len; ++i) {

				char *sig = dbus_signature_iter_get_signature(&structSiter);

				Local<Value> prop_key = prop_names->Get(i);

				if (!EncodeObject(value_object->Get(prop_key), &subIter, sig)) {
					dbus_free(sig);
					printf("Failed to encode element of dictionary\n");
					return false;
				}

				dbus_free(sig);

				if (!dbus_signature_iter_next(&structSiter))
					break;
			}

			dbus_message_iter_close_container(iter, &subIter);

			break;
		}
		default:
		{
			printf("Type not implemented: %i\n", type);
		}
		break;

		}

		return true;
	}

}

<commit_msg>encoder: fix handling of Array of String datatype<commit_after>#include <v8.h>
#include <node.h>
#include <cstring>
#include <stdlib.h>
#include <dbus/dbus.h>
#include <iostream>
#include <nan.h>

#include "encoder.h"

namespace Encoder {

	using namespace node;
	using namespace v8;
	using namespace std;

	bool IsByte(Local<Value> value)
	{
		if(value->IsUint32()) {
			int number = value->Int32Value();
			if(number >= 0 && number <= 255) {
				return true;
			}
		}
		return false;
	}

	const char *GetSignatureFromV8Type(Local<Value>& value)
	{
		if (value->IsTrue() || value->IsFalse() || value->IsBoolean() ) {
			return const_cast<char*>(DBUS_TYPE_BOOLEAN_AS_STRING);
		} else if (IsByte(value)) {
			return const_cast<char*>(DBUS_TYPE_BYTE_AS_STRING);
		} else if (value->IsUint32()) {
			return const_cast<char*>(DBUS_TYPE_UINT32_AS_STRING);
		} else if (value->IsInt32()) {
			return const_cast<char*>(DBUS_TYPE_INT32_AS_STRING);
		} else if (value->IsNumber()) {
			return const_cast<char*>(DBUS_TYPE_DOUBLE_AS_STRING);
		} else if (value->IsString()) {
			return const_cast<char*>(DBUS_TYPE_STRING_AS_STRING);
		} else if (value->IsArray()) {

			Local<Array> arrayData = Local<Array>::Cast(value);
			for (unsigned int i = 0; i < arrayData->Length(); ++i) {
				if (!arrayData->Get(i)->IsString())
					break;
				if (i == (arrayData->Length() - 1))
					return const_cast<char*>(DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING);
			}

			return const_cast<char*>(DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_VARIANT_AS_STRING);
		} else if (value->IsObject()) {
			return const_cast<char*>(DBUS_TYPE_ARRAY_AS_STRING
				DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
				DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
				DBUS_DICT_ENTRY_END_CHAR_AS_STRING);
		} else {
			return NULL;
		}
	}

	bool EncodeObject(Local<Value> value, DBusMessageIter *iter, const char *signature)
	{
		// printf("EncodeObject %s\n",signature);
		// printf("%p", value);
		Nan::HandleScope scope;
		DBusSignatureIter siter;
		int type;

		// Get type of current value
		dbus_signature_iter_init(&siter, signature);
		type = dbus_signature_iter_get_current_type(&siter);

		switch(type) {
		case DBUS_TYPE_INVALID:
		// case DBUS_TYPE_INVALID_AS_STRING:
		{
			printf("Invalid type\n");
		}
		break;
		case DBUS_TYPE_BOOLEAN:
		{
			dbus_bool_t data = value->BooleanValue();

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode boolean value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_INT16:
		{
			dbus_int16_t data = value->IntegerValue();
			// printf("value: %lu\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_INT64:
		{
			dbus_int64_t data = value->IntegerValue();
			// printf("value: %lu\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_UINT16:
		{
			dbus_uint16_t data = value->IntegerValue();
			// printf("value: %lu\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_UINT64:
		{
			dbus_uint64_t data = value->IntegerValue();
			// printf("value: %lu\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_BYTE:
		{
			unsigned char data = value->IntegerValue();
			// printf("DBUS_TYPE_BYTE: ");
			// printf("value: %u\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_UINT32:
		{
			dbus_uint32_t data = value->Uint32Value();
			// printf("DBUS_TYPE_UINT32: ");
			// printf("value: %u\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_INT32:
		{
			dbus_int32_t data = value->Int32Value();
			// printf("DBUS_TYPE_INT32: ");
			// printf("value: %u\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode numeric value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_STRING:
		case DBUS_TYPE_OBJECT_PATH:
		case DBUS_TYPE_SIGNATURE:
		{
			char *data = strdup(*String::Utf8Value(value->ToString()));
			// printf("value: %s\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				dbus_free(data);
				printf("Failed to encode string value\n");
				return false;
			}

			dbus_free(data);

			break;
		}

		case DBUS_TYPE_DOUBLE:
		{
			double data = value->NumberValue();
			// printf("value: %f\n",data);

			if (!dbus_message_iter_append_basic(iter, type, &data)) {
				printf("Failed to encode double value\n");
				return false;
			}

			break;
		}

		case DBUS_TYPE_ARRAY:
		{
			if (!value->IsObject()) {
				printf("Failed to encode dictionary\n");
				return false;
			}

			DBusMessageIter subIter;
			DBusSignatureIter arraySiter;
			char *array_sig = NULL;

			// Getting signature of array object
			dbus_signature_iter_recurse(&siter, &arraySiter);
			array_sig = dbus_signature_iter_get_signature(&arraySiter);

			// Open array container to process elements in there
			if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, array_sig, &subIter)) {
				dbus_free(array_sig);
				printf("Can't open container for Array type\n");
				return false;
			}

			// It's a dictionary
			if (dbus_signature_iter_get_element_type(&siter) == DBUS_TYPE_DICT_ENTRY) {

				dbus_free(array_sig);

				Local<Object> value_object = value->ToObject();
				DBusSignatureIter dictSubSiter;

				// Getting sub-signature object
				dbus_signature_iter_recurse(&arraySiter, &dictSubSiter);
				dbus_signature_iter_next(&dictSubSiter);
				char *sig = dbus_signature_iter_get_signature(&dictSubSiter);

				// process each elements
				Local<Array> prop_names = value_object->GetPropertyNames();
				unsigned int len = prop_names->Length();

				bool failed = false;
				for (unsigned int i = 0; i < len; ++i) {
					DBusMessageIter dict_iter;

					// Open dict entry container
					if (!dbus_message_iter_open_container(&subIter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_iter)) {
						printf("Can't open container for DICT-ENTRY\n");
						failed = true;
						break;
					}

					// Getting the key and value
					Local<Value> prop_key = prop_names->Get(i);
					Local<Value> prop_value = value_object->Get(prop_key);

					// Append the key
					char *prop_key_str = strdup(*String::Utf8Value(prop_key->ToString()));
					// printf("key: %s\n", prop_key_str);
					dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &prop_key_str);
					dbus_free(prop_key_str);

					// Append the value
					if (!EncodeObject(prop_value, &dict_iter, sig)) {
						dbus_message_iter_close_container(&subIter, &dict_iter);
						printf("Failed to encode element of dictionary\n");
						failed = true;
						break;
					}

					dbus_message_iter_close_container(&subIter, &dict_iter);
				}

				dbus_free(sig);
				dbus_message_iter_close_container(iter, &subIter);

				if (failed)
					return false;

				break;
			}

			if (!value->IsArray()) {
				printf("Failed to encode array object\n");
				return false;
			}

			// process each elements
			Local<Array> arrayData = Local<Array>::Cast(value);
			for (unsigned int i = 0; i < arrayData->Length(); ++i) {
				Local<Value> arrayItem = arrayData->Get(i);
				if (!EncodeObject(arrayItem, &subIter, array_sig))
					break;
			}

			dbus_message_iter_close_container(iter, &subIter);
			dbus_free(array_sig);

			break;
		}

		case DBUS_TYPE_VARIANT:
		{
			DBusMessageIter subIter;

			const char *var_sig = GetSignatureFromV8Type(value);

			if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, var_sig, &subIter)) {
				printf("Can't open container for VARIANT type\n");
				return false;
			}

			if (!EncodeObject(value, &subIter, var_sig)) {
				dbus_message_iter_close_container(iter, &subIter);
				return false;
			}

			dbus_message_iter_close_container(iter, &subIter);

			break;
		}
		case DBUS_TYPE_STRUCT:
		{
			// printf("struct\n");
			DBusMessageIter subIter;
			DBusSignatureIter structSiter;

			// Open array container to process elements in there
			if (!dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &subIter)) {
				printf("Can't open container for Struct type\n");
				return false;
			}

			Local<Object> value_object = value->ToObject();

			// Getting sub-signature object
			dbus_signature_iter_recurse(&siter, &structSiter);

			// process each elements
			Local<Array> prop_names = value_object->GetPropertyNames();
			unsigned int len = prop_names->Length();

			for (unsigned int i = 0; i < len; ++i) {

				char *sig = dbus_signature_iter_get_signature(&structSiter);

				Local<Value> prop_key = prop_names->Get(i);

				if (!EncodeObject(value_object->Get(prop_key), &subIter, sig)) {
					dbus_free(sig);
					printf("Failed to encode element of dictionary\n");
					return false;
				}

				dbus_free(sig);

				if (!dbus_signature_iter_next(&structSiter))
					break;
			}

			dbus_message_iter_close_container(iter, &subIter);

			break;
		}
		default:
		{
			printf("Type not implemented: %i\n", type);
		}
		break;

		}

		return true;
	}

}

<|endoftext|>