<commit_before>#include "bigentry.hpp"
#include "log.hpp"
#include <algorithm>
#include <cstdio>
#include <cctype>

#include FILESYSTEM_HEADER
namespace fs = FILESYSTEM_NAMESPACE;

namespace OpenBFME {

uint32_t readUInt32(FILE* file){
    uint8_t val[4];
    fread(val, 1, 4, file);
    return val[0] << 24 | val[1] << 16 | val[2] << 8 | val[3];
}

void writeUInt32(FILE* file, uint32_t value){
    uint8_t val[] { uint8_t(value >> 24), uint8_t(value >> 16), uint8_t(value >> 8), uint8_t(value) };
    fwrite(val, 1, 4, file);
}

string readString(FILE* file, uint32_t limit, char terminator = '\0'){
    string data;
    while(uint32_t(ftell(file)) < limit){
        character c = fgetc(file);
        if(c == '\0' || c == terminator || ferror(file) || feof(file)){
            break;
        }
        data += c;
    }
    return data;
}

BigArchive::BigArchive(const string &filename) : archiveFilename(fs::path(filename).generic_string()), file(nullptr) { }

BigArchive::~BigArchive() {
    close();
}

bool BigArchive::readHeader() {
    if (fs::is_directory(fs::path(archiveFilename))) {
        backend = Folder;

        /* Make sure the archiveFilename ends with a '/' */
        if (archiveFilename.back() != '/')
            archiveFilename += '/';

        for (fs::recursive_directory_iterator dir(archiveFilename), end; dir != end; ++dir) {
            const fs::path& currentPath = dir->path();
            if (fs::is_regular_file(currentPath)) {
                string filename = currentPath.generic_string();

                /* Check to see if the filename contains the parent path, and if so remove it. */
                if (filename.compare(0, archiveFilename.size(), archiveFilename, 0, archiveFilename.size()) == 0)
                    filename.erase(0, archiveFilename.size());
                else
                    /* Otherwise fall back to removing the first element in the path. */
                    filename.erase(0, filename.find_first_of('/') + 1);

                uint32_t filesize = uint32_t(fs::file_size(currentPath));

                entries.emplace(*this, 0, filesize, filename);
                Log::debug("File path: \"%s\" length: %#08x", filename, filesize);
            }
        }

        return true;
    }

    backend = BigFile;

    if (!open())
        return false;

    /* The first 4 bytes shoule be either "BIG4" or "BIGF", quit if they aren't. */
    character id[4] = {0};
    fread(id, 1, 4, file);

    if (id[0] != 'B' || id[1] != 'I' || id[2] != 'G' || (id[3] != '4' && id[3] != 'F')) {
        Log::error("File \"%s\" is not a valid big file!");
        close();
        return false;
    }

    fseek(file, 8, SEEK_SET);

    uint32_t fileCount = readUInt32(file);
    uint32_t headerEnd = readUInt32(file);

    Log::info("File Count: %u Header End: %#08x", fileCount, headerEnd);

    /* Read the individual file information into entries. */
    for (uint32_t f = 0; f < fileCount; ++f) {
        uint32_t start = readUInt32(file);
        uint32_t end   = start + readUInt32(file);
        string path = readString(file, headerEnd);
        std::replace(path.begin(), path.end(), '\\', '/');

        entries.emplace(*this, start, end, path);

        Log::debug("File #%04d start: %#08x end: %#08x path: \"%s\"", f + 1, start, end, path);
    }

    if (uint32_t(ftell(file)) > headerEnd)
        Log::warning("Reading of file info passed through end of header.");

    return true;
}

bool BigArchive::open() {
    /* File is already open. */
    if (file != nullptr)
        return true;

    /* If it's a .big archive we can try to open the file here. */
    if (backend == BigFile)
        file = fopen(archiveFilename.c_str(), "rb");

    return file != nullptr;
}

void BigArchive::close() {
    if (file != nullptr) {
        fclose(file);
        file = nullptr;
    }
    currentEntry = nullptr;
}

bool BigArchive::openEntry(const BigEntry& entry) {
    entry.resetLineNumber();

    switch (backend) {
    case BigFile:
        if (open()) {
            fseek(file, entry.start, SEEK_SET);
            currentEntry = &entry;
            return true;
        }
        break;
    case Folder:
        close();
        file = fopen((archiveFilename + '/' + entry.filename).c_str(), "rb");
        if (file != nullptr) {
            currentEntry = &entry;
            return true;
        }
        break;
    }
    return false;
}

const BigEntry* BigArchive::openFile(const string &filename) {
    /* Find an entry with the filename. */
    auto entry = std::find_if(begin(), end(), [&](const BigEntry& it){ return it.filename == filename; });

    /* If one was found, open it and return it's address. */
    if (entry != end() && openEntry(*entry))
        return &(*entry);

    /* Nothing was found, or it failed to open. */
    return nullptr;
}

character BigArchive::getChar(const BigEntry &entry) {
    /* The archive needs to be open, and we need to be inside the correct file. */
    if (!open() || eof(entry))
        return 0;

    character ch = fgetc(file);

    /* Tell the entry we have a newline. */
    if (ch == '\n')
        entry.incrementLineNumber();

    return ch;
}

void BigArchive::ungetChar(const BigEntry &entry, character c) {
    if (tell(entry) > 0) {
        ungetc(c, file);

        /* If it was a newline we need the line number to go back to how it was before getting the character. */
        if (c == '\n')
            entry.decrementLineNumber();
    }
}

bool BigArchive::seek(const BigEntry &entry, uint32_t pos) {
    /* If the entry isn't the current one try opening it. */
    if (&entry != currentEntry && !openEntry(entry))
        return false;

    /* Invalidate the stored current line number. */
    entry.invalidateLineNumber();

    pos += entry.start;

    /* Make sure it's inside the file before seeking. */
    if (pos < entry.end) {
        fseek(file, pos, SEEK_SET);
        return true;
    }
    return false;
}

uint32_t BigArchive::tell(const BigEntry &entry) {
    /* This only works if the file is already current. */
    if (&entry != currentEntry)
        return 0;

    uint32_t pos = ftell(file);
    if (pos <= entry.start)
        return 0;

    if (pos > entry.end)
        return entry.end - entry.start;

    return pos - entry.start;
}

bool BigArchive::eof(const BigEntry &entry) {
    if (&entry != currentEntry)
        return true;

    uint32_t cpos = ftell(file);
    return cpos < entry.start || cpos >= entry.end;
}

bool BigArchive::extract(const BigEntry& entry, const string &directory, bool fullPath, bool ignore, bool overwrite) {
    if (!openEntry(entry)) {
        Log::error("Error opening entry \"%s\"!", entry.filename);
        return false;
    }

    fs::path path = entry.filename;

    path = fs::canonical(fs::path(directory)) / (fullPath ? path : path.filename());

    fs::create_directories(path.parent_path());

    if (fs::exists(path)) {
        /* The option to ignore existing files takes precedence over overwriting them. */
        if (ignore) {
            Log::info("Skipping existing file: \"%s\".", entry.filename);

            return true;
        } else if (!overwrite) {
            character c = 0;

            while (c != 'n' && c != 'y') {
                Log::info("Overwrite existing file \"%s\"? [Y/N]:", path.generic_string());

                c = std::tolower(std::getchar());

                if (c == '\n') continue;

                /* Only take one letter input. */
                if (std::getchar() != '\n') {
                    c = 0;
                    while(std::getchar() != '\n') continue;
                }
            }

            if (c == 'n') {
                Log::info("Skipping \"%s\".", entry.filename);

                return true;
            }
        }
        Log::info("Overwriting \"%s\"...", entry.filename);
    } else {
        Log::info("Extracting to \"%s\"...", path.generic_string());
    }

    FILE* out = fopen(path.generic_string().c_str(), "wb");

    if (out == nullptr) {
        Log::error("Unable to create file \"%s\"!", path.generic_string());
        return false;
    }
    uint32_t length = entry.end - entry.start;
    uint8_t *buffer = new uint8_t[length];

    fread(buffer, 1, length, file);
    fwrite(buffer, 1, length, out);

    fclose(out);
    delete[] buffer;

    Log::info("File successfully extracted.");

    return true;
}

bool BigArchive::extractAll(const string &directory, bool ignore, bool overwrite) {
    fs::create_directories(fs::path(directory));

    for (auto &entry : entries)
        if (!extract(entry, directory, true, ignore, overwrite))
            return false;

    return true;
}

bool BigArchive::writeBig(const EntryList& entries, const string& filename) {
    Log::info("Preparing to write %d files to \"%s\"", entries.size(), filename);

    /* 8 bytes for every entry + 20 at the start and end. */
    uint32_t headerLength = uint32_t(entries.size() * 8) + 20;

    /* Add the length of the filenames to headerLength. */
    for (auto &entry : entries)
        headerLength += uint32_t(entry.filename.size()) + 1;

    Log::info("Calculated header length: %#08x", headerLength);

    FILE* file = fopen(filename.c_str(), "wb");

    if (file == nullptr) {
        Log::error("Unable to open \"%s\" for writing!", filename);
        return false;
    }

    /* Either that or "BIG4". I'm not sure which... */
    fwrite("BIGF", 1, 4, file);

    writeUInt32(file, 0);

    /* General info about the file. */
    writeUInt32(file, uint32_t(entries.size()));
    writeUInt32(file, headerLength);

    /* Put the first file one byte after the end of the header. */
    uint32_t lastEnd = headerLength + 1;

    /* Write all the file information. */
    for (auto &entry : entries) {
        uint32_t fileLength = entry.end - entry.start;
        writeUInt32(file, lastEnd);
        writeUInt32(file, fileLength);

        Log::info("Writing header for \"%s\". Start: %#08x Length: %#08x", entry.filename, lastEnd, fileLength);

        /* Write the filename and terminating null character. */
        fputs(entry.filename.c_str(), file);
        fputc('\0', file);

        /* So we know where the next file will be written. */
        lastEnd += fileLength;
    }

    /* What exactly is this? */
    fputs("L253", file);

    if (uint32_t(ftell(file)) != headerLength) {
        Log::error("Calculated header length was incorrect! Calculated: %#08x Got: %#08x", headerLength, ftell(file));
        return false;
    }

    /* One more byte until where we start the first file... */
    fputc('\0', file);

    /* Write all the files. */
    for (auto &entry : entries) {
        Log::info("Writing file \"%s\".", entry.filename);
        entry.seek(0);

        for (character c; !entry.eof();) {
            c = entry.getChar();
            fwrite(&c, sizeof(character), 1, file);
        }
    }

    fclose(file);

    Log::info("Finished writing to \"%s\".", filename);

    return true;
}

bool BigArchive::writeBig(const string& filename) {
    /* We only do this on a folder backend, because why would you do it on a .big? */
    if (backend == Folder) {
        return writeBig(entries, filename);
    }
    return false;
}

BigArchive::EntryList::const_iterator BigArchive::begin() {
    return entries.cbegin();
}

BigArchive::EntryList::const_iterator BigArchive::end() {
    return entries.cend();
}

}
<commit_msg>Fixed getWord() hanging when eof of archive is reached.<commit_after>#include "bigentry.hpp"
#include "log.hpp"
#include <algorithm>
#include <cstdio>
#include <cctype>

#include FILESYSTEM_HEADER
namespace fs = FILESYSTEM_NAMESPACE;

namespace OpenBFME {

uint32_t readUInt32(FILE* file){
    uint8_t val[4];
    fread(val, 1, 4, file);
    return val[0] << 24 | val[1] << 16 | val[2] << 8 | val[3];
}

void writeUInt32(FILE* file, uint32_t value){
    uint8_t val[] { uint8_t(value >> 24), uint8_t(value >> 16), uint8_t(value >> 8), uint8_t(value) };
    fwrite(val, 1, 4, file);
}

string readString(FILE* file, uint32_t limit, char terminator = '\0'){
    string data;
    while(uint32_t(ftell(file)) < limit){
        character c = fgetc(file);
        if(c == '\0' || c == terminator || ferror(file) || feof(file)){
            break;
        }
        data += c;
    }
    return data;
}

BigArchive::BigArchive(const string &filename) : archiveFilename(fs::path(filename).generic_string()), file(nullptr) { }

BigArchive::~BigArchive() {
    close();
}

bool BigArchive::readHeader() {
    if (fs::is_directory(fs::path(archiveFilename))) {
        backend = Folder;

        /* Make sure the archiveFilename ends with a '/' */
        if (archiveFilename.back() != '/')
            archiveFilename += '/';

        for (fs::recursive_directory_iterator dir(archiveFilename), end; dir != end; ++dir) {
            const fs::path& currentPath = dir->path();
            if (fs::is_regular_file(currentPath)) {
                string filename = currentPath.generic_string();

                /* Check to see if the filename contains the parent path, and if so remove it. */
                if (filename.compare(0, archiveFilename.size(), archiveFilename, 0, archiveFilename.size()) == 0)
                    filename.erase(0, archiveFilename.size());
                else
                    /* Otherwise fall back to removing the first element in the path. */
                    filename.erase(0, filename.find_first_of('/') + 1);

                uint32_t filesize = uint32_t(fs::file_size(currentPath));

                entries.emplace(*this, 0, filesize, filename);
                Log::debug("File path: \"%s\" length: %#08x", filename, filesize);
            }
        }

        return true;
    }

    backend = BigFile;

    if (!open())
        return false;

    /* The first 4 bytes shoule be either "BIG4" or "BIGF", quit if they aren't. */
    character id[4] = {0};
    fread(id, 1, 4, file);

    if (id[0] != 'B' || id[1] != 'I' || id[2] != 'G' || (id[3] != '4' && id[3] != 'F')) {
        Log::error("File \"%s\" is not a valid big file!");
        close();
        return false;
    }

    fseek(file, 8, SEEK_SET);

    uint32_t fileCount = readUInt32(file);
    uint32_t headerEnd = readUInt32(file);

    Log::info("File Count: %u Header End: %#08x", fileCount, headerEnd);

    /* Read the individual file information into entries. */
    for (uint32_t f = 0; f < fileCount; ++f) {
        uint32_t start = readUInt32(file);
        uint32_t end   = start + readUInt32(file);
        string path = readString(file, headerEnd);
        std::replace(path.begin(), path.end(), '\\', '/');

        entries.emplace(*this, start, end, path);

        Log::debug("File #%04d start: %#08x end: %#08x path: \"%s\"", f + 1, start, end, path);
    }

    if (uint32_t(ftell(file)) > headerEnd)
        Log::warning("Reading of file info passed through end of header.");

    return true;
}

bool BigArchive::open() {
    /* File is already open. */
    if (file != nullptr)
        return true;

    /* If it's a .big archive we can try to open the file here. */
    if (backend == BigFile)
        file = fopen(archiveFilename.c_str(), "rb");

    return file != nullptr;
}

void BigArchive::close() {
    if (file != nullptr) {
        fclose(file);
        file = nullptr;
    }
    currentEntry = nullptr;
}

bool BigArchive::openEntry(const BigEntry& entry) {
    entry.resetLineNumber();

    switch (backend) {
    case BigFile:
        if (open()) {
            fseek(file, entry.start, SEEK_SET);
            currentEntry = &entry;
            return true;
        }
        break;
    case Folder:
        close();
        file = fopen((archiveFilename + '/' + entry.filename).c_str(), "rb");
        if (file != nullptr) {
            currentEntry = &entry;
            return true;
        }
        break;
    }
    return false;
}

const BigEntry* BigArchive::openFile(const string &filename) {
    /* Find an entry with the filename. */
    auto entry = std::find_if(begin(), end(), [&](const BigEntry& it){ return it.filename == filename; });

    /* If one was found, open it and return it's address. */
    if (entry != end() && openEntry(*entry))
        return &(*entry);

    /* Nothing was found, or it failed to open. */
    return nullptr;
}

character BigArchive::getChar(const BigEntry &entry) {
    /* The archive needs to be open, and we need to be inside the correct file. */
    if (!open() || eof(entry))
        return 0;

    character ch = fgetc(file);

    /* Tell the entry we have a newline. */
    if (ch == '\n')
        entry.incrementLineNumber();

    return ch;
}

void BigArchive::ungetChar(const BigEntry &entry, character c) {
    if (tell(entry) > 0) {
        ungetc(c, file);

        /* If it was a newline we need the line number to go back to how it was before getting the character. */
        if (c == '\n')
            entry.decrementLineNumber();
    }
}

bool BigArchive::seek(const BigEntry &entry, uint32_t pos) {
    /* If the entry isn't the current one try opening it. */
    if (&entry != currentEntry && !openEntry(entry))
        return false;

    /* Invalidate the stored current line number. */
    entry.invalidateLineNumber();

    pos += entry.start;

    /* Make sure it's inside the file before seeking. */
    if (pos < entry.end) {
        fseek(file, pos, SEEK_SET);
        return true;
    }
    return false;
}

uint32_t BigArchive::tell(const BigEntry &entry) {
    /* This only works if the file is already current. */
    if (&entry != currentEntry)
        return 0;

    uint32_t pos = ftell(file);
    if (pos <= entry.start)
        return 0;

    if (pos > entry.end)
        return entry.end - entry.start;

    return pos - entry.start;
}

bool BigArchive::eof(const BigEntry &entry) {
    if (&entry != currentEntry || feof(file))
        return true;

    uint32_t cpos = ftell(file);
    return cpos < entry.start || cpos >= entry.end;
}

bool BigArchive::extract(const BigEntry& entry, const string &directory, bool fullPath, bool ignore, bool overwrite) {
    if (!openEntry(entry)) {
        Log::error("Error opening entry \"%s\"!", entry.filename);
        return false;
    }

    fs::path path = entry.filename;

    path = fs::canonical(fs::path(directory)) / (fullPath ? path : path.filename());

    fs::create_directories(path.parent_path());

    if (fs::exists(path)) {
        /* The option to ignore existing files takes precedence over overwriting them. */
        if (ignore) {
            Log::info("Skipping existing file: \"%s\".", entry.filename);

            return true;
        } else if (!overwrite) {
            character c = 0;

            while (c != 'n' && c != 'y') {
                Log::info("Overwrite existing file \"%s\"? [Y/N]:", path.generic_string());

                c = std::tolower(std::getchar());

                if (c == '\n') continue;

                /* Only take one letter input. */
                if (std::getchar() != '\n') {
                    c = 0;
                    while(std::getchar() != '\n') continue;
                }
            }

            if (c == 'n') {
                Log::info("Skipping \"%s\".", entry.filename);

                return true;
            }
        }
        Log::info("Overwriting \"%s\"...", entry.filename);
    } else {
        Log::info("Extracting to \"%s\"...", path.generic_string());
    }

    FILE* out = fopen(path.generic_string().c_str(), "wb");

    if (out == nullptr) {
        Log::error("Unable to create file \"%s\"!", path.generic_string());
        return false;
    }
    uint32_t length = entry.end - entry.start;
    uint8_t *buffer = new uint8_t[length];

    fread(buffer, 1, length, file);
    fwrite(buffer, 1, length, out);

    fclose(out);
    delete[] buffer;

    Log::info("File successfully extracted.");

    return true;
}

bool BigArchive::extractAll(const string &directory, bool ignore, bool overwrite) {
    fs::create_directories(fs::path(directory));

    for (auto &entry : entries)
        if (!extract(entry, directory, true, ignore, overwrite))
            return false;

    return true;
}

bool BigArchive::writeBig(const EntryList& entries, const string& filename) {
    Log::info("Preparing to write %d files to \"%s\"", entries.size(), filename);

    /* 8 bytes for every entry + 20 at the start and end. */
    uint32_t headerLength = uint32_t(entries.size() * 8) + 20;

    /* Add the length of the filenames to headerLength. */
    for (auto &entry : entries)
        headerLength += uint32_t(entry.filename.size()) + 1;

    Log::info("Calculated header length: %#08x", headerLength);

    FILE* file = fopen(filename.c_str(), "wb");

    if (file == nullptr) {
        Log::error("Unable to open \"%s\" for writing!", filename);
        return false;
    }

    /* Either that or "BIG4". I'm not sure which... */
    fwrite("BIGF", 1, 4, file);

    writeUInt32(file, 0);

    /* General info about the file. */
    writeUInt32(file, uint32_t(entries.size()));
    writeUInt32(file, headerLength);

    /* Put the first file one byte after the end of the header. */
    uint32_t lastEnd = headerLength + 1;

    /* Write all the file information. */
    for (auto &entry : entries) {
        uint32_t fileLength = entry.end - entry.start;
        writeUInt32(file, lastEnd);
        writeUInt32(file, fileLength);

        Log::info("Writing header for \"%s\". Start: %#08x Length: %#08x", entry.filename, lastEnd, fileLength);

        /* Write the filename and terminating null character. */
        fputs(entry.filename.c_str(), file);
        fputc('\0', file);

        /* So we know where the next file will be written. */
        lastEnd += fileLength;
    }

    /* What exactly is this? */
    fputs("L253", file);

    if (uint32_t(ftell(file)) != headerLength) {
        Log::error("Calculated header length was incorrect! Calculated: %#08x Got: %#08x", headerLength, ftell(file));
        return false;
    }

    /* One more byte until where we start the first file... */
    fputc('\0', file);

    /* Write all the files. */
    for (auto &entry : entries) {
        Log::info("Writing file \"%s\".", entry.filename);
        entry.seek(0);

        for (character c; !entry.eof();) {
            c = entry.getChar();
            fwrite(&c, sizeof(character), 1, file);
        }
    }

    fclose(file);

    Log::info("Finished writing to \"%s\".", filename);

    return true;
}

bool BigArchive::writeBig(const string& filename) {
    /* We only do this on a folder backend, because why would you do it on a .big? */
    if (backend == Folder) {
        return writeBig(entries, filename);
    }
    return false;
}

BigArchive::EntryList::const_iterator BigArchive::begin() {
    return entries.cbegin();
}

BigArchive::EntryList::const_iterator BigArchive::end() {
    return entries.cend();
}

}
<|endoftext|>


<commit_before>/*
* Certificate Store
* (C) 1999-2019 Jack Lloyd
* (C) 2019      René Meusel
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/build.h>

#include <algorithm>
#include <array>

#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>

#include <botan/assert.h>
#include <botan/ber_dec.h>
#include <botan/certstor_macos.h>
#include <botan/data_src.h>
#include <botan/der_enc.h>
#include <botan/exceptn.h>
#include <botan/x509_dn.h>

namespace Botan {

namespace {

/**
 * Abstract RAII wrapper for CFTypeRef-style object handles
 * All of those xxxRef types are eventually typedefs to void*
 */
template<typename T>
class scoped_CFType
   {
   public:
      explicit scoped_CFType(T value)
         : m_value(value)
         {
         }

      scoped_CFType(const scoped_CFType<T>& rhs) = delete;
      scoped_CFType(scoped_CFType<T>&& rhs) :
         m_value(std::move(rhs.m_value))
         {
         rhs.m_value = nullptr;
         }

      ~scoped_CFType()
         {
         if(m_value)
            {
            CFRelease(m_value);
            }
         }

      operator bool() const { return m_value != nullptr; }

      void assign(T value)
         {
         BOTAN_ASSERT(m_value == nullptr, "scoped_CFType was not set yet");
         m_value = value;
         }

      T& get() { return m_value; }
      const T& get() const { return m_value; }

   private:
      T m_value;
   };

/**
 * Apple's DN parser "normalizes" ASN1 'PrintableString' into upper-case values
 * and strips leading, trailing as well as multiple white spaces.
 * See: opensource.apple.com/source/Security/Security-55471/sec/Security/SecCertificate.c.auto.html
 */
X509_DN normalize(const X509_DN& dn)
   {
   X509_DN result;

   for(const auto& rdn : dn.dn_info())
      {
      // TODO: C++14 - use std::get<ASN1_String>(), resp. std::get<OID>()
      const auto oid = rdn.first;
      auto str = rdn.second;

      if(str.tagging() == ASN1_Tag::PRINTABLE_STRING)
         {
         std::string normalized;
         normalized.reserve(str.value().size());
         for(const char c : str.value())
            {
            if(c != ' ')
               {
               // store all 'normal' characters as upper case
               normalized.push_back(::toupper(c));
               }
            else if(!normalized.empty() && normalized.back() != ' ')
               {
               // remove leading and squash multiple white spaces
               normalized.push_back(c);
               }
            }

         if(normalized.back() == ' ')
            {
            // remove potential remaining single trailing white space char
            normalized.erase(normalized.end() - 1);
            }

         str = ASN1_String(normalized, str.tagging());
         }

      result.add_attribute(oid, str);
      }

   return result;
   }

std::string to_string(const CFStringRef cfstring)
   {
   const char* ccstr = CFStringGetCStringPtr(cfstring, kCFStringEncodingUTF8);

   if(ccstr != nullptr)
      {
      return std::string(ccstr);
      }

   auto utf16_pairs    = CFStringGetLength(cfstring);
   auto max_utf8_bytes = CFStringGetMaximumSizeForEncoding(utf16_pairs, kCFStringEncodingUTF8);

   std::vector<char> cstr(max_utf8_bytes, '\0');
   auto result = CFStringGetCString(cfstring,
                                    cstr.data(), cstr.size(),
                                    kCFStringEncodingUTF8);

   return (result) ? std::string(cstr.data()) : std::string();
   }

std::string to_string(const OSStatus status)
   {
   scoped_CFType<CFStringRef> eCFString(
      SecCopyErrorMessageString(status, nullptr));
   return to_string(eCFString.get());
   }

void check_success(const OSStatus status, const std::string context)
   {
   if(errSecSuccess == status)
      {
      return;
      }

   throw Internal_Error(
      std::string("failed to " + context + ": " + to_string(status)));
   }

template <typename T>
void check_notnull(const scoped_CFType<T>& value, const std::string context)
   {
   if(value)
      {
      return;
      }

   throw Internal_Error(std::string("failed to ") + context);
   }

SecCertificateRef to_SecCertificateRef(CFTypeRef object)
   {
   if(!object || CFGetTypeID(object) != SecCertificateGetTypeID())
      {
      throw Internal_Error("cannot convert CFTypeRef to SecCertificateRef");
      }

   return static_cast<SecCertificateRef>(const_cast<void*>(object));
   }

/**
 * Create a CFDataRef view over some provided std::vector<uint8_t. The data is
 * not copied but the resulting CFDataRef uses the std::vector's buffer as data
 * store. Note that the CFDataRef still needs to be manually freed, hence the
 * scoped_CFType wrapper.
 */
scoped_CFType<CFDataRef> createCFDataView(const std::vector<uint8_t>& data)
   {
   return scoped_CFType<CFDataRef>(
             CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
                                         data.data(),
                                         data.size(),
                                         kCFAllocatorNull));
   }

/**
 * Convert a SecCertificateRef object into a Botan::X509_Certificate
 */
std::shared_ptr<const X509_Certificate> readCertificate(SecCertificateRef cert)
   {
   scoped_CFType<CFDataRef> derData(SecCertificateCopyData(cert));
   check_notnull(derData, "read extracted certificate");

   // TODO: factor this out into a createDataSourceView() as soon as this class
   //       gets a move-constructor
   const auto data   = CFDataGetBytePtr(derData.get());
   const auto length = CFDataGetLength(derData.get());

   DataSource_Memory ds(data, length);
   return std::make_shared<Botan::X509_Certificate>(ds);
   }

}

/**
 * Internal class implementation (i.e. Pimpl) to keep the required platform-
 * dependent members of Certificate_Store_MacOS contained in this compilation
 * unit.
 */
class Certificate_Store_MacOS_Impl
   {
   private:
      static constexpr const char* system_roots =
         "/System/Library/Keychains/SystemRootCertificates.keychain";
      static constexpr const char* system_keychain =
         "/Library/Keychains/System.keychain";

   public:
      using Query = std::vector<std::pair<CFStringRef, CFTypeRef>>;

   public:
      Certificate_Store_MacOS_Impl() :
         m_policy(SecPolicyCreateBasicX509()),
         m_system_roots(nullptr),
         m_system_chain(nullptr),
         m_keychains(nullptr)
         {
         check_success(SecKeychainOpen(system_roots, &m_system_roots.get()),
                       "open system root certificates");
         check_success(SecKeychainOpen(system_keychain, &m_system_chain.get()),
                       "open system keychain");
         check_notnull(m_system_roots, "open system root certificate chain");
         check_notnull(m_system_chain, "open system certificate chain");

         // m_keychains is merely a convenience list view into all open keychain
         // objects. This list is required in prepareQuery().
         std::array<const void*, 2> keychains{{
               m_system_roots.get(),
               m_system_chain.get()
               }};

         m_keychains.assign(
            CFArrayCreate(kCFAllocatorDefault,
                          keychains.data(),
                          keychains.size(),
                          &kCFTypeArrayCallBacks));
         check_notnull(m_keychains, "initialize keychain array");
         }

      CFArrayRef keychains() const { return m_keychains.get(); }
      SecPolicyRef policy() const { return m_policy.get(); }

      /**
       * Searches certificates in all opened system keychains. Takes an optional
       * \p query that defines filter attributes to be searched for. That query
       * is amended by generic attributes for "certificate filtering".
       *
       * \param query  a list of key-value pairs used for filtering
       * \returns      an array with the resulting certificates or nullptr if
       *               no matching certificate was found
       */
      scoped_CFType<CFArrayRef> search(Query query = Query()) const
         {
         scoped_CFType<CFDictionaryRef> fullQuery(
            prepareQuery(std::move(query)));
         check_notnull(fullQuery, "create search query");

         scoped_CFType<CFArrayRef> result(nullptr);
         auto status = SecItemCopyMatching(fullQuery.get(),
                                           (CFTypeRef*)&result.get());
         if(errSecItemNotFound == status)
            {
            return scoped_CFType<CFArrayRef>(nullptr);  // no matches
            }

         check_success(status, "look up certificate");
         check_notnull(result, "look up certificate (invalid result value)");

         return result;
         }

   protected:
      /**
       * Amends the user-provided search query with generic filter rules for
       * the associated system keychains.
       */
      scoped_CFType<CFDictionaryRef> prepareQuery(Query pairs) const
         {
         std::vector<CFStringRef> keys({kSecClass,
                                        kSecReturnRef,
                                        kSecMatchLimit,
                                        kSecMatchTrustedOnly,
                                        kSecMatchSearchList,
                                        kSecMatchPolicy});
         std::vector<CFTypeRef>   values({kSecClassCertificate,
                                          kCFBooleanTrue,
                                          kSecMatchLimitAll,
                                          kCFBooleanTrue,
                                          keychains(),
                                          policy()});
         keys.reserve(pairs.size() + keys.size());
         values.reserve(pairs.size() + values.size());

         for(const auto& pair : pairs)
            {
            keys.push_back(pair.first);
            values.push_back(pair.second);
            }

         BOTAN_ASSERT_EQUAL(keys.size(), values.size(), "valid key-value pairs");

         return scoped_CFType<CFDictionaryRef>(CFDictionaryCreate(
               kCFAllocatorDefault, (const void**)keys.data(),
               (const void**)values.data(), keys.size(),
               &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
         }

   private:
      scoped_CFType<SecPolicyRef>    m_policy;
      scoped_CFType<SecKeychainRef>  m_system_roots;
      scoped_CFType<SecKeychainRef>  m_system_chain;
      scoped_CFType<CFArrayRef>      m_keychains;
   };

Certificate_Store_MacOS::Certificate_Store_MacOS() :
   m_impl(std::make_shared<Certificate_Store_MacOS_Impl>())
   {
   }

std::vector<X509_DN> Certificate_Store_MacOS::all_subjects() const
   {
   scoped_CFType<CFArrayRef> result(m_impl->search());

   if(!result)
      {
      return {};  // not a single certificate found in the keychain
      }

   const auto count = CFArrayGetCount(result.get());
   BOTAN_ASSERT(count > 0, "subject result list contains data");

   std::vector<X509_DN> output;
   output.reserve(count);
   for(unsigned int i = 0; i < count; ++i)
      {
      // Note: Apple's API provides SecCertificateCopyNormalizedSubjectSequence
      //       which would have saved us from reading a Botan::X509_Certificate,
      //       however, this function applies the same DN "normalization" as
      //       stated above.
      auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), i));
      auto cert = readCertificate(cfCert);
      output.emplace_back(cert->subject_dn());
      }

   return output;
   }

std::shared_ptr<const X509_Certificate>
Certificate_Store_MacOS::find_cert(const X509_DN& subject_dn,
                                   const std::vector<uint8_t>& key_id) const
   {
   const auto certs = find_all_certs(subject_dn, key_id);

   if(certs.empty())
      {
      return nullptr;  // certificate not found
      }

   if(certs.size() != 1)
      {
      throw Lookup_Error("ambiguous certificate result");
      }

   return certs.front();
   }

std::vector<std::shared_ptr<const X509_Certificate>> Certificate_Store_MacOS::find_all_certs(
         const X509_DN& subject_dn,
         const std::vector<uint8_t>& key_id) const
   {
   std::vector<uint8_t> dn_data;
   DER_Encoder encoder(dn_data);
   normalize(subject_dn).encode_into(encoder);

   scoped_CFType<CFDataRef> dn_cfdata(createCFDataView(dn_data));
   check_notnull(dn_cfdata, "create DN search object");

   Certificate_Store_MacOS_Impl::Query query_params(
      {
         {kSecAttrSubject, dn_cfdata.get()}
      });

   scoped_CFType<CFDataRef> keyid_cfdata(createCFDataView(key_id));
   check_notnull(keyid_cfdata, "create key ID search object");
   if(!key_id.empty())
      {
      query_params.push_back({kSecAttrSubjectKeyID, keyid_cfdata.get()});
      }

   scoped_CFType<CFArrayRef> result(m_impl->search(std::move(query_params)));

   if(!result)
      {
      return {};  // no certificates found
      }

   const auto count = CFArrayGetCount(result.get());
   BOTAN_ASSERT(count > 0, "certificate result list contains data");

   std::vector<std::shared_ptr<const X509_Certificate>> output;
   output.reserve(count);
   for(unsigned int i = 0; i < count; ++i)
      {
      auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), i));
      output.emplace_back(readCertificate(cfCert));
      }

   return output;
   }

std::shared_ptr<const X509_Certificate>
Certificate_Store_MacOS::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
   {
   if(key_hash.size() != 20)
      {
      throw Invalid_Argument("Certificate_Store_MacOS::find_cert_by_pubkey_sha1 invalid hash");
      }

   scoped_CFType<CFDataRef> key_hash_cfdata(createCFDataView(key_hash));
   check_notnull(key_hash_cfdata, "create key hash search object");

   scoped_CFType<CFArrayRef> result(m_impl->search(
      {
         {kSecAttrPublicKeyHash, key_hash_cfdata.get()},
      }));

   if(!result)
      {
      return nullptr;  // no certificate found
      }

   const auto count = CFArrayGetCount(result.get());
   BOTAN_ASSERT(count > 0, "certificate result list contains an object");

   // `count` might be greater than 1, but we'll just select the first match
   auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), 0));
   return readCertificate(cfCert);
   }

std::shared_ptr<const X509_Certificate>
Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
   {
   BOTAN_UNUSED(subject_hash);
   throw Not_Implemented("Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256");
   }

std::shared_ptr<const X509_CRL> Certificate_Store_MacOS::find_crl_for(const X509_Certificate& subject) const
   {
   BOTAN_UNUSED(subject);
   return {};
   }

}
<commit_msg>FIX: handle duplicated root certs more graceful<commit_after>/*
* Certificate Store
* (C) 1999-2019 Jack Lloyd
* (C) 2019      René Meusel
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/build.h>

#include <algorithm>
#include <array>

#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>

#include <botan/assert.h>
#include <botan/ber_dec.h>
#include <botan/certstor_macos.h>
#include <botan/data_src.h>
#include <botan/der_enc.h>
#include <botan/exceptn.h>
#include <botan/x509_dn.h>

namespace Botan {

namespace {

/**
 * Abstract RAII wrapper for CFTypeRef-style object handles
 * All of those xxxRef types are eventually typedefs to void*
 */
template<typename T>
class scoped_CFType
   {
   public:
      explicit scoped_CFType(T value)
         : m_value(value)
         {
         }

      scoped_CFType(const scoped_CFType<T>& rhs) = delete;
      scoped_CFType(scoped_CFType<T>&& rhs) :
         m_value(std::move(rhs.m_value))
         {
         rhs.m_value = nullptr;
         }

      ~scoped_CFType()
         {
         if(m_value)
            {
            CFRelease(m_value);
            }
         }

      operator bool() const { return m_value != nullptr; }

      void assign(T value)
         {
         BOTAN_ASSERT(m_value == nullptr, "scoped_CFType was not set yet");
         m_value = value;
         }

      T& get() { return m_value; }
      const T& get() const { return m_value; }

   private:
      T m_value;
   };

/**
 * Apple's DN parser "normalizes" ASN1 'PrintableString' into upper-case values
 * and strips leading, trailing as well as multiple white spaces.
 * See: opensource.apple.com/source/Security/Security-55471/sec/Security/SecCertificate.c.auto.html
 */
X509_DN normalize(const X509_DN& dn)
   {
   X509_DN result;

   for(const auto& rdn : dn.dn_info())
      {
      // TODO: C++14 - use std::get<ASN1_String>(), resp. std::get<OID>()
      const auto oid = rdn.first;
      auto str = rdn.second;

      if(str.tagging() == ASN1_Tag::PRINTABLE_STRING)
         {
         std::string normalized;
         normalized.reserve(str.value().size());
         for(const char c : str.value())
            {
            if(c != ' ')
               {
               // store all 'normal' characters as upper case
               normalized.push_back(::toupper(c));
               }
            else if(!normalized.empty() && normalized.back() != ' ')
               {
               // remove leading and squash multiple white spaces
               normalized.push_back(c);
               }
            }

         if(normalized.back() == ' ')
            {
            // remove potential remaining single trailing white space char
            normalized.erase(normalized.end() - 1);
            }

         str = ASN1_String(normalized, str.tagging());
         }

      result.add_attribute(oid, str);
      }

   return result;
   }

std::string to_string(const CFStringRef cfstring)
   {
   const char* ccstr = CFStringGetCStringPtr(cfstring, kCFStringEncodingUTF8);

   if(ccstr != nullptr)
      {
      return std::string(ccstr);
      }

   auto utf16_pairs    = CFStringGetLength(cfstring);
   auto max_utf8_bytes = CFStringGetMaximumSizeForEncoding(utf16_pairs, kCFStringEncodingUTF8);

   std::vector<char> cstr(max_utf8_bytes, '\0');
   auto result = CFStringGetCString(cfstring,
                                    cstr.data(), cstr.size(),
                                    kCFStringEncodingUTF8);

   return (result) ? std::string(cstr.data()) : std::string();
   }

std::string to_string(const OSStatus status)
   {
   scoped_CFType<CFStringRef> eCFString(
      SecCopyErrorMessageString(status, nullptr));
   return to_string(eCFString.get());
   }

void check_success(const OSStatus status, const std::string context)
   {
   if(errSecSuccess == status)
      {
      return;
      }

   throw Internal_Error(
      std::string("failed to " + context + ": " + to_string(status)));
   }

template <typename T>
void check_notnull(const scoped_CFType<T>& value, const std::string context)
   {
   if(value)
      {
      return;
      }

   throw Internal_Error(std::string("failed to ") + context);
   }

SecCertificateRef to_SecCertificateRef(CFTypeRef object)
   {
   if(!object || CFGetTypeID(object) != SecCertificateGetTypeID())
      {
      throw Internal_Error("cannot convert CFTypeRef to SecCertificateRef");
      }

   return static_cast<SecCertificateRef>(const_cast<void*>(object));
   }

/**
 * Create a CFDataRef view over some provided std::vector<uint8_t. The data is
 * not copied but the resulting CFDataRef uses the std::vector's buffer as data
 * store. Note that the CFDataRef still needs to be manually freed, hence the
 * scoped_CFType wrapper.
 */
scoped_CFType<CFDataRef> createCFDataView(const std::vector<uint8_t>& data)
   {
   return scoped_CFType<CFDataRef>(
             CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
                                         data.data(),
                                         data.size(),
                                         kCFAllocatorNull));
   }

/**
 * Convert a SecCertificateRef object into a Botan::X509_Certificate
 */
std::shared_ptr<const X509_Certificate> readCertificate(SecCertificateRef cert)
   {
   scoped_CFType<CFDataRef> derData(SecCertificateCopyData(cert));
   check_notnull(derData, "read extracted certificate");

   // TODO: factor this out into a createDataSourceView() as soon as this class
   //       gets a move-constructor
   const auto data   = CFDataGetBytePtr(derData.get());
   const auto length = CFDataGetLength(derData.get());

   DataSource_Memory ds(data, length);
   return std::make_shared<Botan::X509_Certificate>(ds);
   }

}

/**
 * Internal class implementation (i.e. Pimpl) to keep the required platform-
 * dependent members of Certificate_Store_MacOS contained in this compilation
 * unit.
 */
class Certificate_Store_MacOS_Impl
   {
   private:
      static constexpr const char* system_roots =
         "/System/Library/Keychains/SystemRootCertificates.keychain";
      static constexpr const char* system_keychain =
         "/Library/Keychains/System.keychain";

   public:
      using Query = std::vector<std::pair<CFStringRef, CFTypeRef>>;

   public:
      Certificate_Store_MacOS_Impl() :
         m_policy(SecPolicyCreateBasicX509()),
         m_system_roots(nullptr),
         m_system_chain(nullptr),
         m_keychains(nullptr)
         {
         check_success(SecKeychainOpen(system_roots, &m_system_roots.get()),
                       "open system root certificates");
         check_success(SecKeychainOpen(system_keychain, &m_system_chain.get()),
                       "open system keychain");
         check_notnull(m_system_roots, "open system root certificate chain");
         check_notnull(m_system_chain, "open system certificate chain");

         // m_keychains is merely a convenience list view into all open keychain
         // objects. This list is required in prepareQuery().
         std::array<const void*, 2> keychains{{
               m_system_roots.get(),
               m_system_chain.get()
               }};

         m_keychains.assign(
            CFArrayCreate(kCFAllocatorDefault,
                          keychains.data(),
                          keychains.size(),
                          &kCFTypeArrayCallBacks));
         check_notnull(m_keychains, "initialize keychain array");
         }

      CFArrayRef keychains() const { return m_keychains.get(); }
      SecPolicyRef policy() const { return m_policy.get(); }

      /**
       * Searches certificates in all opened system keychains. Takes an optional
       * \p query that defines filter attributes to be searched for. That query
       * is amended by generic attributes for "certificate filtering".
       *
       * \param query  a list of key-value pairs used for filtering
       * \returns      an array with the resulting certificates or nullptr if
       *               no matching certificate was found
       */
      scoped_CFType<CFArrayRef> search(Query query = Query()) const
         {
         scoped_CFType<CFDictionaryRef> fullQuery(
            prepareQuery(std::move(query)));
         check_notnull(fullQuery, "create search query");

         scoped_CFType<CFArrayRef> result(nullptr);
         auto status = SecItemCopyMatching(fullQuery.get(),
                                           (CFTypeRef*)&result.get());
         if(errSecItemNotFound == status)
            {
            return scoped_CFType<CFArrayRef>(nullptr);  // no matches
            }

         check_success(status, "look up certificate");
         check_notnull(result, "look up certificate (invalid result value)");

         return result;
         }

   protected:
      /**
       * Amends the user-provided search query with generic filter rules for
       * the associated system keychains.
       */
      scoped_CFType<CFDictionaryRef> prepareQuery(Query pairs) const
         {
         std::vector<CFStringRef> keys({kSecClass,
                                        kSecReturnRef,
                                        kSecMatchLimit,
                                        kSecMatchTrustedOnly,
                                        kSecMatchSearchList,
                                        kSecMatchPolicy});
         std::vector<CFTypeRef>   values({kSecClassCertificate,
                                          kCFBooleanTrue,
                                          kSecMatchLimitAll,
                                          kCFBooleanTrue,
                                          keychains(),
                                          policy()});
         keys.reserve(pairs.size() + keys.size());
         values.reserve(pairs.size() + values.size());

         for(const auto& pair : pairs)
            {
            keys.push_back(pair.first);
            values.push_back(pair.second);
            }

         BOTAN_ASSERT_EQUAL(keys.size(), values.size(), "valid key-value pairs");

         return scoped_CFType<CFDictionaryRef>(CFDictionaryCreate(
               kCFAllocatorDefault, (const void**)keys.data(),
               (const void**)values.data(), keys.size(),
               &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
         }

   private:
      scoped_CFType<SecPolicyRef>    m_policy;
      scoped_CFType<SecKeychainRef>  m_system_roots;
      scoped_CFType<SecKeychainRef>  m_system_chain;
      scoped_CFType<CFArrayRef>      m_keychains;
   };

Certificate_Store_MacOS::Certificate_Store_MacOS() :
   m_impl(std::make_shared<Certificate_Store_MacOS_Impl>())
   {
   }

std::vector<X509_DN> Certificate_Store_MacOS::all_subjects() const
   {
   scoped_CFType<CFArrayRef> result(m_impl->search());

   if(!result)
      {
      return {};  // not a single certificate found in the keychain
      }

   const auto count = CFArrayGetCount(result.get());
   BOTAN_ASSERT(count > 0, "subject result list contains data");

   std::vector<X509_DN> output;
   output.reserve(count);
   for(unsigned int i = 0; i < count; ++i)
      {
      // Note: Apple's API provides SecCertificateCopyNormalizedSubjectSequence
      //       which would have saved us from reading a Botan::X509_Certificate,
      //       however, this function applies the same DN "normalization" as
      //       stated above.
      auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), i));
      auto cert = readCertificate(cfCert);
      output.emplace_back(cert->subject_dn());
      }

   return output;
   }

std::shared_ptr<const X509_Certificate>
Certificate_Store_MacOS::find_cert(const X509_DN& subject_dn,
                                   const std::vector<uint8_t>& key_id) const
   {
   const auto certs = find_all_certs(subject_dn, key_id);

   if(certs.empty())
      {
      return nullptr;  // certificate not found
      }

   // `count` might be greater than 1, but we'll just select the first match
   return certs.front();
   }

std::vector<std::shared_ptr<const X509_Certificate>> Certificate_Store_MacOS::find_all_certs(
         const X509_DN& subject_dn,
         const std::vector<uint8_t>& key_id) const
   {
   std::vector<uint8_t> dn_data;
   DER_Encoder encoder(dn_data);
   normalize(subject_dn).encode_into(encoder);

   scoped_CFType<CFDataRef> dn_cfdata(createCFDataView(dn_data));
   check_notnull(dn_cfdata, "create DN search object");

   Certificate_Store_MacOS_Impl::Query query_params(
      {
         {kSecAttrSubject, dn_cfdata.get()}
      });

   scoped_CFType<CFDataRef> keyid_cfdata(createCFDataView(key_id));
   check_notnull(keyid_cfdata, "create key ID search object");
   if(!key_id.empty())
      {
      query_params.push_back({kSecAttrSubjectKeyID, keyid_cfdata.get()});
      }

   scoped_CFType<CFArrayRef> result(m_impl->search(std::move(query_params)));

   if(!result)
      {
      return {};  // no certificates found
      }

   const auto count = CFArrayGetCount(result.get());
   BOTAN_ASSERT(count > 0, "certificate result list contains data");

   std::vector<std::shared_ptr<const X509_Certificate>> output;
   output.reserve(count);
   for(unsigned int i = 0; i < count; ++i)
      {
      auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), i));
      output.emplace_back(readCertificate(cfCert));
      }

   return output;
   }

std::shared_ptr<const X509_Certificate>
Certificate_Store_MacOS::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
   {
   if(key_hash.size() != 20)
      {
      throw Invalid_Argument("Certificate_Store_MacOS::find_cert_by_pubkey_sha1 invalid hash");
      }

   scoped_CFType<CFDataRef> key_hash_cfdata(createCFDataView(key_hash));
   check_notnull(key_hash_cfdata, "create key hash search object");

   scoped_CFType<CFArrayRef> result(m_impl->search(
      {
         {kSecAttrPublicKeyHash, key_hash_cfdata.get()},
      }));

   if(!result)
      {
      return nullptr;  // no certificate found
      }

   const auto count = CFArrayGetCount(result.get());
   BOTAN_ASSERT(count > 0, "certificate result list contains an object");

   // `count` might be greater than 1, but we'll just select the first match
   auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), 0));
   return readCertificate(cfCert);
   }

std::shared_ptr<const X509_Certificate>
Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
   {
   BOTAN_UNUSED(subject_hash);
   throw Not_Implemented("Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256");
   }

std::shared_ptr<const X509_CRL> Certificate_Store_MacOS::find_crl_for(const X509_Certificate& subject) const
   {
   BOTAN_UNUSED(subject);
   return {};
   }

}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <cstdlib>
#include <sstream>

#include <transport/THttpClient.h>
#include <transport/TSocket.h>

namespace apache { namespace thrift { namespace transport {

using namespace std;

THttpClient::THttpClient(boost::shared_ptr<TTransport> transport, std::string host, std::string path) :
  THttpTransport(transport), host_(host), path_(path) {
}

THttpClient::THttpClient(string host, int port, string path) :
  THttpTransport(boost::shared_ptr<TTransport>(new TSocket(host, port))), host_(host), path_(path) {
}

THttpClient::~THttpClient() {}

void THttpClient::parseHeader(char* header) {
  char* colon = strchr(header, ':');
  if (colon == NULL) {
    return;
  }
  uint32_t sz = colon - header;
  char* value = colon+1;

  if (strncmp(header, "Transfer-Encoding", sz) == 0) {
    if (strstr(value, "chunked") != NULL) {
      chunked_ = true;
    }
  } else if (strncmp(header, "Content-Length", sz) == 0) {
    chunked_ = false;
    contentLength_ = atoi(value);
  }
}

bool THttpClient::parseStatusLine(char* status) {
  char* http = status;

  char* code = strchr(http, ' ');
  if (code == NULL) {
    throw TTransportException(string("Bad Status: ") + status);
  }

  *code = '\0';
  while (*(code++) == ' ') {};

  char* msg = strchr(code, ' ');
  if (msg == NULL) {
    throw TTransportException(string("Bad Status: ") + status);
  }
  *msg = '\0';

  if (strcmp(code, "200") == 0) {
    // HTTP 200 = OK, we got the response
    return true;
  } else if (strcmp(code, "100") == 0) {
    // HTTP 100 = continue, just keep reading
    return false;
  } else {
    throw TTransportException(string("Bad Status: ") + status);
  }
}

void THttpClient::flush() {
  // Fetch the contents of the write buffer
  uint8_t* buf;
  uint32_t len;
  writeBuffer_.getBuffer(&buf, &len);

  // Construct the HTTP header
  std::ostringstream h;
  h <<
    "POST " << path_ << " HTTP/1.1" << CRLF <<
    "Host: " << host_ << CRLF <<
    "Content-Type: application/x-thrift" << CRLF <<
    "Content-Length: " << len << CRLF <<
    "Accept: application/x-thrift" << CRLF <<
    "User-Agent: Thrift/" << VERSION << " (C++/THttpClient)" << CRLF <<
    CRLF;
  string header = h.str();

  // Write the header, then the data, then flush
  transport_->write((const uint8_t*)header.c_str(), header.size());
  transport_->write(buf, len);
  transport_->flush();

  // Reset the buffer and header variables
  writeBuffer_.resetBuffer();
  readHeaders_ = true;
}

}}} // apache::thrift::transport
<commit_msg>THRIFT-1030 C++ THttpTransport doesn't support chucked transfer encoding Patch: Rowan Kerr<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <cstdlib>
#include <sstream>
#include <boost/algorithm/string.hpp>

#include <transport/THttpClient.h>
#include <transport/TSocket.h>

namespace apache { namespace thrift { namespace transport {

using namespace std;

THttpClient::THttpClient(boost::shared_ptr<TTransport> transport, std::string host, std::string path) :
  THttpTransport(transport), host_(host), path_(path) {
}

THttpClient::THttpClient(string host, int port, string path) :
  THttpTransport(boost::shared_ptr<TTransport>(new TSocket(host, port))), host_(host), path_(path) {
}

THttpClient::~THttpClient() {}

void THttpClient::parseHeader(char* header) {
  char* colon = strchr(header, ':');
  if (colon == NULL) {
    return;
  }
  uint32_t sz = colon - header;
  char* value = colon+1;

  if (boost::istarts_with(header, "Transfer-Encoding")) {
    if (boost::iends_with(value, "chunked")) {
      chunked_ = true;
    }
  } else if (boost::istarts_with(header, "Content-Length")) {
    chunked_ = false;
    contentLength_ = atoi(value);
  }
}

bool THttpClient::parseStatusLine(char* status) {
  char* http = status;

  char* code = strchr(http, ' ');
  if (code == NULL) {
    throw TTransportException(string("Bad Status: ") + status);
  }

  *code = '\0';
  while (*(code++) == ' ') {};

  char* msg = strchr(code, ' ');
  if (msg == NULL) {
    throw TTransportException(string("Bad Status: ") + status);
  }
  *msg = '\0';

  if (strcmp(code, "200") == 0) {
    // HTTP 200 = OK, we got the response
    return true;
  } else if (strcmp(code, "100") == 0) {
    // HTTP 100 = continue, just keep reading
    return false;
  } else {
    throw TTransportException(string("Bad Status: ") + status);
  }
}

void THttpClient::flush() {
  // Fetch the contents of the write buffer
  uint8_t* buf;
  uint32_t len;
  writeBuffer_.getBuffer(&buf, &len);

  // Construct the HTTP header
  std::ostringstream h;
  h <<
    "POST " << path_ << " HTTP/1.1" << CRLF <<
    "Host: " << host_ << CRLF <<
    "Content-Type: application/x-thrift" << CRLF <<
    "Content-Length: " << len << CRLF <<
    "Accept: application/x-thrift" << CRLF <<
    "User-Agent: Thrift/" << VERSION << " (C++/THttpClient)" << CRLF <<
    CRLF;
  string header = h.str();

  // Write the header, then the data, then flush
  transport_->write((const uint8_t*)header.c_str(), header.size());
  transport_->write(buf, len);
  transport_->flush();

  // Reset the buffer and header variables
  writeBuffer_.resetBuffer();
  readHeaders_ = true;
}

}}} // apache::thrift::transport
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>

#include "icdar/icdar_reader.hpp"

constexpr const std::size_t context = 5;
constexpr const std::size_t window = context * 2 + 1;

int main(){
    auto dataset = icdar::read_2013_dataset(
        "/home/wichtounet/datasets/icdar_2013_natural/train",
        "/home/wichtounet/datasets/icdar_2013_natural/test", 1, 1);

    if(dataset.training_labels.empty() || dataset.training_images.empty()){
        std::cout << "Problem while reading the dataset" << std::endl;

        return -1;
    }

    std::cout << dataset.training_images.size() << " training images" << std::endl;
    std::cout << dataset.test_images.size() << " test images" << std::endl;

    for(auto& image : dataset.training_images){
        std::vector<std::vector<uint8_t>> windows;

        std::cout << image.width << "x" << image.height << std::endl;

        for(std::size_t i = context; i < image.width - context; ++i){
            for(std::size_t j = context; j < image.height - context; ++j){

                windows.emplace_back(window * window);

                //std::cout << i << ":" << j << std::endl;

                for(std::size_t a = i - context; a < i - context + window; ++a){
                    for(std::size_t b = j - context; b <= j - context + window; ++b){
                        //std::cout << a << "<->" << b << std::endl;
                        //std::cout << ((a * image.height) + b) << std::endl;
                        //std::cout << ((i -context + a) * window + (j - context + b)) << std::endl;
                        windows.back().at((i - context + a) * window + (j - context + b)) = 1;//image.pixels.at(a * image.height + b).r;
                    }
                }

            }
        }

    }

    return 0;
}
<commit_msg>Finish window extraction<commit_after>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>

#include "icdar/icdar_reader.hpp"

constexpr const std::size_t context = 5;
constexpr const std::size_t window = context * 2 + 1;

int main(){
    auto dataset = icdar::read_2013_dataset(
        "/home/wichtounet/datasets/icdar_2013_natural/train",
        "/home/wichtounet/datasets/icdar_2013_natural/test", 5, 1);

    if(dataset.training_labels.empty() || dataset.training_images.empty()){
        std::cout << "Problem while reading the dataset" << std::endl;

        return -1;
    }

    std::cout << dataset.training_images.size() << " training images" << std::endl;
    std::cout << dataset.test_images.size() << " test images" << std::endl;

    for(auto& image : dataset.training_images){
        std::vector<std::vector<uint8_t>> windows;
        windows.reserve(image.width * image.height);

        for(std::size_t i = context; i < image.width - context; ++i){
            for(std::size_t j = context; j < image.height - context; ++j){

                windows.emplace_back(window * window);

                for(std::size_t a = i - context; a < i - context + window; ++a){
                    for(std::size_t b = j - context; b < j - context + window; ++b){
                        auto w_i = (a - (i - context));
                        auto w_j = (b - (j - context));
                        windows.back().at(w_i * window + w_j) = image.pixels.at(a * image.height + b).r;
                    }
                }
            }
        }

        std::cout << windows.size() << " windows extracted" << std::endl;
    }

    return 0;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: hldoctp.cxx,v $
 *
 *  $Revision: 1.17 $
 *
 *  last change: $Author: hr $ $Date: 2003-04-04 18:01:07 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "hyperdlg.hxx"

#ifndef _UNOTOOLS_LOCALFILEHELPER_HXX
#include <unotools/localfilehelper.hxx>
#endif
#ifndef _FILEDLGHELPER_HXX
#include <sfx2/filedlghelper.hxx>
#endif

#include "hldoctp.hxx"
#include "hyperdlg.hrc"

sal_Char __READONLY_DATA sHash[]                = "#";
sal_Char __READONLY_DATA sFileScheme[]          = INET_FILE_SCHEME;
sal_Char __READONLY_DATA sPortalFileScheme[]    = "vnd.sun.star.wfs://";
sal_Char __READONLY_DATA sNewsSRVScheme[] = "news://";
    // TODO news:// is nonsense
sal_Char __READONLY_DATA sHTTPScheme[]    = INET_HTTP_SCHEME;

/*************************************************************************
|*
|* Contructor / Destructor
|*
|************************************************************************/

SvxHyperlinkDocTp::SvxHyperlinkDocTp ( Window *pParent, const SfxItemSet& rItemSet)
:   maGrpDocument   ( this, ResId (GRP_DOCUMENT) ),
    maFtPath        ( this, ResId (FT_PATH_DOC) ),
    maCbbPath       ( this, INET_PROT_FILE ),
    maBtFileopen    ( this, ResId (BTN_FILEOPEN) ),
    maGrpTarget     ( this, ResId (GRP_TARGET) ),
    maFtTarget      ( this, ResId (FT_TARGET_DOC) ),
    maEdTarget      ( this, ResId (ED_TARGET_DOC) ),
    maFtURL         ( this, ResId (FT_URL) ),
    maFtFullURL     ( this, ResId (FT_FULL_URL) ),
    maBtBrowse      ( this, ResId (BTN_BROWSE) ),
    mbMarkWndOpen   ( FALSE ),
    SvxHyperlinkTabPageBase ( pParent, SVX_RES( RID_SVXPAGE_HYPERLINK_DOCUMENT ), rItemSet )
{
    // Set HC bitmaps and disable display of bitmap names.
    maBtBrowse.SetModeImage( Image( ResId( IMG_BROWSE_HC ) ), BMP_COLOR_HIGHCONTRAST );
    maBtBrowse.EnableTextDisplay (FALSE);
    maBtFileopen.SetModeImage( Image( ResId( IMG_FILEOPEN_HC ) ), BMP_COLOR_HIGHCONTRAST );
    maBtFileopen.EnableTextDisplay (FALSE);

    InitStdControls();
    FreeResource();

    // Init URL-Box (pos&size, Open-Handler)
    maCbbPath.SetPosSizePixel ( LogicToPixel( Point( COL_2, 15 ), MAP_APPFONT ),
                                LogicToPixel( Size ( 176 - COL_DIFF, 60), MAP_APPFONT ) );
    maCbbPath.Show();
    String aFileScheme( INET_FILE_SCHEME, RTL_TEXTENCODING_ASCII_US );
    maCbbPath.SetBaseURL(aFileScheme);
    maCbbPath.SetHelpId( HID_HYPERDLG_DOC_PATH );

    SetExchangeSupport ();

    // overload handlers
    maBtFileopen.SetClickHdl ( LINK ( this, SvxHyperlinkDocTp, ClickFileopenHdl_Impl ) );
    maBtBrowse.SetClickHdl   ( LINK ( this, SvxHyperlinkDocTp, ClickTargetHdl_Impl ) );
    maCbbPath.SetModifyHdl   ( LINK ( this, SvxHyperlinkDocTp, ModifiedPathHdl_Impl ) );
    maEdTarget.SetModifyHdl  ( LINK ( this, SvxHyperlinkDocTp, ModifiedTargetHdl_Impl ) );

    maCbbPath.SetLoseFocusHdl( LINK ( this, SvxHyperlinkDocTp, LostFocusPathHdl_Impl ) );

    maTimer.SetTimeoutHdl ( LINK ( this, SvxHyperlinkDocTp, TimeoutHdl_Impl ) );
}

SvxHyperlinkDocTp::~SvxHyperlinkDocTp ()
{
}

/*************************************************************************
|*
|* Fill all dialog-controls except controls in groupbox "more..."
|*
|************************************************************************/

void SvxHyperlinkDocTp::FillDlgFields ( String& aStrURL )
{
    INetURLObject aURL ( aStrURL );
    String aStrScheme;

    // set protocoll-radiobuttons
    INetProtocol aProtocol = aURL.GetProtocol();
    switch ( aProtocol )
    {
        case INET_PROT_FILE :
        case INET_PROT_VND_SUN_STAR_WFS :
            aStrScheme.AssignAscii( RTL_CONSTASCII_STRINGPARAM ( sFileScheme ) );
            break;
        case INET_PROT_POP3 :
            aStrScheme.AssignAscii( RTL_CONSTASCII_STRINGPARAM ( INET_POP3_SCHEME ) );
            break;
        case INET_PROT_IMAP :
            aStrScheme.AssignAscii( RTL_CONSTASCII_STRINGPARAM ( INET_IMAP_SCHEME ) );
            break;
        case INET_PROT_OUT :
            aStrScheme.AssignAscii( RTL_CONSTASCII_STRINGPARAM ( INET_OUT_SCHEME ) );
            break;
        default :
            if ( aStrURL.SearchAscii( sNewsSRVScheme ) == 0 )
                aStrScheme.AssignAscii( RTL_CONSTASCII_STRINGPARAM ( sNewsSRVScheme ) );
            else if( aStrURL.SearchAscii( sHash ) == 0 )
                aStrScheme.AssignAscii( RTL_CONSTASCII_STRINGPARAM ( sFileScheme ) );
    }

    if ( aStrScheme != aEmptyStr )
    {
        String aStrMark;

        xub_StrLen nPos = aStrURL.SearchAscii( sHash );
        // path
        maCbbPath.SetText ( aStrURL.Copy( 0, ( nPos == STRING_NOTFOUND ? aStrURL.Len() : nPos ) ) );

        // set target in document at editfield
        if ( nPos != STRING_NOTFOUND && nPos<aStrURL.Len()-1 )
            aStrMark = aStrURL.Copy( nPos+1, aStrURL.Len() );
         maEdTarget.SetText ( aStrMark );
    }
    else
    {
        maCbbPath.SetText ( aEmptyStr );
        maEdTarget.SetText ( aEmptyStr );
    }

    ModifiedPathHdl_Impl ( NULL );
}

/*************************************************************************
|*
|* retrieve current url-string
|*
|************************************************************************/

String SvxHyperlinkDocTp::GetCurrentURL ()
{
    // get data from dialog-controls
    String aStrURL;
    String aStrPath ( maCbbPath.GetText() );
    const String aBaseURL ( maCbbPath.GetBaseURL() );
    String aStrMark( maEdTarget.GetText() );

    if ( aStrPath != aEmptyStr )
    {
        INetURLObject aURL( aStrPath );
        if ( aURL.GetProtocol() != INET_PROT_NOT_VALID )    // maybe the path is already a valid
            aStrURL = aStrPath;                             // hyperlink, then we can use this path directly
        else
            utl::LocalFileHelper::ConvertSystemPathToURL( aStrPath, aBaseURL, aStrURL );

        if ( aStrMark != aEmptyStr )
        {
            aStrURL.AppendAscii( "#" );
            aStrURL += aStrMark;
        }

        // if there is a empty string, the url will be the html-scheme
        // but its better to show only the file-scheme

        if ( aStrURL.SearchAscii( sHTTPScheme ) == 0 )
        {
            aStrURL.Erase( 0, UniString::CreateFromAscii(
                           RTL_CONSTASCII_STRINGPARAM ( sHTTPScheme ) ).Len() );
            String aStrTmp( aStrURL );
            aStrURL.AssignAscii( sHTTPScheme );
            aStrURL += aStrTmp;
        }
    }
    else
        if( aStrMark != aEmptyStr )
        {
            aStrURL.AssignAscii( sHash );
            aStrURL += aStrMark;
        }

    return aStrURL;
}

/*************************************************************************
|*
|* retrieve and prepare data from dialog-fields
|*
|************************************************************************/

void SvxHyperlinkDocTp::GetCurentItemData ( String& aStrURL, String& aStrName,
                                            String& aStrIntName, String& aStrFrame,
                                            SvxLinkInsertMode& eMode )
{
    // get data from standard-fields
    aStrURL = GetCurrentURL();

    if( aStrURL.EqualsIgnoreCaseAscii( sFileScheme ) ||
        aStrURL.EqualsIgnoreCaseAscii( sPortalFileScheme ) )
         aStrURL=aEmptyStr;

    GetDataFromCommonFields( aStrName, aStrIntName, aStrFrame, eMode );
}

/*************************************************************************
|*
|* static method to create Tabpage
|*
|************************************************************************/

IconChoicePage* SvxHyperlinkDocTp::Create( Window* pWindow, const SfxItemSet& rItemSet )
{
    return( new SvxHyperlinkDocTp( pWindow, rItemSet ) );
}

/*************************************************************************
|*
|* Set initial focus
|*
|************************************************************************/

void SvxHyperlinkDocTp::SetInitFocus()
{
    maCbbPath.GrabFocus();
}

/*************************************************************************
|*
|* Click on imagebutton : fileopen
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, ClickFileopenHdl_Impl, void *, EMPTYARG )
{
    // Open Fileopen-Dialog
       ::sfx2::FileDialogHelper aDlg( ::sfx2::FILEOPEN_SIMPLE, 0, GetParent() );
    String aOldURL( GetCurrentURL() );
    if( aOldURL.EqualsIgnoreCaseAscii( sFileScheme, 0, sizeof( sFileScheme ) - 1 ) ||
        aOldURL.EqualsIgnoreCaseAscii( sPortalFileScheme, 0, sizeof( sFileScheme ) - 1 ) )
    {
        aDlg.SetDisplayDirectory( aOldURL );
    }

    if ( aDlg.Execute() == ERRCODE_NONE )
    {
        String aURL( aDlg.GetPath() );
        String aPath;

        utl::LocalFileHelper::ConvertURLToSystemPath( aURL, aPath );

        maCbbPath.SetBaseURL( aURL );
        maCbbPath.SetText( aPath );

        if ( aOldURL != GetCurrentURL() )
            ModifiedPathHdl_Impl (NULL);
    }

    return( 0L );
}

/*************************************************************************
|*
|* Click on imagebutton : target
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, ClickTargetHdl_Impl, void *, EMPTYARG )
{
    if ( GetPathType ( maStrURL ) == Type_ExistsFile  ||
         maStrURL == aEmptyStr                        ||
         maStrURL.EqualsIgnoreCaseAscii( sFileScheme ) ||
         maStrURL.EqualsIgnoreCaseAscii( sPortalFileScheme ) ||
         maStrURL.SearchAscii( sHash ) == 0 )
    {
        mpMarkWnd->SetError( LERR_NOERROR );

        EnterWait();

        if ( maStrURL.EqualsIgnoreCaseAscii( sFileScheme ) ||
             maStrURL.EqualsIgnoreCaseAscii( sPortalFileScheme ) )
            mpMarkWnd->RefreshTree ( aEmptyStr );
        else
            mpMarkWnd->RefreshTree ( maStrURL );

        LeaveWait();
    }
    else
        mpMarkWnd->SetError( LERR_DOCNOTOPEN );

    ShowMarkWnd ();

    return( 0L );
}

/*************************************************************************
|*
|* Contens of combobox "Path" modified
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, ModifiedPathHdl_Impl, void *, EMPTYARG )
{
    maStrURL = GetCurrentURL();

    maTimer.SetTimeout( 2500 );
    maTimer.Start();

    maFtFullURL.SetText( maStrURL );

    return( 0L );
}

/*************************************************************************
|*
|* If path-field was modify, to browse the new doc after timeout
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, TimeoutHdl_Impl, Timer *, EMPTYARG )
{
    if ( IsMarkWndVisible() && ( GetPathType( maStrURL )==Type_ExistsFile ||
                                  maStrURL == aEmptyStr                   ||
                                  maStrURL.EqualsIgnoreCaseAscii( sFileScheme ) ) )
    {
        EnterWait();

        if ( maStrURL.EqualsIgnoreCaseAscii( sFileScheme ) ||
             maStrURL.EqualsIgnoreCaseAscii( sPortalFileScheme ) )
            mpMarkWnd->RefreshTree ( aEmptyStr );
        else
            mpMarkWnd->RefreshTree ( maStrURL );

        LeaveWait();
    }

    return( 0L );
}

/*************************************************************************
|*
|* Contens of editfield "Target" modified
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, ModifiedTargetHdl_Impl, void *, EMPTYARG )
{
    maStrURL = GetCurrentURL();

    if ( IsMarkWndVisible() )
        mpMarkWnd->SelectEntry ( maEdTarget.GetText() );

    maFtFullURL.SetText( maStrURL );

    return( 0L );
}

/*************************************************************************
|*
|* editfield "Target" lost focus
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, LostFocusPathHdl_Impl, void *, EMPTYARG )
{
    maStrURL = GetCurrentURL();

    maFtFullURL.SetText( maStrURL );

    return (0L);
}

/*************************************************************************
|*
|* Get String from Bookmark-Wnd
|*
|************************************************************************/

void SvxHyperlinkDocTp::SetMarkStr ( String& aStrMark )
{
    maEdTarget.SetText ( aStrMark );

    ModifiedTargetHdl_Impl ( NULL );
}

/*************************************************************************
|*
|* retrieve kind of pathstr
|*
|************************************************************************/

SvxHyperlinkDocTp::EPathType SvxHyperlinkDocTp::GetPathType ( String& aStrPath )
{
    BOOL bExists = FALSE;
    INetURLObject aURL( aStrPath, INET_PROT_FILE );

    if( aURL.HasError() )
        return Type_Invalid;
    else
        return Type_ExistsFile;

    return Type_Unknown;
}
<commit_msg>INTEGRATION: CWS draw9 (1.15.2.1.66); FILE MERGED 2003/04/14 15:48:56 af 1.15.2.1.66.2: RESYNC: (1.16-1.17); FILE MERGED 2003/03/25 20:30:57 iha 1.15.2.1.66.1: #105788# create links also from invalid urls<commit_after>/*************************************************************************
 *
 *  $RCSfile: hldoctp.cxx,v $
 *
 *  $Revision: 1.18 $
 *
 *  last change: $Author: rt $ $Date: 2003-04-24 14:46:22 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "hyperdlg.hxx"

#ifndef _UNOTOOLS_LOCALFILEHELPER_HXX
#include <unotools/localfilehelper.hxx>
#endif
#ifndef _FILEDLGHELPER_HXX
#include <sfx2/filedlghelper.hxx>
#endif

#include "hldoctp.hxx"
#include "hyperdlg.hrc"

sal_Char __READONLY_DATA sHash[]                = "#";
sal_Char __READONLY_DATA sFileScheme[]          = INET_FILE_SCHEME;
sal_Char __READONLY_DATA sPortalFileScheme[]    = "vnd.sun.star.wfs://";
sal_Char __READONLY_DATA sNewsSRVScheme[] = "news://";
    // TODO news:// is nonsense
sal_Char __READONLY_DATA sHTTPScheme[]    = INET_HTTP_SCHEME;

/*************************************************************************
|*
|* Contructor / Destructor
|*
|************************************************************************/

SvxHyperlinkDocTp::SvxHyperlinkDocTp ( Window *pParent, const SfxItemSet& rItemSet)
:   maGrpDocument   ( this, ResId (GRP_DOCUMENT) ),
    maFtPath        ( this, ResId (FT_PATH_DOC) ),
    maCbbPath       ( this, INET_PROT_FILE ),
    maBtFileopen    ( this, ResId (BTN_FILEOPEN) ),
    maGrpTarget     ( this, ResId (GRP_TARGET) ),
    maFtTarget      ( this, ResId (FT_TARGET_DOC) ),
    maEdTarget      ( this, ResId (ED_TARGET_DOC) ),
    maFtURL         ( this, ResId (FT_URL) ),
    maFtFullURL     ( this, ResId (FT_FULL_URL) ),
    maBtBrowse      ( this, ResId (BTN_BROWSE) ),
    mbMarkWndOpen   ( FALSE ),
    SvxHyperlinkTabPageBase ( pParent, SVX_RES( RID_SVXPAGE_HYPERLINK_DOCUMENT ), rItemSet )
{
    // Set HC bitmaps and disable display of bitmap names.
    maBtBrowse.SetModeImage( Image( ResId( IMG_BROWSE_HC ) ), BMP_COLOR_HIGHCONTRAST );
    maBtBrowse.EnableTextDisplay (FALSE);
    maBtFileopen.SetModeImage( Image( ResId( IMG_FILEOPEN_HC ) ), BMP_COLOR_HIGHCONTRAST );
    maBtFileopen.EnableTextDisplay (FALSE);

    InitStdControls();
    FreeResource();

    // Init URL-Box (pos&size, Open-Handler)
    maCbbPath.SetPosSizePixel ( LogicToPixel( Point( COL_2, 15 ), MAP_APPFONT ),
                                LogicToPixel( Size ( 176 - COL_DIFF, 60), MAP_APPFONT ) );
    maCbbPath.Show();
    String aFileScheme( INET_FILE_SCHEME, RTL_TEXTENCODING_ASCII_US );
    maCbbPath.SetBaseURL(aFileScheme);
    maCbbPath.SetHelpId( HID_HYPERDLG_DOC_PATH );

    SetExchangeSupport ();

    // overload handlers
    maBtFileopen.SetClickHdl ( LINK ( this, SvxHyperlinkDocTp, ClickFileopenHdl_Impl ) );
    maBtBrowse.SetClickHdl   ( LINK ( this, SvxHyperlinkDocTp, ClickTargetHdl_Impl ) );
    maCbbPath.SetModifyHdl   ( LINK ( this, SvxHyperlinkDocTp, ModifiedPathHdl_Impl ) );
    maEdTarget.SetModifyHdl  ( LINK ( this, SvxHyperlinkDocTp, ModifiedTargetHdl_Impl ) );

    maCbbPath.SetLoseFocusHdl( LINK ( this, SvxHyperlinkDocTp, LostFocusPathHdl_Impl ) );

    maTimer.SetTimeoutHdl ( LINK ( this, SvxHyperlinkDocTp, TimeoutHdl_Impl ) );
}

SvxHyperlinkDocTp::~SvxHyperlinkDocTp ()
{
}

/*************************************************************************
|*
|* Fill all dialog-controls except controls in groupbox "more..."
|*
|************************************************************************/

void SvxHyperlinkDocTp::FillDlgFields ( String& aStrURL )
{
    INetURLObject aURL ( aStrURL );

    String aStrMark;
    xub_StrLen nPos = aStrURL.SearchAscii( sHash );
    // path
    maCbbPath.SetText ( aStrURL.Copy( 0, ( nPos == STRING_NOTFOUND ? aStrURL.Len() : nPos ) ) );

    // set target in document at editfield
    if ( nPos != STRING_NOTFOUND && nPos<aStrURL.Len()-1 )
        aStrMark = aStrURL.Copy( nPos+1, aStrURL.Len() );
     maEdTarget.SetText ( aStrMark );

    ModifiedPathHdl_Impl ( NULL );
}

/*************************************************************************
|*
|* retrieve current url-string
|*
|************************************************************************/

String SvxHyperlinkDocTp::GetCurrentURL ()
{
    // get data from dialog-controls
    String aStrURL;
    String aStrPath ( maCbbPath.GetText() );
    const String aBaseURL ( maCbbPath.GetBaseURL() );
    String aStrMark( maEdTarget.GetText() );

    if ( aStrPath != aEmptyStr )
    {
        INetURLObject aURL( aStrPath );
        if ( aURL.GetProtocol() != INET_PROT_NOT_VALID )    // maybe the path is already a valid
            aStrURL = aStrPath;                             // hyperlink, then we can use this path directly
        else
            utl::LocalFileHelper::ConvertSystemPathToURL( aStrPath, aBaseURL, aStrURL );

        //#105788# always create a URL even if it is not valid
        if( aStrURL == aEmptyStr )
            aStrURL = aStrPath;
    }

    if( aStrMark != aEmptyStr )
    {
        aStrURL.AssignAscii( sHash );
        aStrURL += aStrMark;
    }

    return aStrURL;
}

/*************************************************************************
|*
|* retrieve and prepare data from dialog-fields
|*
|************************************************************************/

void SvxHyperlinkDocTp::GetCurentItemData ( String& aStrURL, String& aStrName,
                                            String& aStrIntName, String& aStrFrame,
                                            SvxLinkInsertMode& eMode )
{
    // get data from standard-fields
    aStrURL = GetCurrentURL();

    if( aStrURL.EqualsIgnoreCaseAscii( sFileScheme ) ||
        aStrURL.EqualsIgnoreCaseAscii( sPortalFileScheme ) )
         aStrURL=aEmptyStr;

    GetDataFromCommonFields( aStrName, aStrIntName, aStrFrame, eMode );
}

/*************************************************************************
|*
|* static method to create Tabpage
|*
|************************************************************************/

IconChoicePage* SvxHyperlinkDocTp::Create( Window* pWindow, const SfxItemSet& rItemSet )
{
    return( new SvxHyperlinkDocTp( pWindow, rItemSet ) );
}

/*************************************************************************
|*
|* Set initial focus
|*
|************************************************************************/

void SvxHyperlinkDocTp::SetInitFocus()
{
    maCbbPath.GrabFocus();
}

/*************************************************************************
|*
|* Click on imagebutton : fileopen
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, ClickFileopenHdl_Impl, void *, EMPTYARG )
{
    // Open Fileopen-Dialog
       ::sfx2::FileDialogHelper aDlg( ::sfx2::FILEOPEN_SIMPLE, 0, GetParent() );
    String aOldURL( GetCurrentURL() );
    if( aOldURL.EqualsIgnoreCaseAscii( sFileScheme, 0, sizeof( sFileScheme ) - 1 ) ||
        aOldURL.EqualsIgnoreCaseAscii( sPortalFileScheme, 0, sizeof( sFileScheme ) - 1 ) )
    {
        aDlg.SetDisplayDirectory( aOldURL );
    }

    if ( aDlg.Execute() == ERRCODE_NONE )
    {
        String aURL( aDlg.GetPath() );
        String aPath;

        utl::LocalFileHelper::ConvertURLToSystemPath( aURL, aPath );

        maCbbPath.SetBaseURL( aURL );
        maCbbPath.SetText( aPath );

        if ( aOldURL != GetCurrentURL() )
            ModifiedPathHdl_Impl (NULL);
    }

    return( 0L );
}

/*************************************************************************
|*
|* Click on imagebutton : target
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, ClickTargetHdl_Impl, void *, EMPTYARG )
{
    if ( GetPathType ( maStrURL ) == Type_ExistsFile  ||
         maStrURL == aEmptyStr                        ||
         maStrURL.EqualsIgnoreCaseAscii( sFileScheme ) ||
         maStrURL.EqualsIgnoreCaseAscii( sPortalFileScheme ) ||
         maStrURL.SearchAscii( sHash ) == 0 )
    {
        mpMarkWnd->SetError( LERR_NOERROR );

        EnterWait();

        if ( maStrURL.EqualsIgnoreCaseAscii( sFileScheme ) ||
             maStrURL.EqualsIgnoreCaseAscii( sPortalFileScheme ) )
            mpMarkWnd->RefreshTree ( aEmptyStr );
        else
            mpMarkWnd->RefreshTree ( maStrURL );

        LeaveWait();
    }
    else
        mpMarkWnd->SetError( LERR_DOCNOTOPEN );

    ShowMarkWnd ();

    return( 0L );
}

/*************************************************************************
|*
|* Contens of combobox "Path" modified
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, ModifiedPathHdl_Impl, void *, EMPTYARG )
{
    maStrURL = GetCurrentURL();

    maTimer.SetTimeout( 2500 );
    maTimer.Start();

    maFtFullURL.SetText( maStrURL );

    return( 0L );
}

/*************************************************************************
|*
|* If path-field was modify, to browse the new doc after timeout
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, TimeoutHdl_Impl, Timer *, EMPTYARG )
{
    if ( IsMarkWndVisible() && ( GetPathType( maStrURL )==Type_ExistsFile ||
                                  maStrURL == aEmptyStr                   ||
                                  maStrURL.EqualsIgnoreCaseAscii( sFileScheme ) ) )
    {
        EnterWait();

        if ( maStrURL.EqualsIgnoreCaseAscii( sFileScheme ) ||
             maStrURL.EqualsIgnoreCaseAscii( sPortalFileScheme ) )
            mpMarkWnd->RefreshTree ( aEmptyStr );
        else
            mpMarkWnd->RefreshTree ( maStrURL );

        LeaveWait();
    }

    return( 0L );
}

/*************************************************************************
|*
|* Contens of editfield "Target" modified
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, ModifiedTargetHdl_Impl, void *, EMPTYARG )
{
    maStrURL = GetCurrentURL();

    if ( IsMarkWndVisible() )
        mpMarkWnd->SelectEntry ( maEdTarget.GetText() );

    maFtFullURL.SetText( maStrURL );

    return( 0L );
}

/*************************************************************************
|*
|* editfield "Target" lost focus
|*
|************************************************************************/

IMPL_LINK ( SvxHyperlinkDocTp, LostFocusPathHdl_Impl, void *, EMPTYARG )
{
    maStrURL = GetCurrentURL();

    maFtFullURL.SetText( maStrURL );

    return (0L);
}

/*************************************************************************
|*
|* Get String from Bookmark-Wnd
|*
|************************************************************************/

void SvxHyperlinkDocTp::SetMarkStr ( String& aStrMark )
{
    maEdTarget.SetText ( aStrMark );

    ModifiedTargetHdl_Impl ( NULL );
}

/*************************************************************************
|*
|* retrieve kind of pathstr
|*
|************************************************************************/

SvxHyperlinkDocTp::EPathType SvxHyperlinkDocTp::GetPathType ( String& aStrPath )
{
    BOOL bExists = FALSE;
    INetURLObject aURL( aStrPath, INET_PROT_FILE );

    if( aURL.HasError() )
        return Type_Invalid;
    else
        return Type_ExistsFile;

    return Type_Unknown;
}
<|endoftext|>