<commit_before>// Copyright (c) 2014-2017 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "governance-validators.h"

#include "base58.h"
#include "utilstrencodings.h"

#include <algorithm>

CProposalValidator::CProposalValidator(const std::string& strDataHexIn)
    : strData(),
      objJSON(UniValue::VOBJ),
      fJSONValid(false),
      strErrorMessages()
{
    if(!strDataHexIn.empty()) {
        SetHexData(strDataHexIn);
    }
}

void CProposalValidator::Clear()
{
    strData = std::string();
    objJSON = UniValue(UniValue::VOBJ);
    fJSONValid = false;
    strErrorMessages = std::string();
}

void CProposalValidator::SetHexData(const std::string& strDataHexIn)
{
    std::vector<unsigned char> v = ParseHex(strDataHexIn);
    strData = std::string(v.begin(), v.end());
    ParseJSONData();
}

bool CProposalValidator::Validate()
{
    if(!ValidateJSON()) {
        strErrorMessages += "JSON parsing error;";
        return false;
    }
    if(!ValidateName()) {
        strErrorMessages += "Invalid name;";
        return false;
    }
    if(!ValidateStartEndEpoch()) {
        strErrorMessages += "Invalid start:end range;";
        return false;
    }
    if(!ValidatePaymentAmount()) {
        strErrorMessages += "Invalid payment amount;";
        return false;
    }
    if(!ValidatePaymentAddress()) {
        strErrorMessages += "Invalid payment address;";
        return false;
    }
    if(!ValidateURL()) {
        strErrorMessages += "Invalid URL;";
        return false;
    }
    return true;
}

bool CProposalValidator::ValidateJSON()
{
    return fJSONValid;
}

bool CProposalValidator::ValidateName()
{
    std::string strName;
    if(!GetDataValue("name", strName)) {
        strErrorMessages += "name field not found;";
        return false;
    }

    std::string strNameStripped = StripWhitespace(strName);

    if(strNameStripped.empty()) {
        strErrorMessages += "name is empty;";
        return false;
    }

    static const std::string strAllowedChars = "-_abcdefghijklmnopqrstuvwxyz0123456789";

    std::transform(strName.begin(), strName.end(), strName.begin(), ::tolower);

    if(strName.find_first_not_of(strAllowedChars) != std::string::npos) {
        strErrorMessages += "name contains invalid characters;";
        return false;
    }

    return true;
}

bool CProposalValidator::ValidateStartEndEpoch()
{
    int64_t nStartEpoch = 0;
    int64_t nEndEpoch = 0;

    if(!GetDataValue("start_epoch", nStartEpoch)) {
        strErrorMessages += "start_epoch field not found;";
        return false;
    }

    if(!GetDataValue("end_epoch", nEndEpoch)) {
        strErrorMessages += "end_epoch field not found;";
        return false;
    }

    if(nEndEpoch <= nStartEpoch) {
        strErrorMessages += "end_epoch <= start_epoch;";
        return false;
    }

    return true;
}

bool CProposalValidator::ValidatePaymentAmount()
{
    double dValue = 0.0;

    if(!GetDataValue("payment_amount", dValue)) {
        strErrorMessages += "payment_amount field not found;";
        return false;
    }

    if(dValue <= 0.0) {
        strErrorMessages += "payment_amount is negative;";
        return false;
    }

    // TODO: Should check for an amount which exceeds the budget but this is
    // currently difficult because start and end epochs are defined in terms of
    // clock time instead of block height.

    return true;
}

bool CProposalValidator::ValidatePaymentAddress()
{
    std::string strPaymentAddress;

    if(!GetDataValue("payment_address", strPaymentAddress)) {
        strErrorMessages += "payment_address field not found;";
        return false;
    }

    static const std::string base58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

    size_t nLength = strPaymentAddress.size();

    if((nLength < 26) || (nLength > 35)) {
        strErrorMessages += "incorrect payment_address length;";
        return false;
    }

    if(strPaymentAddress.find_first_not_of(base58chars) != std::string::npos) {
        strErrorMessages += "payment_address contains invalid characters;";
        return false;
    }

    CBitcoinAddress address(strPaymentAddress);
    if(!address.IsValid()) {
        strErrorMessages += "payment_address is invalid;";
        return false;
    }

    return true;
}

bool CProposalValidator::ValidateURL()
{
    std::string strURL;
    if(!GetDataValue("url", strURL)) {
        strErrorMessages += "url field not found;";
        return false;
    }

    std::string strURLStripped = StripWhitespace(strURL);

    if(strURLStripped.size() < 4U) {
        strErrorMessages += "url too short;";
        return false;
    }

    if(!CheckURL(strURL)) {
        strErrorMessages += "url invalid;";
        return false;
    }

    return true;
}

void CProposalValidator::ParseJSONData()
{
    fJSONValid = false;

    if(strData.empty()) {
        return;
    }

    try {
        UniValue obj(UniValue::VOBJ);
        obj.read(strData);
        std::vector<UniValue> arr1 = obj.getValues();
        std::vector<UniValue> arr2 = arr1.at(0).getValues();
        objJSON = arr2.at(1);
        fJSONValid = true;
    }
    catch(std::exception& e) {
        strErrorMessages += std::string(e.what()) + std::string(";");
    }
    catch(...) {
        strErrorMessages += "Unknown exception;";
    }
}

bool CProposalValidator::GetDataValue(const std::string& strKey, std::string& strValue)
{
    bool fOK = false;
    try  {
        strValue = objJSON[strKey].get_str();
        fOK = true;
    }
    catch(std::exception& e) {
        strErrorMessages += std::string(e.what()) + std::string(";");
    }
    catch(...) {
        strErrorMessages += "Unknown exception;";
    }
    return fOK;
}

bool CProposalValidator::GetDataValue(const std::string& strKey, int64_t& nValue)
{
    bool fOK = false;
    try  {
        const UniValue uValue = objJSON[strKey];
        switch(uValue.getType()) {
        case UniValue::VNUM:
            nValue = uValue.get_int64();
            fOK = true;
            break;
        case UniValue::VSTR:
        {
            std::istringstream istr(uValue.get_str());
            istr >> nValue;
            fOK = ! istr.fail();
        }
        break;
        default:
            break;
        }
    }
    catch(std::exception& e) {
        strErrorMessages += std::string(e.what()) + std::string(";");
    }
    catch(...) {
        strErrorMessages += "Unknown exception;";
    }
    return fOK;
}

bool CProposalValidator::GetDataValue(const std::string& strKey, double& dValue)
{
    bool fOK = false;
    try  {
        const UniValue uValue = objJSON[strKey];
        switch(uValue.getType()) {
        case UniValue::VNUM:
            dValue = uValue.get_real();
            fOK = true;
            break;
        case UniValue::VSTR:
        {
            std::istringstream istr(uValue.get_str());
            istr >> dValue;
            fOK = ! istr.fail();
        }
        break;
        default:
            break;
        }
    }
    catch(std::exception& e) {
        strErrorMessages += std::string(e.what()) + std::string(";");
    }
    catch(...) {
        strErrorMessages += "Unknown exception;";
    }
    return fOK;
}

std::string CProposalValidator::StripWhitespace(const std::string& strIn)
{
    static const std::string strWhitespace = " \f\n\r\t\v";

    std::string::size_type nStart = strIn.find_first_not_of(strWhitespace);
    std::string::size_type nEnd = strIn.find_last_not_of(strWhitespace);

    if((nStart == std::string::npos) || (nEnd == std::string::npos)) {
        return std::string();
    }

    return strIn.substr(nStart, nEnd - nStart + 1);
}

/*
  The purpose of this function is to replicate the behavior of the
  Python urlparse function used by sentinel (urlparse.py).  This function
  should return false whenever urlparse raises an exception and true
  otherwise.
 */
bool CProposalValidator::CheckURL(const std::string& strURLIn)
{
    std::string strRest(strURLIn);
    std::string::size_type nPos = strRest.find(':');

    if(nPos != std::string::npos) {
        //std::string strSchema = strRest.substr(0,nPos);

        if(nPos < strRest.size()) {
            strRest = strRest.substr(nPos + 1);
        }
        else {
            strRest = "";
        }
    }

    // Process netloc
    if((strRest.size() > 2) && (strRest.substr(0,2) == "//")) {
        static const std::string strNetlocDelimiters = "/?#";

        strRest = strRest.substr(2);

        std::string::size_type nPos2 = strRest.find_first_of(strNetlocDelimiters);

        std::string strNetloc = strRest.substr(0,nPos2);

        if((strNetloc.find('[') != std::string::npos) && (strNetloc.find(']') == std::string::npos)) {
            return false;
        }

        if((strNetloc.find(']') != std::string::npos) && (strNetloc.find('[') == std::string::npos)) {
            return false;
        }
    }

    return true;
}
<commit_msg>allow up to 40 chars in proposal name (#1693)<commit_after>// Copyright (c) 2014-2017 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "governance-validators.h"

#include "base58.h"
#include "utilstrencodings.h"

#include <algorithm>

CProposalValidator::CProposalValidator(const std::string& strDataHexIn)
    : strData(),
      objJSON(UniValue::VOBJ),
      fJSONValid(false),
      strErrorMessages()
{
    if(!strDataHexIn.empty()) {
        SetHexData(strDataHexIn);
    }
}

void CProposalValidator::Clear()
{
    strData = std::string();
    objJSON = UniValue(UniValue::VOBJ);
    fJSONValid = false;
    strErrorMessages = std::string();
}

void CProposalValidator::SetHexData(const std::string& strDataHexIn)
{
    std::vector<unsigned char> v = ParseHex(strDataHexIn);
    strData = std::string(v.begin(), v.end());
    ParseJSONData();
}

bool CProposalValidator::Validate()
{
    if(!ValidateJSON()) {
        strErrorMessages += "JSON parsing error;";
        return false;
    }
    if(!ValidateName()) {
        strErrorMessages += "Invalid name;";
        return false;
    }
    if(!ValidateStartEndEpoch()) {
        strErrorMessages += "Invalid start:end range;";
        return false;
    }
    if(!ValidatePaymentAmount()) {
        strErrorMessages += "Invalid payment amount;";
        return false;
    }
    if(!ValidatePaymentAddress()) {
        strErrorMessages += "Invalid payment address;";
        return false;
    }
    if(!ValidateURL()) {
        strErrorMessages += "Invalid URL;";
        return false;
    }
    return true;
}

bool CProposalValidator::ValidateJSON()
{
    return fJSONValid;
}

bool CProposalValidator::ValidateName()
{
    std::string strName;
    if(!GetDataValue("name", strName)) {
        strErrorMessages += "name field not found;";
        return false;
    }

    if(strName.size() > 40) {
        strErrorMessages += "name exceeds 40 characters;";
        return false;
    }

    std::string strNameStripped = StripWhitespace(strName);

    if(strNameStripped.empty()) {
        strErrorMessages += "name is empty;";
        return false;
    }

    static const std::string strAllowedChars = "-_abcdefghijklmnopqrstuvwxyz0123456789";

    std::transform(strName.begin(), strName.end(), strName.begin(), ::tolower);

    if(strName.find_first_not_of(strAllowedChars) != std::string::npos) {
        strErrorMessages += "name contains invalid characters;";
        return false;
    }

    return true;
}

bool CProposalValidator::ValidateStartEndEpoch()
{
    int64_t nStartEpoch = 0;
    int64_t nEndEpoch = 0;

    if(!GetDataValue("start_epoch", nStartEpoch)) {
        strErrorMessages += "start_epoch field not found;";
        return false;
    }

    if(!GetDataValue("end_epoch", nEndEpoch)) {
        strErrorMessages += "end_epoch field not found;";
        return false;
    }

    if(nEndEpoch <= nStartEpoch) {
        strErrorMessages += "end_epoch <= start_epoch;";
        return false;
    }

    return true;
}

bool CProposalValidator::ValidatePaymentAmount()
{
    double dValue = 0.0;

    if(!GetDataValue("payment_amount", dValue)) {
        strErrorMessages += "payment_amount field not found;";
        return false;
    }

    if(dValue <= 0.0) {
        strErrorMessages += "payment_amount is negative;";
        return false;
    }

    // TODO: Should check for an amount which exceeds the budget but this is
    // currently difficult because start and end epochs are defined in terms of
    // clock time instead of block height.

    return true;
}

bool CProposalValidator::ValidatePaymentAddress()
{
    std::string strPaymentAddress;

    if(!GetDataValue("payment_address", strPaymentAddress)) {
        strErrorMessages += "payment_address field not found;";
        return false;
    }

    static const std::string base58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

    size_t nLength = strPaymentAddress.size();

    if((nLength < 26) || (nLength > 35)) {
        strErrorMessages += "incorrect payment_address length;";
        return false;
    }

    if(strPaymentAddress.find_first_not_of(base58chars) != std::string::npos) {
        strErrorMessages += "payment_address contains invalid characters;";
        return false;
    }

    CBitcoinAddress address(strPaymentAddress);
    if(!address.IsValid()) {
        strErrorMessages += "payment_address is invalid;";
        return false;
    }

    return true;
}

bool CProposalValidator::ValidateURL()
{
    std::string strURL;
    if(!GetDataValue("url", strURL)) {
        strErrorMessages += "url field not found;";
        return false;
    }

    std::string strURLStripped = StripWhitespace(strURL);

    if(strURLStripped.size() < 4U) {
        strErrorMessages += "url too short;";
        return false;
    }

    if(!CheckURL(strURL)) {
        strErrorMessages += "url invalid;";
        return false;
    }

    return true;
}

void CProposalValidator::ParseJSONData()
{
    fJSONValid = false;

    if(strData.empty()) {
        return;
    }

    try {
        UniValue obj(UniValue::VOBJ);
        obj.read(strData);
        std::vector<UniValue> arr1 = obj.getValues();
        std::vector<UniValue> arr2 = arr1.at(0).getValues();
        objJSON = arr2.at(1);
        fJSONValid = true;
    }
    catch(std::exception& e) {
        strErrorMessages += std::string(e.what()) + std::string(";");
    }
    catch(...) {
        strErrorMessages += "Unknown exception;";
    }
}

bool CProposalValidator::GetDataValue(const std::string& strKey, std::string& strValue)
{
    bool fOK = false;
    try  {
        strValue = objJSON[strKey].get_str();
        fOK = true;
    }
    catch(std::exception& e) {
        strErrorMessages += std::string(e.what()) + std::string(";");
    }
    catch(...) {
        strErrorMessages += "Unknown exception;";
    }
    return fOK;
}

bool CProposalValidator::GetDataValue(const std::string& strKey, int64_t& nValue)
{
    bool fOK = false;
    try  {
        const UniValue uValue = objJSON[strKey];
        switch(uValue.getType()) {
        case UniValue::VNUM:
            nValue = uValue.get_int64();
            fOK = true;
            break;
        case UniValue::VSTR:
        {
            std::istringstream istr(uValue.get_str());
            istr >> nValue;
            fOK = ! istr.fail();
        }
        break;
        default:
            break;
        }
    }
    catch(std::exception& e) {
        strErrorMessages += std::string(e.what()) + std::string(";");
    }
    catch(...) {
        strErrorMessages += "Unknown exception;";
    }
    return fOK;
}

bool CProposalValidator::GetDataValue(const std::string& strKey, double& dValue)
{
    bool fOK = false;
    try  {
        const UniValue uValue = objJSON[strKey];
        switch(uValue.getType()) {
        case UniValue::VNUM:
            dValue = uValue.get_real();
            fOK = true;
            break;
        case UniValue::VSTR:
        {
            std::istringstream istr(uValue.get_str());
            istr >> dValue;
            fOK = ! istr.fail();
        }
        break;
        default:
            break;
        }
    }
    catch(std::exception& e) {
        strErrorMessages += std::string(e.what()) + std::string(";");
    }
    catch(...) {
        strErrorMessages += "Unknown exception;";
    }
    return fOK;
}

std::string CProposalValidator::StripWhitespace(const std::string& strIn)
{
    static const std::string strWhitespace = " \f\n\r\t\v";

    std::string::size_type nStart = strIn.find_first_not_of(strWhitespace);
    std::string::size_type nEnd = strIn.find_last_not_of(strWhitespace);

    if((nStart == std::string::npos) || (nEnd == std::string::npos)) {
        return std::string();
    }

    return strIn.substr(nStart, nEnd - nStart + 1);
}

/*
  The purpose of this function is to replicate the behavior of the
  Python urlparse function used by sentinel (urlparse.py).  This function
  should return false whenever urlparse raises an exception and true
  otherwise.
 */
bool CProposalValidator::CheckURL(const std::string& strURLIn)
{
    std::string strRest(strURLIn);
    std::string::size_type nPos = strRest.find(':');

    if(nPos != std::string::npos) {
        //std::string strSchema = strRest.substr(0,nPos);

        if(nPos < strRest.size()) {
            strRest = strRest.substr(nPos + 1);
        }
        else {
            strRest = "";
        }
    }

    // Process netloc
    if((strRest.size() > 2) && (strRest.substr(0,2) == "//")) {
        static const std::string strNetlocDelimiters = "/?#";

        strRest = strRest.substr(2);

        std::string::size_type nPos2 = strRest.find_first_of(strNetlocDelimiters);

        std::string strNetloc = strRest.substr(0,nPos2);

        if((strNetloc.find('[') != std::string::npos) && (strNetloc.find(']') == std::string::npos)) {
            return false;
        }

        if((strNetloc.find(']') != std::string::npos) && (strNetloc.find('[') == std::string::npos)) {
            return false;
        }
    }

    return true;
}
<|endoftext|>


<commit_before>/*
* X.509 Certificate Path Validation
* (C) 2010,2011,2012,2014 Jack Lloyd
*
* Distributed under the terms of the Botan license
*/

#include <botan/x509path.h>
#include <botan/ocsp.h>
#include <botan/http_util.h>
#include <botan/parsing.h>
#include <botan/pubkey.h>
#include <botan/oids.h>
#include <algorithm>
#include <chrono>
#include <vector>
#include <set>

#include <iostream>

namespace Botan {

namespace {

const X509_Certificate*
find_issuing_cert(const X509_Certificate& cert,
                  Certificate_Store& end_certs,
                  const std::vector<Certificate_Store*>& certstores)
   {
   const X509_DN issuer_dn = cert.issuer_dn();
   const std::vector<byte> auth_key_id = cert.authority_key_id();

   if(const X509_Certificate* c = end_certs.find_cert(issuer_dn, auth_key_id))
      return c;

   for(size_t i = 0; i != certstores.size(); ++i)
      {
      if(const X509_Certificate* c = certstores[i]->find_cert(issuer_dn, auth_key_id))
         return c;
      }

   return nullptr;
   }

const X509_CRL* find_crls_for(const X509_Certificate& cert,
                              const std::vector<Certificate_Store*>& certstores)
   {
   for(size_t i = 0; i != certstores.size(); ++i)
      {
      if(const X509_CRL* crl = certstores[i]->find_crl_for(cert))
         return crl;
      }

#if 0
   const std::string crl_url = cert.crl_distribution_point();
   if(crl_url != "")
      {
      std::cout << "Downloading CRL " << crl_url << "\n";
      auto http = HTTP::GET_sync(crl_url);

      std::cout << http.status_message() << "\n";

      http.throw_unless_ok();
      // check the mime type

      std::unique_ptr<X509_CRL> crl(new X509_CRL(http.body()));

      return crl.release();
      }
#endif

   return nullptr;
   }

std::vector<std::set<Certificate_Status_Code>>
check_chain(const std::vector<X509_Certificate>& cert_path,
            const Path_Validation_Restrictions& restrictions,
            const std::vector<Certificate_Store*>& certstores)
   {
   const std::set<std::string>& trusted_hashes = restrictions.trusted_hashes();

   const bool self_signed_ee_cert = (cert_path.size() == 1);

   X509_Time current_time(std::chrono::system_clock::now());

   std::vector<std::future<OCSP::Response>> ocsp_responses;

   std::vector<std::set<Certificate_Status_Code>> cert_status(cert_path.size());

   for(size_t i = 0; i != cert_path.size(); ++i)
      {
      std::set<Certificate_Status_Code>& status = cert_status.at(i);

      const bool at_self_signed_root = (i == cert_path.size() - 1);

      const X509_Certificate& subject = cert_path[i];

      const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];

      const Certificate_Store* trusted = certstores[0]; // fixme

      if(i == 0 || restrictions.ocsp_all_intermediates())
         ocsp_responses.push_back(
            std::async(std::launch::async,
                       OCSP::online_check, issuer, subject, trusted));

      // Check all certs for valid time range
      if(current_time < X509_Time(subject.start_time()))
         status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);

      if(current_time > X509_Time(subject.end_time()))
         status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);

      // Check issuer constraints

      // Don't require CA bit set on self-signed end entity cert
      if(!issuer.is_CA_cert() && !self_signed_ee_cert)
         status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);

      if(issuer.path_limit() < i)
         status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);

      std::unique_ptr<Public_Key> issuer_key(issuer.subject_public_key());

      if(subject.check_signature(*issuer_key) == false)
         status.insert(Certificate_Status_Code::SIGNATURE_ERROR);

      if(issuer_key->estimated_strength() < restrictions.minimum_key_strength())
         status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);

      // Allow untrusted hashes on self-signed roots
      if(!trusted_hashes.empty() && !at_self_signed_root)
         {
         if(!trusted_hashes.count(subject.hash_used_for_signature()))
            status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
         }
      }

   for(size_t i = 0; i != cert_path.size() - 1; ++i)
      {
      std::set<Certificate_Status_Code>& status = cert_status.at(i);

      const X509_Certificate& subject = cert_path.at(i);
      const X509_Certificate& ca = cert_path.at(i+1);

      if(i < ocsp_responses.size())
         {
         try
            {
            OCSP::Response ocsp = ocsp_responses[i].get();

            auto ocsp_status = ocsp.status_for(ca, subject);

            status.insert(ocsp_status);

            //std::cout << "OCSP status: " << Path_Validation_Result::status_string(ocsp_status) << "\n";

            // Either way we have a definitive answer, no need to check CRLs
            if(ocsp_status == Certificate_Status_Code::CERT_IS_REVOKED)
               return cert_status;
            else if(ocsp_status == Certificate_Status_Code::OCSP_RESPONSE_GOOD)
               continue;
            }
         catch(std::exception& e)
            {
            //std::cout << "OCSP error: " << e.what() << "\n";
            }
         }

      const X509_CRL* crl_p = find_crls_for(subject, certstores);

      if(!crl_p)
         {
         if(restrictions.require_revocation_information())
            status.insert(Certificate_Status_Code::NO_REVOCATION_DATA);
         continue;
         }

      const X509_CRL& crl = *crl_p;

      if(!ca.allowed_usage(CRL_SIGN))
         status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);

      if(current_time < X509_Time(crl.this_update()))
         status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);

      if(current_time > X509_Time(crl.next_update()))
         status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);

      if(crl.check_signature(ca.subject_public_key()) == false)
         status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);

      if(crl.is_revoked(subject))
         status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
      }

   if(self_signed_ee_cert)
      cert_status.back().insert(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);

   return cert_status;
   }

}

Path_Validation_Result x509_path_validate(
   const std::vector<X509_Certificate>& end_certs,
   const Path_Validation_Restrictions& restrictions,
   const std::vector<Certificate_Store*>& certstores)
   {
   if(end_certs.empty())
      throw std::invalid_argument("x509_path_validate called with no subjects");

   std::vector<X509_Certificate> cert_path;
   cert_path.push_back(end_certs[0]);

   Certificate_Store_Overlay extra(end_certs);

   // iterate until we reach a root or cannot find the issuer
   while(!cert_path.back().is_self_signed())
      {
      const X509_Certificate* cert = find_issuing_cert(cert_path.back(), extra, certstores);
      if(!cert)
         return Path_Validation_Result(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);

      cert_path.push_back(*cert);
      }

   return Path_Validation_Result(check_chain(cert_path, restrictions, certstores),
                                 std::move(cert_path));
   }

Path_Validation_Result x509_path_validate(
   const X509_Certificate& end_cert,
   const Path_Validation_Restrictions& restrictions,
   const std::vector<Certificate_Store*>& certstores)
   {
   std::vector<X509_Certificate> certs;
   certs.push_back(end_cert);
   return x509_path_validate(certs, restrictions, certstores);
   }

Path_Validation_Result x509_path_validate(
   const std::vector<X509_Certificate>& end_certs,
   const Path_Validation_Restrictions& restrictions,
   const Certificate_Store& store)
   {
   std::vector<Certificate_Store*> certstores;
   certstores.push_back(const_cast<Certificate_Store*>(&store));

   return x509_path_validate(end_certs, restrictions, certstores);
   }

Path_Validation_Result x509_path_validate(
   const X509_Certificate& end_cert,
   const Path_Validation_Restrictions& restrictions,
   const Certificate_Store& store)
   {
   std::vector<X509_Certificate> certs;
   certs.push_back(end_cert);

   std::vector<Certificate_Store*> certstores;
   certstores.push_back(const_cast<Certificate_Store*>(&store));

   return x509_path_validate(certs, restrictions, certstores);
   }

Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
                                                           size_t key_strength,
                                                           bool ocsp_all) :
   m_require_revocation_information(require_rev),
   m_ocsp_all_intermediates(ocsp_all),
   m_minimum_key_strength(key_strength)
   {
   if(key_strength <= 80)
      m_trusted_hashes.insert("SHA-160");

   m_trusted_hashes.insert("SHA-224");
   m_trusted_hashes.insert("SHA-256");
   m_trusted_hashes.insert("SHA-384");
   m_trusted_hashes.insert("SHA-512");
   }

Path_Validation_Result::Path_Validation_Result(std::vector<std::set<Certificate_Status_Code>> status,
                                               std::vector<X509_Certificate>&& cert_chain) :
   m_overall(Certificate_Status_Code::VERIFIED),
   m_all_status(status),
   m_cert_path(cert_chain)
   {
   // take the "worst" error as overall
   for(const auto& s : m_all_status)
      {
      if(!s.empty())
         {
         auto worst = *s.rbegin();
         // Leave OCSP confirmations on cert-level status only
         if(worst != Certificate_Status_Code::OCSP_RESPONSE_GOOD)
            m_overall = worst;
         }
      }
   }

const X509_Certificate& Path_Validation_Result::trust_root() const
   {
   return m_cert_path[m_cert_path.size()-1];
   }

std::set<std::string> Path_Validation_Result::trusted_hashes() const
   {
   std::set<std::string> hashes;
   for(size_t i = 0; i != m_cert_path.size(); ++i)
      hashes.insert(m_cert_path[i].hash_used_for_signature());
   return hashes;
   }

bool Path_Validation_Result::successful_validation() const
   {
   if(result() == Certificate_Status_Code::VERIFIED ||
      result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD)
      return true;
   return false;
   }

std::string Path_Validation_Result::result_string() const
   {
   return status_string(result());
   }

const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
   {
   switch(code)
      {
      case Certificate_Status_Code::VERIFIED:
         return "Verified";
      case Certificate_Status_Code::OCSP_RESPONSE_GOOD:
         return "OCSP response good";
      case Certificate_Status_Code::NO_REVOCATION_DATA:
         return "No revocation data";
      case Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK:
         return "Signature method too weak";
      case Certificate_Status_Code::UNTRUSTED_HASH:
         return "Untrusted hash";

      case Certificate_Status_Code::CERT_NOT_YET_VALID:
         return "Certificate is not yet valid";
      case Certificate_Status_Code::CERT_HAS_EXPIRED:
         return "Certificate has expired";
      case Certificate_Status_Code::OCSP_NOT_YET_VALID:
         return "OCSP is not yet valid";
      case Certificate_Status_Code::OCSP_HAS_EXPIRED:
         return "OCSP has expired";
      case Certificate_Status_Code::CRL_NOT_YET_VALID:
         return "CRL is not yet valid";
      case Certificate_Status_Code::CRL_HAS_EXPIRED:
         return "CRL has expired";

      case Certificate_Status_Code::CERT_ISSUER_NOT_FOUND:
         return "Certificate issuer not found";
      case Certificate_Status_Code::CANNOT_ESTABLISH_TRUST:
         return "Cannot establish trust";

      case Certificate_Status_Code::POLICY_ERROR:
         return "Policy error";
      case Certificate_Status_Code::INVALID_USAGE:
         return "Invalid usage";
      case Certificate_Status_Code::CERT_CHAIN_TOO_LONG:
         return "Certificate chain too long";
      case Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER:
         return "CA certificate not allowed to issue certs";
      case Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER:
         return "CA certificate not allowed to issue CRLs";
      case Certificate_Status_Code::OCSP_CERT_NOT_LISTED:
         return "OCSP cert not listed";
      case Certificate_Status_Code::OCSP_BAD_STATUS:
         return "OCSP bad status";

      case Certificate_Status_Code::CERT_IS_REVOKED:
         return "Certificate is revoked";
      case Certificate_Status_Code::CRL_BAD_SIGNATURE:
         return "CRL bad signature";
      case Certificate_Status_Code::SIGNATURE_ERROR:
         return "Signature error";
      default:
         return "Unknown error";
      }
   }

}
<commit_msg>If no certificate stores at all are available skip OCSP checks<commit_after>/*
* X.509 Certificate Path Validation
* (C) 2010,2011,2012,2014 Jack Lloyd
*
* Distributed under the terms of the Botan license
*/

#include <botan/x509path.h>
#include <botan/ocsp.h>
#include <botan/http_util.h>
#include <botan/parsing.h>
#include <botan/pubkey.h>
#include <botan/oids.h>
#include <algorithm>
#include <chrono>
#include <vector>
#include <set>

#include <iostream>

namespace Botan {

namespace {

const X509_Certificate*
find_issuing_cert(const X509_Certificate& cert,
                  Certificate_Store& end_certs,
                  const std::vector<Certificate_Store*>& certstores)
   {
   const X509_DN issuer_dn = cert.issuer_dn();
   const std::vector<byte> auth_key_id = cert.authority_key_id();

   if(const X509_Certificate* c = end_certs.find_cert(issuer_dn, auth_key_id))
      return c;

   for(size_t i = 0; i != certstores.size(); ++i)
      {
      if(const X509_Certificate* c = certstores[i]->find_cert(issuer_dn, auth_key_id))
         return c;
      }

   return nullptr;
   }

const X509_CRL* find_crls_for(const X509_Certificate& cert,
                              const std::vector<Certificate_Store*>& certstores)
   {
   for(size_t i = 0; i != certstores.size(); ++i)
      {
      if(const X509_CRL* crl = certstores[i]->find_crl_for(cert))
         return crl;
      }

#if 0
   const std::string crl_url = cert.crl_distribution_point();
   if(crl_url != "")
      {
      std::cout << "Downloading CRL " << crl_url << "\n";
      auto http = HTTP::GET_sync(crl_url);

      std::cout << http.status_message() << "\n";

      http.throw_unless_ok();
      // check the mime type

      std::unique_ptr<X509_CRL> crl(new X509_CRL(http.body()));

      return crl.release();
      }
#endif

   return nullptr;
   }

std::vector<std::set<Certificate_Status_Code>>
check_chain(const std::vector<X509_Certificate>& cert_path,
            const Path_Validation_Restrictions& restrictions,
            const std::vector<Certificate_Store*>& certstores)
   {
   const std::set<std::string>& trusted_hashes = restrictions.trusted_hashes();

   const bool self_signed_ee_cert = (cert_path.size() == 1);

   X509_Time current_time(std::chrono::system_clock::now());

   std::vector<std::future<OCSP::Response>> ocsp_responses;

   std::vector<std::set<Certificate_Status_Code>> cert_status(cert_path.size());

   for(size_t i = 0; i != cert_path.size(); ++i)
      {
      std::set<Certificate_Status_Code>& status = cert_status.at(i);

      const bool at_self_signed_root = (i == cert_path.size() - 1);

      const X509_Certificate& subject = cert_path[i];

      const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];

      if(i == 0 || restrictions.ocsp_all_intermediates())
         {
         // certstore[0] is treated as trusted for OCSP (FIXME)
         if(certstores.size() > 1)
            ocsp_responses.push_back(
               std::async(std::launch::async,
                          OCSP::online_check, issuer, subject, certstores[0]));
         }

      // Check all certs for valid time range
      if(current_time < X509_Time(subject.start_time()))
         status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);

      if(current_time > X509_Time(subject.end_time()))
         status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);

      // Check issuer constraints

      // Don't require CA bit set on self-signed end entity cert
      if(!issuer.is_CA_cert() && !self_signed_ee_cert)
         status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);

      if(issuer.path_limit() < i)
         status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);

      std::unique_ptr<Public_Key> issuer_key(issuer.subject_public_key());

      if(subject.check_signature(*issuer_key) == false)
         status.insert(Certificate_Status_Code::SIGNATURE_ERROR);

      if(issuer_key->estimated_strength() < restrictions.minimum_key_strength())
         status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);

      // Allow untrusted hashes on self-signed roots
      if(!trusted_hashes.empty() && !at_self_signed_root)
         {
         if(!trusted_hashes.count(subject.hash_used_for_signature()))
            status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
         }
      }

   for(size_t i = 0; i != cert_path.size() - 1; ++i)
      {
      std::set<Certificate_Status_Code>& status = cert_status.at(i);

      const X509_Certificate& subject = cert_path.at(i);
      const X509_Certificate& ca = cert_path.at(i+1);

      if(i < ocsp_responses.size())
         {
         try
            {
            OCSP::Response ocsp = ocsp_responses[i].get();

            auto ocsp_status = ocsp.status_for(ca, subject);

            status.insert(ocsp_status);

            //std::cout << "OCSP status: " << Path_Validation_Result::status_string(ocsp_status) << "\n";

            // Either way we have a definitive answer, no need to check CRLs
            if(ocsp_status == Certificate_Status_Code::CERT_IS_REVOKED)
               return cert_status;
            else if(ocsp_status == Certificate_Status_Code::OCSP_RESPONSE_GOOD)
               continue;
            }
         catch(std::exception& e)
            {
            //std::cout << "OCSP error: " << e.what() << "\n";
            }
         }

      const X509_CRL* crl_p = find_crls_for(subject, certstores);

      if(!crl_p)
         {
         if(restrictions.require_revocation_information())
            status.insert(Certificate_Status_Code::NO_REVOCATION_DATA);
         continue;
         }

      const X509_CRL& crl = *crl_p;

      if(!ca.allowed_usage(CRL_SIGN))
         status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);

      if(current_time < X509_Time(crl.this_update()))
         status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);

      if(current_time > X509_Time(crl.next_update()))
         status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);

      if(crl.check_signature(ca.subject_public_key()) == false)
         status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);

      if(crl.is_revoked(subject))
         status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
      }

   if(self_signed_ee_cert)
      cert_status.back().insert(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);

   return cert_status;
   }

}

Path_Validation_Result x509_path_validate(
   const std::vector<X509_Certificate>& end_certs,
   const Path_Validation_Restrictions& restrictions,
   const std::vector<Certificate_Store*>& certstores)
   {
   if(end_certs.empty())
      throw std::invalid_argument("x509_path_validate called with no subjects");

   std::vector<X509_Certificate> cert_path;
   cert_path.push_back(end_certs[0]);

   Certificate_Store_Overlay extra(end_certs);

   // iterate until we reach a root or cannot find the issuer
   while(!cert_path.back().is_self_signed())
      {
      const X509_Certificate* cert = find_issuing_cert(cert_path.back(), extra, certstores);
      if(!cert)
         return Path_Validation_Result(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);

      cert_path.push_back(*cert);
      }

   return Path_Validation_Result(check_chain(cert_path, restrictions, certstores),
                                 std::move(cert_path));
   }

Path_Validation_Result x509_path_validate(
   const X509_Certificate& end_cert,
   const Path_Validation_Restrictions& restrictions,
   const std::vector<Certificate_Store*>& certstores)
   {
   std::vector<X509_Certificate> certs;
   certs.push_back(end_cert);
   return x509_path_validate(certs, restrictions, certstores);
   }

Path_Validation_Result x509_path_validate(
   const std::vector<X509_Certificate>& end_certs,
   const Path_Validation_Restrictions& restrictions,
   const Certificate_Store& store)
   {
   std::vector<Certificate_Store*> certstores;
   certstores.push_back(const_cast<Certificate_Store*>(&store));

   return x509_path_validate(end_certs, restrictions, certstores);
   }

Path_Validation_Result x509_path_validate(
   const X509_Certificate& end_cert,
   const Path_Validation_Restrictions& restrictions,
   const Certificate_Store& store)
   {
   std::vector<X509_Certificate> certs;
   certs.push_back(end_cert);

   std::vector<Certificate_Store*> certstores;
   certstores.push_back(const_cast<Certificate_Store*>(&store));

   return x509_path_validate(certs, restrictions, certstores);
   }

Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
                                                           size_t key_strength,
                                                           bool ocsp_all) :
   m_require_revocation_information(require_rev),
   m_ocsp_all_intermediates(ocsp_all),
   m_minimum_key_strength(key_strength)
   {
   if(key_strength <= 80)
      m_trusted_hashes.insert("SHA-160");

   m_trusted_hashes.insert("SHA-224");
   m_trusted_hashes.insert("SHA-256");
   m_trusted_hashes.insert("SHA-384");
   m_trusted_hashes.insert("SHA-512");
   }

Path_Validation_Result::Path_Validation_Result(std::vector<std::set<Certificate_Status_Code>> status,
                                               std::vector<X509_Certificate>&& cert_chain) :
   m_overall(Certificate_Status_Code::VERIFIED),
   m_all_status(status),
   m_cert_path(cert_chain)
   {
   // take the "worst" error as overall
   for(const auto& s : m_all_status)
      {
      if(!s.empty())
         {
         auto worst = *s.rbegin();
         // Leave OCSP confirmations on cert-level status only
         if(worst != Certificate_Status_Code::OCSP_RESPONSE_GOOD)
            m_overall = worst;
         }
      }
   }

const X509_Certificate& Path_Validation_Result::trust_root() const
   {
   return m_cert_path[m_cert_path.size()-1];
   }

std::set<std::string> Path_Validation_Result::trusted_hashes() const
   {
   std::set<std::string> hashes;
   for(size_t i = 0; i != m_cert_path.size(); ++i)
      hashes.insert(m_cert_path[i].hash_used_for_signature());
   return hashes;
   }

bool Path_Validation_Result::successful_validation() const
   {
   if(result() == Certificate_Status_Code::VERIFIED ||
      result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD)
      return true;
   return false;
   }

std::string Path_Validation_Result::result_string() const
   {
   return status_string(result());
   }

const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
   {
   switch(code)
      {
      case Certificate_Status_Code::VERIFIED:
         return "Verified";
      case Certificate_Status_Code::OCSP_RESPONSE_GOOD:
         return "OCSP response good";
      case Certificate_Status_Code::NO_REVOCATION_DATA:
         return "No revocation data";
      case Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK:
         return "Signature method too weak";
      case Certificate_Status_Code::UNTRUSTED_HASH:
         return "Untrusted hash";

      case Certificate_Status_Code::CERT_NOT_YET_VALID:
         return "Certificate is not yet valid";
      case Certificate_Status_Code::CERT_HAS_EXPIRED:
         return "Certificate has expired";
      case Certificate_Status_Code::OCSP_NOT_YET_VALID:
         return "OCSP is not yet valid";
      case Certificate_Status_Code::OCSP_HAS_EXPIRED:
         return "OCSP has expired";
      case Certificate_Status_Code::CRL_NOT_YET_VALID:
         return "CRL is not yet valid";
      case Certificate_Status_Code::CRL_HAS_EXPIRED:
         return "CRL has expired";

      case Certificate_Status_Code::CERT_ISSUER_NOT_FOUND:
         return "Certificate issuer not found";
      case Certificate_Status_Code::CANNOT_ESTABLISH_TRUST:
         return "Cannot establish trust";

      case Certificate_Status_Code::POLICY_ERROR:
         return "Policy error";
      case Certificate_Status_Code::INVALID_USAGE:
         return "Invalid usage";
      case Certificate_Status_Code::CERT_CHAIN_TOO_LONG:
         return "Certificate chain too long";
      case Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER:
         return "CA certificate not allowed to issue certs";
      case Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER:
         return "CA certificate not allowed to issue CRLs";
      case Certificate_Status_Code::OCSP_CERT_NOT_LISTED:
         return "OCSP cert not listed";
      case Certificate_Status_Code::OCSP_BAD_STATUS:
         return "OCSP bad status";

      case Certificate_Status_Code::CERT_IS_REVOKED:
         return "Certificate is revoked";
      case Certificate_Status_Code::CRL_BAD_SIGNATURE:
         return "CRL bad signature";
      case Certificate_Status_Code::SIGNATURE_ERROR:
         return "Signature error";
      default:
         return "Unknown error";
      }
   }

}
<|endoftext|>


<commit_before>#include <string>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "jml/arch/timers.h"
#include "jml/utils/exc_check.h"

using namespace std;

void
DoOutput(FILE * in, FILE * out)
{
    int len(0);
    unique_ptr<char> buffer;

    size_t n = ::fread(&len, sizeof(len), 1, in);
    ExcCheckNotEqual(n, 0, "sizeof(len) must be 4");

    buffer.reset(new char[len + 1]);
    n = ::fread(buffer.get(), sizeof(char), len, in);
    ExcCheckNotEqual(n, 0, "received 0 bytes");

    buffer.get()[len] = 0;

    // ::fprintf(stderr, "helper output: %s\n", buffer);

    ::fprintf(out, "%s\n", buffer.get());
    ::fflush(out);
}

void
DoSleep(FILE * in)
{
    char delay_buf[5]; /* in .1 secs units */
    delay_buf[4] = 0;

    size_t r = ::fread(&delay_buf, 1, 4, in);
    if (r != 4) {
        throw ML::Exception("wrong delay: " + to_string(r));
    }

    long delay = ::strtol(delay_buf, NULL, 16);
    ML::sleep(delay * 0.1);
}

void
DoExit(FILE * in)
{
    int code;

    size_t n = ::fread(&code, sizeof(code), 1, in);
    ExcCheckNotEqual(n, 0, "no exit code received");

    printf("helper: exit with code %d\n", code);

    exit(code);
}

int main(int argc, char *argv[])
{
    /** commands:
        err/out|bytes8|nstring
        xit|code(int)
        abt
    */

    printf("helper: ready\n");
    fflush(stdout);

    while (1) {
        char command[3];
        size_t n = ::fread(command, 1, sizeof(command), stdin);
        if (n < 3) {
            if (::feof(stdin)) {
                break;
            }
        }
        if (n == 0) {
            continue;
        }

        if (::strncmp(command, "err", 3) == 0) {
            DoOutput(stdin, stderr);
        }
        else if (::strncmp(command, "out", 3) == 0) {
            DoOutput(stdin, stdout);
        }
        else if (::strncmp(command, "slp", 3) == 0) {
            DoSleep(stdin);
        }
        else if (::strncmp(command, "xit", 3) == 0) {
            DoExit(stdin);
        }
        else if (::strncmp(command, "abt", 3) == 0) {
            ::abort();
        }
    }

    return 0;
}
<commit_msg>runner test: include <memory> for unique_ptr<commit_after>#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <memory>
#include <string>

#include "jml/arch/timers.h"
#include "jml/utils/exc_check.h"

using namespace std;

void
DoOutput(FILE * in, FILE * out)
{
    int len(0);
    unique_ptr<char> buffer;

    size_t n = ::fread(&len, sizeof(len), 1, in);
    ExcCheckNotEqual(n, 0, "sizeof(len) must be 4");

    buffer.reset(new char[len + 1]);
    n = ::fread(buffer.get(), sizeof(char), len, in);
    ExcCheckNotEqual(n, 0, "received 0 bytes");

    buffer.get()[len] = 0;

    // ::fprintf(stderr, "helper output: %s\n", buffer);

    ::fprintf(out, "%s\n", buffer.get());
    ::fflush(out);
}

void
DoSleep(FILE * in)
{
    char delay_buf[5]; /* in .1 secs units */
    delay_buf[4] = 0;

    size_t r = ::fread(&delay_buf, 1, 4, in);
    if (r != 4) {
        throw ML::Exception("wrong delay: " + to_string(r));
    }

    long delay = ::strtol(delay_buf, NULL, 16);
    ML::sleep(delay * 0.1);
}

void
DoExit(FILE * in)
{
    int code;

    size_t n = ::fread(&code, sizeof(code), 1, in);
    ExcCheckNotEqual(n, 0, "no exit code received");

    printf("helper: exit with code %d\n", code);

    exit(code);
}

int main(int argc, char *argv[])
{
    /** commands:
        err/out|bytes8|nstring
        xit|code(int)
        abt
    */

    printf("helper: ready\n");
    fflush(stdout);

    while (1) {
        char command[3];
        size_t n = ::fread(command, 1, sizeof(command), stdin);
        if (n < 3) {
            if (::feof(stdin)) {
                break;
            }
        }
        if (n == 0) {
            continue;
        }

        if (::strncmp(command, "err", 3) == 0) {
            DoOutput(stdin, stderr);
        }
        else if (::strncmp(command, "out", 3) == 0) {
            DoOutput(stdin, stdout);
        }
        else if (::strncmp(command, "slp", 3) == 0) {
            DoSleep(stdin);
        }
        else if (::strncmp(command, "xit", 3) == 0) {
            DoExit(stdin);
        }
        else if (::strncmp(command, "abt", 3) == 0) {
            ::abort();
        }
    }

    return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2021 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/core/SkMath.h"
#include "src/gpu/GrSubRunAllocator.h"

#include <cstddef>
#include <memory>
#include <new>

// -- GrBagOfBytes ---------------------------------------------------------------------------------
GrBagOfBytes::GrBagOfBytes(char* bytes, size_t size, size_t firstHeapAllocation)
        : fFibProgression(size, firstHeapAllocation) {
    SkASSERT_RELEASE(size < kMaxByteSize);
    SkASSERT_RELEASE(firstHeapAllocation < kMaxByteSize);

    std::size_t space = size;
    void* ptr = bytes;
    if (bytes && std::align(kMaxAlignment, sizeof(Block), ptr, space)) {
        this->setupBytesAndCapacity(bytes, size);
        new (fEndByte) Block(nullptr, nullptr);
    }
}

GrBagOfBytes::GrBagOfBytes(size_t firstHeapAllocation)
        : GrBagOfBytes(nullptr, 0, firstHeapAllocation) {}

GrBagOfBytes::~GrBagOfBytes() {
    Block* cursor = reinterpret_cast<Block*>(fEndByte);
    while (cursor != nullptr) {
        char* toDelete = cursor->fBlockStart;
        cursor = cursor->fPrevious;
        delete [] toDelete;
    }
}

GrBagOfBytes::Block::Block(char* previous, char* startOfBlock)
        : fBlockStart{startOfBlock}
        , fPrevious{reinterpret_cast<Block*>(previous)} {}

void* GrBagOfBytes::alignedBytes(int size, int alignment) {
    SkASSERT_RELEASE(0 < size && size < kMaxByteSize);
    SkASSERT_RELEASE(0 < alignment && alignment <= kMaxAlignment);
    SkASSERT_RELEASE(SkIsPow2(alignment));

    return this->allocateBytes(size, alignment);
}

void GrBagOfBytes::setupBytesAndCapacity(char* bytes, int size) {
    // endByte must be aligned to the maximum alignment to allow tracking alignment using capacity;
    // capacity and endByte are both aligned to max alignment.
    intptr_t endByte = reinterpret_cast<intptr_t>(bytes + size - sizeof(Block)) & -kMaxAlignment;
    fEndByte  = reinterpret_cast<char*>(endByte);
    fCapacity = fEndByte - bytes;
}

void GrBagOfBytes::needMoreBytes(int requestedSize, int alignment) {
    int nextBlockSize = fFibProgression.nextBlockSize();
    const int size = PlatformMinimumSizeWithOverhead(
            std::max(requestedSize, nextBlockSize), kAllocationAlignment);
    char* const bytes = new char[size];
    // fEndByte is changed by setupBytesAndCapacity. Remember it to link back to.
    char* const previousBlock = fEndByte;
    this->setupBytesAndCapacity(bytes, size);

    // Make a block to delete these bytes, and points to the previous block.
    new (fEndByte) Block{previousBlock, bytes};

    // Make fCapacity the alignment for the requested object.
    fCapacity = fCapacity & -alignment;
    SkASSERT(fCapacity >= requestedSize);
}

// -- GrSubRunAllocator ----------------------------------------------------------------------------
GrSubRunAllocator::GrSubRunAllocator(char* bytes, int size, int firstHeapAllocation)
        : fAlloc{bytes, SkTo<size_t>(size), SkTo<size_t>(firstHeapAllocation)} {}

GrSubRunAllocator::GrSubRunAllocator(int firstHeapAllocation)
        : GrSubRunAllocator(nullptr, 0, firstHeapAllocation) {}

void* GrSubRunAllocator::alignedBytes(int unsafeSize, int unsafeAlignment) {
    return fAlloc.alignedBytes(unsafeSize, unsafeAlignment);
}
<commit_msg>fail on bad size or firstHeapAllocation creating GrSubRunAllocator<commit_after>/*
 * Copyright 2021 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/core/SkMath.h"
#include "src/gpu/GrSubRunAllocator.h"

#include <cstddef>
#include <memory>
#include <new>

// -- GrBagOfBytes ---------------------------------------------------------------------------------
GrBagOfBytes::GrBagOfBytes(char* bytes, size_t size, size_t firstHeapAllocation)
        : fFibProgression(size, firstHeapAllocation) {
    SkASSERT_RELEASE(size < kMaxByteSize);
    SkASSERT_RELEASE(firstHeapAllocation < kMaxByteSize);

    std::size_t space = size;
    void* ptr = bytes;
    if (bytes && std::align(kMaxAlignment, sizeof(Block), ptr, space)) {
        this->setupBytesAndCapacity(bytes, size);
        new (fEndByte) Block(nullptr, nullptr);
    }
}

GrBagOfBytes::GrBagOfBytes(size_t firstHeapAllocation)
        : GrBagOfBytes(nullptr, 0, firstHeapAllocation) {}

GrBagOfBytes::~GrBagOfBytes() {
    Block* cursor = reinterpret_cast<Block*>(fEndByte);
    while (cursor != nullptr) {
        char* toDelete = cursor->fBlockStart;
        cursor = cursor->fPrevious;
        delete [] toDelete;
    }
}

GrBagOfBytes::Block::Block(char* previous, char* startOfBlock)
        : fBlockStart{startOfBlock}
        , fPrevious{reinterpret_cast<Block*>(previous)} {}

void* GrBagOfBytes::alignedBytes(int size, int alignment) {
    SkASSERT_RELEASE(0 < size && size < kMaxByteSize);
    SkASSERT_RELEASE(0 < alignment && alignment <= kMaxAlignment);
    SkASSERT_RELEASE(SkIsPow2(alignment));

    return this->allocateBytes(size, alignment);
}

void GrBagOfBytes::setupBytesAndCapacity(char* bytes, int size) {
    // endByte must be aligned to the maximum alignment to allow tracking alignment using capacity;
    // capacity and endByte are both aligned to max alignment.
    intptr_t endByte = reinterpret_cast<intptr_t>(bytes + size - sizeof(Block)) & -kMaxAlignment;
    fEndByte  = reinterpret_cast<char*>(endByte);
    fCapacity = fEndByte - bytes;
}

void GrBagOfBytes::needMoreBytes(int requestedSize, int alignment) {
    int nextBlockSize = fFibProgression.nextBlockSize();
    const int size = PlatformMinimumSizeWithOverhead(
            std::max(requestedSize, nextBlockSize), kAllocationAlignment);
    char* const bytes = new char[size];
    // fEndByte is changed by setupBytesAndCapacity. Remember it to link back to.
    char* const previousBlock = fEndByte;
    this->setupBytesAndCapacity(bytes, size);

    // Make a block to delete these bytes, and points to the previous block.
    new (fEndByte) Block{previousBlock, bytes};

    // Make fCapacity the alignment for the requested object.
    fCapacity = fCapacity & -alignment;
    SkASSERT(fCapacity >= requestedSize);
}

// -- GrSubRunAllocator ----------------------------------------------------------------------------
GrSubRunAllocator::GrSubRunAllocator(char* bytes, int size, int firstHeapAllocation)
        : fAlloc{bytes, SkTo<size_t>(size), SkTo<size_t>(firstHeapAllocation)} {
                  SkASSERT_RELEASE(SkTFitsIn<size_t>(size));
                  SkASSERT_RELEASE(SkTFitsIn<size_t>(firstHeapAllocation));
}

GrSubRunAllocator::GrSubRunAllocator(int firstHeapAllocation)
        : GrSubRunAllocator(nullptr, 0, firstHeapAllocation) { }

void* GrSubRunAllocator::alignedBytes(int unsafeSize, int unsafeAlignment) {
    return fAlloc.alignedBytes(unsafeSize, unsafeAlignment);
}
<|endoftext|>


<commit_before>/* Please refer to license.txt */

#include "window.hpp"

#include "../2d/2d.hpp"

#include <iostream>

namespace GEngine
{

namespace mgfx
{

Window::Window()
{
	width = nullptr;
	height = nullptr;
	delete_width = false;
	delete_height = false;

	fullscreen = false;

	windowname = "";

	window2d = nullptr;
	event = nullptr;

	closed = true;

	focus = true;
}

Window::~Window()
{
	if (width) //If the width exists.
	{
		if (delete_width) //If it should be deleted.
		{
			delete width; //Delete it.
			width = nullptr; //Reset it.
		}
		else
		{
			width = nullptr; //Don't delete it, simply reset it.
		}
	}

	if (height) //If the height exists.
	{
		if (delete_height) //Delete it.
		{
			delete height; //Delete it.
			height = nullptr; //Reset it.
		}
		else
		{
			height = nullptr; //Don't delete it, only reset it.
		}
	}

	if (window2d) //If the 2d window exists.
	{
		window2d->close(); //Close the window.
		delete window2d; //Delete the window.
		window2d = nullptr; //Reset it.
	}

	if (event) //If the event exists.
	{
		delete event; //Delete it.
		event = nullptr; //Set to null.
	}
}

bool Window::create(int _width, int _height, bool _fullscreen, std::string _windowname, bool use_opengl)
{
	if (width) //Delete the width if it exists.
	{
		delete width;
	}
	width = new int; //Allocate memory for the width.
	delete_width = true; //The width is to be deleted.
	*width = _width; //Store the width.

	if (height) //Delete the height if it exists.
	{
		delete height;
	}
	height = new int; //Allocate memory for the height.
	delete_height = true; //The height is to be deleted.
	*height = _height; //Store the height.

	fullscreen = _fullscreen; //Store the fullscreen state.

	windowname = _windowname; //Store the window name.

	//TODO: Point the width and the height to the SFML window's width and height? Don't know if that's possible.
	window2d = new sf::RenderWindow; //Create a new (2d) window.

	if(fullscreen) //If it is to be fullscreen, do it.
	{
		window2d->create(sf::VideoMode(*width, *height), _windowname, sf::Style::Fullscreen); //Make the window fullscreen.
	}
	else
	{
		window2d->create(sf::VideoMode(*width, *height), _windowname, sf::Style::Default); //Nope, just a normal window.
	}

	closed = false; //Not closed.

	if (event) //If it already exists.
	{
		delete event; //Delete it.
	}
	event = new sf::Event; //Allocate memory for it.

	//TODO: Error checking?

	return true; //Success.
}

void Window::update()
{
	events.clear(); //Reset all events.

	if (!closed && window2d && event) //If the window is open, the window exists, and event exists.
	{
		while (window2d->pollEvent(*event)) //Poll events. //TODO: Give access to these events to the game.
		{
			if (event->type == sf::Event::Closed) //Check if the event is a window close.
			{
				window2d->close(); //Close the window.
				closed = true; //Update that it's closed.
			}
			else if (event->type == sf::Event::Resized && !closed && window2d) //Window resized.
			{
				window2d->setView(sf::View(sf::FloatRect(0, 0, event->size.width, event->size.height)));
			}
			else if (event->type == sf::Event::LostFocus)
			{
				focus = false; //Lost focus.
			}
			else if (event->type == sf::Event::GainedFocus)
			{
				focus = true; //Gained focus.
			}
			else
			{
				events.push_back(*event); //Save the event for later processing.
			}
		}

		if (!closed && window2d) //If the window was not closed.
		{
			window2d->display(); //Display the window.
			window2d->clear();
		}
	}
}

int Window::getWidth()
{
	if (!window2d)
	{
		std::cout << "\nNo 2d window, fool.\n";
		return -1;
	}

	if (!width || !delete_width) //If width does not exist or if it should not be deleted (meaning it's pointing to some other int).
	{
		width = new int; //Allocate it.
		delete_width = true; //It will be deleted.
	}

	sf::Vector2u vector = window2d->getSize(); //Get the size.

	*width = vector.x; //Set/update the width.

	return *width; //Return the width.
}

int Window::getHeight()
{
	if (!window2d)
	{
		std::cout << "\nNo, seriously, there is no 2d window! Why don't you understand?\n";
		return -1;
	}

	if (!height || !delete_height) //If height does not exist or if it should not be deleted (meaning it's pointing to some other int).
	{
		height = new int; //Allocate it.
		delete_height = true; //It will be deleted.
	}

	sf::Vector2u vector = window2d->getSize(); //Get the size of the window.

	*height = vector.y; //Set/update the height.

	return *height; //Return the height.
}

int Window::getX()
{
	return window2d->getPosition().x;
	//return (using_opengl) ? /*3d*/window3d->getPosition().x /*window*/ : /*2d*/ window2d->getPosition().x /*window*/;
}

int Window::getY()
{
	return window2d->getPosition().y;
	//return (using_opengl) ? /*3d*/window3d->getPosition().y /*window*/ : /*2d*/ window2d->getPosition().y /*window*/;
}

void Window::drawSprite(mgfx::d2d::Sprite &sprite)
{
	if (!window2d)
	{
		std::cout << "\nAnd just what are you trying to achieve with a window that does not exist?\n";
		return;
	}

	window2d->draw(*sprite.sprite); //Draw the sprite.
}

void Window::renderText(std::string text, int _x, int _y, int font_size, sf::Font &font)
{
	sf::Text _text(text, font);
	_text.setCharacterSize(font_size);
	_text.setPosition(_x, _y);

	if (!window2d)
	{
		std::cout << "\nAnd just what are you trying to achieve with a 2D window that does not exist?\n";
		return;
	}
	window2d->draw(_text);
}

void Window::setFramerateLimit(int fps)
{
	//Set the frames per second rate for the window.
	if (!window2d)
	{
		std::cout << "I can't set the framerate limit -- window2d doesn't exist!\n"; //Error.
		return; //Abort this function.
	}

	window2d->setFramerateLimit(fps);
}

void Window::showMouseCursor(bool show)
{
	//Set the frames per second rate for the window.
	if (!window2d)
	{
		std::cout << "I can't show or hide the mouse cursor -- window2d doesn't exist!\n"; //Error.
		return; //Abort this function.
	}

	window2d->setMouseCursorVisible(show);
}

} //namespace mgfx

} //namespace GEngine.
<commit_msg>Made renderText work.<commit_after>/* Please refer to license.txt */

#include "window.hpp"

#include "../2d/2d.hpp"

#include <iostream>

namespace GEngine
{

namespace mgfx
{

Window::Window()
{
	width = nullptr;
	height = nullptr;
	delete_width = false;
	delete_height = false;

	fullscreen = false;

	windowname = "";

	window2d = nullptr;
	event = nullptr;

	closed = true;

	focus = true;
}

Window::~Window()
{
	if (width) //If the width exists.
	{
		if (delete_width) //If it should be deleted.
		{
			delete width; //Delete it.
			width = nullptr; //Reset it.
		}
		else
		{
			width = nullptr; //Don't delete it, simply reset it.
		}
	}

	if (height) //If the height exists.
	{
		if (delete_height) //Delete it.
		{
			delete height; //Delete it.
			height = nullptr; //Reset it.
		}
		else
		{
			height = nullptr; //Don't delete it, only reset it.
		}
	}

	if (window2d) //If the 2d window exists.
	{
		window2d->close(); //Close the window.
		delete window2d; //Delete the window.
		window2d = nullptr; //Reset it.
	}

	if (event) //If the event exists.
	{
		delete event; //Delete it.
		event = nullptr; //Set to null.
	}
}

bool Window::create(int _width, int _height, bool _fullscreen, std::string _windowname, bool use_opengl)
{
	if (width) //Delete the width if it exists.
	{
		delete width;
	}
	width = new int; //Allocate memory for the width.
	delete_width = true; //The width is to be deleted.
	*width = _width; //Store the width.

	if (height) //Delete the height if it exists.
	{
		delete height;
	}
	height = new int; //Allocate memory for the height.
	delete_height = true; //The height is to be deleted.
	*height = _height; //Store the height.

	fullscreen = _fullscreen; //Store the fullscreen state.

	windowname = _windowname; //Store the window name.

	//TODO: Point the width and the height to the SFML window's width and height? Don't know if that's possible.
	window2d = new sf::RenderWindow; //Create a new (2d) window.

	if(fullscreen) //If it is to be fullscreen, do it.
	{
		window2d->create(sf::VideoMode(*width, *height), _windowname, sf::Style::Fullscreen); //Make the window fullscreen.
	}
	else
	{
		window2d->create(sf::VideoMode(*width, *height), _windowname, sf::Style::Default); //Nope, just a normal window.
	}

	closed = false; //Not closed.

	if (event) //If it already exists.
	{
		delete event; //Delete it.
	}
	event = new sf::Event; //Allocate memory for it.

	//TODO: Error checking?

	return true; //Success.
}

void Window::update()
{
	events.clear(); //Reset all events.

	if (!closed && window2d && event) //If the window is open, the window exists, and event exists.
	{
		while (window2d->pollEvent(*event)) //Poll events. //TODO: Give access to these events to the game.
		{
			if (event->type == sf::Event::Closed) //Check if the event is a window close.
			{
				window2d->close(); //Close the window.
				closed = true; //Update that it's closed.
			}
			else if (event->type == sf::Event::Resized && !closed && window2d) //Window resized.
			{
				window2d->setView(sf::View(sf::FloatRect(0, 0, event->size.width, event->size.height)));
			}
			else if (event->type == sf::Event::LostFocus)
			{
				focus = false; //Lost focus.
			}
			else if (event->type == sf::Event::GainedFocus)
			{
				focus = true; //Gained focus.
			}
			else
			{
				events.push_back(*event); //Save the event for later processing.
			}
		}

		if (!closed && window2d) //If the window was not closed.
		{
			window2d->display(); //Display the window.
			window2d->clear();
		}
	}
}

int Window::getWidth()
{
	if (!window2d)
	{
		std::cout << "\nNo 2d window, fool.\n";
		return -1;
	}

	if (!width || !delete_width) //If width does not exist or if it should not be deleted (meaning it's pointing to some other int).
	{
		width = new int; //Allocate it.
		delete_width = true; //It will be deleted.
	}

	sf::Vector2u vector = window2d->getSize(); //Get the size.

	*width = vector.x; //Set/update the width.

	return *width; //Return the width.
}

int Window::getHeight()
{
	if (!window2d)
	{
		std::cout << "\nNo, seriously, there is no 2d window! Why don't you understand?\n";
		return -1;
	}

	if (!height || !delete_height) //If height does not exist or if it should not be deleted (meaning it's pointing to some other int).
	{
		height = new int; //Allocate it.
		delete_height = true; //It will be deleted.
	}

	sf::Vector2u vector = window2d->getSize(); //Get the size of the window.

	*height = vector.y; //Set/update the height.

	return *height; //Return the height.
}

int Window::getX()
{
	return window2d->getPosition().x;
	//return (using_opengl) ? /*3d*/window3d->getPosition().x /*window*/ : /*2d*/ window2d->getPosition().x /*window*/;
}

int Window::getY()
{
	return window2d->getPosition().y;
	//return (using_opengl) ? /*3d*/window3d->getPosition().y /*window*/ : /*2d*/ window2d->getPosition().y /*window*/;
}

void Window::drawSprite(mgfx::d2d::Sprite &sprite)
{
	if (!window2d)
	{
		std::cout << "\nAnd just what are you trying to achieve with a window that does not exist?\n";
		return;
	}

	window2d->draw(*sprite.sprite); //Draw the sprite.
}

void Window::renderText(std::string text, int _x, int _y, int font_size, sf::Font &font)
{
	sf::Text _text(text, font);
	_text.setCharacterSize(font_size);
	_text.setPosition(_x, _y);

	if (!window2d)
	{
		std::cout << "\nAnd just what are you trying to achieve with a 2D window that does not exist?\n";
		return;
	}
	window2d->pushGLStates();
	window2d->draw(_text);
	window2d->popGLStates();
}

void Window::setFramerateLimit(int fps)
{
	//Set the frames per second rate for the window.
	if (!window2d)
	{
		std::cout << "I can't set the framerate limit -- window2d doesn't exist!\n"; //Error.
		return; //Abort this function.
	}

	window2d->setFramerateLimit(fps);
}

void Window::showMouseCursor(bool show)
{
	//Set the frames per second rate for the window.
	if (!window2d)
	{
		std::cout << "I can't show or hide the mouse cursor -- window2d doesn't exist!\n"; //Error.
		return; //Abort this function.
	}

	window2d->setMouseCursorVisible(show);
}

} //namespace mgfx

} //namespace GEngine.
<|endoftext|>


<commit_before>/*
    Copyright (c) 2006 Volker Krause <volker.krause@rwth-aachen.de>

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

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

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

#include "messagecollectionattribute.h"
#include "messagecollectionmodel.h"

#include <kdebug.h>
#include <klocale.h>

#include <QFont>

using namespace PIM;

PIM::MessageCollectionModel::MessageCollectionModel( QObject * parent ) :
    CollectionModel( parent )
{
}

int PIM::MessageCollectionModel::columnCount( const QModelIndex & parent ) const
{
  Q_UNUSED( parent );
  return 3;
}

QVariant PIM::MessageCollectionModel::data( const QModelIndex & index, int role ) const
{
  Collection *col = static_cast<Collection*>( index.internalPointer() );
  MessageCollectionAttribute *attr = col->attribute<MessageCollectionAttribute>();

  if ( role == Qt::DisplayRole && attr && ( index.column() == 1 || index.column() == 2 ) ) {
    int value = -1;
    switch ( index.column() ) {
      case 1: value = attr->unreadCount(); break;
      case 2: value = attr->count(); break;
    }
    if ( value < 0 )
      return QString();
    else if ( value == 0 )
      return QString( "-" );
    else
      return QString::number( value );
  }

  if ( role == Qt::TextAlignmentRole && ( index.column() == 1 || index.column() == 2 ) )
    return Qt::AlignRight;

  // ### that's wrong, we'll need a custom delegate anyway
  if ( role == Qt::FontRole && attr && attr->unreadCount() > 0 ) {
    QFont f;
    f.setBold( true );
    return f;
  }

  return CollectionModel::data( index, role );
}

QVariant PIM::MessageCollectionModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
    switch ( section ) {
      case 1: return i18n( "Unread" );
      case 2: return i18n( "Total" );
    }

  return CollectionModel::headerData( section, orientation, role );
}

#include "messagecollectionmodel.moc"
<commit_msg>fix crash<commit_after>/*
    Copyright (c) 2006 Volker Krause <volker.krause@rwth-aachen.de>

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

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

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

#include "messagecollectionattribute.h"
#include "messagecollectionmodel.h"

#include <kdebug.h>
#include <klocale.h>

#include <QFont>

using namespace PIM;

PIM::MessageCollectionModel::MessageCollectionModel( QObject * parent ) :
    CollectionModel( parent )
{
}

int PIM::MessageCollectionModel::columnCount( const QModelIndex & parent ) const
{
  Q_UNUSED( parent );
  return 3;
}

QVariant PIM::MessageCollectionModel::data( const QModelIndex & index, int role ) const
{
  if ( !index.isValid() )
    return QVariant();

  Collection *col = static_cast<Collection*>( index.internalPointer() );
  Q_ASSERT( col );
  MessageCollectionAttribute *attr = col->attribute<MessageCollectionAttribute>();

  if ( role == Qt::DisplayRole && attr && ( index.column() == 1 || index.column() == 2 ) ) {
    int value = -1;
    switch ( index.column() ) {
      case 1: value = attr->unreadCount(); break;
      case 2: value = attr->count(); break;
    }
    if ( value < 0 )
      return QString();
    else if ( value == 0 )
      return QString( "-" );
    else
      return QString::number( value );
  }

  if ( role == Qt::TextAlignmentRole && ( index.column() == 1 || index.column() == 2 ) )
    return Qt::AlignRight;

  // ### that's wrong, we'll need a custom delegate anyway
  if ( role == Qt::FontRole && attr && attr->unreadCount() > 0 ) {
    QFont f;
    f.setBold( true );
    return f;
  }

  return CollectionModel::data( index, role );
}

QVariant PIM::MessageCollectionModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
    switch ( section ) {
      case 1: return i18n( "Unread" );
      case 2: return i18n( "Total" );
    }

  return CollectionModel::headerData( section, orientation, role );
}

#include "messagecollectionmodel.moc"
<|endoftext|>


<commit_before>/*
 * This file is part of akregatorstorageexporter
 *
 * Copyright (C) 2009 Frank Osterfeld <osterfeld@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */
#include "feedstorage.h"
#include "storage.h"
#include "storagefactory.h"
#include "storagefactoryregistry.h"
#include "plugin.h"

#include <syndication/atom/constants.h>
#include <syndication/constants.h>

#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QIODevice>
#include <QStringList>
#include <QUrl>
#include <QXmlStreamWriter>
#include <QVariant>

#include <KComponentData>
#include <KGlobal>
#include <KPluginLoader>
#include <KService>
#include <KServiceTypeTrader>

#include <cassert>

using namespace Akregator;
using namespace Akregator::Backend;

namespace {
    static QString akregatorNamespace() {
        return QString::fromLatin1("http://akregator.kde.org/StorageExporter#");
    }

    enum TextMode {
        PlainText,
        Html
    };

    enum Status
    {
        Deleted=0x01,
        Trash=0x02,
        New=0x04,
        Read=0x08,
        Keep=0x10
    };

    class Element
    {
    public:
        Element( const QString& ns_, const QString& name_ ) : ns( ns_ ), name( name_ ), qualifiedName( ns + ':' + name )
        {
        }

        const QString ns;
        const QString name;
        const QString qualifiedName;

        void writeStartElement( QXmlStreamWriter& writer ) const
        {
            if ( !ns.isNull() )
                writer.writeStartElement( ns, name );
            else
                writer.writeStartElement( name );
        }

        void write( const QVariant& value , QXmlStreamWriter& writer, TextMode mode = PlainText ) const
        {
            const QVariant qv( value );
            Q_ASSERT( qv.canConvert( QVariant::String ) );
            const QString str = qv.toString();
            if ( str.isEmpty() )
                return;

            if ( ns.isEmpty() )
                writer.writeStartElement( name );
            else
                writer.writeStartElement( ns, name );
            if ( mode == Html )
            {
                writer.writeAttribute( "type", "html" );
            }
            writer.writeCharacters( str );
            writer.writeEndElement();
        }
    };

    struct Elements
    {
        Elements() : atomNS( Syndication::Atom::atom1Namespace() ),
                     akregatorNS(akregatorNamespace() ),
                     commentNS( Syndication::commentApiNamespace() ),
                     title( atomNS, "title" ),
                     summary( atomNS, "summary" ),
                     content( atomNS, "content" ),
                     link( atomNS, "link" ),
                     language( atomNS, "language" ),
                     feed( atomNS, "feed" ),
                     guid( atomNS, "id" ),
                     published( atomNS, "published" ),
                     updated( atomNS, "updated" ),
                     commentsCount( Syndication::slashNamespace(), "comments" ),
                     commentsFeed( commentNS, "commentRss" ),
                     commentPostUri( commentNS, "comment" ),
                     commentsLink( akregatorNS, "commentsLink" ),
                     status( akregatorNS, "status" ),
                     hash( akregatorNS, "hash" ),
                     guidIsHash( akregatorNS, "idIsHash" ),
                     name( atomNS, "name" ),
                     uri( atomNS, "uri" ),
                     email( atomNS, "email" ),
                     author( atomNS, "author" ),
                     category( atomNS, "category" ),
                     entry( atomNS, "entry" ),
                     itemProperties( akregatorNS, "itemProperties" ),
                     readStatus( akregatorNS, "readStatus" ),
                     deleted( akregatorNS, "deleted" ),
                     important( akregatorNS, "important" )

    {}
        const QString atomNS;
        const QString akregatorNS;
        const QString commentNS;
        const Element title;
        const Element summary;
        const Element content;
        const Element link;
        const Element language;
        const Element feed;
        const Element guid;
        const Element published;
        const Element updated;
        const Element commentsCount;
        const Element commentsFeed;
        const Element commentPostUri;
        const Element commentsLink;
        const Element status;
        const Element hash;
        const Element guidIsHash;
        const Element name;
        const Element uri;
        const Element email;
        const Element author;
        const Element category;
        const Element entry;
        const Element itemProperties;
        const Element readStatus;
        const Element deleted;
        const Element important;
        static const Elements instance;
    };

    const Elements Elements::instance;

    void writeAttributeIfNotEmpty( const QString& ns, const QString& element, const QVariant& value, QXmlStreamWriter& writer )
    {
        const QString text = value.toString();
        if ( text.isEmpty() )
            return;
        writer.writeAttribute( ns, element, text );
    }

    void writeAttributeIfNotEmpty( const QString& element, const QVariant& value, QXmlStreamWriter& writer )
    {
        const QString text = value.toString();
        if ( text.isEmpty() )
            return;
        writer.writeAttribute( element, text );
    }

    void writeEnclosure( const QString& url, const QString& type, int length, QXmlStreamWriter& writer )
    {
        Elements::instance.link.writeStartElement( writer );
        writer.writeAttribute( "rel", "enclosure" );
        writeAttributeIfNotEmpty( "href", url, writer );
        writeAttributeIfNotEmpty( "type", type, writer );
        if ( length > 0 )
            writer.writeAttribute( "length", QString::number( length ) );
        writer.writeEndElement();
    }

    void writeLink( const QString& url, QXmlStreamWriter& writer )
    {
        if ( url.isEmpty() )
            return;
        Elements::instance.link.writeStartElement( writer );
        writer.writeAttribute( "rel", "alternate" );
        writeAttributeIfNotEmpty( "href", url, writer );
        writer.writeEndElement();
    }

    void writeAuthor( const QString& name, const QString& uri, const QString& email, QXmlStreamWriter& writer )
    {
        if ( name.isEmpty() && uri.isEmpty() && email.isEmpty() )
            return;

        const QString atomNS = Syndication::Atom::atom1Namespace();
        Elements::instance.author.writeStartElement( writer );
        Elements::instance.name.write( name, writer );
        Elements::instance.uri.write( uri, writer );
        Elements::instance.email.write( email, writer );
        writer.writeEndElement(); // </author>
    }

    static void writeItem( FeedStorage* storage, const QString& guid, QXmlStreamWriter& writer ) {
        Elements::instance.entry.writeStartElement( writer );
        Elements::instance.guid.write( guid, writer );

        const uint published = storage->pubDate( guid );
        if ( published > 0 ) {
            const QString pdStr = QDateTime::fromTime_t( published ).toString( Qt::ISODate );
            Elements::instance.published.write( pdStr, writer );
        }

        const int status = storage->status( guid );

        Elements::instance.itemProperties.writeStartElement( writer );

        if ( status & Deleted ) {
            Elements::instance.deleted.write( QString::fromLatin1("true"), writer );
            writer.writeEndElement(); // </itemProperties>
            writer.writeEndElement(); // </item>
            return;
        }

        Elements::instance.hash.write( QString::number( storage->hash( guid ) ), writer );
        if ( storage->guidIsHash( guid ) )
            Elements::instance.guidIsHash.write( QString::fromLatin1("true"), writer );
        if ( status & New )
            Elements::instance.readStatus.write( QString::fromLatin1("new"), writer );
        else if ( ( status & Read) == 0 )
            Elements::instance.readStatus.write( QString::fromLatin1("unread"), writer );
        if ( status & Keep )
            Elements::instance.important.write( QString::fromLatin1("true"), writer );
        writer.writeEndElement(); // </itemProperties>

        Elements::instance.title.write( storage->title( guid ), writer, Html );
        writeLink( storage->guidIsPermaLink( guid ) ? guid :  storage->link( guid ), writer );

        Elements::instance.summary.write( storage->description( guid ), writer, Html );
        Elements::instance.content.write( storage->content( guid ), writer, Html );
        writeAuthor( storage->authorName( guid ),
                     storage->authorUri( guid ),
                     storage->authorEMail( guid ),
                     writer );

        if ( const int commentsCount = storage->comments( guid ) )
            Elements::instance.commentsCount.write( QString::number( commentsCount ), writer );

        Elements::instance.commentsLink.write( storage->commentsLink( guid ), writer );

        bool hasEnc = false;
        QString encUrl, encType;
        int encLength = 0;
        storage->enclosure( guid, hasEnc, encUrl, encType, encLength );
        if ( hasEnc )
            writeEnclosure( encUrl, encType, encLength, writer );
        writer.writeEndElement(); // </item>
    }

    static void serialize( FeedStorage* storage, QIODevice* device ) {
        assert( storage );
        assert( device );
        QXmlStreamWriter writer( device );
        writer.setAutoFormatting( true );
        writer.setAutoFormattingIndent( 2 );
        writer.writeStartDocument();

        Elements::instance.feed.writeStartElement( writer );

        writer.writeDefaultNamespace( Syndication::Atom::atom1Namespace() );
        writer.writeNamespace( Syndication::commentApiNamespace(), "comment" );
        writer.writeNamespace( akregatorNamespace(), "akregator" );
        writer.writeNamespace( Syndication::itunesNamespace(), "itunes" );

        Elements::instance.title.write( QString::fromLatin1("Akregator Export"), writer, Html );

        Q_FOREACH( const QString& i, storage->articles() )
            writeItem( storage, i, writer );
        writer.writeEndElement(); // </feed>
        writer.writeEndDocument();
    }

    static void serialize( Storage* storage, const QString& url, QIODevice* device ) {
        serialize( storage->archiveFor( url ), device );
    }

    static KService::List queryStoragePlugins() {
        return KServiceTypeTrader::self()->query( "Akregator/Plugin",
            QString::fromLatin1( "[X-KDE-akregator-framework-version] == %1 and [X-KDE-akregator-plugintype] == 'storage' and [X-KDE-akregator-rank] > 0" ).arg( QString::number( AKREGATOR_PLUGIN_INTERFACE_VERSION ) ) );
    }

    static Plugin* createFromService( const KService::Ptr& service )
    {
        KPluginLoader loader( *service );
        KPluginFactory* factory = loader.factory();
        if ( !factory ) {
            qCritical() << QString( " Could not create plugin factory for: %1\n"
                                    " Error message: %2" ).arg( service->library(), loader.errorString() );
            return 0;
        }
        return factory->create<Akregator::Plugin>();
    }

    static void printUsage() {

    }
}

int main( int argc, char** argv ) {
    KGlobal::setActiveComponent( KComponentData( "akregatorstorageexporter" ) );
    const QString backend = QString::fromLatin1( "metakit" );

    if ( argc < 2 ) {
        printUsage();
        return 1;
    }
    const QString url = QUrl::fromEncoded( argv[1] ).toString();

    Q_FOREACH( const KService::Ptr& i, queryStoragePlugins() )
        if ( Plugin* const plugin = createFromService( i ) )
            plugin->initialize();

    const StorageFactory* const storageFactory = StorageFactoryRegistry::self()->getFactory( backend );
    if ( !storageFactory ) {
        qCritical( "Could not create storage factory for %s.", qPrintable( backend ) );
        return 1;
    }

    Storage* const storage = storageFactory->createStorage( QStringList() );
    if ( !storage ) {
        qCritical( "Could not create storage object for %s.", qPrintable( backend ) );
        return 1;
    }

    QFile out;
    if ( !out.open( stdout, QIODevice::WriteOnly ) ) {
        qCritical( "Could not open stdout for writing: %s", qPrintable( out.errorString() ) );
        return 1;
    }

    serialize( storage, url, &out );
    return 0;
}
<commit_msg>decode from base64<commit_after>/*
 * This file is part of akregatorstorageexporter
 *
 * Copyright (C) 2009 Frank Osterfeld <osterfeld@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */
#include "feedstorage.h"
#include "storage.h"
#include "storagefactory.h"
#include "storagefactoryregistry.h"
#include "plugin.h"

#include <syndication/atom/constants.h>
#include <syndication/constants.h>

#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QIODevice>
#include <QStringList>
#include <QUrl>
#include <QXmlStreamWriter>
#include <QVariant>

#include <KComponentData>
#include <KGlobal>
#include <KPluginLoader>
#include <KService>
#include <KServiceTypeTrader>

#include <cassert>

using namespace Akregator;
using namespace Akregator::Backend;

namespace {
    static QString akregatorNamespace() {
        return QString::fromLatin1("http://akregator.kde.org/StorageExporter#");
    }

    enum TextMode {
        PlainText,
        Html
    };

    enum Status
    {
        Deleted=0x01,
        Trash=0x02,
        New=0x04,
        Read=0x08,
        Keep=0x10
    };

    class Element
    {
    public:
        Element( const QString& ns_, const QString& name_ ) : ns( ns_ ), name( name_ ), qualifiedName( ns + ':' + name )
        {
        }

        const QString ns;
        const QString name;
        const QString qualifiedName;

        void writeStartElement( QXmlStreamWriter& writer ) const
        {
            if ( !ns.isNull() )
                writer.writeStartElement( ns, name );
            else
                writer.writeStartElement( name );
        }

        void write( const QVariant& value , QXmlStreamWriter& writer, TextMode mode = PlainText ) const
        {
            const QVariant qv( value );
            Q_ASSERT( qv.canConvert( QVariant::String ) );
            const QString str = qv.toString();
            if ( str.isEmpty() )
                return;

            if ( ns.isEmpty() )
                writer.writeStartElement( name );
            else
                writer.writeStartElement( ns, name );
            if ( mode == Html )
            {
                writer.writeAttribute( "type", "html" );
            }
            writer.writeCharacters( str );
            writer.writeEndElement();
        }
    };

    struct Elements
    {
        Elements() : atomNS( Syndication::Atom::atom1Namespace() ),
                     akregatorNS(akregatorNamespace() ),
                     commentNS( Syndication::commentApiNamespace() ),
                     title( atomNS, "title" ),
                     summary( atomNS, "summary" ),
                     content( atomNS, "content" ),
                     link( atomNS, "link" ),
                     language( atomNS, "language" ),
                     feed( atomNS, "feed" ),
                     guid( atomNS, "id" ),
                     published( atomNS, "published" ),
                     updated( atomNS, "updated" ),
                     commentsCount( Syndication::slashNamespace(), "comments" ),
                     commentsFeed( commentNS, "commentRss" ),
                     commentPostUri( commentNS, "comment" ),
                     commentsLink( akregatorNS, "commentsLink" ),
                     status( akregatorNS, "status" ),
                     hash( akregatorNS, "hash" ),
                     guidIsHash( akregatorNS, "idIsHash" ),
                     name( atomNS, "name" ),
                     uri( atomNS, "uri" ),
                     email( atomNS, "email" ),
                     author( atomNS, "author" ),
                     category( atomNS, "category" ),
                     entry( atomNS, "entry" ),
                     itemProperties( akregatorNS, "itemProperties" ),
                     readStatus( akregatorNS, "readStatus" ),
                     deleted( akregatorNS, "deleted" ),
                     important( akregatorNS, "important" )

    {}
        const QString atomNS;
        const QString akregatorNS;
        const QString commentNS;
        const Element title;
        const Element summary;
        const Element content;
        const Element link;
        const Element language;
        const Element feed;
        const Element guid;
        const Element published;
        const Element updated;
        const Element commentsCount;
        const Element commentsFeed;
        const Element commentPostUri;
        const Element commentsLink;
        const Element status;
        const Element hash;
        const Element guidIsHash;
        const Element name;
        const Element uri;
        const Element email;
        const Element author;
        const Element category;
        const Element entry;
        const Element itemProperties;
        const Element readStatus;
        const Element deleted;
        const Element important;
        static const Elements instance;
    };

    const Elements Elements::instance;

    void writeAttributeIfNotEmpty( const QString& ns, const QString& element, const QVariant& value, QXmlStreamWriter& writer )
    {
        const QString text = value.toString();
        if ( text.isEmpty() )
            return;
        writer.writeAttribute( ns, element, text );
    }

    void writeAttributeIfNotEmpty( const QString& element, const QVariant& value, QXmlStreamWriter& writer )
    {
        const QString text = value.toString();
        if ( text.isEmpty() )
            return;
        writer.writeAttribute( element, text );
    }

    void writeEnclosure( const QString& url, const QString& type, int length, QXmlStreamWriter& writer )
    {
        Elements::instance.link.writeStartElement( writer );
        writer.writeAttribute( "rel", "enclosure" );
        writeAttributeIfNotEmpty( "href", url, writer );
        writeAttributeIfNotEmpty( "type", type, writer );
        if ( length > 0 )
            writer.writeAttribute( "length", QString::number( length ) );
        writer.writeEndElement();
    }

    void writeLink( const QString& url, QXmlStreamWriter& writer )
    {
        if ( url.isEmpty() )
            return;
        Elements::instance.link.writeStartElement( writer );
        writer.writeAttribute( "rel", "alternate" );
        writeAttributeIfNotEmpty( "href", url, writer );
        writer.writeEndElement();
    }

    void writeAuthor( const QString& name, const QString& uri, const QString& email, QXmlStreamWriter& writer )
    {
        if ( name.isEmpty() && uri.isEmpty() && email.isEmpty() )
            return;

        const QString atomNS = Syndication::Atom::atom1Namespace();
        Elements::instance.author.writeStartElement( writer );
        Elements::instance.name.write( name, writer );
        Elements::instance.uri.write( uri, writer );
        Elements::instance.email.write( email, writer );
        writer.writeEndElement(); // </author>
    }

    static void writeItem( FeedStorage* storage, const QString& guid, QXmlStreamWriter& writer ) {
        Elements::instance.entry.writeStartElement( writer );
        Elements::instance.guid.write( guid, writer );

        const uint published = storage->pubDate( guid );
        if ( published > 0 ) {
            const QString pdStr = QDateTime::fromTime_t( published ).toString( Qt::ISODate );
            Elements::instance.published.write( pdStr, writer );
        }

        const int status = storage->status( guid );

        Elements::instance.itemProperties.writeStartElement( writer );

        if ( status & Deleted ) {
            Elements::instance.deleted.write( QString::fromLatin1("true"), writer );
            writer.writeEndElement(); // </itemProperties>
            writer.writeEndElement(); // </item>
            return;
        }

        Elements::instance.hash.write( QString::number( storage->hash( guid ) ), writer );
        if ( storage->guidIsHash( guid ) )
            Elements::instance.guidIsHash.write( QString::fromLatin1("true"), writer );
        if ( status & New )
            Elements::instance.readStatus.write( QString::fromLatin1("new"), writer );
        else if ( ( status & Read) == 0 )
            Elements::instance.readStatus.write( QString::fromLatin1("unread"), writer );
        if ( status & Keep )
            Elements::instance.important.write( QString::fromLatin1("true"), writer );
        writer.writeEndElement(); // </itemProperties>

        Elements::instance.title.write( storage->title( guid ), writer, Html );
        writeLink( storage->guidIsPermaLink( guid ) ? guid :  storage->link( guid ), writer );

        Elements::instance.summary.write( storage->description( guid ), writer, Html );
        Elements::instance.content.write( storage->content( guid ), writer, Html );
        writeAuthor( storage->authorName( guid ),
                     storage->authorUri( guid ),
                     storage->authorEMail( guid ),
                     writer );

        if ( const int commentsCount = storage->comments( guid ) )
            Elements::instance.commentsCount.write( QString::number( commentsCount ), writer );

        Elements::instance.commentsLink.write( storage->commentsLink( guid ), writer );

        bool hasEnc = false;
        QString encUrl, encType;
        int encLength = 0;
        storage->enclosure( guid, hasEnc, encUrl, encType, encLength );
        if ( hasEnc )
            writeEnclosure( encUrl, encType, encLength, writer );
        writer.writeEndElement(); // </item>
    }

    static void serialize( FeedStorage* storage, QIODevice* device ) {
        assert( storage );
        assert( device );
        QXmlStreamWriter writer( device );
        writer.setAutoFormatting( true );
        writer.setAutoFormattingIndent( 2 );
        writer.writeStartDocument();

        Elements::instance.feed.writeStartElement( writer );

        writer.writeDefaultNamespace( Syndication::Atom::atom1Namespace() );
        writer.writeNamespace( Syndication::commentApiNamespace(), "comment" );
        writer.writeNamespace( akregatorNamespace(), "akregator" );
        writer.writeNamespace( Syndication::itunesNamespace(), "itunes" );

        Elements::instance.title.write( QString::fromLatin1("Akregator Export"), writer, Html );

        Q_FOREACH( const QString& i, storage->articles() )
            writeItem( storage, i, writer );
        writer.writeEndElement(); // </feed>
        writer.writeEndDocument();
    }

    static void serialize( Storage* storage, const QString& url, QIODevice* device ) {
        serialize( storage->archiveFor( url ), device );
    }

    static KService::List queryStoragePlugins() {
        return KServiceTypeTrader::self()->query( "Akregator/Plugin",
            QString::fromLatin1( "[X-KDE-akregator-framework-version] == %1 and [X-KDE-akregator-plugintype] == 'storage' and [X-KDE-akregator-rank] > 0" ).arg( QString::number( AKREGATOR_PLUGIN_INTERFACE_VERSION ) ) );
    }

    static Plugin* createFromService( const KService::Ptr& service )
    {
        KPluginLoader loader( *service );
        KPluginFactory* factory = loader.factory();
        if ( !factory ) {
            qCritical() << QString( " Could not create plugin factory for: %1\n"
                                    " Error message: %2" ).arg( service->library(), loader.errorString() );
            return 0;
        }
        return factory->create<Akregator::Plugin>();
    }

    static void printUsage() {

    }
}

int main( int argc, char** argv ) {
    KGlobal::setActiveComponent( KComponentData( "akregatorstorageexporter" ) );
    const QString backend = QString::fromLatin1( "metakit" );

    if ( argc < 2 ) {
        printUsage();
        return 1;
    }
    const QString url = QUrl::fromEncoded( QByteArray::fromBase64( argv[1] ) ).toString();

    Q_FOREACH( const KService::Ptr& i, queryStoragePlugins() )
        if ( Plugin* const plugin = createFromService( i ) )
            plugin->initialize();

    const StorageFactory* const storageFactory = StorageFactoryRegistry::self()->getFactory( backend );
    if ( !storageFactory ) {
        qCritical( "Could not create storage factory for %s.", qPrintable( backend ) );
        return 1;
    }

    Storage* const storage = storageFactory->createStorage( QStringList() );
    if ( !storage ) {
        qCritical( "Could not create storage object for %s.", qPrintable( backend ) );
        return 1;
    }

    QFile out;
    if ( !out.open( stdout, QIODevice::WriteOnly ) ) {
        qCritical( "Could not open stdout for writing: %s", qPrintable( out.errorString() ) );
        return 1;
    }

    serialize( storage, url, &out );
    return 0;
}
<|endoftext|>