<commit_before>#include "multiplayer_server.h"
#include "multiplayer_client.h"
#include "multiplayer_internal.h"
#include "engine.h"

#define MULTIPLAYER_COLLECT_DATA_STATS 0

#if MULTIPLAYER_COLLECT_DATA_STATS
sf::Clock multiplayer_stats_dump;
static std::unordered_map<string, int> multiplayer_stats;
#define ADD_MULTIPLAYER_STATS(name, bytes) multiplayer_stats[name] += (bytes)
#else
#define ADD_MULTIPLAYER_STATS(name, bytes) do {} while(0)
#endif

P<GameServer> game_server;

GameServer::GameServer(string server_name, int version_number, int listen_port)
: server_name(server_name), listen_port(listen_port), version_number(version_number), master_server_update_thread(&GameServer::runMasterServerUpdateThread, this)
{
    assert(!game_server);
    assert(!game_client);
    game_server = this;
    lastGameSpeed = engine->getGameSpeed();
    sendDataRate = 0.0;
    sendDataRatePerClient = 0.0;
    boardcastServerDelay = 0.0;
    aliveClock.restart();

    nextObjectId = 1;
    nextclient_id = 1;

    if (listenSocket.listen(listen_port) != sf::TcpListener::Done)
    {
        LOG(ERROR) << "Failed to listen on TCP port: " << listen_port;
    }
    if (broadcastListenSocket.bind(listen_port) != sf::UdpSocket::Done)
    {
        LOG(ERROR) << "Failed to listen on UDP port: " << listen_port;
    }
    selector.add(listenSocket);
    selector.add(broadcastListenSocket);
}

GameServer::~GameServer()
{
    for(ClientInfo& info : clientList)
        delete info.socket;
    master_server_update_thread.wait();
}

void GameServer::destroy()
{
    for(ClientInfo& info : clientList)
        delete info.socket;
    clientList.clear();

    listenSocket.close();
    broadcastListenSocket.unbind();

    Updatable::destroy();
}

P<MultiplayerObject> GameServer::getObjectById(int32_t id)
{
    if (objectMap.find(id) != objectMap.end())
        return objectMap[id];
    return NULL;
}

void GameServer::update(float gameDelta)
{
    //Calculate our own delta, as we want wall-time delta, the gameDelta can be modified by the current game speed (could even be 0 on pause)
    float delta = updateTimeClock.getElapsedTime().asSeconds();
    updateTimeClock.restart();

    sendDataCounter = 0;
    sendDataCounterPerClient = 0;

    if (lastGameSpeed != engine->getGameSpeed())
    {
        lastGameSpeed = engine->getGameSpeed();
        sf::Packet packet;
        packet << CMD_SET_GAME_SPEED << lastGameSpeed;
        sendAll(packet);
    }

    std::vector<int32_t> delList;
    for(std::unordered_map<int32_t, P<MultiplayerObject> >::iterator i=objectMap.begin(); i != objectMap.end(); i++)
    {
        int id = i->first;
        P<MultiplayerObject> obj = i->second;
        if (obj)
        {
            if (!obj->replicated)
            {
                obj->replicated = true;

                sf::Packet packet;
                genenerateCreatePacketFor(obj, packet);
                //Call the isChanged function for each replication info, so the prev_data is updated.
                for(unsigned int n=0; n<obj->memberReplicationInfo.size(); n++)
                    obj->memberReplicationInfo[n].isChangedFunction(obj->memberReplicationInfo[n].ptr, &obj->memberReplicationInfo[n].prev_data);
                sendAll(packet);
                ADD_MULTIPLAYER_STATS(obj->multiplayerClassIdentifier + "::CREATE", packet.getDataSize());
            }
            sf::Packet packet;
            packet << CMD_UPDATE_VALUE;
            packet << int32_t(obj->multiplayerObjectId);
#if MULTIPLAYER_COLLECT_DATA_STATS
            int overhead = packet.getDataSize();
#endif
            int cnt = 0;
            for(unsigned int n=0; n<obj->memberReplicationInfo.size(); n++)
            {
                if (obj->memberReplicationInfo[n].update_timeout > 0.0)
                {
                    obj->memberReplicationInfo[n].update_timeout -= delta;
                }else{
                    if ((obj->memberReplicationInfo[n].isChangedFunction)(obj->memberReplicationInfo[n].ptr, &obj->memberReplicationInfo[n].prev_data))
                    {
#if MULTIPLAYER_COLLECT_DATA_STATS
                        int packet_size = packet.getDataSize();
#endif
                        packet << int16_t(n);
                        (obj->memberReplicationInfo[n].sendFunction)(obj->memberReplicationInfo[n].ptr, packet);
                        cnt++;
                        ADD_MULTIPLAYER_STATS(obj->multiplayerClassIdentifier + "::" + obj->memberReplicationInfo[n].name, packet.getDataSize() - packet_size);

                        obj->memberReplicationInfo[n].update_timeout = obj->memberReplicationInfo[n].update_delay;
                    }
                }
            }
            if (cnt > 0)
            {
                sendAll(packet);
                ADD_MULTIPLAYER_STATS(obj->multiplayerClassIdentifier + "::OVERHEAD", overhead);
            }
        }else{
            delList.push_back(id);
        }
    }
    for(unsigned int n=0; n<delList.size(); n++)
    {
        sf::Packet packet;
        genenerateDeletePacketFor(delList[n], packet);
        sendAll(packet);
        ADD_MULTIPLAYER_STATS("???::DELETE", packet.getDataSize());
        objectMap.erase(delList[n]);
    }

    selector.wait(sf::microseconds(1));//Seems to delay 1ms on Windows. Not ideal, but fast enough. (other option is using threads, which I rather avoid)
    if (selector.isReady(broadcastListenSocket))
    {
        sf::IpAddress recvAddress;
        unsigned short recvPort;
        sf::Packet recvPacket;
        broadcastListenSocket.receive(recvPacket, recvAddress, recvPort);

        //We do not care about what we received. Reply that we live!
        sf::Packet sendPacket;
        sendPacket << int32_t(multiplayerVerficationNumber) << int32_t(version_number) << server_name;
        broadcastListenSocket.send(sendPacket, recvAddress, recvPort);
    }
    if (boardcastServerDelay > 0.0)
    {
        boardcastServerDelay -= delta;
    }else{
        boardcastServerDelay = 5.0;

        sf::Packet sendPacket;
        sendPacket << int32_t(multiplayerVerficationNumber) << int32_t(version_number) << server_name;
        UDPbroadcastPacket(broadcastListenSocket, sendPacket, broadcastListenSocket.getLocalPort() + 1);
    }

    if (selector.isReady(listenSocket))
    {
        ClientInfo info;
        info.socket = new TcpSocket();
        info.socket->setBlocking(false);
        info.client_id = nextclient_id;
        info.receiveState = CRS_Main;
        nextclient_id++;
        listenSocket.accept(*info.socket);
        clientList.push_back(info);
        selector.add(*info.socket);
        {
            sf::Packet packet;
            packet << CMD_SET_CLIENT_ID << info.client_id;
            info.socket->send(packet);
        }
        {
            sf::Packet packet;
            packet << CMD_SET_GAME_SPEED << lastGameSpeed;
            info.socket->send(packet);
        }

        onNewClient(info.client_id);

        //On a new client, first create all the already existing objects. And update all the values.
        for(std::unordered_map<int32_t, P<MultiplayerObject> >::iterator i=objectMap.begin(); i != objectMap.end(); i++)
        {
            P<MultiplayerObject> obj = i->second;
            if (obj && obj->replicated)
            {
                sf::Packet packet;
                genenerateCreatePacketFor(obj, packet);
                sendDataCounter += packet.getDataSize();
                info.socket->send(packet);
            }
        }
    }

    for(unsigned int n=0; n<clientList.size(); n++)
    {
        clientList[n].socket->update();
        if (selector.isReady(*clientList[n].socket))
        {
            sf::Packet packet;
            sf::TcpSocket::Status status;
            while((status = clientList[n].socket->receive(packet)) == sf::TcpSocket::Done)
            {
                switch(clientList[n].receiveState)
                {
                case CRS_Main:
                    {
                        command_t command;
                        packet >> command;
                        switch(command)
                        {
                        case CMD_CLIENT_COMMAND:
                            packet >> clientList[n].command_object_id;
                            clientList[n].receiveState = CRS_Command;
                            break;
                        case CMD_CLIENT_AUDIO_COMM:
                            {
                                int32_t target_identifier;
                                uint32_t sample_count;
                                std::vector<int16_t> samples;
                                packet >> target_identifier;
                                packet >> sample_count;
                                samples.reserve(sample_count);
                                for(unsigned int n=0; n<sample_count; n++)
                                {
                                    int16_t sample;
                                    packet >> sample;
                                    samples.push_back(sample);
                                }
                                gotAudioPacket(n, target_identifier, samples);
                            }
                        default:
                            LOG(ERROR) << "Unknown command from client: " << command;
                        }
                    }
                    break;
                case CRS_Command:
                    if (objectMap.find(clientList[n].command_object_id) != objectMap.end() && objectMap[clientList[n].command_object_id])
                        objectMap[clientList[n].command_object_id]->onReceiveClientCommand(clientList[n].client_id, packet);
                    clientList[n].receiveState = CRS_Main;
                    break;
                }
            }
            if (status == sf::TcpSocket::Disconnected)
            {
                onDisconnectClient(clientList[n].client_id);
                selector.remove(*clientList[n].socket);
                delete clientList[n].socket;
                clientList.erase(clientList.begin() + n);
                n--;
            }
        }
    }

    if (aliveClock.getElapsedTime().asSeconds() > 10.0)
    {
        aliveClock.restart();

        sf::Packet packet;
        packet << CMD_ALIVE;
        sendAll(packet);
    }

    float dataPerSecond = float(sendDataCounter) / delta;
    sendDataRate = sendDataRate * (1.0 - delta) + dataPerSecond * delta;
    dataPerSecond = float(sendDataCounterPerClient) / delta;
    sendDataRatePerClient = sendDataRatePerClient * (1.0 - delta) + dataPerSecond * delta;

#if MULTIPLAYER_COLLECT_DATA_STATS
    if (multiplayer_stats_dump.getElapsedTime().asSeconds() > 1.0)
    {
        multiplayer_stats_dump.restart();

        int total = 0;
        for(std::unordered_map<string, int >::iterator i=multiplayer_stats.begin(); i != multiplayer_stats.end(); i++)
            total += i->second;
        printf("---------------------------------Total: %d\n", total);
        for(std::unordered_map<string, int >::iterator i=multiplayer_stats.begin(); i != multiplayer_stats.end(); i++)
        {
            printf("%60s: %d (%d%%)\n", i->first.c_str(), i->second, i->second * 100 / total);
        }
        multiplayer_stats.clear();
    }
#endif
}

void GameServer::registerObject(P<MultiplayerObject> obj)
{
    //Note, at this point in time, the pointed object is only of the MultiplayerObject class.
    // This due to the fact that in C++ does not "is" it's final sub-class till construction is completed.
    obj->multiplayerObjectId = nextObjectId;
    obj->replicated = false;
    nextObjectId++;

    objectMap[obj->multiplayerObjectId] = obj;
}

void GameServer::genenerateCreatePacketFor(P<MultiplayerObject> obj, sf::Packet& packet)
{
    packet << CMD_CREATE << obj->multiplayerObjectId << obj->multiplayerClassIdentifier;

    for(unsigned int n=0; n<obj->memberReplicationInfo.size(); n++)
    {
        packet << int16_t(n);
        (obj->memberReplicationInfo[n].sendFunction)(obj->memberReplicationInfo[n].ptr, packet);
    }
}

void GameServer::genenerateDeletePacketFor(int32_t id, sf::Packet& packet)
{
    packet << CMD_DELETE << id;
}

void GameServer::sendAll(sf::Packet& packet)
{
    sendDataCounterPerClient += packet.getDataSize();
    for(unsigned int n=0; n<clientList.size(); n++)
        clientList[n].socket->send(packet);
}

void GameServer::registerOnMasterServer(string master_server_url)
{
    this->master_server_url = master_server_url;
    master_server_update_thread.launch();
}

void GameServer::runMasterServerUpdateThread()
{
    if (!master_server_url.startswith("http://"))
    {
        LOG(ERROR) << "Master server URL does not start with \"http://\"";
        return;
    }
    string hostname = master_server_url.substr(7);
    int path_start = hostname.find("/");
    if (path_start < 0)
    {
        LOG(ERROR) << "Master server URL has no uri after hostname";
        return;
    }
    string uri = hostname.substr(path_start + 1);
    hostname = hostname.substr(0, path_start);

    LOG(INFO) << "Registering at master server";

    sf::Http http(hostname);
    while(!isDestroyed())
    {
        sf::Http::Request request(uri, sf::Http::Request::Post);
        request.setBody("port=" + string(listen_port) + "&name=" + server_name + "&version=" + string(version_number));

        sf::Http::Response response = http.sendRequest(request, sf::seconds(10.0f));

        if (response.getStatus() != sf::Http::Response::Ok)
        {
            LOG(WARNING) << "Failed to register at master server (" << response.getStatus() << ")";
        }

        for(int n=0;n<60 && !isDestroyed();n++)
            sf::sleep(sf::seconds(1.0f));
    }
}

void GameServer::gotAudioPacket(int32_t client_id, int32_t target_identifier, std::vector<int16_t>& audio_data)
{
}
<commit_msg>Small fix to make sure the game server can be destroyed properly.<commit_after>#include "multiplayer_server.h"
#include "multiplayer_client.h"
#include "multiplayer_internal.h"
#include "engine.h"

#define MULTIPLAYER_COLLECT_DATA_STATS 0

#if MULTIPLAYER_COLLECT_DATA_STATS
sf::Clock multiplayer_stats_dump;
static std::unordered_map<string, int> multiplayer_stats;
#define ADD_MULTIPLAYER_STATS(name, bytes) multiplayer_stats[name] += (bytes)
#else
#define ADD_MULTIPLAYER_STATS(name, bytes) do {} while(0)
#endif

P<GameServer> game_server;

GameServer::GameServer(string server_name, int version_number, int listen_port)
: server_name(server_name), listen_port(listen_port), version_number(version_number), master_server_update_thread(&GameServer::runMasterServerUpdateThread, this)
{
    assert(!game_server);
    assert(!game_client);
    game_server = this;
    lastGameSpeed = engine->getGameSpeed();
    sendDataRate = 0.0;
    sendDataRatePerClient = 0.0;
    boardcastServerDelay = 0.0;
    aliveClock.restart();

    nextObjectId = 1;
    nextclient_id = 1;

    if (listenSocket.listen(listen_port) != sf::TcpListener::Done)
    {
        LOG(ERROR) << "Failed to listen on TCP port: " << listen_port;
    }
    if (broadcastListenSocket.bind(listen_port) != sf::UdpSocket::Done)
    {
        LOG(ERROR) << "Failed to listen on UDP port: " << listen_port;
    }
    selector.add(listenSocket);
    selector.add(broadcastListenSocket);
}

GameServer::~GameServer()
{
    for(ClientInfo& info : clientList)
        delete info.socket;

    destroy();
    master_server_update_thread.wait();
}

void GameServer::destroy()
{
    for(ClientInfo& info : clientList)
        delete info.socket;
    clientList.clear();

    listenSocket.close();
    broadcastListenSocket.unbind();

    Updatable::destroy();
}

P<MultiplayerObject> GameServer::getObjectById(int32_t id)
{
    if (objectMap.find(id) != objectMap.end())
        return objectMap[id];
    return NULL;
}

void GameServer::update(float gameDelta)
{
    //Calculate our own delta, as we want wall-time delta, the gameDelta can be modified by the current game speed (could even be 0 on pause)
    float delta = updateTimeClock.getElapsedTime().asSeconds();
    updateTimeClock.restart();

    sendDataCounter = 0;
    sendDataCounterPerClient = 0;

    if (lastGameSpeed != engine->getGameSpeed())
    {
        lastGameSpeed = engine->getGameSpeed();
        sf::Packet packet;
        packet << CMD_SET_GAME_SPEED << lastGameSpeed;
        sendAll(packet);
    }

    std::vector<int32_t> delList;
    for(std::unordered_map<int32_t, P<MultiplayerObject> >::iterator i=objectMap.begin(); i != objectMap.end(); i++)
    {
        int id = i->first;
        P<MultiplayerObject> obj = i->second;
        if (obj)
        {
            if (!obj->replicated)
            {
                obj->replicated = true;

                sf::Packet packet;
                genenerateCreatePacketFor(obj, packet);
                //Call the isChanged function for each replication info, so the prev_data is updated.
                for(unsigned int n=0; n<obj->memberReplicationInfo.size(); n++)
                    obj->memberReplicationInfo[n].isChangedFunction(obj->memberReplicationInfo[n].ptr, &obj->memberReplicationInfo[n].prev_data);
                sendAll(packet);
                ADD_MULTIPLAYER_STATS(obj->multiplayerClassIdentifier + "::CREATE", packet.getDataSize());
            }
            sf::Packet packet;
            packet << CMD_UPDATE_VALUE;
            packet << int32_t(obj->multiplayerObjectId);
#if MULTIPLAYER_COLLECT_DATA_STATS
            int overhead = packet.getDataSize();
#endif
            int cnt = 0;
            for(unsigned int n=0; n<obj->memberReplicationInfo.size(); n++)
            {
                if (obj->memberReplicationInfo[n].update_timeout > 0.0)
                {
                    obj->memberReplicationInfo[n].update_timeout -= delta;
                }else{
                    if ((obj->memberReplicationInfo[n].isChangedFunction)(obj->memberReplicationInfo[n].ptr, &obj->memberReplicationInfo[n].prev_data))
                    {
#if MULTIPLAYER_COLLECT_DATA_STATS
                        int packet_size = packet.getDataSize();
#endif
                        packet << int16_t(n);
                        (obj->memberReplicationInfo[n].sendFunction)(obj->memberReplicationInfo[n].ptr, packet);
                        cnt++;
                        ADD_MULTIPLAYER_STATS(obj->multiplayerClassIdentifier + "::" + obj->memberReplicationInfo[n].name, packet.getDataSize() - packet_size);

                        obj->memberReplicationInfo[n].update_timeout = obj->memberReplicationInfo[n].update_delay;
                    }
                }
            }
            if (cnt > 0)
            {
                sendAll(packet);
                ADD_MULTIPLAYER_STATS(obj->multiplayerClassIdentifier + "::OVERHEAD", overhead);
            }
        }else{
            delList.push_back(id);
        }
    }
    for(unsigned int n=0; n<delList.size(); n++)
    {
        sf::Packet packet;
        genenerateDeletePacketFor(delList[n], packet);
        sendAll(packet);
        ADD_MULTIPLAYER_STATS("???::DELETE", packet.getDataSize());
        objectMap.erase(delList[n]);
    }

    selector.wait(sf::microseconds(1));//Seems to delay 1ms on Windows. Not ideal, but fast enough. (other option is using threads, which I rather avoid)
    if (selector.isReady(broadcastListenSocket))
    {
        sf::IpAddress recvAddress;
        unsigned short recvPort;
        sf::Packet recvPacket;
        broadcastListenSocket.receive(recvPacket, recvAddress, recvPort);

        //We do not care about what we received. Reply that we live!
        sf::Packet sendPacket;
        sendPacket << int32_t(multiplayerVerficationNumber) << int32_t(version_number) << server_name;
        broadcastListenSocket.send(sendPacket, recvAddress, recvPort);
    }
    if (boardcastServerDelay > 0.0)
    {
        boardcastServerDelay -= delta;
    }else{
        boardcastServerDelay = 5.0;

        sf::Packet sendPacket;
        sendPacket << int32_t(multiplayerVerficationNumber) << int32_t(version_number) << server_name;
        UDPbroadcastPacket(broadcastListenSocket, sendPacket, broadcastListenSocket.getLocalPort() + 1);
    }

    if (selector.isReady(listenSocket))
    {
        ClientInfo info;
        info.socket = new TcpSocket();
        info.socket->setBlocking(false);
        info.client_id = nextclient_id;
        info.receiveState = CRS_Main;
        nextclient_id++;
        listenSocket.accept(*info.socket);
        clientList.push_back(info);
        selector.add(*info.socket);
        {
            sf::Packet packet;
            packet << CMD_SET_CLIENT_ID << info.client_id;
            info.socket->send(packet);
        }
        {
            sf::Packet packet;
            packet << CMD_SET_GAME_SPEED << lastGameSpeed;
            info.socket->send(packet);
        }

        onNewClient(info.client_id);

        //On a new client, first create all the already existing objects. And update all the values.
        for(std::unordered_map<int32_t, P<MultiplayerObject> >::iterator i=objectMap.begin(); i != objectMap.end(); i++)
        {
            P<MultiplayerObject> obj = i->second;
            if (obj && obj->replicated)
            {
                sf::Packet packet;
                genenerateCreatePacketFor(obj, packet);
                sendDataCounter += packet.getDataSize();
                info.socket->send(packet);
            }
        }
    }

    for(unsigned int n=0; n<clientList.size(); n++)
    {
        clientList[n].socket->update();
        if (selector.isReady(*clientList[n].socket))
        {
            sf::Packet packet;
            sf::TcpSocket::Status status;
            while((status = clientList[n].socket->receive(packet)) == sf::TcpSocket::Done)
            {
                switch(clientList[n].receiveState)
                {
                case CRS_Main:
                    {
                        command_t command;
                        packet >> command;
                        switch(command)
                        {
                        case CMD_CLIENT_COMMAND:
                            packet >> clientList[n].command_object_id;
                            clientList[n].receiveState = CRS_Command;
                            break;
                        case CMD_CLIENT_AUDIO_COMM:
                            {
                                int32_t target_identifier;
                                uint32_t sample_count;
                                std::vector<int16_t> samples;
                                packet >> target_identifier;
                                packet >> sample_count;
                                samples.reserve(sample_count);
                                for(unsigned int n=0; n<sample_count; n++)
                                {
                                    int16_t sample;
                                    packet >> sample;
                                    samples.push_back(sample);
                                }
                                gotAudioPacket(n, target_identifier, samples);
                            }
                        default:
                            LOG(ERROR) << "Unknown command from client: " << command;
                        }
                    }
                    break;
                case CRS_Command:
                    if (objectMap.find(clientList[n].command_object_id) != objectMap.end() && objectMap[clientList[n].command_object_id])
                        objectMap[clientList[n].command_object_id]->onReceiveClientCommand(clientList[n].client_id, packet);
                    clientList[n].receiveState = CRS_Main;
                    break;
                }
            }
            if (status == sf::TcpSocket::Disconnected)
            {
                onDisconnectClient(clientList[n].client_id);
                selector.remove(*clientList[n].socket);
                delete clientList[n].socket;
                clientList.erase(clientList.begin() + n);
                n--;
            }
        }
    }

    if (aliveClock.getElapsedTime().asSeconds() > 10.0)
    {
        aliveClock.restart();

        sf::Packet packet;
        packet << CMD_ALIVE;
        sendAll(packet);
    }

    float dataPerSecond = float(sendDataCounter) / delta;
    sendDataRate = sendDataRate * (1.0 - delta) + dataPerSecond * delta;
    dataPerSecond = float(sendDataCounterPerClient) / delta;
    sendDataRatePerClient = sendDataRatePerClient * (1.0 - delta) + dataPerSecond * delta;

#if MULTIPLAYER_COLLECT_DATA_STATS
    if (multiplayer_stats_dump.getElapsedTime().asSeconds() > 1.0)
    {
        multiplayer_stats_dump.restart();

        int total = 0;
        for(std::unordered_map<string, int >::iterator i=multiplayer_stats.begin(); i != multiplayer_stats.end(); i++)
            total += i->second;
        printf("---------------------------------Total: %d\n", total);
        for(std::unordered_map<string, int >::iterator i=multiplayer_stats.begin(); i != multiplayer_stats.end(); i++)
        {
            printf("%60s: %d (%d%%)\n", i->first.c_str(), i->second, i->second * 100 / total);
        }
        multiplayer_stats.clear();
    }
#endif
}

void GameServer::registerObject(P<MultiplayerObject> obj)
{
    //Note, at this point in time, the pointed object is only of the MultiplayerObject class.
    // This due to the fact that in C++ does not "is" it's final sub-class till construction is completed.
    obj->multiplayerObjectId = nextObjectId;
    obj->replicated = false;
    nextObjectId++;

    objectMap[obj->multiplayerObjectId] = obj;
}

void GameServer::genenerateCreatePacketFor(P<MultiplayerObject> obj, sf::Packet& packet)
{
    packet << CMD_CREATE << obj->multiplayerObjectId << obj->multiplayerClassIdentifier;

    for(unsigned int n=0; n<obj->memberReplicationInfo.size(); n++)
    {
        packet << int16_t(n);
        (obj->memberReplicationInfo[n].sendFunction)(obj->memberReplicationInfo[n].ptr, packet);
    }
}

void GameServer::genenerateDeletePacketFor(int32_t id, sf::Packet& packet)
{
    packet << CMD_DELETE << id;
}

void GameServer::sendAll(sf::Packet& packet)
{
    sendDataCounterPerClient += packet.getDataSize();
    for(unsigned int n=0; n<clientList.size(); n++)
        clientList[n].socket->send(packet);
}

void GameServer::registerOnMasterServer(string master_server_url)
{
    this->master_server_url = master_server_url;
    master_server_update_thread.launch();
}

void GameServer::runMasterServerUpdateThread()
{
    if (!master_server_url.startswith("http://"))
    {
        LOG(ERROR) << "Master server URL does not start with \"http://\"";
        return;
    }
    string hostname = master_server_url.substr(7);
    int path_start = hostname.find("/");
    if (path_start < 0)
    {
        LOG(ERROR) << "Master server URL has no uri after hostname";
        return;
    }
    string uri = hostname.substr(path_start + 1);
    hostname = hostname.substr(0, path_start);

    LOG(INFO) << "Registering at master server";

    sf::Http http(hostname);
    while(!isDestroyed())
    {
        sf::Http::Request request(uri, sf::Http::Request::Post);
        request.setBody("port=" + string(listen_port) + "&name=" + server_name + "&version=" + string(version_number));

        sf::Http::Response response = http.sendRequest(request, sf::seconds(10.0f));

        if (response.getStatus() != sf::Http::Response::Ok)
        {
            LOG(WARNING) << "Failed to register at master server (" << response.getStatus() << ")";
        }

        for(int n=0;n<60 && !isDestroyed();n++)
            sf::sleep(sf::seconds(1.0f));
    }
}

void GameServer::gotAudioPacket(int32_t client_id, int32_t target_identifier, std::vector<int16_t>& audio_data)
{
}
<|endoftext|>


<commit_before>/*
 *    Copyright (c) 2019, The OpenThread Authors.
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without
 *    modification, are permitted provided that the following conditions are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *    3. Neither the name of the copyright holder nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *    POSSIBILITY OF SUCH DAMAGE.
 */

#define OTBR_LOG_TAG "NCP"

#include "ncp/ncp_openthread.hpp"

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

#include <openthread/backbone_router_ftd.h>
#include <openthread/dataset.h>
#include <openthread/logging.h>
#include <openthread/srp_server.h>
#include <openthread/tasklet.h>
#include <openthread/thread.h>
#include <openthread/thread_ftd.h>
#include <openthread/platform/logging.h>
#include <openthread/platform/misc.h>
#include <openthread/platform/radio.h>
#include <openthread/platform/settings.h>

#include "common/code_utils.hpp"
#include "common/logging.hpp"
#include "common/types.hpp"

#if OTBR_ENABLE_LEGACY
#include <ot-legacy-pairing-ext.h>
#endif

namespace otbr {
namespace Ncp {

static const uint16_t kThreadVersion11 = 2; ///< Thread Version 1.1
static const uint16_t kThreadVersion12 = 3; ///< Thread Version 1.2

ControllerOpenThread::ControllerOpenThread(const char *                     aInterfaceName,
                                           const std::vector<const char *> &aRadioUrls,
                                           const char *                     aBackboneInterfaceName,
                                           bool                             aDryRun)
    : mInstance(nullptr)
{
    VerifyOrDie(aRadioUrls.size() <= OT_PLATFORM_CONFIG_MAX_RADIO_URLS, "Too many Radio URLs!");

    memset(&mConfig, 0, sizeof(mConfig));

    mConfig.mInterfaceName         = aInterfaceName;
    mConfig.mBackboneInterfaceName = aBackboneInterfaceName;
    mConfig.mDryRun                = aDryRun;

    for (const char *url : aRadioUrls)
    {
        mConfig.mRadioUrls[mConfig.mRadioUrlNum++] = url;
    }
    mConfig.mSpeedUpFactor = 1;
}

ControllerOpenThread::~ControllerOpenThread(void)
{
    // Make sure OpenThread Instance was gracefully de-initialized.
    assert(mInstance == nullptr);
}

otbrLogLevel ControllerOpenThread::ConvertToOtbrLogLevel(otLogLevel aLogLevel)
{
    otbrLogLevel otbrLogLevel;

    switch (aLogLevel)
    {
    case OT_LOG_LEVEL_NONE:
        otbrLogLevel = OTBR_LOG_EMERG;
        break;
    case OT_LOG_LEVEL_CRIT:
        otbrLogLevel = OTBR_LOG_CRIT;
        break;
    case OT_LOG_LEVEL_WARN:
        otbrLogLevel = OTBR_LOG_WARNING;
        break;
    case OT_LOG_LEVEL_NOTE:
        otbrLogLevel = OTBR_LOG_NOTICE;
        break;
    case OT_LOG_LEVEL_INFO:
        otbrLogLevel = OTBR_LOG_INFO;
        break;
    case OT_LOG_LEVEL_DEBG:
    default:
        otbrLogLevel = OTBR_LOG_DEBUG;
        break;
    }

    return otbrLogLevel;
}

otLogLevel ControllerOpenThread::ConvertToOtLogLevel(otbrLogLevel aLevel)
{
    otLogLevel level;

    switch (aLevel)
    {
    case OTBR_LOG_EMERG:
    case OTBR_LOG_ALERT:
    case OTBR_LOG_CRIT:
        level = OT_LOG_LEVEL_CRIT;
        break;
    case OTBR_LOG_ERR:
    case OTBR_LOG_WARNING:
        level = OT_LOG_LEVEL_WARN;
        break;
    case OTBR_LOG_NOTICE:
        level = OT_LOG_LEVEL_NOTE;
        break;
    case OTBR_LOG_INFO:
        level = OT_LOG_LEVEL_INFO;
        break;
    case OTBR_LOG_DEBUG:
    default:
        level = OT_LOG_LEVEL_DEBG;
        break;
    }

    return level;
}

void ControllerOpenThread::Init(void)
{
    otbrError  error = OTBR_ERROR_NONE;
    otLogLevel level = ConvertToOtLogLevel(otbrLogGetLevel());

    VerifyOrExit(otLoggingSetLevel(level) == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);

    mInstance = otSysInit(&mConfig);
    assert(mInstance != nullptr);

#if OTBR_ENABLE_LEGACY
    otLegacyInit();
#endif

    {
        otError result = otSetStateChangedCallback(mInstance, &ControllerOpenThread::HandleStateChanged, this);

        agent::ThreadHelper::LogOpenThreadResult("Set state callback", result);
        VerifyOrExit(result == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);
    }

#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
    otSrpServerSetEnabled(mInstance, /* aEnabled */ true);
#endif

    mThreadHelper = std::unique_ptr<otbr::agent::ThreadHelper>(new otbr::agent::ThreadHelper(mInstance, this));

exit:
    SuccessOrDie(error, "Failed to initialize NCP!");
}

void ControllerOpenThread::Deinit(void)
{
    assert(mInstance != nullptr);

    otSysDeinit();
    mInstance = nullptr;
}

void ControllerOpenThread::HandleStateChanged(otChangedFlags aFlags)
{
    if (aFlags & OT_CHANGED_THREAD_ROLE)
    {
        switch (otThreadGetDeviceRole(mInstance))
        {
        case OT_DEVICE_ROLE_DISABLED:
#if OTBR_ENABLE_LEGACY
            otLegacyStop();
#endif
            break;
        case OT_DEVICE_ROLE_CHILD:
        case OT_DEVICE_ROLE_ROUTER:
        case OT_DEVICE_ROLE_LEADER:
#if OTBR_ENABLE_LEGACY
            otLegacyStart();
#endif
            break;
        default:
            break;
        }
    }

    for (auto &stateCallback : mThreadStateChangedCallbacks)
    {
        stateCallback(aFlags);
    }

    mThreadHelper->StateChangedCallback(aFlags);
}

void ControllerOpenThread::Update(MainloopContext &aMainloop)
{
    if (otTaskletsArePending(mInstance))
    {
        aMainloop.mTimeout = ToTimeval(Microseconds::zero());
    }

    otSysMainloopUpdate(mInstance, &aMainloop);
}

void ControllerOpenThread::Process(const MainloopContext &aMainloop)
{
    otTaskletsProcess(mInstance);

    otSysMainloopProcess(mInstance, &aMainloop);

    if (IsAutoAttachEnabled() && mThreadHelper->TryResumeNetwork() == OT_ERROR_NONE)
    {
        DisableAutoAttach();
    }
}

bool ControllerOpenThread::IsAutoAttachEnabled(void)
{
    const char *val = getenv("OTBR_NO_AUTO_ATTACH");

    // Auto Thread attaching is enabled if OTBR_NO_AUTO_ATTACH is unset, empty or "0"
    return (val == nullptr || !strcmp(val, "") || !strcmp(val, "0"));
}

void ControllerOpenThread::DisableAutoAttach(void)
{
    setenv("OTBR_NO_AUTO_ATTACH", "1", 1);
}

void ControllerOpenThread::PostTimerTask(Milliseconds aDelay, TaskRunner::Task<void> aTask)
{
    mTaskRunner.Post(std::move(aDelay), std::move(aTask));
}

void ControllerOpenThread::RegisterResetHandler(std::function<void(void)> aHandler)
{
    mResetHandlers.emplace_back(std::move(aHandler));
}

void ControllerOpenThread::AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)
{
    mThreadStateChangedCallbacks.emplace_back(std::move(aCallback));
}

void ControllerOpenThread::Reset(void)
{
    gPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE;

    otSysDeinit();
    mInstance = nullptr;

    Init();
    for (auto &handler : mResetHandlers)
    {
        handler();
    }
    unsetenv("OTBR_NO_AUTO_ATTACH");
}

const char *ControllerOpenThread::GetThreadVersion(void)
{
    const char *version;

    switch (otThreadGetVersion())
    {
    case kThreadVersion11:
        version = "1.1.1";
        break;
    case kThreadVersion12:
        version = "1.2.0";
        break;
    default:
        otbrLogEmerg("Unexpected thread version %hu", otThreadGetVersion());
        exit(-1);
    }
    return version;
}

/*
 * Provide, if required an "otPlatLog()" function
 */
extern "C" void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
    OT_UNUSED_VARIABLE(aLogRegion);

    otbrLogLevel otbrLogLevel = ControllerOpenThread::ConvertToOtbrLogLevel(aLogLevel);

    va_list ap;
    va_start(ap, aFormat);
    otbrLogvNoFilter(otbrLogLevel, aFormat, ap);
    va_end(ap);
}

extern "C" void otPlatLogHandleLevelChanged(otLogLevel aLogLevel)
{
    otbrLogSetLevel(ControllerOpenThread::ConvertToOtbrLogLevel(aLogLevel));
    otbrLogInfo("OpenThread log level changed to %d", aLogLevel);
}

} // namespace Ncp
} // namespace otbr
<commit_msg>[ncp] add support for "1.3" in `GetThreadVersion()` (#1375)<commit_after>/*
 *    Copyright (c) 2019, The OpenThread Authors.
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without
 *    modification, are permitted provided that the following conditions are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *    3. Neither the name of the copyright holder nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *    POSSIBILITY OF SUCH DAMAGE.
 */

#define OTBR_LOG_TAG "NCP"

#include "ncp/ncp_openthread.hpp"

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

#include <openthread/backbone_router_ftd.h>
#include <openthread/dataset.h>
#include <openthread/logging.h>
#include <openthread/srp_server.h>
#include <openthread/tasklet.h>
#include <openthread/thread.h>
#include <openthread/thread_ftd.h>
#include <openthread/platform/logging.h>
#include <openthread/platform/misc.h>
#include <openthread/platform/radio.h>
#include <openthread/platform/settings.h>

#include "common/code_utils.hpp"
#include "common/logging.hpp"
#include "common/types.hpp"

#if OTBR_ENABLE_LEGACY
#include <ot-legacy-pairing-ext.h>
#endif

namespace otbr {
namespace Ncp {

static const uint16_t kThreadVersion11 = 2; ///< Thread Version 1.1
static const uint16_t kThreadVersion12 = 3; ///< Thread Version 1.2
static const uint16_t kThreadVersion13 = 4; ///< Thread Version 1.3

ControllerOpenThread::ControllerOpenThread(const char *                     aInterfaceName,
                                           const std::vector<const char *> &aRadioUrls,
                                           const char *                     aBackboneInterfaceName,
                                           bool                             aDryRun)
    : mInstance(nullptr)
{
    VerifyOrDie(aRadioUrls.size() <= OT_PLATFORM_CONFIG_MAX_RADIO_URLS, "Too many Radio URLs!");

    memset(&mConfig, 0, sizeof(mConfig));

    mConfig.mInterfaceName         = aInterfaceName;
    mConfig.mBackboneInterfaceName = aBackboneInterfaceName;
    mConfig.mDryRun                = aDryRun;

    for (const char *url : aRadioUrls)
    {
        mConfig.mRadioUrls[mConfig.mRadioUrlNum++] = url;
    }
    mConfig.mSpeedUpFactor = 1;
}

ControllerOpenThread::~ControllerOpenThread(void)
{
    // Make sure OpenThread Instance was gracefully de-initialized.
    assert(mInstance == nullptr);
}

otbrLogLevel ControllerOpenThread::ConvertToOtbrLogLevel(otLogLevel aLogLevel)
{
    otbrLogLevel otbrLogLevel;

    switch (aLogLevel)
    {
    case OT_LOG_LEVEL_NONE:
        otbrLogLevel = OTBR_LOG_EMERG;
        break;
    case OT_LOG_LEVEL_CRIT:
        otbrLogLevel = OTBR_LOG_CRIT;
        break;
    case OT_LOG_LEVEL_WARN:
        otbrLogLevel = OTBR_LOG_WARNING;
        break;
    case OT_LOG_LEVEL_NOTE:
        otbrLogLevel = OTBR_LOG_NOTICE;
        break;
    case OT_LOG_LEVEL_INFO:
        otbrLogLevel = OTBR_LOG_INFO;
        break;
    case OT_LOG_LEVEL_DEBG:
    default:
        otbrLogLevel = OTBR_LOG_DEBUG;
        break;
    }

    return otbrLogLevel;
}

otLogLevel ControllerOpenThread::ConvertToOtLogLevel(otbrLogLevel aLevel)
{
    otLogLevel level;

    switch (aLevel)
    {
    case OTBR_LOG_EMERG:
    case OTBR_LOG_ALERT:
    case OTBR_LOG_CRIT:
        level = OT_LOG_LEVEL_CRIT;
        break;
    case OTBR_LOG_ERR:
    case OTBR_LOG_WARNING:
        level = OT_LOG_LEVEL_WARN;
        break;
    case OTBR_LOG_NOTICE:
        level = OT_LOG_LEVEL_NOTE;
        break;
    case OTBR_LOG_INFO:
        level = OT_LOG_LEVEL_INFO;
        break;
    case OTBR_LOG_DEBUG:
    default:
        level = OT_LOG_LEVEL_DEBG;
        break;
    }

    return level;
}

void ControllerOpenThread::Init(void)
{
    otbrError  error = OTBR_ERROR_NONE;
    otLogLevel level = ConvertToOtLogLevel(otbrLogGetLevel());

    VerifyOrExit(otLoggingSetLevel(level) == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);

    mInstance = otSysInit(&mConfig);
    assert(mInstance != nullptr);

#if OTBR_ENABLE_LEGACY
    otLegacyInit();
#endif

    {
        otError result = otSetStateChangedCallback(mInstance, &ControllerOpenThread::HandleStateChanged, this);

        agent::ThreadHelper::LogOpenThreadResult("Set state callback", result);
        VerifyOrExit(result == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);
    }

#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
    otSrpServerSetEnabled(mInstance, /* aEnabled */ true);
#endif

    mThreadHelper = std::unique_ptr<otbr::agent::ThreadHelper>(new otbr::agent::ThreadHelper(mInstance, this));

exit:
    SuccessOrDie(error, "Failed to initialize NCP!");
}

void ControllerOpenThread::Deinit(void)
{
    assert(mInstance != nullptr);

    otSysDeinit();
    mInstance = nullptr;
}

void ControllerOpenThread::HandleStateChanged(otChangedFlags aFlags)
{
    if (aFlags & OT_CHANGED_THREAD_ROLE)
    {
        switch (otThreadGetDeviceRole(mInstance))
        {
        case OT_DEVICE_ROLE_DISABLED:
#if OTBR_ENABLE_LEGACY
            otLegacyStop();
#endif
            break;
        case OT_DEVICE_ROLE_CHILD:
        case OT_DEVICE_ROLE_ROUTER:
        case OT_DEVICE_ROLE_LEADER:
#if OTBR_ENABLE_LEGACY
            otLegacyStart();
#endif
            break;
        default:
            break;
        }
    }

    for (auto &stateCallback : mThreadStateChangedCallbacks)
    {
        stateCallback(aFlags);
    }

    mThreadHelper->StateChangedCallback(aFlags);
}

void ControllerOpenThread::Update(MainloopContext &aMainloop)
{
    if (otTaskletsArePending(mInstance))
    {
        aMainloop.mTimeout = ToTimeval(Microseconds::zero());
    }

    otSysMainloopUpdate(mInstance, &aMainloop);
}

void ControllerOpenThread::Process(const MainloopContext &aMainloop)
{
    otTaskletsProcess(mInstance);

    otSysMainloopProcess(mInstance, &aMainloop);

    if (IsAutoAttachEnabled() && mThreadHelper->TryResumeNetwork() == OT_ERROR_NONE)
    {
        DisableAutoAttach();
    }
}

bool ControllerOpenThread::IsAutoAttachEnabled(void)
{
    const char *val = getenv("OTBR_NO_AUTO_ATTACH");

    // Auto Thread attaching is enabled if OTBR_NO_AUTO_ATTACH is unset, empty or "0"
    return (val == nullptr || !strcmp(val, "") || !strcmp(val, "0"));
}

void ControllerOpenThread::DisableAutoAttach(void)
{
    setenv("OTBR_NO_AUTO_ATTACH", "1", 1);
}

void ControllerOpenThread::PostTimerTask(Milliseconds aDelay, TaskRunner::Task<void> aTask)
{
    mTaskRunner.Post(std::move(aDelay), std::move(aTask));
}

void ControllerOpenThread::RegisterResetHandler(std::function<void(void)> aHandler)
{
    mResetHandlers.emplace_back(std::move(aHandler));
}

void ControllerOpenThread::AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)
{
    mThreadStateChangedCallbacks.emplace_back(std::move(aCallback));
}

void ControllerOpenThread::Reset(void)
{
    gPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE;

    otSysDeinit();
    mInstance = nullptr;

    Init();
    for (auto &handler : mResetHandlers)
    {
        handler();
    }
    unsetenv("OTBR_NO_AUTO_ATTACH");
}

const char *ControllerOpenThread::GetThreadVersion(void)
{
    const char *version;

    switch (otThreadGetVersion())
    {
    case kThreadVersion11:
        version = "1.1.1";
        break;
    case kThreadVersion12:
        version = "1.2.0";
        break;
    case kThreadVersion13:
        version = "1.3.0";
        break;
    default:
        otbrLogEmerg("Unexpected thread version %hu", otThreadGetVersion());
        exit(-1);
    }
    return version;
}

/*
 * Provide, if required an "otPlatLog()" function
 */
extern "C" void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
    OT_UNUSED_VARIABLE(aLogRegion);

    otbrLogLevel otbrLogLevel = ControllerOpenThread::ConvertToOtbrLogLevel(aLogLevel);

    va_list ap;
    va_start(ap, aFormat);
    otbrLogvNoFilter(otbrLogLevel, aFormat, ap);
    va_end(ap);
}

extern "C" void otPlatLogHandleLevelChanged(otLogLevel aLogLevel)
{
    otbrLogSetLevel(ControllerOpenThread::ConvertToOtbrLogLevel(aLogLevel));
    otbrLogInfo("OpenThread log level changed to %d", aLogLevel);
}

} // namespace Ncp
} // namespace otbr
<|endoftext|>


<commit_before>extern "C" {

#include <ccnet/peer.h>

}
#include <QtGui>
#include <QApplication>
#include <QDesktopServices>
#include <QSet>
#include <QDebug>

#include "seafile-applet.h"
#include "rpc/rpc-client.h"
#include "main-window.h"
#include "settings-dialog.h"
#include "settings-mgr.h"
#include "tray-icon.h"
#if defined(Q_WS_MAC)
#include "traynotificationmanager.h"
#endif

namespace {

const int kRefreshInterval = 5000;
const int kRotateTrayIconIntervalMilli = 250;

#if defined(Q_WS_WIN)
#include <windows.h>
bool
isWindowsVistaOrHigher()
{
    OSVERSIONINFOEX ver = {0};

    ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

    if (!GetVersionEx((LPOSVERSIONINFO)&ver)) {
        return false;
    }

    if (ver.dwMajorVersion >= 6) {
        return true;
    }

    return false;
}
#endif

}

SeafileTrayIcon::SeafileTrayIcon(QObject *parent)
    : QSystemTrayIcon(parent),
      nth_trayicon_(0),
      rotate_counter_(0)
{
    setToolTip(SEAFILE_CLIENT_BRAND);
    setState(STATE_DAEMON_DOWN);
    rotate_timer_ = new QTimer(this);
    connect(rotate_timer_, SIGNAL(timeout()), this, SLOT(rotateTrayIcon()));

    refresh_timer_ = new QTimer(this);
    connect(refresh_timer_, SIGNAL(timeout()), this, SLOT(refreshTrayIcon()));

    createActions();
    createContextMenu();

    connect(this, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this, SLOT(onActivated(QSystemTrayIcon::ActivationReason)));

#if defined(Q_WS_MAC)
    tnm = new TrayNotificationManager(this);
#endif
}

void SeafileTrayIcon::start()
{
    show();
    refresh_timer_->start(kRefreshInterval);
}

void SeafileTrayIcon::createActions()
{
    disable_auto_sync_action_ = new QAction(tr("Disable auto sync"), this);
    connect(disable_auto_sync_action_, SIGNAL(triggered()), this, SLOT(disableAutoSync()));

    enable_auto_sync_action_ = new QAction(tr("Enable auto sync"), this);
    connect(enable_auto_sync_action_, SIGNAL(triggered()), this, SLOT(enableAutoSync()));

    quit_action_ = new QAction(tr("&Quit"), this);
    connect(quit_action_, SIGNAL(triggered()), this, SLOT(quitSeafile()));

#ifndef Q_WS_MAC
    toggle_main_window_action_ = new QAction(tr("Show main window"), this);
    connect(toggle_main_window_action_, SIGNAL(triggered()), this, SLOT(toggleMainWindow()));
#endif

    settings_action_ = new QAction(tr("Settings"), this);
    connect(settings_action_, SIGNAL(triggered()), this, SLOT(showSettingsWindow()));

    about_action_ = new QAction(tr("&About"), this);
    about_action_->setStatusTip(tr("Show the application's About box"));
    connect(about_action_, SIGNAL(triggered()), this, SLOT(about()));

    open_help_action_ = new QAction(tr("&Online help"), this);
    open_help_action_->setStatusTip(tr("open seafile online help"));
    connect(open_help_action_, SIGNAL(triggered()), this, SLOT(openHelp()));
}

void SeafileTrayIcon::createContextMenu()
{
    help_menu_ = new QMenu(tr("Help"), NULL);
    help_menu_->addAction(about_action_);
    help_menu_->addAction(open_help_action_);

    context_menu_ = new QMenu(NULL);
#ifndef Q_WS_MAC
    context_menu_->addAction(toggle_main_window_action_);
#endif
    context_menu_->addAction(settings_action_);
    context_menu_->addMenu(help_menu_);
    context_menu_->addSeparator();
    context_menu_->addAction(enable_auto_sync_action_);
    context_menu_->addAction(disable_auto_sync_action_);
    context_menu_->addSeparator();
    context_menu_->addAction(quit_action_);

    setContextMenu(context_menu_);
    connect(context_menu_, SIGNAL(aboutToShow()), this, SLOT(prepareContextMenu()));
}

void SeafileTrayIcon::prepareContextMenu()
{
    if (seafApplet->settingsManager()->autoSync()) {
        enable_auto_sync_action_->setVisible(false);
        disable_auto_sync_action_->setVisible(true);
    } else {
        enable_auto_sync_action_->setVisible(true);
        disable_auto_sync_action_->setVisible(false);
    }

#ifndef Q_WS_MAC
    if (!seafApplet->mainWindow()->isVisible()) {
        toggle_main_window_action_->setText(tr("Show main window"));
    } else {
        toggle_main_window_action_->setText(tr("Hide main window"));
    }
#endif
}

void SeafileTrayIcon::notify(const QString &title, const QString &content)
{
#if defined(Q_WS_MAC)
    QIcon icon(":/images/info.png");
    TrayNotificationWidget* trayNotification = new TrayNotificationWidget(icon.pixmap(32, 32), title, content);
    tnm->append(trayNotification);
#else
    this->showMessage(title, content);
#endif
}

void SeafileTrayIcon::rotate(bool start)
{
    if (start) {
        rotate_counter_ = 0;
        if (!rotate_timer_->isActive()) {
            nth_trayicon_ = 0;
            rotate_timer_->start(kRotateTrayIconIntervalMilli);
        }
    } else {
        rotate_timer_->stop();
    }
}

void SeafileTrayIcon::rotateTrayIcon()
{
    if (rotate_counter_ >= 8 || !seafApplet->settingsManager()->autoSync()) {
        rotate_timer_->stop();
        if (!seafApplet->settingsManager()->autoSync())
            setState (STATE_DAEMON_AUTOSYNC_DISABLED);
        else
            setState (STATE_DAEMON_UP);
        return;
    }

    TrayState states[] = { STATE_TRANSFER_1, STATE_TRANSFER_2 };
    int index = nth_trayicon_ % 2;
    setIcon(stateToIcon(states[index]));

    nth_trayicon_++;
    rotate_counter_++;
}

void SeafileTrayIcon::resetToolTip ()
{
    QString tip(SEAFILE_CLIENT_BRAND);
    if (!seafApplet->settingsManager()->autoSync()) {
        tip = tr("auto sync is disabled");
    }

    setToolTip(tip);
}

void SeafileTrayIcon::setState(TrayState state)
{
    setIcon(stateToIcon(state));

    // the following two lines solving the problem of tray icon
    // disappear in ubuntu 13.04
#if defined(Q_WS_X11)
    hide();
    show();
#endif

    if (state != STATE_DAEMON_DOWN)
        resetToolTip();
}

QIcon SeafileTrayIcon::getIcon(const QString& name)
{
    if (icon_cache_.contains(name)) {
        return icon_cache_[name];
    }

    QIcon icon(name);
    icon_cache_[name] = icon;
    return icon;
}

QIcon SeafileTrayIcon::stateToIcon(TrayState state)
{
    state_ = state;
#if defined(Q_WS_WIN)
    QString prefix = isWindowsVistaOrHigher() ? ":/images/win/win7/" : ":/images/win/xp/";

    switch (state) {
    case STATE_DAEMON_UP:
        return getIcon(prefix + "daemon_up.ico");
    case STATE_DAEMON_DOWN:
        return getIcon(prefix + "daemon_down.ico");
    case STATE_DAEMON_AUTOSYNC_DISABLED:
        return getIcon(prefix + "seafile_auto_sync_disabled.ico");
    case STATE_TRANSFER_1:
        return getIcon(prefix + "seafile_transfer_1.ico");
    case STATE_TRANSFER_2:
        return getIcon(prefix + "seafile_transfer_2.ico");
    case STATE_SERVERS_NOT_CONNECTED:
        return getIcon(prefix + "seafile_warning.ico");
    }
#elif defined(Q_WS_MAC)
    switch (state) {
    case STATE_DAEMON_UP:
        return getIcon(":/images/mac/daemon_up.png");
    case STATE_DAEMON_DOWN:
        return getIcon(":/images/mac/daemon_down.png");
    case STATE_DAEMON_AUTOSYNC_DISABLED:
        return getIcon(":/images/mac/seafile_auto_sync_disabled.png");
    case STATE_TRANSFER_1:
        return getIcon(":/images/mac/seafile_transfer_1.png");
    case STATE_TRANSFER_2:
        return getIcon(":/images/mac/seafile_transfer_2.png");
    case STATE_SERVERS_NOT_CONNECTED:
        return getIcon(":/images/mac/seafile_warning.png");
    }
#else
    switch (state) {
    case STATE_DAEMON_UP:
        return getIcon(":/images/daemon_up.png");
    case STATE_DAEMON_DOWN:
        return getIcon(":/images/daemon_down.png");
    case STATE_DAEMON_AUTOSYNC_DISABLED:
        return getIcon(":/images/seafile_auto_sync_disabled.png");
    case STATE_TRANSFER_1:
        return getIcon(":/images/seafile_transfer_1.png");
    case STATE_TRANSFER_2:
        return getIcon(":/images/seafile_transfer_2.png");
    case STATE_SERVERS_NOT_CONNECTED:
        return getIcon(":/images/seafile_warning.png");
    }
#endif
}

void SeafileTrayIcon::toggleMainWindow()
{
    MainWindow *main_win = seafApplet->mainWindow();
    if (!main_win->isVisible()) {
        main_win->showWindow();
        main_win->raise();
        main_win->activateWindow();
    } else {
        main_win->hide();
    }
}

void SeafileTrayIcon::about()
{
    QMessageBox::about(seafApplet->mainWindow(), tr("About %1").arg(SEAFILE_CLIENT_BRAND),
                       tr("
%1 Client %2
").arg(SEAFILE_CLIENT_BRAND).arg(
                           STRINGIZE(SEAFILE_CLIENT_VERSION)));
}

void SeafileTrayIcon::openHelp()
{
    QString url;
    if (QLocale::system().name() == "zh_CN") {
        url = "http://seafile.com/help/install_v2/";
    } else {
        url = "http://seafile.com/en/help/install_v2/";
    }

    QDesktopServices::openUrl(QUrl(url));
}

void SeafileTrayIcon::showSettingsWindow()
{
    seafApplet->settingsDialog()->show();
    seafApplet->settingsDialog()->raise();
    seafApplet->settingsDialog()->activateWindow();
}

void SeafileTrayIcon::onActivated(QSystemTrayIcon::ActivationReason reason)
{
    qDebug("onActivated: %d", reason);

#ifndef Q_WS_MAC
    switch(reason) {
    case QSystemTrayIcon::Trigger: // single click
    case QSystemTrayIcon::MiddleClick:
    case QSystemTrayIcon::DoubleClick:
        toggleMainWindow();
        break;
    default:
        return;
    }
#endif
}

void SeafileTrayIcon::disableAutoSync()
{
    seafApplet->settingsManager()->setAutoSync(false);
}

void SeafileTrayIcon::enableAutoSync()
{
    seafApplet->settingsManager()->setAutoSync(true);
}

void SeafileTrayIcon::quitSeafile()
{
    seafApplet->exit(0);
}

void SeafileTrayIcon::refreshTrayIcon()
{
    bool all_server_connected = allServersConnected();
    if (state_ == STATE_DAEMON_UP && !all_server_connected) {
        setState(STATE_SERVERS_NOT_CONNECTED);
        setToolTip(tr("some server is not connected"));
    } else if (state_ == STATE_SERVERS_NOT_CONNECTED && all_server_connected) {
        setState(STATE_DAEMON_UP);
        setToolTip(SEAFILE_CLIENT_BRAND);
    }
}

bool SeafileTrayIcon::allServersConnected()
{
    if (!seafApplet->started()) {
        return true;
    }

    GList *servers = NULL;
    if (seafApplet->rpcClient()->getServers(&servers) < 0) {
        return true;
    }

    if (!servers) {
        return true;
    }

    GList *ptr;
    bool all_server_connected = true;
    for (ptr = servers; ptr ; ptr = ptr->next) {
        CcnetPeer *server = (CcnetPeer *)ptr->data;
        if (server->net_state != PEER_CONNECTED) {
            all_server_connected = false;
            break;
        }
    }

    g_list_foreach (servers, (GFunc)g_object_unref, NULL);
    g_list_free (servers);

    return all_server_connected;
}

<commit_msg>fixed a bug about tray icon<commit_after>extern "C" {

#include <ccnet/peer.h>

}
#include <QtGui>
#include <QApplication>
#include <QDesktopServices>
#include <QSet>
#include <QDebug>

#include "seafile-applet.h"
#include "rpc/rpc-client.h"
#include "main-window.h"
#include "settings-dialog.h"
#include "settings-mgr.h"
#include "tray-icon.h"
#if defined(Q_WS_MAC)
#include "traynotificationmanager.h"
#endif

namespace {

const int kRefreshInterval = 5000;
const int kRotateTrayIconIntervalMilli = 250;

#if defined(Q_WS_WIN)
#include <windows.h>
bool
isWindowsVistaOrHigher()
{
    OSVERSIONINFOEX ver = {0};

    ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

    if (!GetVersionEx((LPOSVERSIONINFO)&ver)) {
        return false;
    }

    if (ver.dwMajorVersion >= 6) {
        return true;
    }

    return false;
}
#endif

}

SeafileTrayIcon::SeafileTrayIcon(QObject *parent)
    : QSystemTrayIcon(parent),
      nth_trayicon_(0),
      rotate_counter_(0)
{
    setToolTip(SEAFILE_CLIENT_BRAND);
    setState(STATE_DAEMON_DOWN);
    rotate_timer_ = new QTimer(this);
    connect(rotate_timer_, SIGNAL(timeout()), this, SLOT(rotateTrayIcon()));

    refresh_timer_ = new QTimer(this);
    connect(refresh_timer_, SIGNAL(timeout()), this, SLOT(refreshTrayIcon()));

    createActions();
    createContextMenu();

    connect(this, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this, SLOT(onActivated(QSystemTrayIcon::ActivationReason)));

    hide();

#if defined(Q_WS_MAC)
    tnm = new TrayNotificationManager(this);
#endif
}

void SeafileTrayIcon::start()
{
    show();
    refresh_timer_->start(kRefreshInterval);
}

void SeafileTrayIcon::createActions()
{
    disable_auto_sync_action_ = new QAction(tr("Disable auto sync"), this);
    connect(disable_auto_sync_action_, SIGNAL(triggered()), this, SLOT(disableAutoSync()));

    enable_auto_sync_action_ = new QAction(tr("Enable auto sync"), this);
    connect(enable_auto_sync_action_, SIGNAL(triggered()), this, SLOT(enableAutoSync()));

    quit_action_ = new QAction(tr("&Quit"), this);
    connect(quit_action_, SIGNAL(triggered()), this, SLOT(quitSeafile()));

#ifndef Q_WS_MAC
    toggle_main_window_action_ = new QAction(tr("Show main window"), this);
    connect(toggle_main_window_action_, SIGNAL(triggered()), this, SLOT(toggleMainWindow()));
#endif

    settings_action_ = new QAction(tr("Settings"), this);
    connect(settings_action_, SIGNAL(triggered()), this, SLOT(showSettingsWindow()));

    about_action_ = new QAction(tr("&About"), this);
    about_action_->setStatusTip(tr("Show the application's About box"));
    connect(about_action_, SIGNAL(triggered()), this, SLOT(about()));

    open_help_action_ = new QAction(tr("&Online help"), this);
    open_help_action_->setStatusTip(tr("open seafile online help"));
    connect(open_help_action_, SIGNAL(triggered()), this, SLOT(openHelp()));
}

void SeafileTrayIcon::createContextMenu()
{
    help_menu_ = new QMenu(tr("Help"), NULL);
    help_menu_->addAction(about_action_);
    help_menu_->addAction(open_help_action_);

    context_menu_ = new QMenu(NULL);
#ifndef Q_WS_MAC
    context_menu_->addAction(toggle_main_window_action_);
#endif
    context_menu_->addAction(settings_action_);
    context_menu_->addMenu(help_menu_);
    context_menu_->addSeparator();
    context_menu_->addAction(enable_auto_sync_action_);
    context_menu_->addAction(disable_auto_sync_action_);
    context_menu_->addSeparator();
    context_menu_->addAction(quit_action_);

    setContextMenu(context_menu_);
    connect(context_menu_, SIGNAL(aboutToShow()), this, SLOT(prepareContextMenu()));
}

void SeafileTrayIcon::prepareContextMenu()
{
    if (seafApplet->settingsManager()->autoSync()) {
        enable_auto_sync_action_->setVisible(false);
        disable_auto_sync_action_->setVisible(true);
    } else {
        enable_auto_sync_action_->setVisible(true);
        disable_auto_sync_action_->setVisible(false);
    }

#ifndef Q_WS_MAC
    if (!seafApplet->mainWindow()->isVisible()) {
        toggle_main_window_action_->setText(tr("Show main window"));
    } else {
        toggle_main_window_action_->setText(tr("Hide main window"));
    }
#endif
}

void SeafileTrayIcon::notify(const QString &title, const QString &content)
{
#if defined(Q_WS_MAC)
    QIcon icon(":/images/info.png");
    TrayNotificationWidget* trayNotification = new TrayNotificationWidget(icon.pixmap(32, 32), title, content);
    tnm->append(trayNotification);
#else
    this->showMessage(title, content);
#endif
}

void SeafileTrayIcon::rotate(bool start)
{
    if (start) {
        rotate_counter_ = 0;
        if (!rotate_timer_->isActive()) {
            nth_trayicon_ = 0;
            rotate_timer_->start(kRotateTrayIconIntervalMilli);
        }
    } else {
        rotate_timer_->stop();
    }
}

void SeafileTrayIcon::rotateTrayIcon()
{
    if (rotate_counter_ >= 8 || !seafApplet->settingsManager()->autoSync()) {
        rotate_timer_->stop();
        if (!seafApplet->settingsManager()->autoSync())
            setState (STATE_DAEMON_AUTOSYNC_DISABLED);
        else
            setState (STATE_DAEMON_UP);
        return;
    }

    TrayState states[] = { STATE_TRANSFER_1, STATE_TRANSFER_2 };
    int index = nth_trayicon_ % 2;
    setIcon(stateToIcon(states[index]));

    nth_trayicon_++;
    rotate_counter_++;
}

void SeafileTrayIcon::resetToolTip ()
{
    QString tip(SEAFILE_CLIENT_BRAND);
    if (!seafApplet->settingsManager()->autoSync()) {
        tip = tr("auto sync is disabled");
    }

    setToolTip(tip);
}

void SeafileTrayIcon::setState(TrayState state)
{
    setIcon(stateToIcon(state));

    // the following two lines solving the problem of tray icon
    // disappear in ubuntu 13.04
#if defined(Q_WS_X11)
    hide();
    show();
#endif

    if (state != STATE_DAEMON_DOWN)
        resetToolTip();
}

QIcon SeafileTrayIcon::getIcon(const QString& name)
{
    if (icon_cache_.contains(name)) {
        return icon_cache_[name];
    }

    QIcon icon(name);
    icon_cache_[name] = icon;
    return icon;
}

QIcon SeafileTrayIcon::stateToIcon(TrayState state)
{
    state_ = state;
#if defined(Q_WS_WIN)
    QString prefix = isWindowsVistaOrHigher() ? ":/images/win/win7/" : ":/images/win/xp/";

    switch (state) {
    case STATE_DAEMON_UP:
        return getIcon(prefix + "daemon_up.ico");
    case STATE_DAEMON_DOWN:
        return getIcon(prefix + "daemon_down.ico");
    case STATE_DAEMON_AUTOSYNC_DISABLED:
        return getIcon(prefix + "seafile_auto_sync_disabled.ico");
    case STATE_TRANSFER_1:
        return getIcon(prefix + "seafile_transfer_1.ico");
    case STATE_TRANSFER_2:
        return getIcon(prefix + "seafile_transfer_2.ico");
    case STATE_SERVERS_NOT_CONNECTED:
        return getIcon(prefix + "seafile_warning.ico");
    }
#elif defined(Q_WS_MAC)
    switch (state) {
    case STATE_DAEMON_UP:
        return getIcon(":/images/mac/daemon_up.png");
    case STATE_DAEMON_DOWN:
        return getIcon(":/images/mac/daemon_down.png");
    case STATE_DAEMON_AUTOSYNC_DISABLED:
        return getIcon(":/images/mac/seafile_auto_sync_disabled.png");
    case STATE_TRANSFER_1:
        return getIcon(":/images/mac/seafile_transfer_1.png");
    case STATE_TRANSFER_2:
        return getIcon(":/images/mac/seafile_transfer_2.png");
    case STATE_SERVERS_NOT_CONNECTED:
        return getIcon(":/images/mac/seafile_warning.png");
    }
#else
    switch (state) {
    case STATE_DAEMON_UP:
        return getIcon(":/images/daemon_up.png");
    case STATE_DAEMON_DOWN:
        return getIcon(":/images/daemon_down.png");
    case STATE_DAEMON_AUTOSYNC_DISABLED:
        return getIcon(":/images/seafile_auto_sync_disabled.png");
    case STATE_TRANSFER_1:
        return getIcon(":/images/seafile_transfer_1.png");
    case STATE_TRANSFER_2:
        return getIcon(":/images/seafile_transfer_2.png");
    case STATE_SERVERS_NOT_CONNECTED:
        return getIcon(":/images/seafile_warning.png");
    }
#endif
}

void SeafileTrayIcon::toggleMainWindow()
{
    MainWindow *main_win = seafApplet->mainWindow();
    if (!main_win->isVisible()) {
        main_win->showWindow();
        main_win->raise();
        main_win->activateWindow();
    } else {
        main_win->hide();
    }
}

void SeafileTrayIcon::about()
{
    QMessageBox::about(seafApplet->mainWindow(), tr("About %1").arg(SEAFILE_CLIENT_BRAND),
                       tr("
%1 Client %2
").arg(SEAFILE_CLIENT_BRAND).arg(
                           STRINGIZE(SEAFILE_CLIENT_VERSION)));
}

void SeafileTrayIcon::openHelp()
{
    QString url;
    if (QLocale::system().name() == "zh_CN") {
        url = "http://seafile.com/help/install_v2/";
    } else {
        url = "http://seafile.com/en/help/install_v2/";
    }

    QDesktopServices::openUrl(QUrl(url));
}

void SeafileTrayIcon::showSettingsWindow()
{
    seafApplet->settingsDialog()->show();
    seafApplet->settingsDialog()->raise();
    seafApplet->settingsDialog()->activateWindow();
}

void SeafileTrayIcon::onActivated(QSystemTrayIcon::ActivationReason reason)
{
    qDebug("onActivated: %d", reason);

#ifndef Q_WS_MAC
    switch(reason) {
    case QSystemTrayIcon::Trigger: // single click
    case QSystemTrayIcon::MiddleClick:
    case QSystemTrayIcon::DoubleClick:
        toggleMainWindow();
        break;
    default:
        return;
    }
#endif
}

void SeafileTrayIcon::disableAutoSync()
{
    seafApplet->settingsManager()->setAutoSync(false);
}

void SeafileTrayIcon::enableAutoSync()
{
    seafApplet->settingsManager()->setAutoSync(true);
}

void SeafileTrayIcon::quitSeafile()
{
    seafApplet->exit(0);
}

void SeafileTrayIcon::refreshTrayIcon()
{
    bool all_server_connected = allServersConnected();
    if (state_ == STATE_DAEMON_UP && !all_server_connected) {
        setState(STATE_SERVERS_NOT_CONNECTED);
        setToolTip(tr("some server is not connected"));
    } else if (state_ == STATE_SERVERS_NOT_CONNECTED && all_server_connected) {
        setState(STATE_DAEMON_UP);
        setToolTip(SEAFILE_CLIENT_BRAND);
    }
}

bool SeafileTrayIcon::allServersConnected()
{
    if (!seafApplet->started()) {
        return true;
    }

    GList *servers = NULL;
    if (seafApplet->rpcClient()->getServers(&servers) < 0) {
        return true;
    }

    if (!servers) {
        return true;
    }

    GList *ptr;
    bool all_server_connected = true;
    for (ptr = servers; ptr ; ptr = ptr->next) {
        CcnetPeer *server = (CcnetPeer *)ptr->data;
        if (server->net_state != PEER_CONNECTED) {
            all_server_connected = false;
            break;
        }
    }

    g_list_foreach (servers, (GFunc)g_object_unref, NULL);
    g_list_free (servers);

    return all_server_connected;
}

<|endoftext|>


<commit_before>#include "vm.h"

#include "opcodes.h"
#include "sections.h"

#include <cassert>
#include <iostream>

#define MASK 0x8000000000000000
#define MASK_STR(STR) ((STR) | (MASK))
#define UNMASK_STR(STR) ((STR) & ~(MASK))
#define IS_STR(STR) ((STR) & (MASK))

#define JS_FUNCTION(FN_NAME) static uintptr_t FN_NAME(ceos::VM &vm, unsigned argv)

#define BASIC_MATH(NAME, OP) \
  JS_FUNCTION(NAME) { \
    assert(argv == 2); \
 \
    return vm.arg(0) OP vm.arg(1); \
  }

BASIC_MATH(add, +)
BASIC_MATH(sub, -)
BASIC_MATH(mul, *)
BASIC_MATH(div, /)

JS_FUNCTION(print) {
  for (unsigned i = 0; i < argv; i++) {
    uintptr_t arg = vm.arg(i);
    if (IS_STR(arg)) {
      std::cout << (reinterpret_cast<std::string *>(UNMASK_STR(arg)))->c_str() << "\n";
    } else {
      std::cout << static_cast<int>(arg) << "\n";
    }
  }

  return 0;
}

#undef BASIC_MATH

namespace ceos {

  void VM::registerBuiltins() {
#define REGISTER(NAME) uintptr_t (*NAME##_)(VM &, unsigned) = NAME; m_functionTable[#NAME] = (uintptr_t)NAME##_

    REGISTER(print);
    REGISTER(add);
    REGISTER(sub);
    REGISTER(mul);
    REGISTER(div);

#undef REGISTER
  }

  void VM::stack_push(uintptr_t value) {
    esp++;
    stack.push_back(value);
  }

  uintptr_t VM::stack_pop() {
    uintptr_t value = stack.back();
    stack.pop_back();
    esp--;
    return value;
  }

  uintptr_t VM::arg(unsigned index) {
    // 2 because the pointers are always 1 ahead and ebp is on top of the stack
    return stack[ebp - index - 2];
  }

  void VM::execute() {
    READ_INT(m_bytecode, ceos);

    // section marker
    assert(ceos == Section::Header);

    while (true) {
      READ_INT(m_bytecode, section);

      switch (section) {
        case Section::Strings:
          loadStrings();
          break;
        case Section::Functions:
          loadFunctions();
          break;
        case Section::Text:
          run(m_bytecode);
          break;
      }
    }
  }

  void VM::loadStrings() {
    while (true) {
      READ_INT(m_bytecode, header);

      if (header == Section::Header) {
        break;
      }

      m_bytecode.seekg(-4, m_bytecode.cur);
      READ_STR(m_bytecode, str);
      m_stringTable.push_back(str);
    }
  }

  void VM::loadFunctions() {
    READ_INT(m_bytecode, initialHeader);
    assert(initialHeader == Section::FunctionHeader);

    while (true) {
      READ_STR(m_bytecode, name);
      READ_INT(m_bytecode, nargs);

      m_userFunctions.push_back(Function(name, nargs));
      Function *fn = &m_userFunctions.back();
      m_functionTable[name] = MASK_STR(reinterpret_cast<uintptr_t>(fn));

      while (true) {
        READ_INT(m_bytecode, opcode);
        if (opcode == Section::Header) {
          return;
        } else if (opcode == Section::FunctionHeader) {
          break;
        }

        fn->bytecode.write(reinterpret_cast<char *>(&opcode), sizeof(opcode));
      }
    }
  }

  void VM::run(std::stringstream &bytecode) {
    while (true) {
      READ_INT(bytecode, opcode);

      switch (opcode) {
        case Opcode::push: {
          READ_INT(bytecode, value);
          stack_push(value);
          break;
        }
        case Opcode::call: {
          int nargs = stack_pop();
          uintptr_t fn_address = stack_pop();

          //assert(fn != nullptr);
          --nargs; //pop'd the callee

          stack_push(ebp);
          ebp = esp;

          uintptr_t ret;
          if (IS_STR(fn_address)) {
            Function *fn = reinterpret_cast<__typeof__(fn)>(UNMASK_STR(fn_address));
            ret = (*fn)(*this, nargs);
          } else {
            uintptr_t (*fn)(VM &, unsigned) = reinterpret_cast<__typeof__(fn)>(fn_address);
            ret = fn(*this, nargs);
          }

          esp = ebp;
          ebp = stack_pop();
          stack.resize(stack.size() - nargs);
          esp -= nargs;

          stack_push(ret);

          break;
        }
        case Opcode::load_string: {
          READ_INT(bytecode, stringID);
          stack_push(MASK_STR(reinterpret_cast<uintptr_t>(&m_stringTable[stringID])));
          break;
        }
        case Opcode::lookup: {
          std::string *fnName = reinterpret_cast<std::string *>(UNMASK_STR(stack_pop()));
          stack_push(reinterpret_cast<uintptr_t>(m_functionTable[*fnName]));
          break;
        }
        case Opcode::jmp:  {
          READ_INT(bytecode, target);
          bytecode.seekg(target, bytecode.cur);
          break;
        }
        case Opcode::jz: {
          READ_INT(bytecode, target);
          int value = static_cast<int>(stack_pop());
          if (value == 0) {
            bytecode.seekg(target, bytecode.cur);
          }
          break;
        }
        case Opcode::push_arg: {
          READ_INT(bytecode, index);
          stack_push(arg(index));
          break;
        }
      }
    }
  }

}
<commit_msg>Warn on unhandled opcodes<commit_after>#include "vm.h"

#include "opcodes.h"
#include "sections.h"

#include <cassert>
#include <iostream>

#define MASK 0x8000000000000000
#define MASK_STR(STR) ((STR) | (MASK))
#define UNMASK_STR(STR) ((STR) & ~(MASK))
#define IS_STR(STR) ((STR) & (MASK))

#define JS_FUNCTION(FN_NAME) static uintptr_t FN_NAME(ceos::VM &vm, unsigned argv)

#define BASIC_MATH(NAME, OP) \
  JS_FUNCTION(NAME) { \
    assert(argv == 2); \
 \
    return vm.arg(0) OP vm.arg(1); \
  }

BASIC_MATH(add, +)
BASIC_MATH(sub, -)
BASIC_MATH(mul, *)
BASIC_MATH(div, /)

JS_FUNCTION(print) {
  for (unsigned i = 0; i < argv; i++) {
    uintptr_t arg = vm.arg(i);
    if (IS_STR(arg)) {
      std::cout << (reinterpret_cast<std::string *>(UNMASK_STR(arg)))->c_str() << "\n";
    } else {
      std::cout << static_cast<int>(arg) << "\n";
    }
  }

  return 0;
}

#undef BASIC_MATH

namespace ceos {

  void VM::registerBuiltins() {
#define REGISTER(NAME) uintptr_t (*NAME##_)(VM &, unsigned) = NAME; m_functionTable[#NAME] = (uintptr_t)NAME##_

    REGISTER(print);
    REGISTER(add);
    REGISTER(sub);
    REGISTER(mul);
    REGISTER(div);

#undef REGISTER
  }

  void VM::stack_push(uintptr_t value) {
    esp++;
    stack.push_back(value);
  }

  uintptr_t VM::stack_pop() {
    uintptr_t value = stack.back();
    stack.pop_back();
    esp--;
    return value;
  }

  uintptr_t VM::arg(unsigned index) {
    // 2 because the pointers are always 1 ahead and ebp is on top of the stack
    return stack[ebp - index - 2];
  }

  void VM::execute() {
    READ_INT(m_bytecode, ceos);

    // section marker
    assert(ceos == Section::Header);

    while (true) {
      READ_INT(m_bytecode, section);

      switch (section) {
        case Section::Strings:
          loadStrings();
          break;
        case Section::Functions:
          loadFunctions();
          break;
        case Section::Text:
          run(m_bytecode);
          break;
      }
    }
  }

  void VM::loadStrings() {
    while (true) {
      READ_INT(m_bytecode, header);

      if (header == Section::Header) {
        break;
      }

      m_bytecode.seekg(-4, m_bytecode.cur);
      READ_STR(m_bytecode, str);
      m_stringTable.push_back(str);
    }
  }

  void VM::loadFunctions() {
    READ_INT(m_bytecode, initialHeader);
    assert(initialHeader == Section::FunctionHeader);

    while (true) {
      READ_STR(m_bytecode, name);
      READ_INT(m_bytecode, nargs);

      m_userFunctions.push_back(Function(name, nargs));
      Function *fn = &m_userFunctions.back();
      m_functionTable[name] = MASK_STR(reinterpret_cast<uintptr_t>(fn));

      while (true) {
        READ_INT(m_bytecode, opcode);
        if (opcode == Section::Header) {
          return;
        } else if (opcode == Section::FunctionHeader) {
          break;
        }

        fn->bytecode.write(reinterpret_cast<char *>(&opcode), sizeof(opcode));
      }
    }
  }

  void VM::run(std::stringstream &bytecode) {
    while (true) {
      READ_INT(bytecode, opcode);

      switch (opcode) {
        case Opcode::push: {
          READ_INT(bytecode, value);
          stack_push(value);
          break;
        }
        case Opcode::call: {
          int nargs = stack_pop();
          uintptr_t fn_address = stack_pop();

          //assert(fn != nullptr);
          --nargs; //pop'd the callee

          stack_push(ebp);
          ebp = esp;

          uintptr_t ret;
          if (IS_STR(fn_address)) {
            Function *fn = reinterpret_cast<__typeof__(fn)>(UNMASK_STR(fn_address));
            ret = (*fn)(*this, nargs);
          } else {
            uintptr_t (*fn)(VM &, unsigned) = reinterpret_cast<__typeof__(fn)>(fn_address);
            ret = fn(*this, nargs);
          }

          esp = ebp;
          ebp = stack_pop();
          stack.resize(stack.size() - nargs);
          esp -= nargs;

          stack_push(ret);

          break;
        }
        case Opcode::load_string: {
          READ_INT(bytecode, stringID);
          stack_push(MASK_STR(reinterpret_cast<uintptr_t>(&m_stringTable[stringID])));
          break;
        }
        case Opcode::lookup: {
          std::string *fnName = reinterpret_cast<std::string *>(UNMASK_STR(stack_pop()));
          stack_push(reinterpret_cast<uintptr_t>(m_functionTable[*fnName]));
          break;
        }
        case Opcode::jmp:  {
          READ_INT(bytecode, target);
          bytecode.seekg(target, bytecode.cur);
          break;
        }
        case Opcode::jz: {
          READ_INT(bytecode, target);
          int value = static_cast<int>(stack_pop());
          if (value == 0) {
            bytecode.seekg(target, bytecode.cur);
          }
          break;
        }
        case Opcode::push_arg: {
          READ_INT(bytecode, index);
          stack_push(arg(index));
          break;
        }
        default:
          std::cerr << "Unhandled opcode: " << Opcode::typeName(static_cast<Opcode::Type>(opcode)) << "\n";
          exit(1);
      }
    }
  }

}
<|endoftext|>


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

// Author: jefftk@google.com (Jeff Kaufman)

#include "ngx_rewrite_options.h"

extern "C" {
  #include <ngx_config.h>
  #include <ngx_core.h>
  #include <ngx_http.h>
}

#include "ngx_pagespeed.h"
#include "ngx_rewrite_driver_factory.h"

#include "net/instaweb/public/version.h"
#include "net/instaweb/rewriter/public/file_load_policy.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/system/public/system_caches.h"
#include "net/instaweb/util/public/timer.h"

namespace net_instaweb {

namespace {

const char kNgxPagespeedStatisticsHandlerPath[] = "/ngx_pagespeed_statistics";

}  // namespace

RewriteOptions::Properties* NgxRewriteOptions::ngx_properties_ = NULL;

NgxRewriteOptions::NgxRewriteOptions(ThreadSystem* thread_system)
    : SystemRewriteOptions(thread_system) {
  Init();
}

void NgxRewriteOptions::Init() {
  DCHECK(ngx_properties_ != NULL)
      << "Call NgxRewriteOptions::Initialize() before construction";
  InitializeOptions(ngx_properties_);

  // Nginx-specific default.
  // TODO(sligocki): Get rid of this line and let both Apache and Nginx use
  // /pagespeed_statistics as the handler.
  statistics_handler_path_.set_default(kNgxPagespeedStatisticsHandlerPath);
}

void NgxRewriteOptions::AddProperties() {
  // Nothing ngx-specific for now.

  MergeSubclassProperties(ngx_properties_);
  // We create a dummy NgxRewriteOptions object here so that we can
  // call InitializeSignaturesAndDefaults on it, and in turn get the
  // global defaults (for say, X-Page-Speed header value) setup correctly.
  NgxRewriteOptions dummy_config(NULL);
  dummy_config.InitializeSignaturesAndDefaults();
}

void NgxRewriteOptions::InitializeSignaturesAndDefaults() {
  // Calls to foo_.DoNotUseForSignatureComputation() would go here.

  // Set default header value.
  set_default_x_header_value(kModPagespeedVersion);
}

void NgxRewriteOptions::Initialize() {
  if (Properties::Initialize(&ngx_properties_)) {
    SystemRewriteOptions::Initialize();
    AddProperties();
  }
}

void NgxRewriteOptions::Terminate() {
  if (Properties::Terminate(&ngx_properties_)) {
    SystemRewriteOptions::Terminate();
  }
}

bool NgxRewriteOptions::IsDirective(StringPiece config_directive,
                                    StringPiece compare_directive) {
  return StringCaseEqual(config_directive, compare_directive);
}

RewriteOptions::OptionSettingResult NgxRewriteOptions::ParseAndSetOptions0(
    StringPiece directive, GoogleString* msg, MessageHandler* handler) {
  if (IsDirective(directive, "on")) {
    set_enabled(RewriteOptions::kEnabledOn);
  } else if (IsDirective(directive, "off")) {
    set_enabled(RewriteOptions::kEnabledOff);
  } else if (IsDirective(directive, "unplugged")) {
    set_enabled(RewriteOptions::kEnabledUnplugged);
  } else {
    return RewriteOptions::kOptionNameUnknown;
  }
  return RewriteOptions::kOptionOk;
}

RewriteOptions::OptionSettingResult
    NgxRewriteOptions::ParseAndSetOptionFromEnum1(
        OptionEnum directive, StringPiece arg,
        GoogleString* msg, MessageHandler* handler) {
  // FileCachePath needs error checking.
  if (directive == kFileCachePath) {
    if (!StringCaseStartsWith(arg, "/")) {
      *msg = "must start with a slash";
      return RewriteOptions::kOptionValueInvalid;
    }
  }

  // TODO(jefftk): port these (no enums for them yet, even!)
  //  DangerPermitFetchFromUnknownHosts, FetchWithGzip, ForceCaching

  return SystemRewriteOptions::ParseAndSetOptionFromEnum1(
      directive, arg, msg, handler);
}

RewriteOptions::OptionSettingResult ParseAndSetOptionHelper(
    StringPiece option_value,
    NgxRewriteDriverFactory* driver_factory,
    void (NgxRewriteDriverFactory::*set_option_method)(bool)) {
  bool parsed_value;
  if (StringCaseEqual(option_value, "on") ||
      StringCaseEqual(option_value, "true")) {
    parsed_value = true;
  } else if (StringCaseEqual(option_value, "off") ||
             StringCaseEqual(option_value, "false")) {
    parsed_value = false;
  } else {
    return RewriteOptions::kOptionValueInvalid;
  }

  (driver_factory->*set_option_method)(parsed_value);
  return RewriteOptions::kOptionOk;
}

// Very similar to apache/mod_instaweb::ParseDirective.
const char* NgxRewriteOptions::ParseAndSetOptions(
    StringPiece* args, int n_args, ngx_pool_t* pool, MessageHandler* handler,
    NgxRewriteDriverFactory* driver_factory) {
  CHECK_GE(n_args, 1);

  StringPiece directive = args[0];

  // Remove initial "ModPagespeed" if there is one.
  StringPiece mod_pagespeed("ModPagespeed");
  if (StringCaseStartsWith(directive, mod_pagespeed)) {
    directive.remove_prefix(mod_pagespeed.size());
  }

  GoogleString msg;
  OptionSettingResult result;
  if (n_args == 1) {
    result = ParseAndSetOptions0(directive, &msg, handler);
  } else if (n_args == 2) {
    StringPiece arg = args[1];
    // TODO(morlovich): Remove these special hacks, and handle these via
    // ParseAndSetOptionFromEnum1.
    if (IsDirective(directive, "UsePerVHostStatistics")) {
      result = ParseAndSetOptionHelper(
          arg, driver_factory,
          &NgxRewriteDriverFactory::set_use_per_vhost_statistics);
    } else if (IsDirective(directive, "InstallCrashHandler")) {
      result = ParseAndSetOptionHelper(
          arg, driver_factory,
          &NgxRewriteDriverFactory::set_install_crash_handler);
    } else if (IsDirective(directive, "MessageBufferSize")) {
      int message_buffer_size;
      bool ok = StringToInt(arg.as_string(), &message_buffer_size);
      if (ok && message_buffer_size >= 0) {
        driver_factory->set_message_buffer_size(message_buffer_size);
        result = RewriteOptions::kOptionOk;
      } else {
        result = RewriteOptions::kOptionValueInvalid;
      }
    } else if (IsDirective(directive, "UseNativeFetcher")) {
      result = ParseAndSetOptionHelper(
          arg, driver_factory,
          &NgxRewriteDriverFactory::set_use_native_fetcher);
    } else if (IsDirective(directive, "RateLimitBackgroundFetches")) {
      result = ParseAndSetOptionHelper(
          arg, driver_factory,
          &NgxRewriteDriverFactory::set_rate_limit_background_fetches);
    } else {
      result = ParseAndSetOptionFromName1(directive, args[1], &msg, handler);
    }
  } else if (n_args == 3) {
    // Short-term special handling, until this moves to common code.
    // TODO(morlovich): Clean this up.
    if (StringCaseEqual(directive, "CreateSharedMemoryMetadataCache")) {
      int64 kb = 0;
      if (!StringToInt64(args[2], &kb) || kb < 0) {
        result = RewriteOptions::kOptionValueInvalid;
        msg = "size_kb must be a positive 64-bit integer";
      } else {
        bool ok = driver_factory->caches()->CreateShmMetadataCache(
            args[1].as_string(), kb, &msg);
        result = ok ? kOptionOk : kOptionValueInvalid;
      }
    } else {
      result = ParseAndSetOptionFromName2(directive, args[1], args[2],
                                          &msg, handler);
    }
  } else if (n_args == 4) {
    result = ParseAndSetOptionFromName3(
        directive, args[1], args[2], args[3], &msg, handler);
  } else {
    return "unknown option";
  }

  switch (result) {
    case RewriteOptions::kOptionOk:
      return NGX_CONF_OK;
    case RewriteOptions::kOptionNameUnknown:
      return "unknown option";
    case RewriteOptions::kOptionValueInvalid: {
      GoogleString full_directive = "\"";
      for (int i = 0 ; i < n_args ; i++) {
        StrAppend(&full_directive, i == 0 ? "" : " ", args[i]);
      }
      StrAppend(&full_directive, "\": ", msg);
      char* s = ngx_psol::string_piece_to_pool_string(pool, full_directive);
      if (s == NULL) {
        return "failed to allocate memory";
      }
      return s;
    }
  }

  CHECK(false);
  return NULL;
}

NgxRewriteOptions* NgxRewriteOptions::Clone() const {
  NgxRewriteOptions* options = new NgxRewriteOptions(thread_system());
  options->Merge(*this);
  return options;
}

const NgxRewriteOptions* NgxRewriteOptions::DynamicCast(
    const RewriteOptions* instance) {
  return dynamic_cast<const NgxRewriteOptions*>(instance);
}

NgxRewriteOptions* NgxRewriteOptions::DynamicCast(RewriteOptions* instance) {
  return dynamic_cast<NgxRewriteOptions*>(instance);
}

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

// Author: jefftk@google.com (Jeff Kaufman)

#include "ngx_rewrite_options.h"

extern "C" {
  #include <ngx_config.h>
  #include <ngx_core.h>
  #include <ngx_http.h>
}

#include "ngx_pagespeed.h"
#include "ngx_rewrite_driver_factory.h"

#include "net/instaweb/public/version.h"
#include "net/instaweb/rewriter/public/file_load_policy.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/system/public/system_caches.h"
#include "net/instaweb/util/public/timer.h"

namespace net_instaweb {

namespace {

const char kNgxPagespeedStatisticsHandlerPath[] = "/ngx_pagespeed_statistics";

}  // namespace

RewriteOptions::Properties* NgxRewriteOptions::ngx_properties_ = NULL;

NgxRewriteOptions::NgxRewriteOptions(ThreadSystem* thread_system)
    : SystemRewriteOptions(thread_system) {
  Init();
}

void NgxRewriteOptions::Init() {
  DCHECK(ngx_properties_ != NULL)
      << "Call NgxRewriteOptions::Initialize() before construction";
  InitializeOptions(ngx_properties_);

  // Nginx-specific default.
  // TODO(sligocki): Get rid of this line and let both Apache and Nginx use
  // /pagespeed_statistics as the handler.
  statistics_handler_path_.set_default(kNgxPagespeedStatisticsHandlerPath);
}

void NgxRewriteOptions::AddProperties() {
  // Nothing ngx-specific for now.

  MergeSubclassProperties(ngx_properties_);
  // We create a dummy NgxRewriteOptions object here so that we can
  // call InitializeSignaturesAndDefaults on it, and in turn get the
  // global defaults (for say, X-Page-Speed header value) setup correctly.
  NgxRewriteOptions dummy_config(NULL);
  dummy_config.InitializeSignaturesAndDefaults();
}

void NgxRewriteOptions::InitializeSignaturesAndDefaults() {
  // Calls to foo_.DoNotUseForSignatureComputation() would go here.

  // Set default header value.
  set_default_x_header_value(kModPagespeedVersion);
}

void NgxRewriteOptions::Initialize() {
  if (Properties::Initialize(&ngx_properties_)) {
    SystemRewriteOptions::Initialize();
    AddProperties();
  }
}

void NgxRewriteOptions::Terminate() {
  if (Properties::Terminate(&ngx_properties_)) {
    SystemRewriteOptions::Terminate();
  }
}

bool NgxRewriteOptions::IsDirective(StringPiece config_directive,
                                    StringPiece compare_directive) {
  return StringCaseEqual(config_directive, compare_directive);
}

RewriteOptions::OptionSettingResult NgxRewriteOptions::ParseAndSetOptions0(
    StringPiece directive, GoogleString* msg, MessageHandler* handler) {
  if (IsDirective(directive, "on")) {
    set_enabled(RewriteOptions::kEnabledOn);
  } else if (IsDirective(directive, "off")) {
    set_enabled(RewriteOptions::kEnabledOff);
  } else if (IsDirective(directive, "unplugged")) {
    set_enabled(RewriteOptions::kEnabledUnplugged);
  } else {
    return RewriteOptions::kOptionNameUnknown;
  }
  return RewriteOptions::kOptionOk;
}

RewriteOptions::OptionSettingResult
    NgxRewriteOptions::ParseAndSetOptionFromEnum1(
        OptionEnum directive, StringPiece arg,
        GoogleString* msg, MessageHandler* handler) {
  // FileCachePath needs error checking.
  if (directive == kFileCachePath) {
    if (!StringCaseStartsWith(arg, "/")) {
      *msg = "must start with a slash";
      return RewriteOptions::kOptionValueInvalid;
    }
  }

  // TODO(jefftk): port these (no enums for them yet, even!)
  //  DangerPermitFetchFromUnknownHosts, FetchWithGzip, ForceCaching

  return SystemRewriteOptions::ParseAndSetOptionFromEnum1(
      directive, arg, msg, handler);
}

RewriteOptions::OptionSettingResult ParseAndSetOptionHelper(
    StringPiece option_value,
    NgxRewriteDriverFactory* driver_factory,
    void (NgxRewriteDriverFactory::*set_option_method)(bool)) {
  bool parsed_value;
  if (StringCaseEqual(option_value, "on") ||
      StringCaseEqual(option_value, "true")) {
    parsed_value = true;
  } else if (StringCaseEqual(option_value, "off") ||
             StringCaseEqual(option_value, "false")) {
    parsed_value = false;
  } else {
    return RewriteOptions::kOptionValueInvalid;
  }

  (driver_factory->*set_option_method)(parsed_value);
  return RewriteOptions::kOptionOk;
}

// Very similar to apache/mod_instaweb::ParseDirective.
const char* NgxRewriteOptions::ParseAndSetOptions(
    StringPiece* args, int n_args, ngx_pool_t* pool, MessageHandler* handler,
    NgxRewriteDriverFactory* driver_factory) {
  CHECK_GE(n_args, 1);

  StringPiece directive = args[0];

  // Remove initial "ModPagespeed" if there is one.
  StringPiece mod_pagespeed("ModPagespeed");
  if (StringCaseStartsWith(directive, mod_pagespeed)) {
    directive.remove_prefix(mod_pagespeed.size());
  }

  GoogleString msg;
  OptionSettingResult result;
  if (n_args == 1) {
    result = ParseAndSetOptions0(directive, &msg, handler);
  } else if (n_args == 2) {
    StringPiece arg = args[1];
    // TODO(morlovich): Remove these special hacks, and handle these via
    // ParseAndSetOptionFromEnum1.
    if (IsDirective(directive, "UsePerVHostStatistics")) {
      result = ParseAndSetOptionHelper(
          arg, driver_factory,
          &NgxRewriteDriverFactory::set_use_per_vhost_statistics);
    } else if (IsDirective(directive, "InstallCrashHandler")) {
      result = ParseAndSetOptionHelper(
          arg, driver_factory,
          &NgxRewriteDriverFactory::set_install_crash_handler);
    } else if (IsDirective(directive, "MessageBufferSize")) {
      int message_buffer_size;
      bool ok = StringToInt(arg.as_string(), &message_buffer_size);
      if (ok && message_buffer_size >= 0) {
        driver_factory->set_message_buffer_size(message_buffer_size);
        result = RewriteOptions::kOptionOk;
      } else {
        result = RewriteOptions::kOptionValueInvalid;
      }
    } else if (IsDirective(directive, "UseNativeFetcher")) {
      result = ParseAndSetOptionHelper(
          arg, driver_factory,
          &NgxRewriteDriverFactory::set_use_native_fetcher);
    } else if (IsDirective(directive, "RateLimitBackgroundFetches")) {
      result = ParseAndSetOptionHelper(
          arg, driver_factory,
          &NgxRewriteDriverFactory::set_rate_limit_background_fetches);
    } else if (IsDirective(directive, "ForceCaching")) {
      result = ParseAndSetOptionHelper(
          arg, driver_factory,
          &NgxRewriteDriverFactory::set_force_caching);
    } else {
      result = ParseAndSetOptionFromName1(directive, args[1], &msg, handler);
    }
  } else if (n_args == 3) {
    // Short-term special handling, until this moves to common code.
    // TODO(morlovich): Clean this up.
    if (StringCaseEqual(directive, "CreateSharedMemoryMetadataCache")) {
      int64 kb = 0;
      if (!StringToInt64(args[2], &kb) || kb < 0) {
        result = RewriteOptions::kOptionValueInvalid;
        msg = "size_kb must be a positive 64-bit integer";
      } else {
        bool ok = driver_factory->caches()->CreateShmMetadataCache(
            args[1].as_string(), kb, &msg);
        result = ok ? kOptionOk : kOptionValueInvalid;
      }
    } else {
      result = ParseAndSetOptionFromName2(directive, args[1], args[2],
                                          &msg, handler);
    }
  } else if (n_args == 4) {
    result = ParseAndSetOptionFromName3(
        directive, args[1], args[2], args[3], &msg, handler);
  } else {
    return "unknown option";
  }

  switch (result) {
    case RewriteOptions::kOptionOk:
      return NGX_CONF_OK;
    case RewriteOptions::kOptionNameUnknown:
      return "unknown option";
    case RewriteOptions::kOptionValueInvalid: {
      GoogleString full_directive = "\"";
      for (int i = 0 ; i < n_args ; i++) {
        StrAppend(&full_directive, i == 0 ? "" : " ", args[i]);
      }
      StrAppend(&full_directive, "\": ", msg);
      char* s = ngx_psol::string_piece_to_pool_string(pool, full_directive);
      if (s == NULL) {
        return "failed to allocate memory";
      }
      return s;
    }
  }

  CHECK(false);
  return NULL;
}

NgxRewriteOptions* NgxRewriteOptions::Clone() const {
  NgxRewriteOptions* options = new NgxRewriteOptions(thread_system());
  options->Merge(*this);
  return options;
}

const NgxRewriteOptions* NgxRewriteOptions::DynamicCast(
    const RewriteOptions* instance) {
  return dynamic_cast<const NgxRewriteOptions*>(instance);
}

NgxRewriteOptions* NgxRewriteOptions::DynamicCast(RewriteOptions* instance) {
  return dynamic_cast<NgxRewriteOptions*>(instance);
}

}  // namespace net_instaweb
<|endoftext|>