<commit_before>/*
 * Copyright (C) 2012 Kolibre
 *
 * This file is part of kolibre-clientcore.
 *
 * Kolibre-clientcore is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Kolibre-clientcore 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 kolibre-clientcore. If not, see <http://www.gnu.org/licenses/>.
 */

#include "Db.h"
#include "Settings.h"

#include <string>
#include <iostream>
#include <fstream>

#include <log4cxx/logger.h>

// create logger which will become a child to logger kolibre.clientcore
log4cxx::LoggerPtr settingsLog(log4cxx::Logger::getLogger("kolibre.clientcore.settings"));

using namespace std;

#define DB_VERSION 1

bool filecopy(string fromfile, string tofile)
{
    LOG4CXX_DEBUG(settingsLog, "copy(" << fromfile << ", "<< tofile << ")");

    fstream infile(fromfile.c_str(), fstream::in | fstream::binary);
    fstream outfile(tofile.c_str(), fstream::out | fstream::trunc | fstream::binary);
    if (not (outfile and infile))
    {
        return false;
    }
    outfile << infile.rdbuf();

    return true;
}

Settings * Settings::pinstance = 0;

Settings * Settings::Instance()
{
    if (pinstance == 0)
    {
        pinstance = new Settings;
    }

    return pinstance;
}

void Settings::DeleteInstance()
{
    pthread_mutex_lock(&settings_mutex);
    //Nothing going on
    pthread_mutex_unlock(&settings_mutex);
    pthread_mutex_destroy(&settings_mutex);
    Settings::pinstance=NULL;
    delete this;
}

Settings::Settings():
    mCurrentDomain("localhost")
{
    LOG4CXX_TRACE(settingsLog, "Constructor");

    pthread_mutex_init (&settings_mutex, NULL);
    LOG4CXX_TRACE(settingsLog, "Settings mutex initialized");

    string settingsfile = Utils::getDatapath() + "settings.db";

    LOG4CXX_INFO(settingsLog, "Opening settings.db in '"<< settingsfile << "'");

    bool settingsFileExists = fstream(settingsfile.c_str(), fstream::in).is_open();

    pDBHandle = new settings::DB(settingsfile);

    if (!pDBHandle->connect())
    {
        LOG4CXX_ERROR(settingsLog, "Could not open settings.db: '" << settingsfile << "', error '" << pDBHandle->getLasterror() << "'");

        delete pDBHandle;
        pDBHandle = NULL;
        return;
    }

    if (settingsFileExists)
    {
        const int fileVersion = getVersion();

        if (fileVersion != DB_VERSION)
        {
            LOG4CXX_DEBUG(settingsLog, "DB_VERSION missmatch, migrating db");
            stringstream backupName;
            backupName << settingsfile << fileVersion;

            delete pDBHandle;
            pDBHandle = NULL;

            if (not filecopy(settingsfile, backupName.str()))
            {
                LOG4CXX_ERROR(settingsLog, "Could not make backup of old settings.db");
                return;
            }

            if (fileVersion > DB_VERSION)
            {
                //Reopen a backup or an empty file
                if (not restoreClosestDbBackup(settingsfile))
                {
                    settingsFileExists = false; // Trigger a setup downstairs
                    fstream f(settingsfile.c_str(), fstream::out | fstream::trunc); //empty the file
                }
            }

            pDBHandle = new settings::DB(settingsfile);

            if (!pDBHandle->connect())
            {
                LOG4CXX_ERROR(settingsLog, "Could not reopen settings.db: '" << settingsfile << "', error '" << pDBHandle->getLasterror() << "'");
                delete pDBHandle;
                pDBHandle = NULL;
                return;
            }

            if (settingsFileExists && getVersion() < DB_VERSION)
            {
                // DO migrate up to DB_VERSION here, 0->1 requires none?
                if (not setVersion(DB_VERSION))
                {
                    LOG4CXX_ERROR(settingsLog, "migration failed");
                }
            }
        }
    }

    if (not settingsFileExists)
    //Verify database structure
    {
        LOG4CXX_DEBUG(settingsLog, "Verifying database structure");

        if (!pDBHandle->prepare("create table if not exists setting (setting TEXT, value TEXT, type INT, domain TEXT, UNIQUE(setting, domain))"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not create/open settings table: '" << pDBHandle->getLasterror() << "'");
            return;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Create table setting failed '" << pDBHandle->getLasterror() << "'");
            return;
        }

        if (!pDBHandle->prepare("create table if not exists parameter (setting_id INT, key TEXT, value TEXT, type INT, UNIQUE(setting_id, key))"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not create/open parameters table: '" << pDBHandle->getLasterror() << "'");
            return;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Create table parameter failed '" << pDBHandle->getLasterror() << "'");
            return;
        }

        if (not setVersion(DB_VERSION))
        {
            LOG4CXX_ERROR(settingsLog, "Setting db version failed");
        }
        LOG4CXX_DEBUG(settingsLog, "Database structure verified");
    }

}

Settings::~Settings()
{
    LOG4CXX_INFO(settingsLog, "Deleting settings instance");
    delete pDBHandle;
    pDBHandle=NULL;
}

bool Settings::setVersion(int version)
{
    try {
        pthread_mutex_lock( &settings_mutex );
        if (!pDBHandle->prepare("create table if not exists version (number INT)"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not create version table: '" << pDBHandle->getLasterror().c_str() << "'");
            throw 1;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Create table version failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }
        pthread_mutex_unlock(&settings_mutex);
        int version = getVersion();
        pthread_mutex_lock( &settings_mutex );
        if (version == 0)
        {
            LOG4CXX_DEBUG(settingsLog, "insert into version " << version);
            if (!pDBHandle->prepare("insert into version values(?)"))
            {
                LOG4CXX_ERROR(settingsLog, "Could not prepare insert '" << pDBHandle->getLasterror() << "'");
                throw 1;
            }
        }
        else
        {
            LOG4CXX_DEBUG(settingsLog, "update version " << version);
            if (!pDBHandle->prepare("update version set number=?"))
            {
                LOG4CXX_ERROR(settingsLog, "Could not prepare update '" << pDBHandle->getLasterror() << "'");
                throw 1;
            }
        }

        if (!pDBHandle->bind(1, version))
        {
            LOG4CXX_ERROR(settingsLog, "Bind failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Could not perform insert version number '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }
        pthread_mutex_unlock( &settings_mutex );
    } catch(int e) {
        LOG4CXX_TRACE(settingsLog, "Set version exception was thrown");
        pthread_mutex_unlock(&settings_mutex);
        return false;
    }

    return true;
}

int Settings::getVersion()
{
    int version = 0;
    try {
        pthread_mutex_lock( &settings_mutex );
        if (!pDBHandle->prepare("SELECT number FROM version"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not read version: '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        DBResult result;
        if (!pDBHandle->perform(&result))
        {
            LOG4CXX_ERROR(settingsLog, "Query failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        while (result.loadRow())
        {
            version = result.getInt(0);
        }
        pthread_mutex_unlock( &settings_mutex );
    } catch(int e) {
        LOG4CXX_TRACE(settingsLog, "Get version exception was thrown");
        pthread_mutex_unlock(&settings_mutex);
        return 0;
    }

    return version;
}

ostream& operator <<(ostream& os, const SettingsItem& i)
{
    return os << i.getName() << '@' << i.getDomain() << '=' << i.getValue() << endl;;
}

// Loads a setting with key 'key' from database
bool Settings::read(SettingsItem &item, const string &_setting)
{
    string setting = _setting;
    Utils::trim(setting);

    if (pDBHandle == NULL)
    {
        LOG4CXX_ERROR(settingsLog, "Database not open");
        return false;
    }

    // Try to read the setting values
    try {
        pthread_mutex_lock( &settings_mutex );
        if (!pDBHandle->prepare("SELECT rowid, value, type FROM setting WHERE setting=? ORDER BY domain=?"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not read setting: '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        LOG4CXX_TRACE(settingsLog, "Binding setting: '" << setting << "' in domain: " << mCurrentDomain);
        if (!pDBHandle->bind(1, setting.c_str()) || !pDBHandle->bind(2, mCurrentDomain.c_str()))
        {
            LOG4CXX_ERROR(settingsLog, "Bind failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        DBResult result;
        if (!pDBHandle->perform(&result))
        {
            LOG4CXX_ERROR(settingsLog, "Query failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        int count = 0;
        while (result.loadRow())
        {
            item.mRowid = result.getInt(0);
            item.mName = setting;
            item.mDomain = mCurrentDomain;
            item.mValue = result.getText(1);
            item.mType = (SettingsItem::Type) result.getInt(2);
            count++;
        }
        pthread_mutex_unlock( &settings_mutex );

        // Check if we got any rows
        if (count == 0)
            return false;

        pthread_mutex_lock( &settings_mutex );
        // Try to read the setting parameter values
        if (!pDBHandle->prepare("SELECT key, value, type FROM parameter WHERE setting_id=?"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not read parameter: '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        if (!pDBHandle->bind(1, item.mRowid))
        {
            LOG4CXX_ERROR(settingsLog, "Bind failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        DBResult result2;
        if (!pDBHandle->perform(&result2))
        {
            LOG4CXX_ERROR(settingsLog, "Query failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        count = 0;
        while (result.loadRow())
        {
            string key = result.getText(0);
            string value = result.getText(0);
            item.mParameters[key] = value;
        }
        pthread_mutex_unlock( &settings_mutex );
    } catch(int e) {
        LOG4CXX_TRACE(settingsLog, "Read exception was thrown");
        pthread_mutex_unlock( &settings_mutex );
        return false;
    }

    return true;
}

// Overwrites values and parameters for a setting in database
bool Settings::write(SettingsItem &item)
{
    if (pDBHandle == NULL)
    {
        LOG4CXX_ERROR(settingsLog, "Database not open");
        return false;
    }

    // Try to insert the value
    try {
        pthread_mutex_lock( &settings_mutex );
        if (!pDBHandle->prepare("INSERT OR REPLACE INTO setting (setting, value, type, domain) VALUES (?,?,?,?)"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not create/open setting table: '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }
        string name = item.getName();
        string value = item.getValue();
        if (!pDBHandle->bind(1, name.c_str()) || !pDBHandle->bind(2, value.c_str()) || !pDBHandle->bind(3, (int) item.getType()) || !pDBHandle->bind(4, mCurrentDomain.c_str()))
        {
            LOG4CXX_ERROR(settingsLog, "Bind failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Query failed while inserting '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }
        pthread_mutex_unlock( &settings_mutex );
    } catch(int e) {
        LOG4CXX_TRACE(settingsLog, "Write exception was thrown");
        pthread_mutex_unlock( &settings_mutex );
        return false;
    }

    return true;
}

// Overwrites values and parameters for a setting in database
bool Settings::update(SettingsItem &item)
{
    if (pDBHandle == NULL)
    {
        LOG4CXX_ERROR(settingsLog, "Database not open");
        return false;
    }

    // Try to insert the value
    try {
        pthread_mutex_lock( &settings_mutex );
        if (!pDBHandle->prepare("UPDATE setting SET value=? WHERE rowid=?"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not create update setting query: '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        if (!pDBHandle->bind(1, item.getValue().c_str()) || !pDBHandle->bind(2, item.getRowid()))
        {
            LOG4CXX_ERROR(settingsLog, "Bind failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Query failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }
        pthread_mutex_unlock( &settings_mutex );
    } catch(int e) {
        LOG4CXX_TRACE(settingsLog, "Write exception was thrown");
        pthread_mutex_unlock( &settings_mutex );
        return false;
    }

    return true;

}

bool Settings::restoreClosestDbBackup(string settingsfile)
{
    int v = DB_VERSION;
    for (; v >= 0; --v)
    {
        stringstream closestDbBackupFilename;
        closestDbBackupFilename << settingsfile << v;

        if (not fstream(closestDbBackupFilename.str().c_str(), fstream::in))
        {
            continue;
        }

        if (not filecopy(closestDbBackupFilename.str(), settingsfile))
        {
            LOG4CXX_ERROR(settingsLog, "Failed to restore backup settings.db");
            return false;
        }
        return true;
    }
    LOG4CXX_ERROR(settingsLog, "Failed to restore backup settings.db");
    return false;
}
<commit_msg>Don't overwrite new version number<commit_after>/*
 * Copyright (C) 2012 Kolibre
 *
 * This file is part of kolibre-clientcore.
 *
 * Kolibre-clientcore is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Kolibre-clientcore 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 kolibre-clientcore. If not, see <http://www.gnu.org/licenses/>.
 */

#include "Db.h"
#include "Settings.h"

#include <string>
#include <iostream>
#include <fstream>

#include <log4cxx/logger.h>

// create logger which will become a child to logger kolibre.clientcore
log4cxx::LoggerPtr settingsLog(log4cxx::Logger::getLogger("kolibre.clientcore.settings"));

using namespace std;

#define DB_VERSION 1

bool filecopy(string fromfile, string tofile)
{
    LOG4CXX_DEBUG(settingsLog, "copy(" << fromfile << ", "<< tofile << ")");

    fstream infile(fromfile.c_str(), fstream::in | fstream::binary);
    fstream outfile(tofile.c_str(), fstream::out | fstream::trunc | fstream::binary);
    if (not (outfile and infile))
    {
        return false;
    }
    outfile << infile.rdbuf();

    return true;
}

Settings * Settings::pinstance = 0;

Settings * Settings::Instance()
{
    if (pinstance == 0)
    {
        pinstance = new Settings;
    }

    return pinstance;
}

void Settings::DeleteInstance()
{
    pthread_mutex_lock(&settings_mutex);
    //Nothing going on
    pthread_mutex_unlock(&settings_mutex);
    pthread_mutex_destroy(&settings_mutex);
    Settings::pinstance=NULL;
    delete this;
}

Settings::Settings():
    mCurrentDomain("localhost")
{
    LOG4CXX_TRACE(settingsLog, "Constructor");

    pthread_mutex_init (&settings_mutex, NULL);
    LOG4CXX_TRACE(settingsLog, "Settings mutex initialized");

    string settingsfile = Utils::getDatapath() + "settings.db";

    LOG4CXX_INFO(settingsLog, "Opening settings.db in '"<< settingsfile << "'");

    bool settingsFileExists = fstream(settingsfile.c_str(), fstream::in).is_open();

    pDBHandle = new settings::DB(settingsfile);

    if (!pDBHandle->connect())
    {
        LOG4CXX_ERROR(settingsLog, "Could not open settings.db: '" << settingsfile << "', error '" << pDBHandle->getLasterror() << "'");

        delete pDBHandle;
        pDBHandle = NULL;
        return;
    }

    if (settingsFileExists)
    {
        const int fileVersion = getVersion();

        if (fileVersion != DB_VERSION)
        {
            LOG4CXX_DEBUG(settingsLog, "DB_VERSION missmatch, migrating db");
            stringstream backupName;
            backupName << settingsfile << fileVersion;

            delete pDBHandle;
            pDBHandle = NULL;

            if (not filecopy(settingsfile, backupName.str()))
            {
                LOG4CXX_ERROR(settingsLog, "Could not make backup of old settings.db");
                return;
            }

            if (fileVersion > DB_VERSION)
            {
                //Reopen a backup or an empty file
                if (not restoreClosestDbBackup(settingsfile))
                {
                    settingsFileExists = false; // Trigger a setup downstairs
                    fstream f(settingsfile.c_str(), fstream::out | fstream::trunc); //empty the file
                }
            }

            pDBHandle = new settings::DB(settingsfile);

            if (!pDBHandle->connect())
            {
                LOG4CXX_ERROR(settingsLog, "Could not reopen settings.db: '" << settingsfile << "', error '" << pDBHandle->getLasterror() << "'");
                delete pDBHandle;
                pDBHandle = NULL;
                return;
            }

            if (settingsFileExists && getVersion() < DB_VERSION)
            {
                // DO migrate up to DB_VERSION here, 0->1 requires none?
                if (not setVersion(DB_VERSION))
                {
                    LOG4CXX_ERROR(settingsLog, "migration failed");
                }
            }
        }
    }

    if (not settingsFileExists)
    //Verify database structure
    {
        LOG4CXX_DEBUG(settingsLog, "Verifying database structure");

        if (!pDBHandle->prepare("create table if not exists setting (setting TEXT, value TEXT, type INT, domain TEXT, UNIQUE(setting, domain))"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not create/open settings table: '" << pDBHandle->getLasterror() << "'");
            return;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Create table setting failed '" << pDBHandle->getLasterror() << "'");
            return;
        }

        if (!pDBHandle->prepare("create table if not exists parameter (setting_id INT, key TEXT, value TEXT, type INT, UNIQUE(setting_id, key))"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not create/open parameters table: '" << pDBHandle->getLasterror() << "'");
            return;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Create table parameter failed '" << pDBHandle->getLasterror() << "'");
            return;
        }

        if (not setVersion(DB_VERSION))
        {
            LOG4CXX_ERROR(settingsLog, "Setting db version failed");
        }
        LOG4CXX_DEBUG(settingsLog, "Database structure verified");
    }

}

Settings::~Settings()
{
    LOG4CXX_INFO(settingsLog, "Deleting settings instance");
    delete pDBHandle;
    pDBHandle=NULL;
}

bool Settings::setVersion(int version)
{
    LOG4CXX_DEBUG(settingsLog, "Setting database version to " << version);
    try {
        pthread_mutex_lock( &settings_mutex );
        if (!pDBHandle->prepare("create table if not exists version (number INT)"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not create version table: '" << pDBHandle->getLasterror().c_str() << "'");
            throw 1;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Create table version failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }
        pthread_mutex_unlock(&settings_mutex);
        int previousVersion = getVersion();
        pthread_mutex_lock( &settings_mutex );
        if (previousVersion == 0)
        {
            LOG4CXX_DEBUG(settingsLog, "insert into version " << version);
            if (!pDBHandle->prepare("insert into version values(?)"))
            {
                LOG4CXX_ERROR(settingsLog, "Could not prepare insert '" << pDBHandle->getLasterror() << "'");
                throw 1;
            }
        }
        else
        {
            LOG4CXX_DEBUG(settingsLog, "update version " << version);
            if (!pDBHandle->prepare("update version set number=?"))
            {
                LOG4CXX_ERROR(settingsLog, "Could not prepare update '" << pDBHandle->getLasterror() << "'");
                throw 1;
            }
        }

        if (!pDBHandle->bind(1, version))
        {
            LOG4CXX_ERROR(settingsLog, "Bind failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Could not perform insert version number '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }
        pthread_mutex_unlock( &settings_mutex );
    } catch(int e) {
        LOG4CXX_TRACE(settingsLog, "Set version exception was thrown");
        pthread_mutex_unlock(&settings_mutex);
        return false;
    }

    return true;
}

int Settings::getVersion()
{
    int version = 0;
    try {
        pthread_mutex_lock( &settings_mutex );
        if (!pDBHandle->prepare("SELECT number FROM version"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not read version: '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        DBResult result;
        if (!pDBHandle->perform(&result))
        {
            LOG4CXX_ERROR(settingsLog, "Query failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        while (result.loadRow())
        {
            version = result.getInt(0);
        }
        pthread_mutex_unlock( &settings_mutex );
    } catch(int e) {
        LOG4CXX_TRACE(settingsLog, "Get version exception was thrown");
        pthread_mutex_unlock(&settings_mutex);
        return 0;
    }

    return version;
}

ostream& operator <<(ostream& os, const SettingsItem& i)
{
    return os << i.getName() << '@' << i.getDomain() << '=' << i.getValue() << endl;;
}

// Loads a setting with key 'key' from database
bool Settings::read(SettingsItem &item, const string &_setting)
{
    string setting = _setting;
    Utils::trim(setting);

    if (pDBHandle == NULL)
    {
        LOG4CXX_ERROR(settingsLog, "Database not open");
        return false;
    }

    // Try to read the setting values
    try {
        pthread_mutex_lock( &settings_mutex );
        if (!pDBHandle->prepare("SELECT rowid, value, type FROM setting WHERE setting=? ORDER BY domain=?"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not read setting: '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        LOG4CXX_TRACE(settingsLog, "Binding setting: '" << setting << "' in domain: " << mCurrentDomain);
        if (!pDBHandle->bind(1, setting.c_str()) || !pDBHandle->bind(2, mCurrentDomain.c_str()))
        {
            LOG4CXX_ERROR(settingsLog, "Bind failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        DBResult result;
        if (!pDBHandle->perform(&result))
        {
            LOG4CXX_ERROR(settingsLog, "Query failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        int count = 0;
        while (result.loadRow())
        {
            item.mRowid = result.getInt(0);
            item.mName = setting;
            item.mDomain = mCurrentDomain;
            item.mValue = result.getText(1);
            item.mType = (SettingsItem::Type) result.getInt(2);
            count++;
        }
        pthread_mutex_unlock( &settings_mutex );

        // Check if we got any rows
        if (count == 0)
            return false;

        pthread_mutex_lock( &settings_mutex );
        // Try to read the setting parameter values
        if (!pDBHandle->prepare("SELECT key, value, type FROM parameter WHERE setting_id=?"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not read parameter: '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        if (!pDBHandle->bind(1, item.mRowid))
        {
            LOG4CXX_ERROR(settingsLog, "Bind failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        DBResult result2;
        if (!pDBHandle->perform(&result2))
        {
            LOG4CXX_ERROR(settingsLog, "Query failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        count = 0;
        while (result.loadRow())
        {
            string key = result.getText(0);
            string value = result.getText(0);
            item.mParameters[key] = value;
        }
        pthread_mutex_unlock( &settings_mutex );
    } catch(int e) {
        LOG4CXX_TRACE(settingsLog, "Read exception was thrown");
        pthread_mutex_unlock( &settings_mutex );
        return false;
    }

    return true;
}

// Overwrites values and parameters for a setting in database
bool Settings::write(SettingsItem &item)
{
    if (pDBHandle == NULL)
    {
        LOG4CXX_ERROR(settingsLog, "Database not open");
        return false;
    }

    // Try to insert the value
    try {
        pthread_mutex_lock( &settings_mutex );
        if (!pDBHandle->prepare("INSERT OR REPLACE INTO setting (setting, value, type, domain) VALUES (?,?,?,?)"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not create/open setting table: '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }
        string name = item.getName();
        string value = item.getValue();
        if (!pDBHandle->bind(1, name.c_str()) || !pDBHandle->bind(2, value.c_str()) || !pDBHandle->bind(3, (int) item.getType()) || !pDBHandle->bind(4, mCurrentDomain.c_str()))
        {
            LOG4CXX_ERROR(settingsLog, "Bind failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Query failed while inserting '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }
        pthread_mutex_unlock( &settings_mutex );
    } catch(int e) {
        LOG4CXX_TRACE(settingsLog, "Write exception was thrown");
        pthread_mutex_unlock( &settings_mutex );
        return false;
    }

    return true;
}

// Overwrites values and parameters for a setting in database
bool Settings::update(SettingsItem &item)
{
    if (pDBHandle == NULL)
    {
        LOG4CXX_ERROR(settingsLog, "Database not open");
        return false;
    }

    // Try to insert the value
    try {
        pthread_mutex_lock( &settings_mutex );
        if (!pDBHandle->prepare("UPDATE setting SET value=? WHERE rowid=?"))
        {
            LOG4CXX_ERROR(settingsLog, "Could not create update setting query: '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        if (!pDBHandle->bind(1, item.getValue().c_str()) || !pDBHandle->bind(2, item.getRowid()))
        {
            LOG4CXX_ERROR(settingsLog, "Bind failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }

        if (!pDBHandle->perform())
        {
            LOG4CXX_ERROR(settingsLog, "Query failed '" << pDBHandle->getLasterror() << "'");
            throw 1;
        }
        pthread_mutex_unlock( &settings_mutex );
    } catch(int e) {
        LOG4CXX_TRACE(settingsLog, "Write exception was thrown");
        pthread_mutex_unlock( &settings_mutex );
        return false;
    }

    return true;

}

bool Settings::restoreClosestDbBackup(string settingsfile)
{
    int v = DB_VERSION;
    for (; v >= 0; --v)
    {
        stringstream closestDbBackupFilename;
        closestDbBackupFilename << settingsfile << v;

        if (not fstream(closestDbBackupFilename.str().c_str(), fstream::in))
        {
            continue;
        }

        if (not filecopy(closestDbBackupFilename.str(), settingsfile))
        {
            LOG4CXX_ERROR(settingsLog, "Failed to restore backup settings.db");
            return false;
        }
        return true;
    }
    LOG4CXX_ERROR(settingsLog, "Failed to restore backup settings.db");
    return false;
}
<|endoftext|>


<commit_before>/*
 * $Id: XMH323EndPoint.cpp,v 1.37 2008/08/29 08:50:22 hfriederich Exp $
 *
 * Copyright (c) 2005-2007 XMeeting Project ("http://xmeeting.sf.net").
 * All rights reserved.
 * Copyright (c) 2005-2007 Hannes Friederich. All rights reserved.
 */

#include "XMH323EndPoint.h"

#include <ptclib/random.h>
#include <opal/call.h>
#include <h323/h323pdu.h>
#include <h323/gkclient.h>
#include <ptclib/url.h>
#include <ptclib/pils.h>

#include <h224/h323h224.h>
//#include <h323/h46018handler.h>

#include "XMCallbackBridge.h"
#include "XMOpalManager.h"
#include "XMH323Connection.h"

#include <opal/transcoders.h>

#pragma mark Init & Deallocation

class XMH323Gatekeeper : public H323Gatekeeper
{
  PCLASSINFO(XMH323Gatekeeper, H323Gatekeeper);

public:
  XMH323Gatekeeper(XMH323EndPoint & endpoint, H323Transport * transport);
  virtual bool MakeRequest(Request & request);
  virtual bool OnReceiveRegistrationConfirm(const H225_RegistrationConfirm &);

private:
  XMH323EndPoint & ep;
};

class XMH323GkRegistrationThread : public PThread
{
  PCLASSINFO(XMH323GkRegistrationThread, PThread);

public:
  XMH323GkRegistrationThread(XMH323EndPoint *ep,
                             const PString & address,
                             const PString & terminalAlias1,
                             const PString & terminalAlias2,
                             const PString & password);
  virtual void Main();
private:
  XMH323EndPoint *ep;
  PString address;
  PString terminalAlias1;
  PString terminalAlias2;
  PString password;
};

//OPAL_REGISTER_H224_CAPABILITY();

XMH323EndPoint::XMH323EndPoint(OpalManager & manager)
: H323EndPoint(manager),
  isListening(false),
  gatekeeperAddress(""),
  hasGkRegistrationThread(false),
  connectionToken("")
{
  // Don't use OPAL's bandwidth management system as this is not flexible enough
  // for our purposes
	SetInitialBandwidth(UINT_MAX);

  // Use TTL values when doing RRQ. 30s seems appropriate
  SetGatekeeperTimeToLive(PTimeInterval(0, 30));

  // Make data streams work. This should be removed in OPAL
	//autoStartReceiveData = autoStartTransmitData = true;

  // Manually register H.460 features as the plugin system seems not to work
  //features.AddFeature(new H460_FeatureStd18());

  releasingConnections.DisallowDeleteObjects();
}

XMH323EndPoint::~XMH323EndPoint()
{
}

#pragma mark Endpoint Setup

bool XMH323EndPoint::EnableListeners(bool flag)
{
	bool result = true;

	if (flag == true) {
		if (isListening == false) {
			result = StartListeners(GetDefaultListeners());
			if (result == true) {
				isListening = true;
			}
		}
	} else {
		if (isListening == true) {
			RemoveListener(NULL);
			isListening = false;
		}
	}

	return result;
}

void XMH323EndPoint::SetGatekeeper(const PString & address,
                                   const PString & terminalAlias1,
                                   const PString & terminalAlias2,
                                   const PString & password,
                                   bool block)
{
  // wait until any running GK registration thread did finish
  while (hasGkRegistrationThread) {
    usleep(100*1000); // wait 100 ms
  }

  if (block == true) {
    DoSetGatekeeper(address, terminalAlias1, terminalAlias2, password);
  } else {
    // if a gatekeeper is present, only force re-registration in for the following
    // fail reasons:
    // - InvalidListener
    // - DuplicateAlias
    // - SecurityDenied
    // In the other cases, the subsystem will automatically try to re-register
    if (gatekeeper != NULL) {
      H323Gatekeeper::RegistrationFailReasons failReason = gatekeeper->GetRegistrationFailReason();

      switch (failReason) {
        case H323Gatekeeper::InvalidListener:
        case H323Gatekeeper::DuplicateAlias:
        case H323Gatekeeper::SecurityDenied:
          break; // continue;
        default:
          return; // don't force re-register
      }
    }
    // Run a separate thread for the registration
    hasGkRegistrationThread = true;
    new XMH323GkRegistrationThread(this, address, terminalAlias1, terminalAlias2, password);
  }
}

void XMH323EndPoint::DoSetGatekeeper(const PString & address,
                                     const PString & terminalAlias1,
                                     const PString & terminalAlias2,
                                     const PString & password)
{
  // Use a gatekeeper if terminalAlias1 is not empty
  if (!terminalAlias1.IsEmpty()) {
    // Change the gatekeeper if the address differs
    if (gatekeeper != NULL && address.Compare(gatekeeperAddress) != PObject::EqualTo) {
      RemoveGatekeeper();
      _XMHandleGatekeeperUnregistration();
    }

    gatekeeperAddress = address;

    // update the alias names list
    {
      PWaitAndSignal m(GetAliasNamesMutex());
      SetLocalUserName(terminalAlias1);
      if (!terminalAlias2.IsEmpty()) {
        AddAliasName(terminalAlias2);
      }
    }

    if (!password.IsEmpty()) {
      // This should automatically trigger an URQ/RRQ sequence
      SetGatekeeperPassword(password, terminalAlias1);
    } else if (gatekeeper != NULL) {
      // Do a full RRQ with the updated terminal aliases
      gatekeeper->OnTerminalAliasChanged();
    }

    if (gatekeeper == NULL) {
      bool result = UseGatekeeper(address);
      if (result == false) {
        if (gatekeeper == NULL) {
          // GRQ failed
          _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_GatekeeperNotFound);
        }
      } else if (result == true && gatekeeper != NULL) {
        // When the initial OnRegistrationConfirm() is called, gatekeeper is still NULL.
        // Handle the RCF here
        HandleRegistrationConfirm();
      }
    }
  } else {
    // Not using a gatekeeper
    if (gatekeeper != NULL) {
      RemoveGatekeeper();
      _XMHandleGatekeeperUnregistration();
    }
    PWaitAndSignal m(GetAliasNamesMutex());
    SetLocalUserName(GetManager().GetDefaultUserName());
  }
}

void XMH323EndPoint::HandleGkRegistrationThreadFinished()
{
  hasGkRegistrationThread = false;
}

void XMH323EndPoint::HandleRegistrationConfirm() const
{
  if (gatekeeper != NULL) {
    PString gatekeeperName = gatekeeper->GetName();
    PStringList aliases = GetAliasNames();
    PStringStream aliasStream;
    for (PINDEX i = 0; i < aliases.GetSize(); i++) {
      if (i != 0) {
        aliasStream << "\n";
      }
      aliasStream << aliases[i];
    }
    _XMHandleGatekeeperRegistration(gatekeeperName, aliasStream);
  }
}

void XMH323EndPoint::HandleNetworkStatusChange()
{
}

H323Gatekeeper * XMH323EndPoint::CreateGatekeeper(H323Transport * transport)
{
  return new XMH323Gatekeeper(*this, transport);
}

void XMH323EndPoint::OnRegistrationConfirm()
{
	H323EndPoint::OnRegistrationConfirm();
  HandleRegistrationConfirm();
}

#pragma mark Getting call statistics

void XMH323EndPoint::GetCallStatistics(XMCallStatisticsRecord *callStatistics)
{
	PSafePtr<H323Connection> connection = FindConnectionWithLock(connectionToken, PSafeReadOnly);

	if (connection != NULL)
	{
		unsigned roundTripDelay = connection->GetRoundTripDelay().GetMilliSeconds();
		callStatistics->roundTripDelay = roundTripDelay;

    XMOpalManager::ExtractCallStatistics(*connection, callStatistics);
	}
}

#pragma mark Overriding Callbacks

void XMH323EndPoint::OnEstablished(OpalConnection & connection)
{
	XMOpalManager * manager = (XMOpalManager *)(&GetManager());

	connectionToken = connection.GetToken();

    const PString & remotePartyName = connection.GetRemotePartyName();
    const PString & remotePartyNumber = connection.GetRemotePartyNumber();
    const PString & remotePartyApplication = connection.GetRemoteApplication();

    const H323Transport *signallingChannel = ((H323Connection & )connection).GetSignallingChannel();
    const PString & remotePartyAddress = signallingChannel->GetRemoteAddress().GetHostName();

	manager->SetCallInformation(connectionToken,
								remotePartyName,
								remotePartyNumber,
								remotePartyAddress,
								remotePartyApplication,
								XMCallProtocol_H323);

	H323EndPoint::OnEstablished(connection);
}

void XMH323EndPoint::OnReleased(OpalConnection & connection)
{
	if (connection.GetToken() == connectionToken)
	{
		XMOpalManager * manager = (XMOpalManager *)(&GetManager());
		PString empty = "";

		manager->SetCallInformation(connectionToken,
									empty,
									empty,
									empty,
									empty,
									XMCallProtocol_H323);

		connectionToken = "";
	}

	H323EndPoint::OnReleased(connection);
}

H323Connection * XMH323EndPoint::CreateConnection(OpalCall & call,
												  const PString & token,
												  void * userData,
												  OpalTransport & transport,
												  const PString & alias,
												  const H323TransportAddress & address,
												  H323SignalPDU * setupPDU,
												  unsigned options,
                                                  OpalConnection::StringOptions * stringOptions)
{
	return new XMH323Connection(call, *this, token, alias, address, options, stringOptions);
}

#pragma mark -
#pragma mark H.460 support

bool XMH323EndPoint::OnSendFeatureSet(unsigned id, H225_FeatureSet & message)
{
	return features.SendFeature(id, message);
}

void XMH323EndPoint::OnReceiveFeatureSet(unsigned id, const H225_FeatureSet & message)
{
	features.ReceiveFeature(id, message);
}

#pragma mark -
#pragma mark Clean Up

void XMH323EndPoint::CleanUp()
{
    PWaitAndSignal m(releasingConnectionsMutex);

    for (PINDEX i = 0; i < releasingConnections.GetSize(); i++)
    {
        releasingConnections[i].CleanUp();
    }
}

void XMH323EndPoint::AddReleasingConnection(XMH323Connection * connection)
{
    PWaitAndSignal m(releasingConnectionsMutex);

    releasingConnections.Append(connection);
}

void XMH323EndPoint::RemoveReleasingConnection(XMH323Connection * connection)
{
    PWaitAndSignal m(releasingConnectionsMutex);

    releasingConnections.Remove(connection);
}

#pragma mark -
#pragma mark XMH323Gatekeeper methods

XMH323Gatekeeper::XMH323Gatekeeper(XMH323EndPoint & theEp, H323Transport * transport)
: H323Gatekeeper(theEp, transport),
  ep(theEp)
{
}

bool XMH323Gatekeeper::MakeRequest(Request & request)
{
  bool result = H323Gatekeeper::MakeRequest(request);

  if (request.requestPDU.GetPDU().GetTag() == H225_RasMessage::e_registrationRequest) {
    if (result == false) {
      switch(request.responseResult) {
        case Request::AwaitingResponse:
        case Request::NoResponseReceived:
          if (GetRegistrationFailReason() == H323Gatekeeper::UnregisteredByGatekeeper) {
            // GK unregistered client and obviously went offline afterwards
            _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_UnregisteredByGatekeeper);
          } else {
            _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_GatekeeperNotFound);
          }
          break;

        // notify registration failures here as H323EndPoint::OnRegistrationReject()
        // is called without updated registrationFailReason info
        case Request::RejectReceived:
          switch(request.rejectReason) {

            case H225_RegistrationRejectReason::e_invalidCallSignalAddress :
              _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_TransportError);
              break;

            case H225_RegistrationRejectReason::e_duplicateAlias :
              _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_DuplicateAlias);
              break;

            case H225_RegistrationRejectReason::e_securityDenial :
              _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_SecurityDenial);
              break;

            case H225_RegistrationRejectReason::e_discoveryRequired:
            case H225_RegistrationRejectReason::e_fullRegistrationRequired:
              // Do nothing, as we try to re-register ASAP.
              break;

            default:
              _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_UnknownRegistrationFailure);
              break;
          }
          break;

        case Request::BadCryptoTokens:
          _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_SecurityDenial);
          break;

        default:
          // Do nothing
          break;
      }
    }
  }

  return result;
}

bool XMH323Gatekeeper::OnReceiveRegistrationConfirm(const H225_RegistrationConfirm & confirm) {
  // possible race condition since RCF may cause additional aliases to be added
  PWaitAndSignal m(ep.GetAliasNamesMutex());
  return H323Gatekeeper::OnReceiveRegistrationConfirm(confirm);
}

#pragma mark -
#pragma mark XMH323GkRegistrationThread methods

XMH323GkRegistrationThread::XMH323GkRegistrationThread(XMH323EndPoint *theEp,
                                                       const PString & theAddress,
                                                       const PString & theTerminalAlias1,
                                                       const PString & theTerminalAlias2,
                                                       const PString & thePassword)
: PThread(10000), // stack size no longer used apparently
  ep(theEp),
  address(theAddress),
  terminalAlias1(theTerminalAlias1),
  terminalAlias2(theTerminalAlias2),
  password(thePassword)
{
  Resume();
}

void XMH323GkRegistrationThread::Main()
{
  ep->DoSetGatekeeper(address, terminalAlias1, terminalAlias2, password);
  ep->HandleGkRegistrationThreadFinished();
}
<commit_msg>- MAJOR: Workaround to ensure the gatekeeper transport uses STUN - MINOR: Use reference instead of pointer in GK update thread<commit_after>/*
 * $Id: XMH323EndPoint.cpp,v 1.38 2008/09/03 22:28:05 hfriederich Exp $
 *
 * Copyright (c) 2005-2007 XMeeting Project ("http://xmeeting.sf.net").
 * All rights reserved.
 * Copyright (c) 2005-2007 Hannes Friederich. All rights reserved.
 */

#include "XMH323EndPoint.h"

#include <ptclib/random.h>
#include <opal/call.h>
#include <h323/h323pdu.h>
#include <h323/gkclient.h>
#include <ptclib/url.h>
#include <ptclib/pils.h>

#include <h224/h323h224.h>
//#include <h323/h46018handler.h>

#include "XMCallbackBridge.h"
#include "XMOpalManager.h"
#include "XMH323Connection.h"

#include <opal/transcoders.h>

#pragma mark Init & Deallocation

class XMOpalTransportUDP : public OpalTransportUDP
{
  PCLASSINFO(XMOpalTransportUDP, OpalTransportUDP);

public:
  XMOpalTransportUDP(OpalEndPoint & endpoint, PIPSocket::Address binding = PIPSocket::GetDefaultIpAny(), WORD port = 0, bool reuseAddr = false);
};

class XMH323Gatekeeper : public H323Gatekeeper
{
  PCLASSINFO(XMH323Gatekeeper, H323Gatekeeper);

public:
  XMH323Gatekeeper(XMH323EndPoint & endpoint, H323Transport * transport);
  virtual H323Transport * CreateTransport(PIPSocket::Address binding = PIPSocket::GetDefaultIpAny(), WORD port = 0, bool reuseAddr = false);
  virtual bool MakeRequest(Request & request);
  virtual bool OnReceiveRegistrationConfirm(const H225_RegistrationConfirm &);

private:
  XMH323EndPoint & ep;
};

class XMH323GkRegistrationThread : public PThread
{
  PCLASSINFO(XMH323GkRegistrationThread, PThread);

public:
  XMH323GkRegistrationThread(XMH323EndPoint & ep,
                             const PString & address,
                             const PString & terminalAlias1,
                             const PString & terminalAlias2,
                             const PString & password);
  virtual void Main();
private:
  XMH323EndPoint & ep;
  PString address;
  PString terminalAlias1;
  PString terminalAlias2;
  PString password;
};

//OPAL_REGISTER_H224_CAPABILITY();

XMH323EndPoint::XMH323EndPoint(OpalManager & manager)
: H323EndPoint(manager),
  isListening(false),
  gatekeeperAddress(""),
  hasGkRegistrationThread(false),
  connectionToken("")
{
  // Don't use OPAL's bandwidth management system as this is not flexible enough
  // for our purposes
	SetInitialBandwidth(UINT_MAX);

  // Use TTL values when doing RRQ. 30s seems appropriate
  SetGatekeeperTimeToLive(PTimeInterval(0, 30));

  // Make data streams work. This should be removed in OPAL
	//autoStartReceiveData = autoStartTransmitData = true;

  // Manually register H.460 features as the plugin system seems not to work
  //features.AddFeature(new H460_FeatureStd18());

  releasingConnections.DisallowDeleteObjects();
}

XMH323EndPoint::~XMH323EndPoint()
{
}

#pragma mark Endpoint Setup

bool XMH323EndPoint::EnableListeners(bool flag)
{
	bool result = true;

	if (flag == true) {
		if (isListening == false) {
			result = StartListeners(GetDefaultListeners());
			if (result == true) {
				isListening = true;
			}
		}
	} else {
		if (isListening == true) {
			RemoveListener(NULL);
			isListening = false;
		}
	}

	return result;
}

void XMH323EndPoint::SetGatekeeper(const PString & address,
                                   const PString & terminalAlias1,
                                   const PString & terminalAlias2,
                                   const PString & password,
                                   bool block)
{
  // wait until any running GK registration thread did finish
  while (hasGkRegistrationThread) {
    usleep(100*1000); // wait 100 ms
  }

  if (block == true) {
    DoSetGatekeeper(address, terminalAlias1, terminalAlias2, password);
  } else {
    // if a gatekeeper is present, only force re-registration in for the following
    // fail reasons:
    // - InvalidListener
    // - DuplicateAlias
    // - SecurityDenied
    // In the other cases, the subsystem will automatically try to re-register
    if (gatekeeper != NULL) {
      H323Gatekeeper::RegistrationFailReasons failReason = gatekeeper->GetRegistrationFailReason();

      switch (failReason) {
        case H323Gatekeeper::InvalidListener:
        case H323Gatekeeper::DuplicateAlias:
        case H323Gatekeeper::SecurityDenied:
          break; // continue;
        default:
          return; // don't force re-register
      }
    }
    // Run a separate thread for the registration
    hasGkRegistrationThread = true;
    new XMH323GkRegistrationThread(*this, address, terminalAlias1, terminalAlias2, password);
  }
}

void XMH323EndPoint::DoSetGatekeeper(const PString & address,
                                     const PString & terminalAlias1,
                                     const PString & terminalAlias2,
                                     const PString & password)
{
  // Use a gatekeeper if terminalAlias1 is not empty
  if (!terminalAlias1.IsEmpty()) {
    // Change the gatekeeper if the address differs
    if (gatekeeper != NULL && address.Compare(gatekeeperAddress) != PObject::EqualTo) {
      RemoveGatekeeper();
      _XMHandleGatekeeperUnregistration();
    }

    gatekeeperAddress = address;

    // update the alias names list
    {
      PWaitAndSignal m(GetAliasNamesMutex());
      SetLocalUserName(terminalAlias1);
      if (!terminalAlias2.IsEmpty()) {
        AddAliasName(terminalAlias2);
      }
    }

    if (!password.IsEmpty()) {
      // This should automatically trigger an URQ/RRQ sequence
      SetGatekeeperPassword(password, terminalAlias1);
    } else if (gatekeeper != NULL) {
      // Do a full RRQ with the updated terminal aliases
      gatekeeper->OnTerminalAliasChanged();
    }

    if (gatekeeper == NULL) {
      bool result = UseGatekeeper(address);
      if (result == false) {
        if (gatekeeper == NULL) {
          // GRQ failed
          _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_GatekeeperNotFound);
        }
      } else if (result == true && gatekeeper != NULL) {
        // When the initial OnRegistrationConfirm() is called, gatekeeper is still NULL.
        // Handle the RCF here
        HandleRegistrationConfirm();
      }
    }
  } else {
    // Not using a gatekeeper
    if (gatekeeper != NULL) {
      RemoveGatekeeper();
      _XMHandleGatekeeperUnregistration();
    }
    PWaitAndSignal m(GetAliasNamesMutex());
    SetLocalUserName(GetManager().GetDefaultUserName());
  }
}

void XMH323EndPoint::HandleGkRegistrationThreadFinished()
{
  hasGkRegistrationThread = false;
}

void XMH323EndPoint::HandleRegistrationConfirm() const
{
  if (gatekeeper != NULL) {
    PString gatekeeperName = gatekeeper->GetName();
    PStringList aliases = GetAliasNames();
    PStringStream aliasStream;
    for (PINDEX i = 0; i < aliases.GetSize(); i++) {
      if (i != 0) {
        aliasStream << "\n";
      }
      aliasStream << aliases[i];
    }
    _XMHandleGatekeeperRegistration(gatekeeperName, aliasStream);
  }
}

void XMH323EndPoint::HandleNetworkStatusChange()
{
}

H323Gatekeeper * XMH323EndPoint::CreateGatekeeper(H323Transport * transport)
{
  // use a custom XMOpalTransportUDP instance to ensure the underlying socket bundle
  // uses a NAT method (probably STUN)
  delete transport;
  return new XMH323Gatekeeper(*this, new XMOpalTransportUDP(*this));
}

void XMH323EndPoint::OnRegistrationConfirm()
{
	H323EndPoint::OnRegistrationConfirm();
  HandleRegistrationConfirm();
}

#pragma mark Getting call statistics

void XMH323EndPoint::GetCallStatistics(XMCallStatisticsRecord *callStatistics)
{
	PSafePtr<H323Connection> connection = FindConnectionWithLock(connectionToken, PSafeReadOnly);

	if (connection != NULL)
	{
		unsigned roundTripDelay = connection->GetRoundTripDelay().GetMilliSeconds();
		callStatistics->roundTripDelay = roundTripDelay;

    XMOpalManager::ExtractCallStatistics(*connection, callStatistics);
	}
}

#pragma mark Overriding Callbacks

void XMH323EndPoint::OnEstablished(OpalConnection & connection)
{
	XMOpalManager * manager = (XMOpalManager *)(&GetManager());

	connectionToken = connection.GetToken();

    const PString & remotePartyName = connection.GetRemotePartyName();
    const PString & remotePartyNumber = connection.GetRemotePartyNumber();
    const PString & remotePartyApplication = connection.GetRemoteApplication();

    const H323Transport *signallingChannel = ((H323Connection & )connection).GetSignallingChannel();
    const PString & remotePartyAddress = signallingChannel->GetRemoteAddress().GetHostName();

	manager->SetCallInformation(connectionToken,
								remotePartyName,
								remotePartyNumber,
								remotePartyAddress,
								remotePartyApplication,
								XMCallProtocol_H323);

	H323EndPoint::OnEstablished(connection);
}

void XMH323EndPoint::OnReleased(OpalConnection & connection)
{
	if (connection.GetToken() == connectionToken)
	{
		XMOpalManager * manager = (XMOpalManager *)(&GetManager());
		PString empty = "";

		manager->SetCallInformation(connectionToken,
									empty,
									empty,
									empty,
									empty,
									XMCallProtocol_H323);

		connectionToken = "";
	}

	H323EndPoint::OnReleased(connection);
}

H323Connection * XMH323EndPoint::CreateConnection(OpalCall & call,
												  const PString & token,
												  void * userData,
												  OpalTransport & transport,
												  const PString & alias,
												  const H323TransportAddress & address,
												  H323SignalPDU * setupPDU,
												  unsigned options,
                                                  OpalConnection::StringOptions * stringOptions)
{
	return new XMH323Connection(call, *this, token, alias, address, options, stringOptions);
}

#pragma mark -
#pragma mark H.460 support

bool XMH323EndPoint::OnSendFeatureSet(unsigned id, H225_FeatureSet & message)
{
	return features.SendFeature(id, message);
}

void XMH323EndPoint::OnReceiveFeatureSet(unsigned id, const H225_FeatureSet & message)
{
	features.ReceiveFeature(id, message);
}

#pragma mark -
#pragma mark Clean Up

void XMH323EndPoint::CleanUp()
{
    PWaitAndSignal m(releasingConnectionsMutex);

    for (PINDEX i = 0; i < releasingConnections.GetSize(); i++)
    {
        releasingConnections[i].CleanUp();
    }
}

void XMH323EndPoint::AddReleasingConnection(XMH323Connection * connection)
{
    PWaitAndSignal m(releasingConnectionsMutex);

    releasingConnections.Append(connection);
}

void XMH323EndPoint::RemoveReleasingConnection(XMH323Connection * connection)
{
    PWaitAndSignal m(releasingConnectionsMutex);

    releasingConnections.Remove(connection);
}

#pragma mark -
#pragma mark XMH323Gatekeeper methods

XMOpalTransportUDP::XMOpalTransportUDP(OpalEndPoint & endpoint, PIPSocket::Address binding, WORD port, bool reuseAddr)
: OpalTransportUDP(endpoint, binding, port)
{
  if (writeChannel) {
    PMonitoredSocketsPtr bundle = ((PMonitoredSocketChannel *)writeChannel)->GetMonitoredSockets();

    // reopen the bundle's socket with STUN nat traversal
    bundle->Close();
    bundle->SetNatMethod(endpoint.GetManager().GetSTUN());
    bundle->Open(port);
  }
}

XMH323Gatekeeper::XMH323Gatekeeper(XMH323EndPoint & theEp, H323Transport * transport)
: H323Gatekeeper(theEp, transport),
  ep(theEp)
{
}

OpalTransport * XMH323Gatekeeper::CreateTransport(PIPSocket::Address binding, WORD port, bool reuseAddr)
{
  return new XMOpalTransportUDP(endpoint, binding, port, reuseAddr);
}

bool XMH323Gatekeeper::MakeRequest(Request & request)
{
  bool result = H323Gatekeeper::MakeRequest(request);

  if (request.requestPDU.GetPDU().GetTag() == H225_RasMessage::e_registrationRequest) {
    if (result == false) {
      switch(request.responseResult) {
        case Request::AwaitingResponse:
        case Request::NoResponseReceived:
          if (GetRegistrationFailReason() == H323Gatekeeper::UnregisteredByGatekeeper) {
            // GK unregistered client and obviously went offline afterwards
            _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_UnregisteredByGatekeeper);
          } else {
            _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_GatekeeperNotFound);
          }
          break;

        // notify registration failures here as H323EndPoint::OnRegistrationReject()
        // is called without updated registrationFailReason info
        case Request::RejectReceived:
          switch(request.rejectReason) {

            case H225_RegistrationRejectReason::e_invalidCallSignalAddress :
              _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_TransportError);
              break;

            case H225_RegistrationRejectReason::e_duplicateAlias :
              _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_DuplicateAlias);
              break;

            case H225_RegistrationRejectReason::e_securityDenial :
              _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_SecurityDenial);
              break;

            case H225_RegistrationRejectReason::e_discoveryRequired:
            case H225_RegistrationRejectReason::e_fullRegistrationRequired:
              // Do nothing, as we try to re-register ASAP.
              break;

            default:
              _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_UnknownRegistrationFailure);
              break;
          }
          break;

        case Request::BadCryptoTokens:
          _XMHandleGatekeeperRegistrationFailure(XMGatekeeperRegistrationStatus_SecurityDenial);
          break;

        default:
          // Do nothing
          break;
      }
    }
  }

  return result;
}

bool XMH323Gatekeeper::OnReceiveRegistrationConfirm(const H225_RegistrationConfirm & confirm) {
  // possible race condition since RCF may cause additional aliases to be added
  PWaitAndSignal m(ep.GetAliasNamesMutex());
  return H323Gatekeeper::OnReceiveRegistrationConfirm(confirm);
}

#pragma mark -
#pragma mark XMH323GkRegistrationThread methods

XMH323GkRegistrationThread::XMH323GkRegistrationThread(XMH323EndPoint  & _ep,
                                                       const PString & _address,
                                                       const PString & _terminalAlias1,
                                                       const PString & _terminalAlias2,
                                                       const PString & _password)
: PThread(10000), // stack size no longer used apparently
  ep(_ep),
  address(_address),
  terminalAlias1(_terminalAlias1),
  terminalAlias2(_terminalAlias2),
  password(_password)
{
  Resume();
}

void XMH323GkRegistrationThread::Main()
{
  ep.DoSetGatekeeper(address, terminalAlias1, terminalAlias2, password);
  ep.HandleGkRegistrationThreadFinished();
}
<|endoftext|>


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

#include "gm.h"
#include "SkCanvas.h"
#include "SkPath.h"
#include "SkGradientShader.h"
#include "SkTypeface.h"

static SkShader* make_heatGradient(const SkPoint pts[2]) {
    const SkColor colors[] = {
        SK_ColorBLACK, SK_ColorBLUE, SK_ColorCYAN, SK_ColorGREEN,
        SK_ColorYELLOW, SK_ColorRED, SK_ColorWHITE
    };
    const SkColor bw[] = { SK_ColorBLACK, SK_ColorWHITE };

    return SkGradientShader::CreateLinear(pts, bw, NULL,
                                          SK_ARRAY_COUNT(bw),
                                          SkShader::kClamp_TileMode);
}

static bool setFont(SkPaint* paint, const char name[]) {
    SkTypeface* tf = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
    if (tf) {
        paint->setTypeface(tf)->unref();
        return true;
    }
}

#ifdef SK_BUILD_FOR_MAC
#import <ApplicationServices/ApplicationServices.h>
#define BITMAP_INFO_RGB     (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)

static CGContextRef makeCG(const SkBitmap& bm) {
    if (SkBitmap::kARGB_8888_Config != bm.config() ||
        NULL == bm.getPixels()) {
        return NULL;
    }
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
    CGContextRef cg = CGBitmapContextCreate(bm.getPixels(), bm.width(), bm.height(),
                                            8, bm.rowBytes(), space, BITMAP_INFO_RGB);
    CFRelease(space);

    CGContextSetAllowsFontSubpixelQuantization(cg, false);
    CGContextSetShouldSubpixelQuantizeFonts(cg, false);

    return cg;
}

extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);

static CGFontRef typefaceToCGFont(const SkTypeface* face) {
    if (NULL == face) {
        return 0;
    }

    CTFontRef ct = SkTypeface_GetCTFontRef(face);
    return CTFontCopyGraphicsFont(ct, NULL);
}

static void cgSetPaintForText(CGContextRef cg, const SkPaint& paint) {
    SkColor c = paint.getColor();
    CGFloat rgba[] = {
        SkColorGetB(c) / 255.0,
        SkColorGetG(c) / 255.0,
        SkColorGetR(c) / 255.0,
        SkColorGetA(c) / 255.0,
    };
    CGContextSetRGBFillColor(cg, rgba[0], rgba[1], rgba[2], rgba[3]);

    CGContextSetTextDrawingMode(cg, kCGTextFill);
    CGContextSetFont(cg, typefaceToCGFont(paint.getTypeface()));
    CGContextSetFontSize(cg, SkScalarToFloat(paint.getTextSize()));

    CGContextSetAllowsFontSubpixelPositioning(cg, paint.isSubpixelText());
    CGContextSetShouldSubpixelPositionFonts(cg, paint.isSubpixelText());

    CGContextSetShouldAntialias(cg, paint.isAntiAlias());
    CGContextSetShouldSmoothFonts(cg, paint.isLCDRenderText());
}

static void cgDrawText(CGContextRef cg, const void* text, size_t len,
                       float x, float y, const SkPaint& paint) {
    if (cg) {
        cgSetPaintForText(cg, paint);

        uint16_t glyphs[200];
        int count = paint.textToGlyphs(text, len, glyphs);

        CGContextShowGlyphsAtPoint(cg, x, y, glyphs, count);
    }
}
#endif

namespace skiagm {

/**
   Test a set of clipping problems discovered while writing blitAntiRect,
   and test all the code paths through the clipping blitters.
   Each region should show as a blue center surrounded by a 2px green
   border, with no red.
*/

#define HEIGHT 480

class GammaTextGM : public GM {
public:
    GammaTextGM() {

    }

protected:
    virtual SkString onShortName() {
        return SkString("gammatext");
    }

    virtual SkISize onISize() {
        return make_isize(1024, HEIGHT);
    }

    static void drawGrad(SkCanvas* canvas) {
        SkPoint pts[] = { { 0, 0 }, { 0, HEIGHT } };
#if 0
        const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
        SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
#else
        SkShader* s = make_heatGradient(pts);
#endif

        canvas->clear(SK_ColorRED);
        SkPaint paint;
        paint.setShader(s)->unref();
        SkRect r = { 0, 0, 1024, HEIGHT };
        canvas->drawRect(r, paint);
    }

    virtual void onDraw(SkCanvas* canvas) {
#ifdef SK_BUILD_FOR_MAC
        CGContextRef cg = makeCG(canvas->getDevice()->accessBitmap(false));
#endif

        drawGrad(canvas);

        const SkColor fg[] = {
            0xFFFFFFFF,
            0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF,
            0xFFFF0000, 0xFF00FF00, 0xFF0000FF,
            0xFF000000,
        };

        const char* text = "Hamburgefons";
        size_t len = strlen(text);

        SkPaint paint;
        setFont(&paint, "Times");
        paint.setTextSize(SkIntToScalar(16));
        paint.setAntiAlias(true);
        paint.setLCDRenderText(true);

        SkScalar x = 10;
        for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) {
            paint.setColor(fg[i]);

            SkScalar y = 40;
            SkScalar stopy = HEIGHT;
            while (y < stopy) {
#if 1
                canvas->drawText(text, len, x, y, paint);
#else
                cgDrawText(cg, text, len, x, HEIGHT - y, paint);
#endif
                y += paint.getTextSize() * 2;
            }
            x += SkIntToScalar(1024) / SK_ARRAY_COUNT(fg);
        }
    }

private:
    typedef GM INHERITED;
};

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

static GM* MyFactory(void*) { return new GammaTextGM; }
static GMRegistry reg(MyFactory);

}
<commit_msg>return false if the typeface cannot be found from https://codereview.appspot.com/5756059/<commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm.h"
#include "SkCanvas.h"
#include "SkPath.h"
#include "SkGradientShader.h"
#include "SkTypeface.h"

static SkShader* make_heatGradient(const SkPoint pts[2]) {
    const SkColor colors[] = {
        SK_ColorBLACK, SK_ColorBLUE, SK_ColorCYAN, SK_ColorGREEN,
        SK_ColorYELLOW, SK_ColorRED, SK_ColorWHITE
    };
    const SkColor bw[] = { SK_ColorBLACK, SK_ColorWHITE };

    return SkGradientShader::CreateLinear(pts, bw, NULL,
                                          SK_ARRAY_COUNT(bw),
                                          SkShader::kClamp_TileMode);
}

static bool setFont(SkPaint* paint, const char name[]) {
    SkTypeface* tf = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
    if (tf) {
        paint->setTypeface(tf)->unref();
        return true;
    }
    return false;
}

#ifdef SK_BUILD_FOR_MAC
#import <ApplicationServices/ApplicationServices.h>
#define BITMAP_INFO_RGB     (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)

static CGContextRef makeCG(const SkBitmap& bm) {
    if (SkBitmap::kARGB_8888_Config != bm.config() ||
        NULL == bm.getPixels()) {
        return NULL;
    }
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
    CGContextRef cg = CGBitmapContextCreate(bm.getPixels(), bm.width(), bm.height(),
                                            8, bm.rowBytes(), space, BITMAP_INFO_RGB);
    CFRelease(space);

    CGContextSetAllowsFontSubpixelQuantization(cg, false);
    CGContextSetShouldSubpixelQuantizeFonts(cg, false);

    return cg;
}

extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);

static CGFontRef typefaceToCGFont(const SkTypeface* face) {
    if (NULL == face) {
        return 0;
    }

    CTFontRef ct = SkTypeface_GetCTFontRef(face);
    return CTFontCopyGraphicsFont(ct, NULL);
}

static void cgSetPaintForText(CGContextRef cg, const SkPaint& paint) {
    SkColor c = paint.getColor();
    CGFloat rgba[] = {
        SkColorGetB(c) / 255.0,
        SkColorGetG(c) / 255.0,
        SkColorGetR(c) / 255.0,
        SkColorGetA(c) / 255.0,
    };
    CGContextSetRGBFillColor(cg, rgba[0], rgba[1], rgba[2], rgba[3]);

    CGContextSetTextDrawingMode(cg, kCGTextFill);
    CGContextSetFont(cg, typefaceToCGFont(paint.getTypeface()));
    CGContextSetFontSize(cg, SkScalarToFloat(paint.getTextSize()));

    CGContextSetAllowsFontSubpixelPositioning(cg, paint.isSubpixelText());
    CGContextSetShouldSubpixelPositionFonts(cg, paint.isSubpixelText());

    CGContextSetShouldAntialias(cg, paint.isAntiAlias());
    CGContextSetShouldSmoothFonts(cg, paint.isLCDRenderText());
}

static void cgDrawText(CGContextRef cg, const void* text, size_t len,
                       float x, float y, const SkPaint& paint) {
    if (cg) {
        cgSetPaintForText(cg, paint);

        uint16_t glyphs[200];
        int count = paint.textToGlyphs(text, len, glyphs);

        CGContextShowGlyphsAtPoint(cg, x, y, glyphs, count);
    }
}
#endif

namespace skiagm {

/**
   Test a set of clipping problems discovered while writing blitAntiRect,
   and test all the code paths through the clipping blitters.
   Each region should show as a blue center surrounded by a 2px green
   border, with no red.
*/

#define HEIGHT 480

class GammaTextGM : public GM {
public:
    GammaTextGM() {

    }

protected:
    virtual SkString onShortName() {
        return SkString("gammatext");
    }

    virtual SkISize onISize() {
        return make_isize(1024, HEIGHT);
    }

    static void drawGrad(SkCanvas* canvas) {
        SkPoint pts[] = { { 0, 0 }, { 0, HEIGHT } };
#if 0
        const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
        SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
#else
        SkShader* s = make_heatGradient(pts);
#endif

        canvas->clear(SK_ColorRED);
        SkPaint paint;
        paint.setShader(s)->unref();
        SkRect r = { 0, 0, 1024, HEIGHT };
        canvas->drawRect(r, paint);
    }

    virtual void onDraw(SkCanvas* canvas) {
#ifdef SK_BUILD_FOR_MAC
        CGContextRef cg = makeCG(canvas->getDevice()->accessBitmap(false));
#endif

        drawGrad(canvas);

        const SkColor fg[] = {
            0xFFFFFFFF,
            0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF,
            0xFFFF0000, 0xFF00FF00, 0xFF0000FF,
            0xFF000000,
        };

        const char* text = "Hamburgefons";
        size_t len = strlen(text);

        SkPaint paint;
        setFont(&paint, "Times");
        paint.setTextSize(SkIntToScalar(16));
        paint.setAntiAlias(true);
        paint.setLCDRenderText(true);

        SkScalar x = 10;
        for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) {
            paint.setColor(fg[i]);

            SkScalar y = 40;
            SkScalar stopy = HEIGHT;
            while (y < stopy) {
#if 1
                canvas->drawText(text, len, x, y, paint);
#else
                cgDrawText(cg, text, len, x, HEIGHT - y, paint);
#endif
                y += paint.getTextSize() * 2;
            }
            x += SkIntToScalar(1024) / SK_ARRAY_COUNT(fg);
        }
    }

private:
    typedef GM INHERITED;
};

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

static GM* MyFactory(void*) { return new GammaTextGM; }
static GMRegistry reg(MyFactory);

}
<|endoftext|>


<commit_before>#include<sstream>
#include<iostream>
#include<boost/lexical_cast.hpp>

#include"StringPartitionSet.h"
#include"myUtils.h"
#include"const.h"

using namespace cpb;
extern const int cpb::FAIL;
extern const int cpb::SUCCESS;
/***************************************************
 *     Author: C. Bleak 23.02.2015
 *     .cc file realising functionality of class StringPartitionSet.
 *
 *      Member Data for StringPartitionSet:
 *
 *      std::set<StringSet > thisPartition
 *      StringSet letters;
 */

/***************************************************
 * C'tor:  Receives set of words.
 *
 * Given a set of words.  Creates the discrete partition and learns the alphabet
 * Note:  Assumes each alphabet chars are length one in original words.
 */
StringPartitionSet::StringPartitionSet(const StringSet & theseStrings)
{
    //create discrete partition.
    for (auto word : theseStrings.cset())
    {
        StringSet thisSet;
        thisSet.insert(word);
        this->_partition.insert(thisSet);
        this->_strings.insert(word);
    }
    this->_letters = lettersInWords(theseStrings);
}

/***************************************************
 * C'tor:  Receives set of words.
 *
 * makes a discrete partition for all words from an alphabet of size
 * numLetters < 10 (1-9) and lengths less than length
 */
StringPartitionSet::StringPartitionSet(unsigned int numLetters, unsigned int length)
{
    std::stringstream ss;
    std::string letter;
    for (int ll = 0; ll < numLetters; ++ll)
    {

        _letters.insert(toString<int>(ll));
        letter = "";
    }
    std::cout << "Alphabet is ";
    for (auto c : _letters.cset())
    {
        std::cout << c << " ";
    }
    std::cout << std::endl;

    StringSet shortWords, longWords;
    shortWords.insert("");
    while (length > 0)
    {
        for (auto word : shortWords.cset())
        {
            for (auto letter : _letters.cset())
            {
                std::string result(word + letter);
                longWords.insert(result);
            }
        }
        shortWords.sset().clear();
        for (auto word : longWords.cset())
        {
            shortWords.insert(word);
        }
        longWords.sset().clear();
        --length;
    }

    //create discrete partition.
    for (auto word : shortWords.cset())
    {
        StringSet thisSet;
        thisSet.insert(word);
        _partition.insert(thisSet);
        _strings.insert(word);
    }
    this->makeSortString();
}

/************************
 * C'tor
 *
 * Input: old StringPartitionSet, and two words
 * Creates: new StringPartitionSet that is the finest partition of initial
 * set of words so that the new partition contains the old partition and
 * glues together the two classes containing the two handed in words, as
 * well as gluing together all consequential classes.
 * */
StringPartitionSet::StringPartitionSet(const StringPartitionSet & theseStrings, const std::string & firstString, const std::string & secondString)
{
}

// returns cpb::FAIL if fails to find partition element with the given word in it.  empty StringSet returned if not found.

void StringPartitionSet::makeSortString()
{
    this->_sortstring = "[";
    for (auto ss : this->_partition)
    {
        this->_sortstring += ss.sortString();
        this->_sortstring += ", ";
    }
    int len(_sortstring.length());
    if (len > 1)
    {
        _sortstring = _sortstring.substr(0, len - 2);
    }
    this->_sortstring += "]";
}

StringSet StringPartitionSet::lettersInWord(const std::string & word)
{
    StringSet letters;
    for (auto c : word)
    {
        std::string letter(boost::lexical_cast<std::string>(c));
        letters.insert(letter);
    }
    return letters;
}

StringSet StringPartitionSet::lettersInWords(const StringSet & words)
{
    StringSet letters;
    for (auto word : words.cset())
    {
        StringSet theseLetters(lettersInWord(word));
        for (auto letter : theseLetters.cset())
        {
            letters.insert(letter);
        }
    }
    return letters;
}

// modifies this StringPartitionSet by gluing two member stringSets together and forcing remaining consequences.
// will not glue if the two candidate sets are not members.

int StringPartitionSet::glueTwoSetsNoForce(const StringSet & oneSet, const StringSet & otherSet, StringSet & resultSet)
{
    resultSet.clear();

    if (oneSet == otherSet)
    {
        resultSet = oneSet;
        return 0;
    }

    std::set<StringSet>::iterator pitOne = _partition.find(oneSet);
    std::set<StringSet>::iterator pitOther = _partition.find(otherSet);
    const std::set<StringSet>::const_iterator pitE = _partition.end();

    if (pitOne != pitE && pitOther != pitE)
    {
        _partition.erase(pitOne);
        _partition.erase(pitOther);

        StringSet newSet;
        for (auto w : oneSet.cset())
        {
            resultSet.insert(w);
        }
        for (auto w : otherSet.cset())
        {
            resultSet.insert(w);
        }

        _partition.insert(resultSet);
        this->makeSortString();

        return 0;
    } else return cpb::FAIL;
}

// returns cpb::FAIL if fails to find partition element with the given word in it.  empty StringSet returned if not found.

int StringPartitionSet::setWithWord(const std::string & word, StringSet & answerSet)
{
    std::set<StringSet>::iterator pit(_partition.begin());
    const std::set<StringSet>::iterator pitE(_partition.end());
    for (; pit != pitE; ++pit)
    {
        if (pit->cset().find(word) != pit->cset().end())
        {
            break;
        }
    }
    if (pit != pitE)
    {
        answerSet = (*pit);
        return 0;
    }
    return cpb::FAIL;
}

int StringPartitionSet::glueSetsByWords(const StringSet & words)
{
    int retVal = cpb::FAIL;
    StringSet containerSet;
    std::set<StringSet> sets;
    for (auto word : words.cset())
    {
        if (cpb::FAIL != this->setWithWord(word, containerSet))
        {
            sets.insert(containerSet);
        } else
        {
            return cpb::FAIL;
        }
    }
    glueSetOfSets(sets);
    return 0;
}

int StringPartitionSet::glueSetOfSets(const std::set<StringSet> & theseSets)
{
    StringSet firstSet;
    StringSet secondSet;
    StringSet answerSet;

    if (1 < theseSets.size())
    {
        answerSet = *(theseSets.begin());
        for (auto set : theseSets)
        {
            firstSet = answerSet;
            if (cpb::FAIL == glueTwoSetsNoForce(firstSet, set, answerSet))
            {
                return cpb::FAIL;
            }
        }
    _forceQueue.insert(answerSet);
    force();
    }
    return 0;
}

int StringPartitionSet::force()
{
    std::set<StringSet> rolledSets;
    StringSet thisSet;
    StringSet rollSet;
    while (0 == setPop(_forceQueue, thisSet))
    {
        rolledSets.insert(thisSet);
        for (auto letter : _letters.cset())
        {
            if (0 == roll(letter, thisSet, rollSet))
            {
                rolledSets.insert(rollSet);
            }
        }
        for(auto rSet: rolledSets)
        {glueSetsByWords(rSet);
        }
    }
    return 0;
}

int StringPartitionSet::roll(std::string letter, const StringSet & setToRoll, StringSet & rollSet)
{
    rollSet.clear();
    for(auto word: setToRoll.cset())
    {
        word=word.substr(1,word.length()-1)+letter;
        rollSet.insert(word);
    }
    return 0;
}<commit_msg>commit this stuff.<commit_after>
#include<sstream>
#include<iostream>
#include<boost/lexical_cast.hpp>

#include"StringPartitionSet.h"
#include"myUtils.h"
#include"const.h"

using namespace cpb;
extern const int cpb::FAIL;
extern const int cpb::SUCCESS;
/***************************************************
 *     Author: C. Bleak 23.02.2015
 *     .cc file realising functionality of class StringPartitionSet.
 *
 *      Member Data for StringPartitionSet:
 *
 *      std::set<StringSet > thisPartition
 *      StringSet letters;
 */

/***************************************************
 * C'tor:  Receives set of words.
 *
 * Given a set of words.  Creates the discrete partition and learns the alphabet
 * Note:  Assumes each alphabet chars are length one in original words.
 */
StringPartitionSet::StringPartitionSet(const StringSet & theseStrings)
{
    //create discrete partition.
    for (auto word : theseStrings.cset())
    {
        StringSet thisSet;
        thisSet.insert(word);
        this->_partition.insert(thisSet);
        this->_strings.insert(word);
    }
    this->_letters = lettersInWords(theseStrings);
}

/***************************************************
 * C'tor:  Receives set of words.
 *
 * makes a discrete partition for all words from an alphabet of size
 * numLetters < 10 (1-9) and lengths less than length
 */
StringPartitionSet::StringPartitionSet(unsigned int numLetters, unsigned int length)
{
    std::stringstream ss;
    std::string letter;
    for (int ll = 0; ll < numLetters; ++ll)
    {

        _letters.insert(toString<int>(ll));
        letter = "";
    }
    std::cout << "Alphabet is ";
    for (auto c : _letters.cset())
    {
        std::cout << c << " ";
    }
    std::cout << std::endl;

    StringSet shortWords, longWords;
    shortWords.insert("");
    while (length > 0)
    {
        for (auto word : shortWords.cset())
        {
            for (auto letter : _letters.cset())
            {
                std::string result(word + letter);
                longWords.insert(result);
            }
        }
        shortWords.sset().clear();
        for (auto word : longWords.cset())
        {
            shortWords.insert(word);
        }
        longWords.sset().clear();
        --length;
    }

    //create discrete partition.
    for (auto word : shortWords.cset())
    {
        StringSet thisSet;
        thisSet.insert(word);
        _partition.insert(thisSet);
        _strings.insert(word);
    }
    this->makeSortString();
}

/************************
 * C'tor
 *
 * Input: old StringPartitionSet, and two words
 * Creates: new StringPartitionSet that is the finest partition of initial
 * set of words so that the new partition contains the old partition and
 * glues together the two classes containing the two handed in words, as
 * well as gluing together all consequential classes.
 * */
StringPartitionSet::StringPartitionSet(const StringPartitionSet & theseStrings, const std::string & firstString, const std::string & secondString)
{
}

// returns cpb::FAIL if fails to find partition element with the given word in it.  empty StringSet returned if not found.

void StringPartitionSet::makeSortString()
{
    this->_sortstring = "[";
    for (auto ss : this->_partition)
    {
        this->_sortstring += ss.sortString();
        this->_sortstring += ", ";
    }
    int len(_sortstring.length());
    if (len > 1)
    {
        _sortstring = _sortstring.substr(0, len - 2);
    }
    this->_sortstring += "]";
}

StringSet StringPartitionSet::lettersInWord(const std::string & word)
{
    StringSet letters;
    for (auto c : word)
    {
        std::string letter(boost::lexical_cast<std::string>(c));
        letters.insert(letter);
    }
    return letters;
}

StringSet StringPartitionSet::lettersInWords(const StringSet & words)
{
    StringSet letters;
    for (auto word : words.cset())
    {
        StringSet theseLetters(lettersInWord(word));
        for (auto letter : theseLetters.cset())
        {
            letters.insert(letter);
        }
    }
    return letters;
}

// modifies this StringPartitionSet by gluing two member stringSets together and forcing remaining consequences.
// will not glue if the two candidate sets are not members.

int StringPartitionSet::glueTwoSetsNoForce(const StringSet & oneSet, const StringSet & otherSet, StringSet & resultSet)
{
    resultSet.clear();

    if (oneSet == otherSet)
    {
        resultSet = oneSet;
        return 0;
    }

    std::set<StringSet>::iterator pitOne = _partition.find(oneSet);
    std::set<StringSet>::iterator pitOther = _partition.find(otherSet);
    const std::set<StringSet>::const_iterator pitE = _partition.end();

    if (pitOne != pitE && pitOther != pitE)
    {
        _partition.erase(pitOne);
        _partition.erase(pitOther);

        StringSet newSet;
        for (auto w : oneSet.cset())
        {
            resultSet.insert(w);
        }
        for (auto w : otherSet.cset())
        {
            resultSet.insert(w);
        }

        _partition.insert(resultSet);
        this->makeSortString();

        return 0;
    } else return cpb::FAIL;
}

// returns cpb::FAIL if fails to find partition element with the given word in it.  empty StringSet returned if not found.

int StringPartitionSet::setWithWord(const std::string & word, StringSet & answerSet)
{
    std::set<StringSet>::iterator pit(_partition.begin());
    const std::set<StringSet>::iterator pitE(_partition.end());
    for (; pit != pitE; ++pit)
    {
        if (pit->cset().find(word) != pit->cset().end())
        {
            break;
        }
    }
    if (pit != pitE)
    {
        answerSet = (*pit);
        return 0;
    }
    return cpb::FAIL;
}

int StringPartitionSet::glueSetsByWords(const StringSet & words)
{
    int retVal = cpb::FAIL;
    StringSet containerSet;
    std::set<StringSet> sets;
    for (auto word : words.cset())
    {
        if (cpb::FAIL != this->setWithWord(word, containerSet))
        {
            sets.insert(containerSet);
        } else
        {
            return cpb::FAIL;
        }
    }
    glueSetOfSets(sets);
    return 0;
}

int StringPartitionSet::glueSetOfSets(const std::set<StringSet> & theseSets)
{
    StringSet firstSet;
    StringSet secondSet;
    StringSet answerSet;

    if (1 < theseSets.size())
    {
        answerSet = *(theseSets.begin());
        for (auto set : theseSets)
        {
            firstSet = answerSet;
            if (cpb::FAIL == glueTwoSetsNoForce(firstSet, set, answerSet))
            {
                return cpb::FAIL;
            }
        }
    _forceQueue.insert(answerSet);
    force();
    }
    return 0;
}

int StringPartitionSet::force()
{
    std::set<StringSet> rolledSets;
    StringSet thisSet;
    StringSet rollSet;
    while (0 == setPop(_forceQueue, thisSet))
    {
        rolledSets.insert(thisSet);
        for (auto letter : _letters.cset())
        {
            if (0 == roll(letter, thisSet, rollSet))
            {
                rolledSets.insert(rollSet);
            }
        }
        for(auto rSet: rolledSets)
        {glueSetsByWords(rSet);
        }
    }
    return 0;
}

int StringPartitionSet::roll(std::string letter, const StringSet & setToRoll, StringSet & rollSet)
{
    rollSet.clear();
    for(auto word: setToRoll.cset())
    {
        word=word.substr(1,word.length()-1)+letter;
        rollSet.insert(word);
    }
    return 0;
}
<|endoftext|>