<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2019-2020 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "d3d11_test.h"

RD_TEST(D3D11_Deferred_Map, D3D11GraphicsTest)
{
  static constexpr const char *Description =
      "Test that does Map() on a deferred context on buffers and textures.";

  std::string pixel = R"EOSHADER(

struct v2f
{
	float4 pos : SV_POSITION;
	float4 col : COLOR0;
	float4 uv : TEXCOORD0;
};

Texture2D<float4> tex;

float4 main(v2f IN) : SV_Target0
{
	clip(float2(0.9999f, 0.9999f) - IN.uv.xy);
	return tex.Load(int3(IN.uv.xyz*64.0f));
}

)EOSHADER";

  int main()
  {
    // initialise, create window, create device, etc
    if(!Init())
      return 3;

    ID3D11DeviceContextPtr defctx;
    dev->CreateDeferredContext(0, &defctx);

    ID3DBlobPtr vsblob = Compile(D3DDefaultVertex, "main", "vs_5_0");
    ID3DBlobPtr psblob = Compile(pixel, "main", "ps_5_0");

    CreateDefaultInputLayout(vsblob);

    ID3D11VertexShaderPtr vs = CreateVS(vsblob);
    ID3D11PixelShaderPtr ps = CreatePS(psblob);

    DefaultA2V triangle[] = {
        {Vec3f(-1.0f, 0.0f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
        {Vec3f(-1.0f, 2.0f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 2.0f)},
        {Vec3f(3.0f, 0.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(2.0f, 0.0f)},

        {Vec3f(-1.0f, -1.0f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
        {Vec3f(-1.0f, 1.0f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 2.0f)},
        {Vec3f(3.0f, -1.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(2.0f, 0.0f)},
    };

    ID3D11BufferPtr vb = MakeBuffer().Vertex().Data(triangle);

    ID3D11Texture2DPtr tex =
        MakeTexture(DXGI_FORMAT_R32G32B32A32_FLOAT, 64, 64).SRV().Mips(1).Mappable();
    ID3D11ShaderResourceViewPtr srv = MakeSRV(tex);

    ID3D11CommandListPtr cmdList;

    D3D11_MAPPED_SUBRESOURCE mapped = {};
    Vec4f *data = NULL;
    while(Running())
    {
      ClearRenderTargetView(bbRTV, {0.4f, 0.5f, 0.6f, 1.0f});

      IASetVertexBuffer(vb, sizeof(DefaultA2V), 0);
      ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
      ctx->IASetInputLayout(defaultLayout);

      ctx->VSSetShader(vs, NULL, 0);
      ctx->PSSetShader(ps, NULL, 0);

      ctx->PSSetShaderResources(0, 1, &srv.GetInterfacePtr());

      RSSetViewport({0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f});

      ctx->OMSetRenderTargets(1, &bbRTV.GetInterfacePtr(), NULL);

      // set mip 0 to green on the deferred context
      defctx->Map(tex, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);

      data = (Vec4f *)mapped.pData;
      for(int y = 0; y < 64; y++)
        for(int x = 0; x < 64; x++)
          data[y * 64 + x] = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);

      defctx->Unmap(tex, 0);

      defctx->FinishCommandList(TRUE, &cmdList);

      // set mip 0 to red on the immediate context
      ctx->Map(tex, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);

      data = (Vec4f *)mapped.pData;
      for(int y = 0; y < 64; y++)
        for(int x = 0; x < 64; x++)
          data[y * 64 + x] = Vec4f(1.0f, 0.0f, 0.0f, 1.0f);

      ctx->Unmap(tex, 0);

      ctx->Draw(3, 0);

      ctx->ExecuteCommandList(cmdList, TRUE);

      ctx->Draw(3, 3);

      cmdList = NULL;

      Present();
    }

    return 0;
  }
};

REGISTER_TEST();
<commit_msg>Fix D3D error in D3D11_Deferred_Map<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2019-2020 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "d3d11_test.h"

RD_TEST(D3D11_Deferred_Map, D3D11GraphicsTest)
{
  static constexpr const char *Description =
      "Test that does Map() on a deferred context on buffers and textures.";

  std::string pixel = R"EOSHADER(

struct v2f
{
	float4 pos : SV_POSITION;
	float4 col : COLOR0;
	float2 uv : TEXCOORD0;
};

Texture2D<float4> tex;

float4 main(v2f IN) : SV_Target0
{
	clip(float2(0.9999f, 0.9999f) - IN.uv.xy);
	return tex.Load(int3(IN.uv.xy*64.0f, 0));
}

)EOSHADER";

  int main()
  {
    // initialise, create window, create device, etc
    if(!Init())
      return 3;

    ID3D11DeviceContextPtr defctx;
    dev->CreateDeferredContext(0, &defctx);

    ID3DBlobPtr vsblob = Compile(D3DDefaultVertex, "main", "vs_5_0");
    ID3DBlobPtr psblob = Compile(pixel, "main", "ps_5_0");

    CreateDefaultInputLayout(vsblob);

    ID3D11VertexShaderPtr vs = CreateVS(vsblob);
    ID3D11PixelShaderPtr ps = CreatePS(psblob);

    DefaultA2V triangle[] = {
        {Vec3f(-1.0f, 0.0f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
        {Vec3f(-1.0f, 2.0f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 2.0f)},
        {Vec3f(3.0f, 0.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(2.0f, 0.0f)},

        {Vec3f(-1.0f, -1.0f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
        {Vec3f(-1.0f, 1.0f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 2.0f)},
        {Vec3f(3.0f, -1.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(2.0f, 0.0f)},
    };

    ID3D11BufferPtr vb = MakeBuffer().Vertex().Data(triangle);

    ID3D11Texture2DPtr tex =
        MakeTexture(DXGI_FORMAT_R32G32B32A32_FLOAT, 64, 64).SRV().Mips(1).Mappable();
    ID3D11ShaderResourceViewPtr srv = MakeSRV(tex);

    ID3D11CommandListPtr cmdList;

    D3D11_MAPPED_SUBRESOURCE mapped = {};
    Vec4f *data = NULL;
    while(Running())
    {
      ClearRenderTargetView(bbRTV, {0.4f, 0.5f, 0.6f, 1.0f});

      IASetVertexBuffer(vb, sizeof(DefaultA2V), 0);
      ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
      ctx->IASetInputLayout(defaultLayout);

      ctx->VSSetShader(vs, NULL, 0);
      ctx->PSSetShader(ps, NULL, 0);

      ctx->PSSetShaderResources(0, 1, &srv.GetInterfacePtr());

      RSSetViewport({0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f});

      ctx->OMSetRenderTargets(1, &bbRTV.GetInterfacePtr(), NULL);

      // set mip 0 to green on the deferred context
      defctx->Map(tex, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);

      data = (Vec4f *)mapped.pData;
      for(int y = 0; y < 64; y++)
        for(int x = 0; x < 64; x++)
          data[y * 64 + x] = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);

      defctx->Unmap(tex, 0);

      defctx->FinishCommandList(TRUE, &cmdList);

      // set mip 0 to red on the immediate context
      ctx->Map(tex, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);

      data = (Vec4f *)mapped.pData;
      for(int y = 0; y < 64; y++)
        for(int x = 0; x < 64; x++)
          data[y * 64 + x] = Vec4f(1.0f, 0.0f, 0.0f, 1.0f);

      ctx->Unmap(tex, 0);

      ctx->Draw(3, 0);

      ctx->ExecuteCommandList(cmdList, TRUE);

      ctx->Draw(3, 3);

      cmdList = NULL;

      Present();
    }

    return 0;
  }
};

REGISTER_TEST();
<|endoftext|>


<commit_before>#include "rooms.hpp"
#include "packets.hpp"
#include <ctime>

MemberInfo::MemberInfo(){

}

MemberInfo::MemberInfo(MemberPtr member){
	user_id = member->getClient()->getID();
	nick = member->getNick();
	girl = member->isGirl();
	color = member->getColor();
}

Json::Value MemberInfo::serialize(){
	Json::Value v(Json::objectValue);

	v["user_id"] = user_id;
	v["nick"] = nick;
	v["girl"] = girl;
	v["color"] = color;

	return v;
}

void MemberInfo::deserialize(const Json::Value &val){
	user_id = val["user_id"].asUInt();
	nick = val["nick"].asString();
	girl = val["girl"].asBool();
	color = val["color"].asString();
}

void Member::sendPacket(const Packet &pack){
	client->sendPacket(pack);
}

void Member::setNick(const string &nnick){
	if (nick == nnick){
		return;
	}

	string oldnick = nick;
	nick = nnick;
	auto roomp = room.lock();

	PacketStatus spack(self.lock());
	if (!oldnick.empty()){
		if (nick.empty()){
			spack.status = Member::Status::offline;
			spack.name = oldnick;
		} else {
			spack.status = Member::Status::nick_change;
			spack.data = oldnick;
		}
	}

	roomp->sendPacketToAll(spack);
}

bool Member::isAdmin(){ return client->isAdmin(); }
bool Member::isOwner(){ return !room.expired() && client->getID() == room.lock()->getOwner(); }
bool Member::isModer(){ return false; }

Room::Room(Server *srv){
	server = srv;
	ownerId = -1;
	nextMemberId = 0;
}

Room::~Room(){

}

void Room::onCreate(){

}

void Room::onDestroy(){
	auto mems = members;
	auto ptr = self.lock();
	for (MemberPtr m : mems){
		m->client->leaveRoom(ptr);
	}
}

Json::Value Room::serialize(){
	Json::Value val;
	val["owner_id"] = ownerId;
	val["name"] = name;

	val["history"] = Json::Value(Json::arrayValue);
	auto &hist = val["history"];
	for (string p : history){
		hist.append(p);
	}

	val["members_info"] = Json::Value(Json::arrayValue);
	auto &mi = val["members_info"];
	for (auto &p : membersInfo){
		mi.append(p.second.serialize());
	}

	return val;
}

void Room::deserialize(const Json::Value &val){
	ownerId = val["owner_id"].asUInt();
	name = val["name"].asString();

	history.clear();
	for (auto &v : val["history"]){
		history.push_back(v.asString());
	}

	membersInfo.clear();
	for (auto &v : val["members_info"]){
		MemberInfo info;
		info.deserialize(v);
		membersInfo[info.user_id] = info;
	}
}

uint Room::genNextMemberId(){
	do {
		++nextMemberId;
	} while (nextMemberId == 0 || findMemberById(nextMemberId));
	return nextMemberId;
}

void Room::addToHistory(const Packet &pack){
	if (pack.type == Packet::Type::message && ((const PacketMessage &) pack).to_id == 0){
		Json::FastWriter wr;
		history.push_back(wr.write(pack.serialize()));
		if (history.size() > 50){
			history.pop_front();
		}
	}
}

MemberPtr Room::findMemberByClient(ClientPtr client){
	for (MemberPtr m : members){
		if (m->client == client){
			return m;
		}
	}

	return nullptr;
}

MemberPtr Room::findMemberByNick(string nick){
	for (MemberPtr m : members){
		if (m->nick == nick){
			return m;
		}
	}

	return nullptr;
}

MemberPtr Room::findMemberById(uint id){
	for (MemberPtr m : members){
		if (m->id == id){
			return m;
		}
	}

	return nullptr;
}

void Room::setOwner(int nid){
	ownerId = nid;
}

MemberPtr Room::addMember(ClientPtr user){
	auto ptr = self.lock();
	auto m = make_shared<Member>(ptr, user);
	m->setSelfPtr(m);
	m->id = genNextMemberId();

	uint uid = user->getID();
	if (user->isGuest() || membersInfo.find(uid) == membersInfo.end()){
		m->nick = user->getName();
		m->girl = user->isGirl();
		m->color = user->getColor();
	} else {
		MemberInfo info = membersInfo[uid];
		m->nick = info.nick;
		m->girl = info.girl;
		m->color = info.color;
	}

	bool warn = false;
	string nick;
	auto member = findMemberByNick(m->nick);
	if (member){
		if (m->nick == user->getName()){
			kickMember(member);
		} else {
			warn = true;
			nick = m->nick;
			m->nick = "";
		}
	}

	auto res = members.insert(m);

	user->sendPacket(PacketJoin(m));

	for (const string &s : getHistory()){
		user->sendRawData(s);
	}

	if (warn){
		m->sendPacket(PacketSystem(name, string("Выбранный вами ранее ник (") + nick + ") занят, выберите другой ник"));
	}

	if (!m->getNick().empty()){
		sendPacketToAll(PacketStatus(m, Member::Status::online));
	}

	if (res.second){
		return *res.first;
	}

	return nullptr;
}

bool Room::removeMember(ClientPtr user){
	auto m = findMemberByClient(user);
	if (!m->getNick().empty()){
		sendPacketToAll(PacketStatus(m, Member::Status::offline));
	}

	user->sendPacket(PacketLeave(name));

	auto cli = m->getClient();
	if (!cli->isGuest()){
		membersInfo[cli->getID()] = MemberInfo(m);
	}

	return members.erase(m) > 0;
}

bool Room::kickMember(ClientPtr user, string reason){
	auto m = findMemberByClient(user);
	if (m){
		return kickMember(m, reason);
		return true;
	}

	return false;
}

bool Room::kickMember(MemberPtr member, string reason){
	auto ptr = self.lock();
	member->getClient()->onKick(ptr);
	if (!member->getNick().empty()){
		sendPacketToAll(PacketStatus(member, Member::Status::offline));
	}
	return members.erase(member) > 0;
}

void Room::sendPacketToAll(const Packet &pack){
	addToHistory(pack);
	for (MemberPtr m : members){
		m->getClient()->sendPacket(pack);
	}
}

<commit_msg>- fix: невозможность нескольких подключений с пустым ником<commit_after>#include "rooms.hpp"
#include "packets.hpp"
#include <ctime>

MemberInfo::MemberInfo(){

}

MemberInfo::MemberInfo(MemberPtr member){
	user_id = member->getClient()->getID();
	nick = member->getNick();
	girl = member->isGirl();
	color = member->getColor();
}

Json::Value MemberInfo::serialize(){
	Json::Value v(Json::objectValue);

	v["user_id"] = user_id;
	v["nick"] = nick;
	v["girl"] = girl;
	v["color"] = color;

	return v;
}

void MemberInfo::deserialize(const Json::Value &val){
	user_id = val["user_id"].asUInt();
	nick = val["nick"].asString();
	girl = val["girl"].asBool();
	color = val["color"].asString();
}

void Member::sendPacket(const Packet &pack){
	client->sendPacket(pack);
}

void Member::setNick(const string &nnick){
	if (nick == nnick){
		return;
	}

	string oldnick = nick;
	nick = nnick;
	auto roomp = room.lock();

	PacketStatus spack(self.lock());
	if (!oldnick.empty()){
		if (nick.empty()){
			spack.status = Member::Status::offline;
			spack.name = oldnick;
		} else {
			spack.status = Member::Status::nick_change;
			spack.data = oldnick;
		}
	}

	roomp->sendPacketToAll(spack);
}

bool Member::isAdmin(){ return client->isAdmin(); }
bool Member::isOwner(){ return !room.expired() && client->getID() == room.lock()->getOwner(); }
bool Member::isModer(){ return false; }

Room::Room(Server *srv){
	server = srv;
	ownerId = -1;
	nextMemberId = 0;
}

Room::~Room(){

}

void Room::onCreate(){

}

void Room::onDestroy(){
	auto mems = members;
	auto ptr = self.lock();
	for (MemberPtr m : mems){
		m->client->leaveRoom(ptr);
	}
}

Json::Value Room::serialize(){
	Json::Value val;
	val["owner_id"] = ownerId;
	val["name"] = name;

	val["history"] = Json::Value(Json::arrayValue);
	auto &hist = val["history"];
	for (string p : history){
		hist.append(p);
	}

	val["members_info"] = Json::Value(Json::arrayValue);
	auto &mi = val["members_info"];
	for (auto &p : membersInfo){
		mi.append(p.second.serialize());
	}

	return val;
}

void Room::deserialize(const Json::Value &val){
	ownerId = val["owner_id"].asUInt();
	name = val["name"].asString();

	history.clear();
	for (auto &v : val["history"]){
		history.push_back(v.asString());
	}

	membersInfo.clear();
	for (auto &v : val["members_info"]){
		MemberInfo info;
		info.deserialize(v);
		membersInfo[info.user_id] = info;
	}
}

uint Room::genNextMemberId(){
	do {
		++nextMemberId;
	} while (nextMemberId == 0 || findMemberById(nextMemberId));
	return nextMemberId;
}

void Room::addToHistory(const Packet &pack){
	if (pack.type == Packet::Type::message && ((const PacketMessage &) pack).to_id == 0){
		Json::FastWriter wr;
		history.push_back(wr.write(pack.serialize()));
		if (history.size() > 50){
			history.pop_front();
		}
	}
}

MemberPtr Room::findMemberByClient(ClientPtr client){
	for (MemberPtr m : members){
		if (m->client == client){
			return m;
		}
	}

	return nullptr;
}

MemberPtr Room::findMemberByNick(string nick){
	for (MemberPtr m : members){
		if (!m->nick.empty() && m->nick == nick){
			return m;
		}
	}

	return nullptr;
}

MemberPtr Room::findMemberById(uint id){
	for (MemberPtr m : members){
		if (m->id == id){
			return m;
		}
	}

	return nullptr;
}

void Room::setOwner(int nid){
	ownerId = nid;
}

MemberPtr Room::addMember(ClientPtr user){
	auto ptr = self.lock();
	auto m = make_shared<Member>(ptr, user);
	m->setSelfPtr(m);
	m->id = genNextMemberId();

	uint uid = user->getID();
	if (user->isGuest() || membersInfo.find(uid) == membersInfo.end()){
		m->nick = user->getName();
		m->girl = user->isGirl();
		m->color = user->getColor();
	} else {
		MemberInfo info = membersInfo[uid];
		m->nick = info.nick;
		m->girl = info.girl;
		m->color = info.color;
	}

	bool warn = false;
	string nick;
	auto member = findMemberByNick(m->nick);
	if (member){
		if (m->nick == user->getName()){
			kickMember(member);
		} else {
			warn = true;
			nick = m->nick;
			m->nick = "";
		}
	}

	auto res = members.insert(m);

	user->sendPacket(PacketJoin(m));

	for (const string &s : getHistory()){
		user->sendRawData(s);
	}

	if (warn){
		m->sendPacket(PacketSystem(name, string("Выбранный вами ранее ник (") + nick + ") занят, выберите другой ник"));
	}

	if (!m->getNick().empty()){
		sendPacketToAll(PacketStatus(m, Member::Status::online));
	}

	if (res.second){
		return *res.first;
	}

	return nullptr;
}

bool Room::removeMember(ClientPtr user){
	auto m = findMemberByClient(user);
	if (!m->getNick().empty()){
		sendPacketToAll(PacketStatus(m, Member::Status::offline));
	}

	user->sendPacket(PacketLeave(name));

	auto cli = m->getClient();
	if (!cli->isGuest()){
		membersInfo[cli->getID()] = MemberInfo(m);
	}

	return members.erase(m) > 0;
}

bool Room::kickMember(ClientPtr user, string reason){
	auto m = findMemberByClient(user);
	if (m){
		return kickMember(m, reason);
		return true;
	}

	return false;
}

bool Room::kickMember(MemberPtr member, string reason){
	auto ptr = self.lock();
	member->getClient()->onKick(ptr);
	if (!member->getNick().empty()){
		sendPacketToAll(PacketStatus(member, Member::Status::offline));
	}
	return members.erase(member) > 0;
}

void Room::sendPacketToAll(const Packet &pack){
	addToHistory(pack);
	for (MemberPtr m : members){
		m->getClient()->sendPacket(pack);
	}
}

<|endoftext|>


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

#ifndef _SLIDESHOW_ANIMATIONTRANSITIONFILTERNODE_HXX
#define _SLIDESHOW_ANIMATIONTRANSITIONFILTERNODE_HXX

#include <activityanimationbasenode.hxx>

#ifndef _COM_SUN_STAR_ANIMATIONS_XTRANSITIONFILTER_HPP_
#include <com/sun/star/animations/XTransitionFilter.hpp>
#endif

namespace presentation
{
    namespace internal
    {
        class AnimationTransitionFilterNode : public ActivityAnimationBaseNode
        {
        public:
            AnimationTransitionFilterNode( const ::com::sun::star::uno::Reference<
                                               ::com::sun::star::animations::XAnimationNode >&  xNode,
                                           const BaseContainerNodeSharedPtr&                rParent,
                                           const NodeContext&                               rContext );

            virtual void dispose();
            virtual bool init();

#if defined(VERBOSE) && defined(DBG_UTIL)
            virtual const char* getDescription() const;
#endif

        private:
            ::com::sun::star::uno::Reference<
                ::com::sun::star::animations::XTransitionFilter >   mxTransitionFilterNode;
        };
    }
}

#endif /* _SLIDESHOW_ANIMATIONTRANSITIONFILTERNODE_HXX */
<commit_msg>INTEGRATION: CWS ooo19126 (1.2.36); FILE MERGED 2005/09/05 17:41:18 rt 1.2.36.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: animationtransitionfilternode.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 20:43:11 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SLIDESHOW_ANIMATIONTRANSITIONFILTERNODE_HXX
#define _SLIDESHOW_ANIMATIONTRANSITIONFILTERNODE_HXX

#include <activityanimationbasenode.hxx>

#ifndef _COM_SUN_STAR_ANIMATIONS_XTRANSITIONFILTER_HPP_
#include <com/sun/star/animations/XTransitionFilter.hpp>
#endif

namespace presentation
{
    namespace internal
    {
        class AnimationTransitionFilterNode : public ActivityAnimationBaseNode
        {
        public:
            AnimationTransitionFilterNode( const ::com::sun::star::uno::Reference<
                                               ::com::sun::star::animations::XAnimationNode >&  xNode,
                                           const BaseContainerNodeSharedPtr&                rParent,
                                           const NodeContext&                               rContext );

            virtual void dispose();
            virtual bool init();

#if defined(VERBOSE) && defined(DBG_UTIL)
            virtual const char* getDescription() const;
#endif

        private:
            ::com::sun::star::uno::Reference<
                ::com::sun::star::animations::XTransitionFilter >   mxTransitionFilterNode;
        };
    }
}

#endif /* _SLIDESHOW_ANIMATIONTRANSITIONFILTERNODE_HXX */
<|endoftext|>


<commit_before>
// ServerHandleImpl.cpp

// Implements the cServerHandleImpl class implementing the TCP server functionality

#include "Globals.h"
#include "ServerHandleImpl.h"
#include "TCPLinkImpl.h"
#include "NetworkSingleton.h"

////////////////////////////////////////////////////////////////////////////////
// Globals:

static bool IsValidSocket(evutil_socket_t a_Socket)
{
	#ifdef _WIN32
		return (a_Socket != INVALID_SOCKET);
	#else  // _WIN32
		return (a_Socket >= 0);
	#endif  // else _WIN32
}

////////////////////////////////////////////////////////////////////////////////
// cServerHandleImpl:

cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks):
	m_ListenCallbacks(a_ListenCallbacks),
	m_ConnListener(nullptr),
	m_SecondaryConnListener(nullptr),
	m_IsListening(false),
	m_ErrorCode(0)
{
}

cServerHandleImpl::~cServerHandleImpl()
{
	if (m_ConnListener != nullptr)
	{
		evconnlistener_free(m_ConnListener);
	}
	if (m_SecondaryConnListener != nullptr)
	{
		evconnlistener_free(m_SecondaryConnListener);
	}
}

void cServerHandleImpl::Close(void)
{
	// Stop the listener sockets:
	evconnlistener_disable(m_ConnListener);
	if (m_SecondaryConnListener != nullptr)
	{
		evconnlistener_disable(m_SecondaryConnListener);
	}
	m_IsListening = false;

	// Shutdown all connections:
	cTCPLinkImplPtrs Conns;
	{
		cCSLock Lock(m_CS);
		std::swap(Conns, m_Connections);
	}
	for (auto conn: Conns)
	{
		conn->Shutdown();
	}

	// Remove the ptr to self, so that the object may be freed:
	m_SelfPtr.reset();

	// Remove self from cNetworkSingleton:
	cNetworkSingleton::Get().RemoveServer(this);
}

cServerHandleImplPtr cServerHandleImpl::Listen(
	UInt16 a_Port,
	cNetwork::cListenCallbacksPtr a_ListenCallbacks
)
{
	cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks)};
	res->m_SelfPtr = res;
	if (res->Listen(a_Port))
	{
		cNetworkSingleton::Get().AddServer(res);
	}
	else
	{
		a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg);
		res->m_SelfPtr.reset();
	}
	return res;
}

bool cServerHandleImpl::Listen(UInt16 a_Port)
{
	// Make sure the cNetwork internals are innitialized:
	cNetworkSingleton::Get();

	// Set up the main socket:
	// It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available.
	bool NeedsTwoSockets = false;
	int err;
	evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
	if (!IsValidSocket(MainSock))
	{
		// Failed to create IPv6 socket, create an IPv4 one instead:
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
		MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (!IsValidSocket(MainSock))
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
			return false;
		}

		// Bind to all interfaces:
		sockaddr_in name;
		memset(&name, 0, sizeof(name));
		name.sin_family = AF_INET;
		name.sin_port = ntohs(a_Port);
		if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
			evutil_closesocket(MainSock);
			return false;
		}
	}
	else
	{
		// IPv6 socket created, switch it into "dualstack" mode:
		UInt32 Zero = 0;
		#ifdef _WIN32
			// WinXP doesn't support this feature, so if the setting fails, create another socket later on:
			int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
			err = EVUTIL_SOCKET_ERROR();
			NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT));
		#else
			setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
		#endif

		// Bind to all interfaces:
		sockaddr_in6 name;
		memset(&name, 0, sizeof(name));
		name.sin6_family = AF_INET6;
		name.sin6_port = ntohs(a_Port);
		if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
			evutil_closesocket(MainSock);
			return false;
		}
	}
	if (evutil_make_socket_nonblocking(MainSock) != 0)
	{
		m_ErrorCode = EVUTIL_SOCKET_ERROR();
		Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
		evutil_closesocket(MainSock);
		return false;
	}
	if (listen(MainSock, 0) != 0)
	{
		m_ErrorCode = EVUTIL_SOCKET_ERROR();
		Printf(m_ErrorMsg, "Cannot listen on port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
		evutil_closesocket(MainSock);
		return false;
	}
	m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock);
	m_IsListening = true;
	if (!NeedsTwoSockets)
	{
		return true;
	}

	// If a secondary socket is required (WinXP dual-stack), create it here:
	LOGD("Creating a second socket for IPv4");
	evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (!IsValidSocket(SecondSock))
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
		return true;  // Report as success, the primary socket is working
	}

	// Make the secondary socket nonblocking:
	if (evutil_make_socket_nonblocking(SecondSock) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	// Bind to all IPv4 interfaces:
	sockaddr_in name;
	memset(&name, 0, sizeof(name));
	name.sin_family = AF_INET;
	name.sin_port = ntohs(a_Port);
	if (bind(SecondSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	if (listen(SecondSock, 0) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock);
	return true;
}

void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self)
{
	// Cast to true self:
	cServerHandleImpl * Self = reinterpret_cast<cServerHandleImpl *>(a_Self);
	ASSERT(Self != nullptr);
	ASSERT(Self->m_SelfPtr != nullptr);

	// Get the textual IP address and port number out of a_Addr:
	char IPAddress[128];
	UInt16 Port = 0;
	switch (a_Addr->sa_family)
	{
		case AF_INET:
		{
			sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr);
			evutil_inet_ntop(AF_INET, sin, IPAddress, ARRAYCOUNT(IPAddress));
			Port = ntohs(sin->sin_port);
			break;
		}
		case AF_INET6:
		{
			sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(a_Addr);
			evutil_inet_ntop(AF_INET, sin6, IPAddress, ARRAYCOUNT(IPAddress));
			Port = ntohs(sin6->sin6_port);
			break;
		}
	}

	// Call the OnIncomingConnection callback to get the link callbacks to use:
	cTCPLink::cCallbacksPtr LinkCallbacks = Self->m_ListenCallbacks->OnIncomingConnection(IPAddress, Port);
	if (LinkCallbacks == nullptr)
	{
		// Drop the connection:
		evutil_closesocket(a_Socket);
		return;
	}

	// Create a new cTCPLink for the incoming connection:
	cTCPLinkImplPtr Link = std::make_shared<cTCPLinkImpl>(a_Socket, LinkCallbacks, Self->m_SelfPtr, a_Addr, static_cast<socklen_t>(a_Len));
	{
		cCSLock Lock(Self->m_CS);
		Self->m_Connections.push_back(Link);
	}  // Lock(m_CS)
	LinkCallbacks->OnLinkCreated(Link);
	Link->Enable(Link);

	// Call the OnAccepted callback:
	Self->m_ListenCallbacks->OnAccepted(*Link);
}

void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link)
{
	cCSLock Lock(m_CS);
	for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
	{
		if (itr->get() == a_Link)
		{
			m_Connections.erase(itr);
			return;
		}
	}  // for itr - m_Connections[]
}

////////////////////////////////////////////////////////////////////////////////
// cNetwork API:

cServerHandlePtr cNetwork::Listen(
	UInt16 a_Port,
	cNetwork::cListenCallbacksPtr a_ListenCallbacks
)
{
	return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks);
}

<commit_msg>Set reuse flag to sockets Should fix #1726<commit_after>
// ServerHandleImpl.cpp

// Implements the cServerHandleImpl class implementing the TCP server functionality

#include "Globals.h"
#include "ServerHandleImpl.h"
#include "TCPLinkImpl.h"
#include "NetworkSingleton.h"

////////////////////////////////////////////////////////////////////////////////
// Globals:

static bool IsValidSocket(evutil_socket_t a_Socket)
{
	#ifdef _WIN32
		return (a_Socket != INVALID_SOCKET);
	#else  // _WIN32
		return (a_Socket >= 0);
	#endif  // else _WIN32
}

////////////////////////////////////////////////////////////////////////////////
// cServerHandleImpl:

cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks):
	m_ListenCallbacks(a_ListenCallbacks),
	m_ConnListener(nullptr),
	m_SecondaryConnListener(nullptr),
	m_IsListening(false),
	m_ErrorCode(0)
{
}

cServerHandleImpl::~cServerHandleImpl()
{
	if (m_ConnListener != nullptr)
	{
		evconnlistener_free(m_ConnListener);
	}
	if (m_SecondaryConnListener != nullptr)
	{
		evconnlistener_free(m_SecondaryConnListener);
	}
}

void cServerHandleImpl::Close(void)
{
	// Stop the listener sockets:
	evconnlistener_disable(m_ConnListener);
	if (m_SecondaryConnListener != nullptr)
	{
		evconnlistener_disable(m_SecondaryConnListener);
	}
	m_IsListening = false;

	// Shutdown all connections:
	cTCPLinkImplPtrs Conns;
	{
		cCSLock Lock(m_CS);
		std::swap(Conns, m_Connections);
	}
	for (auto conn: Conns)
	{
		conn->Shutdown();
	}

	// Remove the ptr to self, so that the object may be freed:
	m_SelfPtr.reset();

	// Remove self from cNetworkSingleton:
	cNetworkSingleton::Get().RemoveServer(this);
}

cServerHandleImplPtr cServerHandleImpl::Listen(
	UInt16 a_Port,
	cNetwork::cListenCallbacksPtr a_ListenCallbacks
)
{
	cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks)};
	res->m_SelfPtr = res;
	if (res->Listen(a_Port))
	{
		cNetworkSingleton::Get().AddServer(res);
	}
	else
	{
		a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg);
		res->m_SelfPtr.reset();
	}
	return res;
}

bool cServerHandleImpl::Listen(UInt16 a_Port)
{
	// Make sure the cNetwork internals are innitialized:
	cNetworkSingleton::Get();

	// Set up the main socket:
	// It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available.
	bool NeedsTwoSockets = false;
	int err;
	evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);

	// Set reuse flag
	{
		#if defined(_WIN32) || defined(ANDROID_NDK)
			char yes = 1;
		#else
			int yes = 1;
		#endif
		setsockopt(MainSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
	}

	if (!IsValidSocket(MainSock))
	{
		// Failed to create IPv6 socket, create an IPv4 one instead:
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
		MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (!IsValidSocket(MainSock))
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
			return false;
		}

		// Bind to all interfaces:
		sockaddr_in name;
		memset(&name, 0, sizeof(name));
		name.sin_family = AF_INET;
		name.sin_port = ntohs(a_Port);
		if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
			evutil_closesocket(MainSock);
			return false;
		}
	}
	else
	{
		// IPv6 socket created, switch it into "dualstack" mode:
		UInt32 Zero = 0;
		#ifdef _WIN32
			// WinXP doesn't support this feature, so if the setting fails, create another socket later on:
			int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
			err = EVUTIL_SOCKET_ERROR();
			NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT));
		#else
			setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
		#endif

		// Bind to all interfaces:
		sockaddr_in6 name;
		memset(&name, 0, sizeof(name));
		name.sin6_family = AF_INET6;
		name.sin6_port = ntohs(a_Port);
		if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
			evutil_closesocket(MainSock);
			return false;
		}
	}
	if (evutil_make_socket_nonblocking(MainSock) != 0)
	{
		m_ErrorCode = EVUTIL_SOCKET_ERROR();
		Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
		evutil_closesocket(MainSock);
		return false;
	}
	if (listen(MainSock, 0) != 0)
	{
		m_ErrorCode = EVUTIL_SOCKET_ERROR();
		Printf(m_ErrorMsg, "Cannot listen on port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
		evutil_closesocket(MainSock);
		return false;
	}
	m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock);
	m_IsListening = true;

	if (!NeedsTwoSockets)
	{
		return true;
	}

	// If a secondary socket is required (WinXP dual-stack), create it here:
	LOGD("Creating a second socket for IPv4");
	evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	// Set reuse flag
	{
		#if defined(_WIN32) || defined(ANDROID_NDK)
			char yes = 1;
		#else
			int yes = 1;
		#endif
		setsockopt(SecondSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
	}

	if (!IsValidSocket(SecondSock))
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
		return true;  // Report as success, the primary socket is working
	}

	// Make the secondary socket nonblocking:
	if (evutil_make_socket_nonblocking(SecondSock) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	// Bind to all IPv4 interfaces:
	sockaddr_in name;
	memset(&name, 0, sizeof(name));
	name.sin_family = AF_INET;
	name.sin_port = ntohs(a_Port);
	if (bind(SecondSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	if (listen(SecondSock, 0) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Cannot listen on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock);
	return true;
}

void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self)
{
	// Cast to true self:
	cServerHandleImpl * Self = reinterpret_cast<cServerHandleImpl *>(a_Self);
	ASSERT(Self != nullptr);
	ASSERT(Self->m_SelfPtr != nullptr);

	// Get the textual IP address and port number out of a_Addr:
	char IPAddress[128];
	UInt16 Port = 0;
	switch (a_Addr->sa_family)
	{
		case AF_INET:
		{
			sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr);
			evutil_inet_ntop(AF_INET, sin, IPAddress, ARRAYCOUNT(IPAddress));
			Port = ntohs(sin->sin_port);
			break;
		}
		case AF_INET6:
		{
			sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(a_Addr);
			evutil_inet_ntop(AF_INET, sin6, IPAddress, ARRAYCOUNT(IPAddress));
			Port = ntohs(sin6->sin6_port);
			break;
		}
	}

	// Call the OnIncomingConnection callback to get the link callbacks to use:
	cTCPLink::cCallbacksPtr LinkCallbacks = Self->m_ListenCallbacks->OnIncomingConnection(IPAddress, Port);
	if (LinkCallbacks == nullptr)
	{
		// Drop the connection:
		evutil_closesocket(a_Socket);
		return;
	}

	// Create a new cTCPLink for the incoming connection:
	cTCPLinkImplPtr Link = std::make_shared<cTCPLinkImpl>(a_Socket, LinkCallbacks, Self->m_SelfPtr, a_Addr, static_cast<socklen_t>(a_Len));
	{
		cCSLock Lock(Self->m_CS);
		Self->m_Connections.push_back(Link);
	}  // Lock(m_CS)
	LinkCallbacks->OnLinkCreated(Link);
	Link->Enable(Link);

	// Call the OnAccepted callback:
	Self->m_ListenCallbacks->OnAccepted(*Link);
}

void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link)
{
	cCSLock Lock(m_CS);
	for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
	{
		if (itr->get() == a_Link)
		{
			m_Connections.erase(itr);
			return;
		}
	}  // for itr - m_Connections[]
}

////////////////////////////////////////////////////////////////////////////////
// cNetwork API:

cServerHandlePtr cNetwork::Listen(
	UInt16 a_Port,
	cNetwork::cListenCallbacksPtr a_ListenCallbacks
)
{
	return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks);
}

<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/tcp_client_socket.h"

#include "base/string_util.h"
#include "base/trace_event.h"
#include "net/base/net_errors.h"
#include "net/base/winsock_init.h"

namespace net {

//-----------------------------------------------------------------------------

static int MapWinsockError(DWORD err) {
  // There are numerous Winsock error codes, but these are the ones we thus far
  // find interesting.
  switch (err) {
    case WSAENETDOWN:
      return ERR_INTERNET_DISCONNECTED;
    case WSAETIMEDOUT:
      return ERR_TIMED_OUT;
    case WSAECONNRESET:
    case WSAENETRESET:  // Related to keep-alive
      return ERR_CONNECTION_RESET;
    case WSAECONNABORTED:
      return ERR_CONNECTION_ABORTED;
    case WSAECONNREFUSED:
      return ERR_CONNECTION_REFUSED;
    case WSAEDISCON:
      // Returned by WSARecv or WSARecvFrom for message-oriented sockets (where
      // a return value of zero means a zero-byte message) to indicate graceful
      // connection shutdown.  We should not ever see this error code for TCP
      // sockets, which are byte stream oriented.
      NOTREACHED();
      return ERR_CONNECTION_CLOSED;
    case WSAEHOSTUNREACH:
    case WSAENETUNREACH:
      return ERR_ADDRESS_UNREACHABLE;
    case WSAEADDRNOTAVAIL:
      return ERR_ADDRESS_INVALID;
    case WSA_IO_INCOMPLETE:
      return ERR_UNEXPECTED;
    case ERROR_SUCCESS:
      return OK;
    default:
      LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
      return ERR_FAILED;
  }
}

//-----------------------------------------------------------------------------

TCPClientSocket::TCPClientSocket(const AddressList& addresses)
    : socket_(INVALID_SOCKET),
      addresses_(addresses),
      current_ai_(addresses_.head()),
      wait_state_(NOT_WAITING),
      callback_(NULL) {
  memset(&overlapped_, 0, sizeof(overlapped_));
  EnsureWinsockInit();
}

TCPClientSocket::~TCPClientSocket() {
  Disconnect();
}

int TCPClientSocket::Connect(CompletionCallback* callback) {
  // If already connected, then just return OK.
  if (socket_ != INVALID_SOCKET)
    return OK;

  TRACE_EVENT_BEGIN("socket.connect", this, "");
  const struct addrinfo* ai = current_ai_;
  DCHECK(ai);

  int rv = CreateSocket(ai);
  if (rv != OK)
    return rv;

  overlapped_.hEvent = WSACreateEvent();
  // WSAEventSelect sets the socket to non-blocking mode as a side effect.
  // Our connect() and recv() calls require that the socket be non-blocking.
  WSAEventSelect(socket_, overlapped_.hEvent, FD_CONNECT);

  if (!connect(socket_, ai->ai_addr, static_cast<int>(ai->ai_addrlen))) {
    TRACE_EVENT_END("socket.connect", this, "");
    // Connected without waiting!
    return OK;
  }

  DWORD err = WSAGetLastError();
  if (err != WSAEWOULDBLOCK) {
    LOG(ERROR) << "connect failed: " << err;
    return MapWinsockError(err);
  }

  watcher_.StartWatching(overlapped_.hEvent, this);
  wait_state_ = WAITING_CONNECT;
  callback_ = callback;
  return ERR_IO_PENDING;
}

int TCPClientSocket::ReconnectIgnoringLastError(CompletionCallback* callback) {
  // No ignorable errors!
  return ERR_UNEXPECTED;
}

void TCPClientSocket::Disconnect() {
  if (socket_ == INVALID_SOCKET)
    return;

  TRACE_EVENT_INSTANT("socket.disconnect", this, "");

  // Make sure the message loop is not watching this object anymore.
  watcher_.StopWatching();

  // In most socket implementations, closing a socket results in a graceful
  // connection shutdown, but in Winsock we have to call shutdown explicitly.
  // See the MSDN page "Graceful Shutdown, Linger Options, and Socket Closure"
  // at http://msdn.microsoft.com/en-us/library/ms738547.aspx
  shutdown(socket_, SD_SEND);

  // This cancels any pending IO.
  closesocket(socket_);
  socket_ = INVALID_SOCKET;

  WSACloseEvent(overlapped_.hEvent);
  memset(&overlapped_, 0, sizeof(overlapped_));

  // Reset for next time.
  current_ai_ = addresses_.head();
}

bool TCPClientSocket::IsConnected() const {
  if (socket_ == INVALID_SOCKET || wait_state_ == WAITING_CONNECT)
    return false;

  // Check if connection is alive.
  char c;
  int rv = recv(socket_, &c, 1, MSG_PEEK);
  if (rv == 0)
    return false;
  if (rv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
    return false;

  return true;
}

int TCPClientSocket::Read(char* buf,
                          int buf_len,
                          CompletionCallback* callback) {
  DCHECK(socket_ != INVALID_SOCKET);
  DCHECK(wait_state_ == NOT_WAITING);
  DCHECK(!callback_);

  buffer_.len = buf_len;
  buffer_.buf = buf;

  TRACE_EVENT_BEGIN("socket.read", this, "");
  // TODO(wtc): Remove the CHECKs after enough testing.
  CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_TIMEOUT);
  DWORD num, flags = 0;
  int rv = WSARecv(socket_, &buffer_, 1, &num, &flags, &overlapped_, NULL);
  if (rv == 0) {
    CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_OBJECT_0);
    BOOL ok = WSAResetEvent(overlapped_.hEvent);
    CHECK(ok);
    TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", num));
    return static_cast<int>(num);
  }
  int err = WSAGetLastError();
  if (err == WSA_IO_PENDING) {
    watcher_.StartWatching(overlapped_.hEvent, this);
    wait_state_ = WAITING_READ;
    callback_ = callback;
    return ERR_IO_PENDING;
  }
  return MapWinsockError(err);
}

int TCPClientSocket::Write(const char* buf,
                           int buf_len,
                           CompletionCallback* callback) {
  DCHECK(socket_ != INVALID_SOCKET);
  DCHECK(wait_state_ == NOT_WAITING);
  DCHECK(!callback_);

  buffer_.len = buf_len;
  buffer_.buf = const_cast<char*>(buf);

  TRACE_EVENT_BEGIN("socket.write", this, "");
  // TODO(wtc): Remove the CHECKs after enough testing.
  CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_TIMEOUT);
  DWORD num;
  int rv = WSASend(socket_, &buffer_, 1, &num, 0, &overlapped_, NULL);
  if (rv == 0) {
    CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_OBJECT_0);
    BOOL ok = WSAResetEvent(overlapped_.hEvent);
    CHECK(ok);
    TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", num));
    return static_cast<int>(num);
  }
  int err = WSAGetLastError();
  if (err == WSA_IO_PENDING) {
    watcher_.StartWatching(overlapped_.hEvent, this);
    wait_state_ = WAITING_WRITE;
    callback_ = callback;
    return ERR_IO_PENDING;
  }
  return MapWinsockError(err);
}

int TCPClientSocket::CreateSocket(const struct addrinfo* ai) {
  socket_ = WSASocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, NULL, 0,
                      WSA_FLAG_OVERLAPPED);
  if (socket_ == INVALID_SOCKET) {
    DWORD err = WSAGetLastError();
    LOG(ERROR) << "WSASocket failed: " << err;
    return MapWinsockError(err);
  }

  // Increase the socket buffer sizes from the default sizes.
  // In performance testing, there is substantial benefit by increasing
  // from 8KB to 32KB.  I tested 64, 128, and 256KB as well, but did not
  // see additional performance benefit (will be network dependent).
  // See also:
  //    http://support.microsoft.com/kb/823764/EN-US
  // On XP, the default buffer sizes are 8KB.
  const int kSocketBufferSize = 32 * 1024;
  int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
      reinterpret_cast<const char*>(&kSocketBufferSize),
      sizeof(kSocketBufferSize));
  DCHECK(!rv) << "Could not set socket send buffer size";
  rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
      reinterpret_cast<const char*>(&kSocketBufferSize),
      sizeof(kSocketBufferSize));
  DCHECK(!rv) << "Could not set socket receive buffer size";

  // Disable Nagle.
  // The Nagle implementation on windows is governed by RFC 896.  The idea
  // behind Nagle is to reduce small packets on the network.  When Nagle is
  // enabled, if a partial packet has been sent, the TCP stack will disallow
  // further *partial* packets until an ACK has been received from the other
  // side.  Good applications should always strive to send as much data as
  // possible and avoid partial-packet sends.  However, in most real world
  // applications, there are edge cases where this does not happen, and two
  // partil packets may be sent back to back.  For a browser, it is NEVER
  // a benefit to delay for an RTT before the second packet is sent.
  //
  // As a practical example in Chromium today, consider the case of a small
  // POST.  I have verified this:
  //     Client writes 649 bytes of header  (partial packet #1)
  //     Client writes 50 bytes of POST data (partial packet #2)
  // In the above example, with Nagle, a RTT delay is inserted between these
  // two sends due to nagle.  RTTs can easily be 100ms or more.  The best
  // fix is to make sure that for POSTing data, we write as much data as
  // possible and minimize partial packets.  We will fix that.  But disabling
  // Nagle also ensure we don't run into this delay in other edge cases.
  // See also:
  //    http://technet.microsoft.com/en-us/library/bb726981.aspx
  const BOOL kDisableNagle = TRUE;
  rv = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY,
      reinterpret_cast<const char*>(&kDisableNagle), sizeof(kDisableNagle));
  DCHECK(!rv) << "Could not disable nagle";

  return OK;
}

void TCPClientSocket::DoCallback(int rv) {
  DCHECK(rv != ERR_IO_PENDING);
  DCHECK(callback_);

  // since Run may result in Read being called, clear callback_ up front.
  CompletionCallback* c = callback_;
  callback_ = NULL;
  c->Run(rv);
}

void TCPClientSocket::DidCompleteConnect() {
  int result;

  TRACE_EVENT_END("socket.connect", this, "");
  wait_state_ = NOT_WAITING;

  WSANETWORKEVENTS events;
  int rv = WSAEnumNetworkEvents(socket_, overlapped_.hEvent, &events);
  if (rv == SOCKET_ERROR) {
    NOTREACHED();
    result = MapWinsockError(WSAGetLastError());
  } else if (events.lNetworkEvents & FD_CONNECT) {
    wait_state_ = NOT_WAITING;
    DWORD error_code = static_cast<DWORD>(events.iErrorCode[FD_CONNECT_BIT]);
    if (current_ai_->ai_next && (
        error_code == WSAEADDRNOTAVAIL ||
        error_code == WSAEAFNOSUPPORT ||
        error_code == WSAECONNREFUSED ||
        error_code == WSAENETUNREACH ||
        error_code == WSAEHOSTUNREACH ||
        error_code == WSAETIMEDOUT)) {
      // Try using the next address.
      const struct addrinfo* next = current_ai_->ai_next;
      Disconnect();
      current_ai_ = next;
      result = Connect(callback_);
    } else {
      result = MapWinsockError(error_code);
    }
  } else {
    NOTREACHED();
    result = ERR_UNEXPECTED;
  }

  if (result != ERR_IO_PENDING)
    DoCallback(result);
}

void TCPClientSocket::DidCompleteIO() {
  DWORD num_bytes, flags;
  BOOL ok = WSAGetOverlappedResult(
      socket_, &overlapped_, &num_bytes, FALSE, &flags);
  WSAResetEvent(overlapped_.hEvent);
  if (wait_state_ == WAITING_READ) {
    TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", num_bytes));
  } else {
    TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", num_bytes));
  }
  wait_state_ = NOT_WAITING;
  DoCallback(ok ? num_bytes : MapWinsockError(WSAGetLastError()));
}

void TCPClientSocket::OnObjectSignaled(HANDLE object) {
  DCHECK(object == overlapped_.hEvent);

  switch (wait_state_) {
    case WAITING_CONNECT:
      DidCompleteConnect();
      break;
    case WAITING_READ:
    case WAITING_WRITE:
      DidCompleteIO();
      break;
    default:
      NOTREACHED();
      break;
  }
}

}  // namespace net

<commit_msg>Cancel any pending IO and wait for it to be aborted before returning from the Disconnect method.  This allows the caller to destroy the read or write buffer safely when the destructor (which calls Disconnect) returns.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/tcp_client_socket.h"

#include "base/string_util.h"
#include "base/trace_event.h"
#include "net/base/net_errors.h"
#include "net/base/winsock_init.h"

namespace net {

//-----------------------------------------------------------------------------

static int MapWinsockError(DWORD err) {
  // There are numerous Winsock error codes, but these are the ones we thus far
  // find interesting.
  switch (err) {
    case WSAENETDOWN:
      return ERR_INTERNET_DISCONNECTED;
    case WSAETIMEDOUT:
      return ERR_TIMED_OUT;
    case WSAECONNRESET:
    case WSAENETRESET:  // Related to keep-alive
      return ERR_CONNECTION_RESET;
    case WSAECONNABORTED:
      return ERR_CONNECTION_ABORTED;
    case WSAECONNREFUSED:
      return ERR_CONNECTION_REFUSED;
    case WSAEDISCON:
      // Returned by WSARecv or WSARecvFrom for message-oriented sockets (where
      // a return value of zero means a zero-byte message) to indicate graceful
      // connection shutdown.  We should not ever see this error code for TCP
      // sockets, which are byte stream oriented.
      NOTREACHED();
      return ERR_CONNECTION_CLOSED;
    case WSAEHOSTUNREACH:
    case WSAENETUNREACH:
      return ERR_ADDRESS_UNREACHABLE;
    case WSAEADDRNOTAVAIL:
      return ERR_ADDRESS_INVALID;
    case WSA_IO_INCOMPLETE:
      return ERR_UNEXPECTED;
    case ERROR_SUCCESS:
      return OK;
    default:
      LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
      return ERR_FAILED;
  }
}

//-----------------------------------------------------------------------------

TCPClientSocket::TCPClientSocket(const AddressList& addresses)
    : socket_(INVALID_SOCKET),
      addresses_(addresses),
      current_ai_(addresses_.head()),
      wait_state_(NOT_WAITING),
      callback_(NULL) {
  memset(&overlapped_, 0, sizeof(overlapped_));
  EnsureWinsockInit();
}

TCPClientSocket::~TCPClientSocket() {
  Disconnect();
}

int TCPClientSocket::Connect(CompletionCallback* callback) {
  // If already connected, then just return OK.
  if (socket_ != INVALID_SOCKET)
    return OK;

  TRACE_EVENT_BEGIN("socket.connect", this, "");
  const struct addrinfo* ai = current_ai_;
  DCHECK(ai);

  int rv = CreateSocket(ai);
  if (rv != OK)
    return rv;

  // WSACreateEvent creates a manual-reset event object.
  overlapped_.hEvent = WSACreateEvent();
  // WSAEventSelect sets the socket to non-blocking mode as a side effect.
  // Our connect() and recv() calls require that the socket be non-blocking.
  WSAEventSelect(socket_, overlapped_.hEvent, FD_CONNECT);

  if (!connect(socket_, ai->ai_addr, static_cast<int>(ai->ai_addrlen))) {
    TRACE_EVENT_END("socket.connect", this, "");
    // Connected without waiting!
    return OK;
  }

  DWORD err = WSAGetLastError();
  if (err != WSAEWOULDBLOCK) {
    LOG(ERROR) << "connect failed: " << err;
    return MapWinsockError(err);
  }

  watcher_.StartWatching(overlapped_.hEvent, this);
  wait_state_ = WAITING_CONNECT;
  callback_ = callback;
  return ERR_IO_PENDING;
}

int TCPClientSocket::ReconnectIgnoringLastError(CompletionCallback* callback) {
  // No ignorable errors!
  return ERR_UNEXPECTED;
}

void TCPClientSocket::Disconnect() {
  if (socket_ == INVALID_SOCKET)
    return;

  TRACE_EVENT_INSTANT("socket.disconnect", this, "");

  // Make sure the message loop is not watching this object anymore.
  watcher_.StopWatching();

  // Cancel any pending IO and wait for it to be aborted.
  if (wait_state_ == WAITING_READ || wait_state_ == WAITING_WRITE) {
    CancelIo(reinterpret_cast<HANDLE>(socket_));
    WaitForSingleObject(overlapped_.hEvent, INFINITE);
    wait_state_ = NOT_WAITING;
  }

  // In most socket implementations, closing a socket results in a graceful
  // connection shutdown, but in Winsock we have to call shutdown explicitly.
  // See the MSDN page "Graceful Shutdown, Linger Options, and Socket Closure"
  // at http://msdn.microsoft.com/en-us/library/ms738547.aspx
  shutdown(socket_, SD_SEND);

  closesocket(socket_);
  socket_ = INVALID_SOCKET;

  WSACloseEvent(overlapped_.hEvent);
  memset(&overlapped_, 0, sizeof(overlapped_));

  // Reset for next time.
  current_ai_ = addresses_.head();
}

bool TCPClientSocket::IsConnected() const {
  if (socket_ == INVALID_SOCKET || wait_state_ == WAITING_CONNECT)
    return false;

  // Check if connection is alive.
  char c;
  int rv = recv(socket_, &c, 1, MSG_PEEK);
  if (rv == 0)
    return false;
  if (rv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
    return false;

  return true;
}

int TCPClientSocket::Read(char* buf,
                          int buf_len,
                          CompletionCallback* callback) {
  DCHECK(socket_ != INVALID_SOCKET);
  DCHECK(wait_state_ == NOT_WAITING);
  DCHECK(!callback_);

  buffer_.len = buf_len;
  buffer_.buf = buf;

  TRACE_EVENT_BEGIN("socket.read", this, "");
  // TODO(wtc): Remove the CHECKs after enough testing.
  CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_TIMEOUT);
  DWORD num, flags = 0;
  int rv = WSARecv(socket_, &buffer_, 1, &num, &flags, &overlapped_, NULL);
  if (rv == 0) {
    CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_OBJECT_0);
    BOOL ok = WSAResetEvent(overlapped_.hEvent);
    CHECK(ok);
    TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", num));
    return static_cast<int>(num);
  }
  int err = WSAGetLastError();
  if (err == WSA_IO_PENDING) {
    watcher_.StartWatching(overlapped_.hEvent, this);
    wait_state_ = WAITING_READ;
    callback_ = callback;
    return ERR_IO_PENDING;
  }
  return MapWinsockError(err);
}

int TCPClientSocket::Write(const char* buf,
                           int buf_len,
                           CompletionCallback* callback) {
  DCHECK(socket_ != INVALID_SOCKET);
  DCHECK(wait_state_ == NOT_WAITING);
  DCHECK(!callback_);

  buffer_.len = buf_len;
  buffer_.buf = const_cast<char*>(buf);

  TRACE_EVENT_BEGIN("socket.write", this, "");
  // TODO(wtc): Remove the CHECKs after enough testing.
  CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_TIMEOUT);
  DWORD num;
  int rv = WSASend(socket_, &buffer_, 1, &num, 0, &overlapped_, NULL);
  if (rv == 0) {
    CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_OBJECT_0);
    BOOL ok = WSAResetEvent(overlapped_.hEvent);
    CHECK(ok);
    TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", num));
    return static_cast<int>(num);
  }
  int err = WSAGetLastError();
  if (err == WSA_IO_PENDING) {
    watcher_.StartWatching(overlapped_.hEvent, this);
    wait_state_ = WAITING_WRITE;
    callback_ = callback;
    return ERR_IO_PENDING;
  }
  return MapWinsockError(err);
}

int TCPClientSocket::CreateSocket(const struct addrinfo* ai) {
  socket_ = WSASocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, NULL, 0,
                      WSA_FLAG_OVERLAPPED);
  if (socket_ == INVALID_SOCKET) {
    DWORD err = WSAGetLastError();
    LOG(ERROR) << "WSASocket failed: " << err;
    return MapWinsockError(err);
  }

  // Increase the socket buffer sizes from the default sizes.
  // In performance testing, there is substantial benefit by increasing
  // from 8KB to 32KB.  I tested 64, 128, and 256KB as well, but did not
  // see additional performance benefit (will be network dependent).
  // See also:
  //    http://support.microsoft.com/kb/823764/EN-US
  // On XP, the default buffer sizes are 8KB.
  const int kSocketBufferSize = 32 * 1024;
  int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
      reinterpret_cast<const char*>(&kSocketBufferSize),
      sizeof(kSocketBufferSize));
  DCHECK(!rv) << "Could not set socket send buffer size";
  rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
      reinterpret_cast<const char*>(&kSocketBufferSize),
      sizeof(kSocketBufferSize));
  DCHECK(!rv) << "Could not set socket receive buffer size";

  // Disable Nagle.
  // The Nagle implementation on windows is governed by RFC 896.  The idea
  // behind Nagle is to reduce small packets on the network.  When Nagle is
  // enabled, if a partial packet has been sent, the TCP stack will disallow
  // further *partial* packets until an ACK has been received from the other
  // side.  Good applications should always strive to send as much data as
  // possible and avoid partial-packet sends.  However, in most real world
  // applications, there are edge cases where this does not happen, and two
  // partil packets may be sent back to back.  For a browser, it is NEVER
  // a benefit to delay for an RTT before the second packet is sent.
  //
  // As a practical example in Chromium today, consider the case of a small
  // POST.  I have verified this:
  //     Client writes 649 bytes of header  (partial packet #1)
  //     Client writes 50 bytes of POST data (partial packet #2)
  // In the above example, with Nagle, a RTT delay is inserted between these
  // two sends due to nagle.  RTTs can easily be 100ms or more.  The best
  // fix is to make sure that for POSTing data, we write as much data as
  // possible and minimize partial packets.  We will fix that.  But disabling
  // Nagle also ensure we don't run into this delay in other edge cases.
  // See also:
  //    http://technet.microsoft.com/en-us/library/bb726981.aspx
  const BOOL kDisableNagle = TRUE;
  rv = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY,
      reinterpret_cast<const char*>(&kDisableNagle), sizeof(kDisableNagle));
  DCHECK(!rv) << "Could not disable nagle";

  return OK;
}

void TCPClientSocket::DoCallback(int rv) {
  DCHECK(rv != ERR_IO_PENDING);
  DCHECK(callback_);

  // since Run may result in Read being called, clear callback_ up front.
  CompletionCallback* c = callback_;
  callback_ = NULL;
  c->Run(rv);
}

void TCPClientSocket::DidCompleteConnect() {
  int result;

  TRACE_EVENT_END("socket.connect", this, "");
  wait_state_ = NOT_WAITING;

  WSANETWORKEVENTS events;
  int rv = WSAEnumNetworkEvents(socket_, overlapped_.hEvent, &events);
  if (rv == SOCKET_ERROR) {
    NOTREACHED();
    result = MapWinsockError(WSAGetLastError());
  } else if (events.lNetworkEvents & FD_CONNECT) {
    wait_state_ = NOT_WAITING;
    DWORD error_code = static_cast<DWORD>(events.iErrorCode[FD_CONNECT_BIT]);
    if (current_ai_->ai_next && (
        error_code == WSAEADDRNOTAVAIL ||
        error_code == WSAEAFNOSUPPORT ||
        error_code == WSAECONNREFUSED ||
        error_code == WSAENETUNREACH ||
        error_code == WSAEHOSTUNREACH ||
        error_code == WSAETIMEDOUT)) {
      // Try using the next address.
      const struct addrinfo* next = current_ai_->ai_next;
      Disconnect();
      current_ai_ = next;
      result = Connect(callback_);
    } else {
      result = MapWinsockError(error_code);
    }
  } else {
    NOTREACHED();
    result = ERR_UNEXPECTED;
  }

  if (result != ERR_IO_PENDING)
    DoCallback(result);
}

void TCPClientSocket::DidCompleteIO() {
  DWORD num_bytes, flags;
  BOOL ok = WSAGetOverlappedResult(
      socket_, &overlapped_, &num_bytes, FALSE, &flags);
  WSAResetEvent(overlapped_.hEvent);
  if (wait_state_ == WAITING_READ) {
    TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", num_bytes));
  } else {
    TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", num_bytes));
  }
  wait_state_ = NOT_WAITING;
  DoCallback(ok ? num_bytes : MapWinsockError(WSAGetLastError()));
}

void TCPClientSocket::OnObjectSignaled(HANDLE object) {
  DCHECK(object == overlapped_.hEvent);

  switch (wait_state_) {
    case WAITING_CONNECT:
      DidCompleteConnect();
      break;
    case WAITING_READ:
    case WAITING_WRITE:
      DidCompleteIO();
      break;
    default:
      NOTREACHED();
      break;
  }
}

}  // namespace net

<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/tools/flip_server/spdy_ssl.h"

#include "base/logging.h"
#include "openssl/err.h"
#include "openssl/ssl.h"

namespace net {

#define NEXT_PROTO_STRING "\x06spdy/2\x08http/1.1\x08http/1.0"
#define SSL_CIPHER_LIST "!aNULL:!ADH:!eNull:!LOW:!EXP:RC4+RSA:MEDIUM:HIGH"

int ssl_set_npn_callback(SSL *s,
                         const unsigned char **data,
                         unsigned int *len,
                         void *arg) {
  VLOG(1) <<  "SSL NPN callback: advertising protocols.";
  *data = (const unsigned char *) NEXT_PROTO_STRING;
  *len = strlen(NEXT_PROTO_STRING);
  return SSL_TLSEXT_ERR_OK;
}

void InitSSL(SSLState* state,
             std::string ssl_cert_name,
             std::string ssl_key_name,
             bool use_npn,
             int session_expiration_time,
             bool disable_ssl_compression) {
  SSL_library_init();
  PrintSslError();

  SSL_load_error_strings();
  PrintSslError();

  state->ssl_method = SSLv23_method();
  state->ssl_ctx = SSL_CTX_new(state->ssl_method);
  if (!state->ssl_ctx) {
    PrintSslError();
    LOG(FATAL) << "Unable to create SSL context";
  }
  // Disable SSLv2 support.
  SSL_CTX_set_options(state->ssl_ctx,
                      SSL_OP_NO_SSLv2 | SSL_OP_CIPHER_SERVER_PREFERENCE);
  if (SSL_CTX_use_certificate_file(state->ssl_ctx,
                                   ssl_cert_name.c_str(),
                                   SSL_FILETYPE_PEM) <= 0) {
    PrintSslError();
    LOG(FATAL) << "Unable to use cert.pem as SSL cert.";
  }
  if (SSL_CTX_use_PrivateKey_file(state->ssl_ctx,
                                  ssl_key_name.c_str(),
                                  SSL_FILETYPE_PEM) <= 0) {
    PrintSslError();
    LOG(FATAL) << "Unable to use key.pem as SSL key.";
  }
  if (!SSL_CTX_check_private_key(state->ssl_ctx)) {
    PrintSslError();
    LOG(FATAL) << "The cert.pem and key.pem files don't match";
  }
  if (use_npn) {
    SSL_CTX_set_next_protos_advertised_cb(state->ssl_ctx,
                                          ssl_set_npn_callback, NULL);
  }
  VLOG(1) << "SSL CTX default cipher list: " << SSL_CIPHER_LIST;
  SSL_CTX_set_cipher_list(state->ssl_ctx, SSL_CIPHER_LIST);

  VLOG(1) << "SSL CTX session expiry: " << session_expiration_time
          << " seconds";
  SSL_CTX_set_timeout(state->ssl_ctx, session_expiration_time);

#ifdef SSL_MODE_RELEASE_BUFFERS
  VLOG(1) << "SSL CTX: Setting Release Buffers mode.";
  SSL_CTX_set_mode(state->ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
#endif

  // Proper methods to disable compression don't exist until 0.9.9+. For now
  // we must manipulate the stack of compression methods directly.
  if (disable_ssl_compression) {
    STACK_OF(SSL_COMP) *ssl_comp_methods = SSL_COMP_get_compression_methods();
    int num_methods = sk_SSL_COMP_num(ssl_comp_methods);
    int i;
    for (i = 0; i < num_methods; i++) {
      static_cast<void>(sk_SSL_COMP_delete(ssl_comp_methods, i));
    }
  }
}

SSL* CreateSSLContext(SSL_CTX* ssl_ctx) {
  SSL* ssl = SSL_new(ssl_ctx);
  SSL_set_accept_state(ssl);
  PrintSslError();
  return ssl;
}

void PrintSslError() {
  char buf[128];  // this buffer must be at least 120 chars long.
  int error_num = ERR_get_error();
  while (error_num != 0) {
    ERR_error_string_n(error_num, buf, sizeof(buf));
    LOG(ERROR) << buf;
    error_num = ERR_get_error();
  }
}

}  // namespace net

<commit_msg>Support loading of chained certificates from the cert file.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/tools/flip_server/spdy_ssl.h"

#include "base/logging.h"
#include "openssl/err.h"
#include "openssl/ssl.h"

namespace net {

#define NEXT_PROTO_STRING "\x06spdy/2\x08http/1.1\x08http/1.0"
#define SSL_CIPHER_LIST "!aNULL:!ADH:!eNull:!LOW:!EXP:RC4+RSA:MEDIUM:HIGH"

int ssl_set_npn_callback(SSL *s,
                         const unsigned char **data,
                         unsigned int *len,
                         void *arg) {
  VLOG(1) <<  "SSL NPN callback: advertising protocols.";
  *data = (const unsigned char *) NEXT_PROTO_STRING;
  *len = strlen(NEXT_PROTO_STRING);
  return SSL_TLSEXT_ERR_OK;
}

void InitSSL(SSLState* state,
             std::string ssl_cert_name,
             std::string ssl_key_name,
             bool use_npn,
             int session_expiration_time,
             bool disable_ssl_compression) {
  SSL_library_init();
  PrintSslError();

  SSL_load_error_strings();
  PrintSslError();

  state->ssl_method = SSLv23_method();
  state->ssl_ctx = SSL_CTX_new(state->ssl_method);
  if (!state->ssl_ctx) {
    PrintSslError();
    LOG(FATAL) << "Unable to create SSL context";
  }
  // Disable SSLv2 support.
  SSL_CTX_set_options(state->ssl_ctx,
                      SSL_OP_NO_SSLv2 | SSL_OP_CIPHER_SERVER_PREFERENCE);
  if (SSL_CTX_use_certificate_chain_file(state->ssl_ctx,
                                         ssl_cert_name.c_str()) <= 0) {
    PrintSslError();
    LOG(FATAL) << "Unable to use cert.pem as SSL cert.";
  }
  if (SSL_CTX_use_PrivateKey_file(state->ssl_ctx,
                                  ssl_key_name.c_str(),
                                  SSL_FILETYPE_PEM) <= 0) {
    PrintSslError();
    LOG(FATAL) << "Unable to use key.pem as SSL key.";
  }
  if (!SSL_CTX_check_private_key(state->ssl_ctx)) {
    PrintSslError();
    LOG(FATAL) << "The cert.pem and key.pem files don't match";
  }
  if (use_npn) {
    SSL_CTX_set_next_protos_advertised_cb(state->ssl_ctx,
                                          ssl_set_npn_callback, NULL);
  }
  VLOG(1) << "SSL CTX default cipher list: " << SSL_CIPHER_LIST;
  SSL_CTX_set_cipher_list(state->ssl_ctx, SSL_CIPHER_LIST);

  VLOG(1) << "SSL CTX session expiry: " << session_expiration_time
          << " seconds";
  SSL_CTX_set_timeout(state->ssl_ctx, session_expiration_time);

#ifdef SSL_MODE_RELEASE_BUFFERS
  VLOG(1) << "SSL CTX: Setting Release Buffers mode.";
  SSL_CTX_set_mode(state->ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
#endif

  // Proper methods to disable compression don't exist until 0.9.9+. For now
  // we must manipulate the stack of compression methods directly.
  if (disable_ssl_compression) {
    STACK_OF(SSL_COMP) *ssl_comp_methods = SSL_COMP_get_compression_methods();
    int num_methods = sk_SSL_COMP_num(ssl_comp_methods);
    int i;
    for (i = 0; i < num_methods; i++) {
      static_cast<void>(sk_SSL_COMP_delete(ssl_comp_methods, i));
    }
  }
}

SSL* CreateSSLContext(SSL_CTX* ssl_ctx) {
  SSL* ssl = SSL_new(ssl_ctx);
  SSL_set_accept_state(ssl);
  PrintSslError();
  return ssl;
}

void PrintSslError() {
  char buf[128];  // this buffer must be at least 120 chars long.
  int error_num = ERR_get_error();
  while (error_num != 0) {
    ERR_error_string_n(error_num, buf, sizeof(buf));
    LOG(ERROR) << buf;
    error_num = ERR_get_error();
  }
}

}  // namespace net

<|endoftext|>


<commit_before>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (c) 2011 Arjen Hiemstra <ahiemstra@heimr.nl>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "writer.h"

#include <QtCore/QSizeF>
#include <QtCore/QList>
#include <QtCore/QMetaProperty>
#include <QtCore/QDebug>
#include <QtGui/QVector2D>
#include <QtGui/QVector3D>
#include <QtGui/QVector4D>
#include <QtGui/QQuaternion>
#include <QtGui/QColor>

#include <core/gluonobject.h>
#include <core/gluonobjectfactory.h>

using namespace GDL;

Writer::Writer()
{
    m_device = 0;
    m_indentLevel = 0;
}

Writer::~Writer()
{

}

void Writer::write( const GluonCore::GluonObjectList& objects, QIODevice* device)
{
    m_device = device;
    m_indentLevel = 0;

    foreach( GluonCore::GluonObject* object, objects )
    {
        writeObject( object );
    }
}

void Writer::writeObject( QObject* object)
{
    GluonCore::GluonObject* o = qobject_cast< GluonCore::GluonObject* >( object );
    if( !o )
        return;

    indent();
    m_device->write( QString( "{ %1(\"%2\")\n" ).arg( o->metaObject()->className() ).arg( o->name() ).toUtf8() );

    m_indentLevel++;

    const int numProperties = o->metaObject()->propertyCount();
    for( int i = 0; i < numProperties; ++i )
    {
        QMetaProperty property = o->metaObject()->property( i );
        writeProperty( property.name(), property.read( object)  );
    }

    QList<QByteArray> dynamicProperties = o->dynamicPropertyNames();
    foreach( const QByteArray& name, dynamicProperties )
    {
        writeProperty( QString( name ), o->property( name ) );
    }

    if( o->shouldSerializeChildren() )
    {
        foreach( QObject* child, o->children() )
        {
            writeObject( child );
        }
    }

    m_indentLevel--;
    indent();
    m_device->write( "}\n" );
}

void Writer::indent()
{
    m_device->write( QByteArray().fill( ' ', m_indentLevel * 4 ) );
}

void Writer::writeProperty(const QString& name, const QVariant& value)
{
    if( name == "name" || name == "objectName" || !value.isValid() )
        return;

    QString valueString = writePropertyValue( value );

    if( valueString.isEmpty() )
        return;

    indent();
    m_device->write( name.toUtf8() );
    m_device->write( " " );
    m_device->write( valueString.toUtf8() );
    m_device->write( "\n" );
}

QString Writer::writePropertyValue(const QVariant& value)
{
    QString valueString;
    switch( value.type() )
    {
        case QVariant::Bool:
            valueString = writeBoolValue( value );
            break;
        case QVariant::Int:
            valueString = writeIntValue( value );
            break;
        case QVariant::UInt:
            valueString = writeUIntValue( value );
            break;
        case QVariant::LongLong:
            valueString = writeLongLongValue( value );
            break;
        case 137: //QVariant::Float
        case QVariant::Double:
            valueString = writeFloatValue( value );
            break;
        case QVariant::String:
            valueString = writeStringValue( value );
            break;
        case QVariant::Url:
            valueString = writeUrlValue( value );
            break;
        case QVariant::Vector2D:
            valueString = writeVector2DValue( value );
            break;
        case QVariant::Vector3D:
            valueString = writeVector3DValue( value );
            break;
        case QVariant::Vector4D:
            valueString = writeVector4DValue( value );
            break;
        case QVariant::Quaternion:
            valueString = writeQuaternionValue( value );
            break;
        case QVariant::SizeF:
            valueString = writeSize2DValue( value );
            break;
        case QVariant::Color:
            valueString = writeRgbaValue( value );
            break;
        case QVariant::List:
            valueString = writeListValue( value );
            break;
        default:
            if( GluonCore::GluonObjectFactory::instance()->wrappedObject( value ) )
            {
                valueString = writeObjectValue( value );
            }
    }
    return valueString;
}

QString Writer::writeBoolValue( const QVariant& value)
{
    if( value.toBool() )
    {
        return QString( "bool(true)" );
    }
    else
    {
        return QString( "bool(false)" );
    }
}

QString Writer::writeIntValue(const QVariant& value)
{
    return QString( "int(%1)" ).arg( value.toInt() );
}

QString Writer::writeUIntValue(const QVariant& value)
{
    return QString( "uint(%1)" ).arg( value.toUInt() );
}

QString Writer::writeLongLongValue(const QVariant& value)
{
    return QString( "longlong(%1)" ).arg( value.toLongLong() );
}

QString Writer::writeFloatValue(const QVariant& value)
{
    return QString( "float(%1)" ).arg( value.toFloat() );
}

QString Writer::writeStringValue(const QVariant& value)
{
    if( value.toString().isEmpty() )
        return QString();

    return QString( "string(\"%1\")" ).arg( value.toString() );
}

QString Writer::writeUrlValue(const QVariant& value)
{
    if( value.toUrl().isEmpty() )
        return QString();

    return QString( "url(\"%1\")" ).arg( value.toUrl().toString() );
}

QString Writer::writeVector2DValue(const QVariant& value)
{
    QVector2D v = value.value< QVector2D >();

    if( v.isNull() )
        return QString();

    return QString( "vector2d(%1;%2)" ).arg( v.x() ).arg( v.y() );
}

QString Writer::writeVector3DValue(const QVariant& value)
{
    QVector3D v = value.value< QVector3D >();

    if( v.isNull() )
        return QString();

    return QString( "vector3d(%1;%2;%3)" ).arg( v.x() ).arg( v.y() ).arg( v.z() );
}

QString Writer::writeVector4DValue(const QVariant& value)
{
    QVector4D v = value.value< QVector4D >();

    if( v.isNull() )
        return QString();

    return QString( "vector4d(%1;%2;%3;%4)" ).arg( v.x() ).arg( v.y() ).arg( v.z() ).arg( v.w() );
}

QString Writer::writeSize2DValue(const QVariant& value)
{
    QSizeF v = value.toSizeF();

    if( v.isNull() )
        return QString();

    return QString( "size2d(%1;%2)" ).arg( v.width() ).arg( v.height() );
}

QString Writer::writeRgbaValue(const QVariant& value)
{
    QColor v = value.value< QColor >();

    if( !v.isValid() )
        return QString();

    return QString( "rgba(%1;%2;%3;%4)" ).arg( v.red() ).arg( v.green() ).arg( v.blue() ).arg( v.alpha() );
}

QString Writer::writeQuaternionValue(const QVariant& value)
{
    QQuaternion v = value.value< QQuaternion >();

    if( v.isNull() || v.isIdentity() )
        return QString();

    return QString( "quaternion(%1;%2;%3;%4)" ).arg( v.x() ).arg( v.y() ).arg( v.z() ).arg( v.scalar() );
}

QString Writer::writeObjectValue(const QVariant& value)
{
    GluonCore::GluonObject* obj = GluonCore::GluonObjectFactory::instance()->wrappedObject( value );
    if( !obj )
        return QString();

    return QString( "%1(\"%2\")" ).arg( obj->metaObject()->className() ).arg( obj->fullyQualifiedName() );
}

QString Writer::writeListValue(const QVariant& value)
{
    QVariantList list = value.toList();

    if( list.isEmpty() )
        return QString();

    QString listValue = QString("list([\n");
    foreach( QVariant val, list )
    {
        QString valueString = writePropertyValue( val );
        if( !valueString.isEmpty() )
            listValue.append( QString().fill( ' ', ( m_indentLevel + 1 ) * 4 ) + valueString + ";\n");
    }
    listValue += QString().fill( ' ', m_indentLevel * 4 ) + QString("])");
    return listValue;
}
<commit_msg>Fix "Check for foreach loop issues [foreach]..."<commit_after>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (c) 2011 Arjen Hiemstra <ahiemstra@heimr.nl>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "writer.h"

#include <QtCore/QSizeF>
#include <QtCore/QList>
#include <QtCore/QMetaProperty>
#include <QtCore/QDebug>
#include <QtGui/QVector2D>
#include <QtGui/QVector3D>
#include <QtGui/QVector4D>
#include <QtGui/QQuaternion>
#include <QtGui/QColor>

#include <core/gluonobject.h>
#include <core/gluonobjectfactory.h>

using namespace GDL;

Writer::Writer()
{
    m_device = 0;
    m_indentLevel = 0;
}

Writer::~Writer()
{

}

void Writer::write( const GluonCore::GluonObjectList& objects, QIODevice* device)
{
    m_device = device;
    m_indentLevel = 0;

    foreach( GluonCore::GluonObject* object, objects )
    {
        writeObject( object );
    }
}

void Writer::writeObject( QObject* object)
{
    GluonCore::GluonObject* o = qobject_cast< GluonCore::GluonObject* >( object );
    if( !o )
        return;

    indent();
    m_device->write( QString( "{ %1(\"%2\")\n" ).arg( o->metaObject()->className() ).arg( o->name() ).toUtf8() );

    m_indentLevel++;

    const int numProperties = o->metaObject()->propertyCount();
    for( int i = 0; i < numProperties; ++i )
    {
        QMetaProperty property = o->metaObject()->property( i );
        writeProperty( property.name(), property.read( object)  );
    }

    QList<QByteArray> dynamicProperties = o->dynamicPropertyNames();
    foreach( const QByteArray& name, dynamicProperties )
    {
        writeProperty( QString( name ), o->property( name ) );
    }

    if( o->shouldSerializeChildren() )
    {
        foreach( QObject* child, o->children() )
        {
            writeObject( child );
        }
    }

    m_indentLevel--;
    indent();
    m_device->write( "}\n" );
}

void Writer::indent()
{
    m_device->write( QByteArray().fill( ' ', m_indentLevel * 4 ) );
}

void Writer::writeProperty(const QString& name, const QVariant& value)
{
    if( name == "name" || name == "objectName" || !value.isValid() )
        return;

    QString valueString = writePropertyValue( value );

    if( valueString.isEmpty() )
        return;

    indent();
    m_device->write( name.toUtf8() );
    m_device->write( " " );
    m_device->write( valueString.toUtf8() );
    m_device->write( "\n" );
}

QString Writer::writePropertyValue(const QVariant& value)
{
    QString valueString;
    switch( value.type() )
    {
        case QVariant::Bool:
            valueString = writeBoolValue( value );
            break;
        case QVariant::Int:
            valueString = writeIntValue( value );
            break;
        case QVariant::UInt:
            valueString = writeUIntValue( value );
            break;
        case QVariant::LongLong:
            valueString = writeLongLongValue( value );
            break;
        case 137: //QVariant::Float
        case QVariant::Double:
            valueString = writeFloatValue( value );
            break;
        case QVariant::String:
            valueString = writeStringValue( value );
            break;
        case QVariant::Url:
            valueString = writeUrlValue( value );
            break;
        case QVariant::Vector2D:
            valueString = writeVector2DValue( value );
            break;
        case QVariant::Vector3D:
            valueString = writeVector3DValue( value );
            break;
        case QVariant::Vector4D:
            valueString = writeVector4DValue( value );
            break;
        case QVariant::Quaternion:
            valueString = writeQuaternionValue( value );
            break;
        case QVariant::SizeF:
            valueString = writeSize2DValue( value );
            break;
        case QVariant::Color:
            valueString = writeRgbaValue( value );
            break;
        case QVariant::List:
            valueString = writeListValue( value );
            break;
        default:
            if( GluonCore::GluonObjectFactory::instance()->wrappedObject( value ) )
            {
                valueString = writeObjectValue( value );
            }
    }
    return valueString;
}

QString Writer::writeBoolValue( const QVariant& value)
{
    if( value.toBool() )
    {
        return QString( "bool(true)" );
    }
    else
    {
        return QString( "bool(false)" );
    }
}

QString Writer::writeIntValue(const QVariant& value)
{
    return QString( "int(%1)" ).arg( value.toInt() );
}

QString Writer::writeUIntValue(const QVariant& value)
{
    return QString( "uint(%1)" ).arg( value.toUInt() );
}

QString Writer::writeLongLongValue(const QVariant& value)
{
    return QString( "longlong(%1)" ).arg( value.toLongLong() );
}

QString Writer::writeFloatValue(const QVariant& value)
{
    return QString( "float(%1)" ).arg( value.toFloat() );
}

QString Writer::writeStringValue(const QVariant& value)
{
    if( value.toString().isEmpty() )
        return QString();

    return QString( "string(\"%1\")" ).arg( value.toString() );
}

QString Writer::writeUrlValue(const QVariant& value)
{
    if( value.toUrl().isEmpty() )
        return QString();

    return QString( "url(\"%1\")" ).arg( value.toUrl().toString() );
}

QString Writer::writeVector2DValue(const QVariant& value)
{
    QVector2D v = value.value< QVector2D >();

    if( v.isNull() )
        return QString();

    return QString( "vector2d(%1;%2)" ).arg( v.x() ).arg( v.y() );
}

QString Writer::writeVector3DValue(const QVariant& value)
{
    QVector3D v = value.value< QVector3D >();

    if( v.isNull() )
        return QString();

    return QString( "vector3d(%1;%2;%3)" ).arg( v.x() ).arg( v.y() ).arg( v.z() );
}

QString Writer::writeVector4DValue(const QVariant& value)
{
    QVector4D v = value.value< QVector4D >();

    if( v.isNull() )
        return QString();

    return QString( "vector4d(%1;%2;%3;%4)" ).arg( v.x() ).arg( v.y() ).arg( v.z() ).arg( v.w() );
}

QString Writer::writeSize2DValue(const QVariant& value)
{
    QSizeF v = value.toSizeF();

    if( v.isNull() )
        return QString();

    return QString( "size2d(%1;%2)" ).arg( v.width() ).arg( v.height() );
}

QString Writer::writeRgbaValue(const QVariant& value)
{
    QColor v = value.value< QColor >();

    if( !v.isValid() )
        return QString();

    return QString( "rgba(%1;%2;%3;%4)" ).arg( v.red() ).arg( v.green() ).arg( v.blue() ).arg( v.alpha() );
}

QString Writer::writeQuaternionValue(const QVariant& value)
{
    QQuaternion v = value.value< QQuaternion >();

    if( v.isNull() || v.isIdentity() )
        return QString();

    return QString( "quaternion(%1;%2;%3;%4)" ).arg( v.x() ).arg( v.y() ).arg( v.z() ).arg( v.scalar() );
}

QString Writer::writeObjectValue(const QVariant& value)
{
    GluonCore::GluonObject* obj = GluonCore::GluonObjectFactory::instance()->wrappedObject( value );
    if( !obj )
        return QString();

    return QString( "%1(\"%2\")" ).arg( obj->metaObject()->className() ).arg( obj->fullyQualifiedName() );
}

QString Writer::writeListValue(const QVariant& value)
{
    QVariantList list = value.toList();

    if( list.isEmpty() )
        return QString();

    QString listValue = QString("list([\n");
    foreach( const QVariant& val, list )
    {
        QString valueString = writePropertyValue( val );
        if( !valueString.isEmpty() )
            listValue.append( QString().fill( ' ', ( m_indentLevel + 1 ) * 4 ) + valueString + ";\n");
    }
    listValue += QString().fill( ' ', m_indentLevel * 4 ) + QString("])");
    return listValue;
}
<|endoftext|>