<commit_before>#include "isa.h"
#include "Eigen/LU"
#include <iostream>

ISA::ISA(int numVisibles, int numHiddens, int sSize, int numScales) :
	mNumVisibles(numVisibles), mNumHiddens(numHiddens)
{
	if(mNumHiddens < mNumVisibles)
		mNumHiddens = mNumVisibles;
	mBasis = MatrixXd::Random(mNumVisibles, mNumHiddens);

	for(int i = 0; i < mNumHiddens / sSize; ++i)
		mSubspaces.push_back(GSM(sSize, numScales));

	if(mNumHiddens % sSize)
		mSubspaces.push_back(GSM(mNumHiddens % sSize, numScales));
}

ISA::~ISA() {
}

void ISA::train(const MatrixXd& data, Parameters params) {
	if(params.callback)
		// call callback function once before training
		if(!(*params.callback)(0, *this))
			return;

	for(int i = 0; i < params.maxIter; ++i) {
		// optimize basis
		bool improved = trainSGD(data, basis(), params);

		if(params.adaptive)
			params.SGD.stepWidth *= improved ? 1.1 : 0.5;

		// optimize marginal distributions
		trainPrior(basis().inverse() * data, params);

		if(params.callback)
			if(!(*params.callback)(i + 1, *this))
				break;
	}
}

void ISA::trainPrior(const MatrixXd& states, const Parameters params) {
	// TODO: parallelize
	for(int from = 0, i = 0; i < numSubspaces(); from += mSubspaces[i].dim(), ++i)
		mSubspaces[i].train(states.middleRows(from, mSubspaces[i].dim()),
			params.GSM.maxIter,
			params.GSM.tol);
}

bool ISA::trainSGD(
	const MatrixXd& complData,
	const MatrixXd& complBasis,
	const Parameters params)
{
	// filter matrix and momentum
	MatrixXd W = complBasis.inverse();
	MatrixXd P = MatrixXd::Zero(complBasis.rows(), complBasis.cols());

	for(int i = 0; i < params.SGD.maxIter; ++i) {
		for(int j = 0; j + params.SGD.batchSize <= complData.cols(); j += params.SGD.batchSize) {
			MatrixXd X = complData.middleCols(j, params.SGD.batchSize);

			// update momentum with natural gradient
			P = params.SGD.momentum * P + W
				- priorEnergyGradient(W * X) * X.transpose() * (W.transpose() * W);

			// update filter matrix
			W += params.SGD.stepWidth / params.SGD.batchSize * P;
		}
	}

	// update basis
	setBasis(W.inverse().leftCols(numHiddens()));

	return true;
}

MatrixXd ISA::sample(int numSamples) {
	return basis() * samplePrior(numSamples);
}

MatrixXd ISA::samplePrior(int numSamples) {
	MatrixXd samples = MatrixXd::Zero(numHiddens(), numSamples);

	for(int from = 0, i = 0; i < numSubspaces(); from += mSubspaces[i].dim(), ++i)
		samples.middleRows(from, mSubspaces[i].dim()) =
			mSubspaces[i].sample(numSamples);

	return samples;
}

MatrixXd ISA::samplePosterior(const MatrixXd& data) {
	// TODO: implement Gibbs sampling
	return samplePrior(data.cols());
}

MatrixXd ISA::priorEnergy(const MatrixXd& states) {
	MatrixXd energy = MatrixXd::Zero(states.rows(), states.cols());

	// TODO: parallelize
	for(int from = 0, i = 0; i < numSubspaces(); from += mSubspaces[i].dim(), ++i)
		energy.middleRows(from, mSubspaces[i].dim()) =
			mSubspaces[i].energy(states.middleRows(from, mSubspaces[i].dim()));

	return energy.colwise().sum();
}

MatrixXd ISA::priorEnergyGradient(const MatrixXd& states) {
	MatrixXd gradient = MatrixXd::Zero(states.rows(), states.cols());

	// TODO: parallelize
	for(int from = 0, i = 0; i < numSubspaces(); from += mSubspaces[i].dim(), ++i)
		gradient.middleRows(from, mSubspaces[i].dim()) =
			mSubspaces[i].energyGradient(states.middleRows(from, mSubspaces[i].dim()));

	return gradient;
}

MatrixXd ISA::logLikelihood(const MatrixXd& data) {
	// LU decomposition
	PartialPivLU<MatrixXd> basisLU(mBasis);

	// compute log-determinant of basis
	double logDet = basisLU.matrixLU().diagonal().array().abs().log().sum();

	MatrixXd states = basisLU.solve(data);
	MatrixXd logLik = MatrixXd::Zero(states.rows(), states.cols());

	// TODO: parallelize
	for(int from = 0, i = 0; i < numSubspaces(); from += mSubspaces[i].dim(), ++i)
		logLik.middleRows(from, mSubspaces[i].dim()) =
			mSubspaces[i].logLikelihood(states.middleRows(from, mSubspaces[i].dim()));

	return logLik.colwise().sum().array() - logDet;
}
<commit_msg>Implemented pocket algorithm.<commit_after>#include "isa.h"
#include "Eigen/LU"
#include <iostream>

ISA::ISA(int numVisibles, int numHiddens, int sSize, int numScales) :
	mNumVisibles(numVisibles), mNumHiddens(numHiddens)
{
	if(mNumHiddens < mNumVisibles)
		mNumHiddens = mNumVisibles;
	mBasis = ArrayXXd::Random(mNumVisibles, mNumHiddens) / 10.;

	for(int i = 0; i < mNumHiddens / sSize; ++i)
		mSubspaces.push_back(GSM(sSize, numScales));

	if(mNumHiddens % sSize)
		mSubspaces.push_back(GSM(mNumHiddens % sSize, numScales));
}

ISA::~ISA() {
}

void ISA::train(const MatrixXd& data, Parameters params) {
	if(params.callback)
		// call callback function once before training
		if(!(*params.callback)(0, *this))
			return;

	for(int i = 0; i < params.maxIter; ++i) {
		// optimize basis
		bool improved = trainSGD(data, basis(), params);

		if(params.adaptive)
			params.SGD.stepWidth *= improved ? 1.1 : 0.5;

		// optimize marginal distributions
		trainPrior(basis().inverse() * data, params);

		if(params.callback)
			if(!(*params.callback)(i + 1, *this))
				break;
	}
}

void ISA::trainPrior(const MatrixXd& states, const Parameters params) {
	// TODO: parallelize
	for(int from = 0, i = 0; i < numSubspaces(); from += mSubspaces[i].dim(), ++i)
		mSubspaces[i].train(states.middleRows(from, mSubspaces[i].dim()),
			params.GSM.maxIter,
			params.GSM.tol);
}

bool ISA::trainSGD(
	const MatrixXd& complData,
	const MatrixXd& complBasis,
	const Parameters params)
{
	// LU decomposition
	PartialPivLU<MatrixXd> basisLU(complBasis);

	// filter matrix and momentum
	MatrixXd W = basisLU.inverse();
	MatrixXd P = MatrixXd::Zero(complBasis.rows(), complBasis.cols());

	// compute value of lower bound
	double logDet = basisLU.matrixLU().diagonal().array().abs().log().sum();
	double energy = priorEnergy(W * complData).array().mean() + logDet;

	for(int i = 0; i < params.SGD.maxIter; ++i) {
		for(int j = 0; j + params.SGD.batchSize <= complData.cols(); j += params.SGD.batchSize) {
			MatrixXd X = complData.middleCols(j, params.SGD.batchSize);

			// update momentum with natural gradient
			P = params.SGD.momentum * P + W
				- priorEnergyGradient(W * X) * X.transpose() * (W.transpose() * W);

			// update filter matrix
			W += params.SGD.stepWidth / params.SGD.batchSize * P;
		}
	}

	// compute LU decomposition from filter matrix
	PartialPivLU<MatrixXd> filterLU(W);

	// compute new value of lower bound
	double logDetNew = filterLU.matrixLU().diagonal().array().abs().log().sum();
	double energyNew = priorEnergy(W * complData).array().mean() - logDetNew;

	if(params.SGD.pocket && energy < energyNew)
		// don't update basis
		return false;

	// update basis
	setBasis(filterLU.inverse().leftCols(numHiddens()));

	return energyNew < energy;
}

MatrixXd ISA::sample(int numSamples) {
	return basis() * samplePrior(numSamples);
}

MatrixXd ISA::samplePrior(int numSamples) {
	MatrixXd samples = MatrixXd::Zero(numHiddens(), numSamples);

	for(int from = 0, i = 0; i < numSubspaces(); from += mSubspaces[i].dim(), ++i)
		samples.middleRows(from, mSubspaces[i].dim()) =
			mSubspaces[i].sample(numSamples);

	return samples;
}

MatrixXd ISA::samplePosterior(const MatrixXd& data) {
	// TODO: implement Gibbs sampling
	return samplePrior(data.cols());
}

MatrixXd ISA::priorEnergy(const MatrixXd& states) {
	MatrixXd energy = MatrixXd::Zero(states.rows(), states.cols());

	// TODO: parallelize
	for(int from = 0, i = 0; i < numSubspaces(); from += mSubspaces[i].dim(), ++i)
		energy.middleRows(from, mSubspaces[i].dim()) =
			mSubspaces[i].energy(states.middleRows(from, mSubspaces[i].dim()));

	return energy.colwise().sum();
}

MatrixXd ISA::priorEnergyGradient(const MatrixXd& states) {
	MatrixXd gradient = MatrixXd::Zero(states.rows(), states.cols());

	// TODO: parallelize
	for(int from = 0, i = 0; i < numSubspaces(); from += mSubspaces[i].dim(), ++i)
		gradient.middleRows(from, mSubspaces[i].dim()) =
			mSubspaces[i].energyGradient(states.middleRows(from, mSubspaces[i].dim()));

	return gradient;
}

MatrixXd ISA::logLikelihood(const MatrixXd& data) {
	// LU decomposition
	PartialPivLU<MatrixXd> basisLU(mBasis);

	// compute log-determinant of basis
	double logDet = basisLU.matrixLU().diagonal().array().abs().log().sum();

	MatrixXd states = basisLU.solve(data);
	MatrixXd logLik = MatrixXd::Zero(states.rows(), states.cols());

	// TODO: parallelize
	for(int from = 0, i = 0; i < numSubspaces(); from += mSubspaces[i].dim(), ++i)
		logLik.middleRows(from, mSubspaces[i].dim()) =
			mSubspaces[i].logLikelihood(states.middleRows(from, mSubspaces[i].dim()));

	return logLik.colwise().sum().array() - logDet;
}
<|endoftext|>


<commit_before>/*
    Copyright (C) 1998-2001 by Jorrit Tyberghein

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

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

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"
#include "walktest/walktest.h"
#include "walktest/infmaze.h"
#include "qint.h"
#include "qsqrt.h"
#include "csgeom/frustum.h"
#include "ivaria/view.h"
#include "imesh/thing/polygon.h"
#include "imesh/terrfunc.h"
#include "csgeom/csrect.h"
#include "csutil/dataobj.h"
#include "cstool/keyval.h"
#include "cstool/collider.h"
#include "cstool/cspixmap.h"
#include "ivaria/collider.h"
#include "iengine/movable.h"
#include "iutil/objreg.h"
#include "iutil/plugin.h"

extern WalkTest *Sys;

int FindIntersection(csCollisionPair& cd,csVector3 line[2])
{
  csVector3 tri1[3]; tri1[0]=cd.a1; tri1[1]=cd.b1; tri1[2]=cd.c1;
  csVector3 tri2[3]; tri2[0]=cd.a2; tri2[1]=cd.b2; tri2[2]=cd.c2;

  return csMath3::FindIntersection(tri1,tri2,line);
}

// Define the player bounding box.
// The camera's lens or person's eye is assumed to be
// at 0,0,0.  The height (DY), width (DX) and depth (DZ).
// Is the size of the camera/person and the origin
// coordinates (OX,OY,OZ) locate the bbox with respect to the eye.
// This player is 1.8 metres tall (assuming 1cs unit = 1m) (6feet)
#define DX    cfg_body_width
#define DY    cfg_body_height
#define DZ    cfg_body_depth
#define OY    Sys->cfg_eye_offset

#define DX_L  cfg_legs_width
#define DZ_L  cfg_legs_depth

#define DX_2  (DX/2)
#define DZ_2  (DZ/2)

#define DX_2L (DX_L/2)
#define DZ_2L (DZ_L/2)

#define OYL  Sys->cfg_legs_offset
#define DYL  (OY-OYL)

void WalkTest::CreateColliders ()
{
  iPolygon3D *p;
  iPolygonMesh* mesh;
  iPluginManager* plugin_mgr = CS_QUERY_REGISTRY (object_reg, iPluginManager);
  iMeshObjectType* ThingType = CS_QUERY_PLUGIN_CLASS (plugin_mgr,
  	"crystalspace.mesh.object.thing", iMeshObjectType);
  if (!ThingType)
    ThingType = CS_LOAD_PLUGIN (plugin_mgr,
    	"crystalspace.mesh.object.thing", iMeshObjectType);

  iMeshObjectFactory* thing_fact = ThingType->NewFactory ();
  iMeshObject* mesh_obj = SCF_QUERY_INTERFACE (thing_fact, iMeshObject);
  thing_fact->DecRef ();
  plbody = Engine->CreateMeshWrapper (mesh_obj, "Player's Body");
  iThingState* thing_state = SCF_QUERY_INTERFACE (mesh_obj, iThingState);
  mesh_obj->DecRef ();

  thing_state->CreateVertex (csVector3 (-DX_2, OY,    -DZ_2));
  thing_state->CreateVertex (csVector3 (-DX_2, OY,    DZ_2));
  thing_state->CreateVertex (csVector3 (-DX_2, OY+DY, DZ_2));
  thing_state->CreateVertex (csVector3 (-DX_2, OY+DY, -DZ_2));
  thing_state->CreateVertex (csVector3 (DX_2,  OY,    -DZ_2));
  thing_state->CreateVertex (csVector3 (DX_2,  OY,    DZ_2));
  thing_state->CreateVertex (csVector3 (DX_2,  OY+DY, DZ_2));
  thing_state->CreateVertex (csVector3 (DX_2,  OY+DY, -DZ_2));

  // Left
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (1);
  p->CreateVertex (2); p->CreateVertex (3);

  // Right
  p = thing_state->CreatePolygon ();
  p->CreateVertex (4); p->CreateVertex (5);
  p->CreateVertex (6); p->CreateVertex (7);

  // Bottom
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (1);
  p->CreateVertex (5); p->CreateVertex (4);

  // Top
  p = thing_state->CreatePolygon ();
  p->CreateVertex (3); p->CreateVertex (2);
  p->CreateVertex (6); p->CreateVertex (7);

  // Front
  p = thing_state->CreatePolygon ();
  p->CreateVertex (1); p->CreateVertex (5);
  p->CreateVertex (6); p->CreateVertex (2);

  // Back
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (4);
  p->CreateVertex (7); p->CreateVertex (3);

  mesh = SCF_QUERY_INTERFACE (mesh_obj, iPolygonMesh);
  body = new csColliderWrapper (plbody->QueryObject (), collide_system, mesh);
  body->SetName ("player body");
  plbody->GetRadius (body_radius, body_center);
  mesh->DecRef ();
  thing_state->DecRef ();

  thing_fact = ThingType-> NewFactory ();
  ThingType->DecRef ();
  mesh_obj = SCF_QUERY_INTERFACE (thing_fact, iMeshObject);
  thing_fact->DecRef ();
  pllegs = Engine->CreateMeshWrapper (mesh_obj, "Player's Legs");
  thing_state = SCF_QUERY_INTERFACE (mesh_obj, iThingState);
  mesh_obj->DecRef ();

  thing_state->CreateVertex (csVector3 (-DX_2L, OYL,     -DZ_2L));
  thing_state->CreateVertex (csVector3 (-DX_2L, OYL,     DZ_2L));
  thing_state->CreateVertex (csVector3 (-DX_2L, OYL+DYL, DZ_2L));
  thing_state->CreateVertex (csVector3 (-DX_2L, OYL+DYL, -DZ_2L));
  thing_state->CreateVertex (csVector3 (DX_2L,  OYL,     -DZ_2L));
  thing_state->CreateVertex (csVector3 (DX_2L,  OYL,     DZ_2L));
  thing_state->CreateVertex (csVector3 (DX_2L,  OYL+DYL, DZ_2L));
  thing_state->CreateVertex (csVector3 (DX_2L,  OYL+DYL, -DZ_2L));

  // Left
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (1);
  p->CreateVertex (2); p->CreateVertex (3);

  // Right
  p = thing_state->CreatePolygon ();
  p->CreateVertex (4); p->CreateVertex (5);
  p->CreateVertex (6); p->CreateVertex (7);

  // Bottom
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (1);
  p->CreateVertex (5); p->CreateVertex (4);

  // Top
  p = thing_state->CreatePolygon ();
  p->CreateVertex (3); p->CreateVertex (2);
  p->CreateVertex (6); p->CreateVertex (7);

  // Front
  p = thing_state->CreatePolygon ();
  p->CreateVertex (1); p->CreateVertex (5);
  p->CreateVertex (6); p->CreateVertex (2);

  // Back
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (4);
  p->CreateVertex (7); p->CreateVertex (3);

  mesh = SCF_QUERY_INTERFACE (mesh_obj, iPolygonMesh);
  legs = new csColliderWrapper (pllegs->QueryObject (), collide_system, mesh);
  legs->SetName ("player legs");
  pllegs->GetRadius ( legs_radius, legs_center);
  mesh->DecRef ();
  thing_state->DecRef ();

  SCF_DEC_REF (pllegs);
  SCF_DEC_REF (plbody);

  SCF_DEC_REF (legs);
  SCF_DEC_REF (body);

  if (!body || !legs)
    do_cd = false;
}

#define MAXSECTORSOCCUPIED  20

// No more than 1000 collisions ;)
csCollisionPair our_cd_contact[1000];
int num_our_cd;

int FindSectors (csVector3 v, csVector3 d, iSector *s, iSector **sa)
{
  int c = 0;
  // @@@ Avoid this sqrt somehow? i.e. by having it in the objects.
  float size = qsqrt (d.x * d.x + d.y * d.y + d.z * d.z);
  iSectorIterator* it = Sys->Engine->GetNearbySectors (s, v, size);
  iSector* sector;
  while ((sector = it->Fetch ()) != NULL)
  {
    sa[c++] = sector;
    if (c >= MAXSECTORSOCCUPIED) break;
  }
  it->DecRef ();
  return c;
}

int CollisionDetect (csColliderWrapper *c, iSector* sp,
	csReversibleTransform *cdt)
{
  int hit = 0;
  int i, j;

  // Check collision with this sector.
  Sys->collide_system->ResetCollisionPairs ();
  if (c->Collide (sp->QueryObject (), cdt)) hit++;
  csCollisionPair* CD_contact = Sys->collide_system->GetCollisionPairs ();

  for (i=0 ; i<Sys->collide_system->GetCollisionPairCount () ; i++)
    our_cd_contact[num_our_cd++] = CD_contact[i];

  if (Sys->collide_system->GetOneHitOnly () && hit)
    return 1;

  // Check collision with the meshes in this sector.
  iMeshList* ml = sp->GetMeshes ();
  for (i = 0 ; i < ml->GetCount () ; i++)
  {
    iMeshWrapper* tp = ml->Get (i);
    Sys->collide_system->ResetCollisionPairs ();
    if (c->Collide (tp->QueryObject (), cdt,
    	&tp->GetMovable ()->GetTransform ())) hit++;

    CD_contact = Sys->collide_system->GetCollisionPairs ();
    for (j=0 ; j<Sys->collide_system->GetCollisionPairCount () ; j++)
      our_cd_contact[num_our_cd++] = CD_contact[j];

    if (Sys->collide_system->GetOneHitOnly () && hit)
      return 1;
    // TODO, should test which one is the closest.
  }

  return hit;
}

void DoGravity (csVector3& pos, csVector3& vel)
{
  pos=Sys->view->GetCamera ()->GetTransform ().GetOrigin ();

  csVector3 new_pos = pos+vel;
  csMatrix3 m;
  csOrthoTransform test (m, new_pos);

  iSector *n[MAXSECTORSOCCUPIED];
  int num_sectors = FindSectors (new_pos, 4.0f*Sys->body_radius,
    Sys->view->GetCamera()->GetSector(), n);

  num_our_cd = 0;
  Sys->collide_system->SetOneHitOnly (false);
  int hits = 0;

  // Check to see if there are any terrains, if so test against those.
  // This routine will automatically adjust the transform to the highest
  // terrain at this point.
  int k;
  for ( k = 0; k < num_sectors ; k++)
  {
    iMeshList* ml = n[k]->GetMeshes ();
    if (ml->GetCount () > 0)
    {
      int i;
      for (i = 0 ; i < ml->GetCount () ; i++)
      {
	iMeshWrapper* terrain = ml->Get (i);
	iTerrFuncState* state = SCF_QUERY_INTERFACE (terrain
		->GetMeshObject (), iTerrFuncState);
	if (state)
	{
	  hits += state->CollisionDetect( &test );
	  state->DecRef ();
        }
      }
    }
  }

  // If there were hits with the terrain we update our new position
  // here. Side note: this could moved outside the loop above because
  // a compiler bug with gcc 2.7.2 prevented it from working when inside
  // the loop.
  if (hits) new_pos = test.GetOrigin ();

  int j;

  if (hits == 0)
  {
    Sys->collide_system->ResetCollisionPairs ();

    for ( ; num_sectors-- ; )
      hits += CollisionDetect (Sys->body, n[num_sectors], &test);

//printf ("body: hits=%d num_our_cd=%d\n", hits, num_our_cd);
    for (j=0 ; j<num_our_cd ; j++)
    {
      csCollisionPair& cd = our_cd_contact[j];
      csVector3 n = ((cd.c2-cd.b2)%(cd.b2-cd.a2)).Unit();
      if (n*vel<0)
        continue;
      vel = -(vel%n)%n;
    }

    // We now know our (possible) velocity. Let's try to move up or down, if possible
    new_pos = pos+vel;
    test = csOrthoTransform (csMatrix3(), new_pos);

    num_sectors = FindSectors (new_pos, 4.0f*Sys->legs_radius,
		Sys->view->GetCamera()->GetSector(), n);

    num_our_cd = 0;
    Sys->collide_system->SetOneHitOnly (false);
    Sys->collide_system->ResetCollisionPairs ();
    int hit = 0;

    for ( ; num_sectors-- ; )
      hit += CollisionDetect (Sys->legs, n[num_sectors], &test);

    if (!hit)
    {
      Sys->on_ground = false;
      if (Sys->do_gravity && !Sys->move_3d)
	vel.y -= 0.002;
    }
    else
    {
      float max_y=-1e10;

      for (j=0 ; j<num_our_cd ; j++)
      {
	csCollisionPair cd = our_cd_contact[j];
        csVector3 n = ((cd.c2-cd.b2)%(cd.b2-cd.a2)).Unit();

	if (n*csVector3(0,-1,0)<0.7) continue;

	csVector3 line[2];

	cd.a1 += new_pos;
	cd.b1 += new_pos;
	cd.c1 += new_pos;

	if (FindIntersection (cd,line))
	{
	  if (line[0].y>max_y)
	    max_y=line[0].y;
	  if (line[1].y>max_y)
	    max_y=line[1].y;
	}
      }

      float p = new_pos.y-max_y+OYL+0.01;
      if (ABS(p)<DYL-0.01)
      {
	if (max_y != -1e10)
	  new_pos.y = max_y-OYL-0.01;

	if (vel.y<0)
	  vel.y = 0;
      }
      Sys->on_ground = true;
    }
  }
  new_pos -= Sys->view->GetCamera ()->GetTransform ().GetOrigin ();
  Sys->view->GetCamera ()->MoveWorld (new_pos);
  Sys->velocity = Sys->view->GetCamera ()->GetTransform ().GetO2T ()*vel;

  if(!Sys->do_gravity)
    Sys->velocity.y -= SIGN (Sys->velocity.y) * MIN (0.017, ABS (Sys->velocity.y));

}

<commit_msg>plugin_mgr was not decref'ed<commit_after>/*
    Copyright (C) 1998-2001 by Jorrit Tyberghein

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

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

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"
#include "walktest/walktest.h"
#include "walktest/infmaze.h"
#include "qint.h"
#include "qsqrt.h"
#include "csgeom/frustum.h"
#include "ivaria/view.h"
#include "imesh/thing/polygon.h"
#include "imesh/terrfunc.h"
#include "csgeom/csrect.h"
#include "csutil/dataobj.h"
#include "cstool/keyval.h"
#include "cstool/collider.h"
#include "cstool/cspixmap.h"
#include "ivaria/collider.h"
#include "iengine/movable.h"
#include "iutil/objreg.h"
#include "iutil/plugin.h"

extern WalkTest *Sys;

int FindIntersection(csCollisionPair& cd,csVector3 line[2])
{
  csVector3 tri1[3]; tri1[0]=cd.a1; tri1[1]=cd.b1; tri1[2]=cd.c1;
  csVector3 tri2[3]; tri2[0]=cd.a2; tri2[1]=cd.b2; tri2[2]=cd.c2;

  return csMath3::FindIntersection(tri1,tri2,line);
}

// Define the player bounding box.
// The camera's lens or person's eye is assumed to be
// at 0,0,0.  The height (DY), width (DX) and depth (DZ).
// Is the size of the camera/person and the origin
// coordinates (OX,OY,OZ) locate the bbox with respect to the eye.
// This player is 1.8 metres tall (assuming 1cs unit = 1m) (6feet)
#define DX    cfg_body_width
#define DY    cfg_body_height
#define DZ    cfg_body_depth
#define OY    Sys->cfg_eye_offset

#define DX_L  cfg_legs_width
#define DZ_L  cfg_legs_depth

#define DX_2  (DX/2)
#define DZ_2  (DZ/2)

#define DX_2L (DX_L/2)
#define DZ_2L (DZ_L/2)

#define OYL  Sys->cfg_legs_offset
#define DYL  (OY-OYL)

void WalkTest::CreateColliders ()
{
  iPolygon3D *p;
  iPolygonMesh* mesh;
  iPluginManager* plugin_mgr = CS_QUERY_REGISTRY (object_reg, iPluginManager);
  iMeshObjectType* ThingType = CS_QUERY_PLUGIN_CLASS (plugin_mgr,
  	"crystalspace.mesh.object.thing", iMeshObjectType);
  if (!ThingType)
    ThingType = CS_LOAD_PLUGIN (plugin_mgr,
    	"crystalspace.mesh.object.thing", iMeshObjectType);

  plugin_mgr->DecRef ();
  iMeshObjectFactory* thing_fact = ThingType->NewFactory ();
  iMeshObject* mesh_obj = SCF_QUERY_INTERFACE (thing_fact, iMeshObject);
  thing_fact->DecRef ();
  plbody = Engine->CreateMeshWrapper (mesh_obj, "Player's Body");
  iThingState* thing_state = SCF_QUERY_INTERFACE (mesh_obj, iThingState);
  mesh_obj->DecRef ();

  thing_state->CreateVertex (csVector3 (-DX_2, OY,    -DZ_2));
  thing_state->CreateVertex (csVector3 (-DX_2, OY,    DZ_2));
  thing_state->CreateVertex (csVector3 (-DX_2, OY+DY, DZ_2));
  thing_state->CreateVertex (csVector3 (-DX_2, OY+DY, -DZ_2));
  thing_state->CreateVertex (csVector3 (DX_2,  OY,    -DZ_2));
  thing_state->CreateVertex (csVector3 (DX_2,  OY,    DZ_2));
  thing_state->CreateVertex (csVector3 (DX_2,  OY+DY, DZ_2));
  thing_state->CreateVertex (csVector3 (DX_2,  OY+DY, -DZ_2));

  // Left
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (1);
  p->CreateVertex (2); p->CreateVertex (3);

  // Right
  p = thing_state->CreatePolygon ();
  p->CreateVertex (4); p->CreateVertex (5);
  p->CreateVertex (6); p->CreateVertex (7);

  // Bottom
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (1);
  p->CreateVertex (5); p->CreateVertex (4);

  // Top
  p = thing_state->CreatePolygon ();
  p->CreateVertex (3); p->CreateVertex (2);
  p->CreateVertex (6); p->CreateVertex (7);

  // Front
  p = thing_state->CreatePolygon ();
  p->CreateVertex (1); p->CreateVertex (5);
  p->CreateVertex (6); p->CreateVertex (2);

  // Back
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (4);
  p->CreateVertex (7); p->CreateVertex (3);

  mesh = SCF_QUERY_INTERFACE (mesh_obj, iPolygonMesh);
  body = new csColliderWrapper (plbody->QueryObject (), collide_system, mesh);
  body->SetName ("player body");
  plbody->GetRadius (body_radius, body_center);
  mesh->DecRef ();
  thing_state->DecRef ();

  thing_fact = ThingType-> NewFactory ();
  ThingType->DecRef ();
  mesh_obj = SCF_QUERY_INTERFACE (thing_fact, iMeshObject);
  thing_fact->DecRef ();
  pllegs = Engine->CreateMeshWrapper (mesh_obj, "Player's Legs");
  thing_state = SCF_QUERY_INTERFACE (mesh_obj, iThingState);
  mesh_obj->DecRef ();

  thing_state->CreateVertex (csVector3 (-DX_2L, OYL,     -DZ_2L));
  thing_state->CreateVertex (csVector3 (-DX_2L, OYL,     DZ_2L));
  thing_state->CreateVertex (csVector3 (-DX_2L, OYL+DYL, DZ_2L));
  thing_state->CreateVertex (csVector3 (-DX_2L, OYL+DYL, -DZ_2L));
  thing_state->CreateVertex (csVector3 (DX_2L,  OYL,     -DZ_2L));
  thing_state->CreateVertex (csVector3 (DX_2L,  OYL,     DZ_2L));
  thing_state->CreateVertex (csVector3 (DX_2L,  OYL+DYL, DZ_2L));
  thing_state->CreateVertex (csVector3 (DX_2L,  OYL+DYL, -DZ_2L));

  // Left
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (1);
  p->CreateVertex (2); p->CreateVertex (3);

  // Right
  p = thing_state->CreatePolygon ();
  p->CreateVertex (4); p->CreateVertex (5);
  p->CreateVertex (6); p->CreateVertex (7);

  // Bottom
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (1);
  p->CreateVertex (5); p->CreateVertex (4);

  // Top
  p = thing_state->CreatePolygon ();
  p->CreateVertex (3); p->CreateVertex (2);
  p->CreateVertex (6); p->CreateVertex (7);

  // Front
  p = thing_state->CreatePolygon ();
  p->CreateVertex (1); p->CreateVertex (5);
  p->CreateVertex (6); p->CreateVertex (2);

  // Back
  p = thing_state->CreatePolygon ();
  p->CreateVertex (0); p->CreateVertex (4);
  p->CreateVertex (7); p->CreateVertex (3);

  mesh = SCF_QUERY_INTERFACE (mesh_obj, iPolygonMesh);
  legs = new csColliderWrapper (pllegs->QueryObject (), collide_system, mesh);
  legs->SetName ("player legs");
  pllegs->GetRadius ( legs_radius, legs_center);
  mesh->DecRef ();
  thing_state->DecRef ();

  SCF_DEC_REF (pllegs);
  SCF_DEC_REF (plbody);

  SCF_DEC_REF (legs);
  SCF_DEC_REF (body);

  if (!body || !legs)
    do_cd = false;
}

#define MAXSECTORSOCCUPIED  20

// No more than 1000 collisions ;)
csCollisionPair our_cd_contact[1000];
int num_our_cd;

int FindSectors (csVector3 v, csVector3 d, iSector *s, iSector **sa)
{
  int c = 0;
  // @@@ Avoid this sqrt somehow? i.e. by having it in the objects.
  float size = qsqrt (d.x * d.x + d.y * d.y + d.z * d.z);
  iSectorIterator* it = Sys->Engine->GetNearbySectors (s, v, size);
  iSector* sector;
  while ((sector = it->Fetch ()) != NULL)
  {
    sa[c++] = sector;
    if (c >= MAXSECTORSOCCUPIED) break;
  }
  it->DecRef ();
  return c;
}

int CollisionDetect (csColliderWrapper *c, iSector* sp,
	csReversibleTransform *cdt)
{
  int hit = 0;
  int i, j;

  // Check collision with this sector.
  Sys->collide_system->ResetCollisionPairs ();
  if (c->Collide (sp->QueryObject (), cdt)) hit++;
  csCollisionPair* CD_contact = Sys->collide_system->GetCollisionPairs ();

  for (i=0 ; i<Sys->collide_system->GetCollisionPairCount () ; i++)
    our_cd_contact[num_our_cd++] = CD_contact[i];

  if (Sys->collide_system->GetOneHitOnly () && hit)
    return 1;

  // Check collision with the meshes in this sector.
  iMeshList* ml = sp->GetMeshes ();
  for (i = 0 ; i < ml->GetCount () ; i++)
  {
    iMeshWrapper* tp = ml->Get (i);
    Sys->collide_system->ResetCollisionPairs ();
    if (c->Collide (tp->QueryObject (), cdt,
    	&tp->GetMovable ()->GetTransform ())) hit++;

    CD_contact = Sys->collide_system->GetCollisionPairs ();
    for (j=0 ; j<Sys->collide_system->GetCollisionPairCount () ; j++)
      our_cd_contact[num_our_cd++] = CD_contact[j];

    if (Sys->collide_system->GetOneHitOnly () && hit)
      return 1;
    // TODO, should test which one is the closest.
  }

  return hit;
}

void DoGravity (csVector3& pos, csVector3& vel)
{
  pos=Sys->view->GetCamera ()->GetTransform ().GetOrigin ();

  csVector3 new_pos = pos+vel;
  csMatrix3 m;
  csOrthoTransform test (m, new_pos);

  iSector *n[MAXSECTORSOCCUPIED];
  int num_sectors = FindSectors (new_pos, 4.0f*Sys->body_radius,
    Sys->view->GetCamera()->GetSector(), n);

  num_our_cd = 0;
  Sys->collide_system->SetOneHitOnly (false);
  int hits = 0;

  // Check to see if there are any terrains, if so test against those.
  // This routine will automatically adjust the transform to the highest
  // terrain at this point.
  int k;
  for ( k = 0; k < num_sectors ; k++)
  {
    iMeshList* ml = n[k]->GetMeshes ();
    if (ml->GetCount () > 0)
    {
      int i;
      for (i = 0 ; i < ml->GetCount () ; i++)
      {
	iMeshWrapper* terrain = ml->Get (i);
	iTerrFuncState* state = SCF_QUERY_INTERFACE (terrain
		->GetMeshObject (), iTerrFuncState);
	if (state)
	{
	  hits += state->CollisionDetect( &test );
	  state->DecRef ();
        }
      }
    }
  }

  // If there were hits with the terrain we update our new position
  // here. Side note: this could moved outside the loop above because
  // a compiler bug with gcc 2.7.2 prevented it from working when inside
  // the loop.
  if (hits) new_pos = test.GetOrigin ();

  int j;

  if (hits == 0)
  {
    Sys->collide_system->ResetCollisionPairs ();

    for ( ; num_sectors-- ; )
      hits += CollisionDetect (Sys->body, n[num_sectors], &test);

//printf ("body: hits=%d num_our_cd=%d\n", hits, num_our_cd);
    for (j=0 ; j<num_our_cd ; j++)
    {
      csCollisionPair& cd = our_cd_contact[j];
      csVector3 n = ((cd.c2-cd.b2)%(cd.b2-cd.a2)).Unit();
      if (n*vel<0)
        continue;
      vel = -(vel%n)%n;
    }

    // We now know our (possible) velocity. Let's try to move up or down, if possible
    new_pos = pos+vel;
    test = csOrthoTransform (csMatrix3(), new_pos);

    num_sectors = FindSectors (new_pos, 4.0f*Sys->legs_radius,
		Sys->view->GetCamera()->GetSector(), n);

    num_our_cd = 0;
    Sys->collide_system->SetOneHitOnly (false);
    Sys->collide_system->ResetCollisionPairs ();
    int hit = 0;

    for ( ; num_sectors-- ; )
      hit += CollisionDetect (Sys->legs, n[num_sectors], &test);

    if (!hit)
    {
      Sys->on_ground = false;
      if (Sys->do_gravity && !Sys->move_3d)
	vel.y -= 0.002;
    }
    else
    {
      float max_y=-1e10;

      for (j=0 ; j<num_our_cd ; j++)
      {
	csCollisionPair cd = our_cd_contact[j];
        csVector3 n = ((cd.c2-cd.b2)%(cd.b2-cd.a2)).Unit();

	if (n*csVector3(0,-1,0)<0.7) continue;

	csVector3 line[2];

	cd.a1 += new_pos;
	cd.b1 += new_pos;
	cd.c1 += new_pos;

	if (FindIntersection (cd,line))
	{
	  if (line[0].y>max_y)
	    max_y=line[0].y;
	  if (line[1].y>max_y)
	    max_y=line[1].y;
	}
      }

      float p = new_pos.y-max_y+OYL+0.01;
      if (ABS(p)<DYL-0.01)
      {
	if (max_y != -1e10)
	  new_pos.y = max_y-OYL-0.01;

	if (vel.y<0)
	  vel.y = 0;
      }
      Sys->on_ground = true;
    }
  }
  new_pos -= Sys->view->GetCamera ()->GetTransform ().GetOrigin ();
  Sys->view->GetCamera ()->MoveWorld (new_pos);
  Sys->velocity = Sys->view->GetCamera ()->GetTransform ().GetO2T ()*vel;

  if(!Sys->do_gravity)
    Sys->velocity.y -= SIGN (Sys->velocity.y) * MIN (0.017, ABS (Sys->velocity.y));

}

<|endoftext|>


<commit_before>// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include "sdk/httpapi.h"
#include "sdk/iot_logging.h"

#include "util/HTTPSClient.h"

#define POOL_SIZE 1

HTTPSClient httpsClients[POOL_SIZE];

HTTPAPI_RESULT HTTPAPI_Init(void)
{
    for (int i = 0; i < POOL_SIZE; i++) {
        httpsClients[i] = HTTPSClient();
    }
    return HTTPAPI_OK;
}

void HTTPAPI_Deinit(void)
{
}

HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName)
{
    HTTPSClient* client = NULL;

    // find an available client to use
    for (int i = 0; i < POOL_SIZE; i++) {
        if (!httpsClients[i].connected()) {
            client = &httpsClients[i];
            break;
        }
    }

    if (client) {
        if (!client->begin(hostName)) {
            // connection failed
            LogError("HTTPS connection to %s failed\n", hostName);
            client = NULL;
        }
    } else {
        // no clients available
        LogError("No HTTPS clients available\n");
    }

    return ((HTTP_HANDLE)client);
}

void HTTPAPI_CloseConnection(HTTP_HANDLE handle)
{
    HTTPSClient* client = (HTTPSClient*)handle;

    client->end();
}

static const char* HTTPRequestTypes[] = {
    "GET",
    "POST",
    "PUT",
    "DELETE",
    "PATCH"
};

HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle,
        HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
        HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content,
        size_t contentLength, unsigned int* statusCode,
        HTTP_HEADERS_HANDLE responseHeadersHandle,
        BUFFER_HANDLE responseContent)
{
    int result;
    size_t headersCount;
    char* header;

    HTTPSClient* client = (HTTPSClient*)handle;

    if (!client->connected()) {
        // client not connected
        LogError("HTTPS request failed, client not connected\n");
        return HTTPAPI_OPEN_REQUEST_FAILED;
    }

    result = client->sendRequest(HTTPRequestTypes[requestType], relativePath);
    if (!result) {
        LogError("HTTPS send request failed\n");
        return HTTPAPI_SEND_REQUEST_FAILED;
    }

    HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount);

    for (size_t i = 0; i < headersCount && result; i++) {
        HTTPHeaders_GetHeader(httpHeadersHandle, i, &header);
        result = client->sendHeader(header);
        free(header);
    }

    if (!result) {
        LogError("HTTPS send header failed\n");
        return HTTPAPI_SEND_REQUEST_FAILED;
    }

    result = client->sendBody(content, contentLength);
    if (!result) {
        LogError("HTTPS send body failed\n");
        return HTTPAPI_SEND_REQUEST_FAILED;
    }

    result = client->readStatus();
    if (result == -1) {
        return HTTPAPI_STRING_PROCESSING_ERROR;
    }
    *statusCode = result;

    while (result > 0) {
        String headerName;
        String headerValue;

        result = client->readHeader(headerName, headerValue);
        HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, headerName.c_str(), headerValue.c_str());
    }

    if (result == -1) {
        LogError("HTTPS header parsing failed\n");
        return HTTPAPI_HTTP_HEADERS_FAILED;
    }

    contentLength = client->contentLength();
    if (contentLength) {
        BUFFER_pre_build(responseContent, contentLength);
        client->readBody(BUFFER_u_char(responseContent), contentLength);
    }

    return HTTPAPI_OK;
}

HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName,
        const void* value)
{
    HTTPSClient* client = (HTTPSClient*)handle;
    HTTPAPI_RESULT result = HTTPAPI_INVALID_ARG;

    if (strcmp("timeout", optionName) == 0) {
        client->setTimeout(*(const unsigned int*)value);

        result = HTTPAPI_OK;
    }

    return result;
}

HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value,
        const void** savedValue)
{
    HTTPAPI_RESULT result = HTTPAPI_INVALID_ARG;

    if (strcmp("timeout", optionName) == 0) {
        unsigned int* timeout = (unsigned int*)malloc(sizeof(unsigned int));

        *timeout = *(const unsigned int*)value;

        *savedValue = &timeout;

        result = HTTPAPI_OK;
    }

    return result;
}
<commit_msg>Fetch time via NTP and use to set time of day (in HTTPAPI_Init for now)<commit_after>// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include <time.h>
#include <sys/time.h>

#include "sdk/httpapi.h"
#include "sdk/iot_logging.h"

#include "util/HTTPSClient.h"
#include "util/NTPClient.h"

#define POOL_SIZE 1

HTTPSClient httpsClients[POOL_SIZE];

HTTPAPI_RESULT HTTPAPI_Init(void)
{
    time_t epochTime = (time_t)-1;
    NTPClient ntpClient;

    ntpClient.begin();
    while (true) {
        epochTime = ntpClient.getEpochTime("0.pool.ntp.org");

        if (epochTime == (time_t)-1) {
            LogError("Fetching NTP epoch time failed!\n");
            delay(5000);
        } else {
            LogInfo("Fetched NTP epoch time is: %lu\n", epochTime);
            break;
        }
    }
    ntpClient.end();

    struct timeval tv;
    tv.tv_sec = epochTime;
    tv.tv_usec = 0;

    settimeofday(&tv, NULL);

    for (int i = 0; i < POOL_SIZE; i++) {
        httpsClients[i] = HTTPSClient();
    }
    return HTTPAPI_OK;
}

void HTTPAPI_Deinit(void)
{
}

HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName)
{
    HTTPSClient* client = NULL;

    // find an available client to use
    for (int i = 0; i < POOL_SIZE; i++) {
        if (!httpsClients[i].connected()) {
            client = &httpsClients[i];
            break;
        }
    }

    if (client) {
        if (!client->begin(hostName)) {
            // connection failed
            LogError("HTTPS connection to %s failed\n", hostName);
            client = NULL;
        }
    } else {
        // no clients available
        LogError("No HTTPS clients available\n");
    }

    return ((HTTP_HANDLE)client);
}

void HTTPAPI_CloseConnection(HTTP_HANDLE handle)
{
    HTTPSClient* client = (HTTPSClient*)handle;

    client->end();
}

static const char* HTTPRequestTypes[] = {
    "GET",
    "POST",
    "PUT",
    "DELETE",
    "PATCH"
};

HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle,
        HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
        HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content,
        size_t contentLength, unsigned int* statusCode,
        HTTP_HEADERS_HANDLE responseHeadersHandle,
        BUFFER_HANDLE responseContent)
{
    int result;
    size_t headersCount;
    char* header;

    HTTPSClient* client = (HTTPSClient*)handle;

    if (!client->connected()) {
        // client not connected
        LogError("HTTPS request failed, client not connected\n");
        return HTTPAPI_OPEN_REQUEST_FAILED;
    }

    result = client->sendRequest(HTTPRequestTypes[requestType], relativePath);
    if (!result) {
        LogError("HTTPS send request failed\n");
        return HTTPAPI_SEND_REQUEST_FAILED;
    }

    HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount);

    for (size_t i = 0; i < headersCount && result; i++) {
        HTTPHeaders_GetHeader(httpHeadersHandle, i, &header);
        result = client->sendHeader(header);
        free(header);
    }

    if (!result) {
        LogError("HTTPS send header failed\n");
        return HTTPAPI_SEND_REQUEST_FAILED;
    }

    result = client->sendBody(content, contentLength);
    if (!result) {
        LogError("HTTPS send body failed\n");
        return HTTPAPI_SEND_REQUEST_FAILED;
    }

    result = client->readStatus();
    if (result == -1) {
        return HTTPAPI_STRING_PROCESSING_ERROR;
    }
    *statusCode = result;

    while (result > 0) {
        String headerName;
        String headerValue;

        result = client->readHeader(headerName, headerValue);
        HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, headerName.c_str(), headerValue.c_str());
    }

    if (result == -1) {
        LogError("HTTPS header parsing failed\n");
        return HTTPAPI_HTTP_HEADERS_FAILED;
    }

    contentLength = client->contentLength();
    if (contentLength) {
        BUFFER_pre_build(responseContent, contentLength);
        client->readBody(BUFFER_u_char(responseContent), contentLength);
    }

    return HTTPAPI_OK;
}

HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName,
        const void* value)
{
    HTTPSClient* client = (HTTPSClient*)handle;
    HTTPAPI_RESULT result = HTTPAPI_INVALID_ARG;

    if (strcmp("timeout", optionName) == 0) {
        client->setTimeout(*(const unsigned int*)value);

        result = HTTPAPI_OK;
    }

    return result;
}

HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value,
        const void** savedValue)
{
    HTTPAPI_RESULT result = HTTPAPI_INVALID_ARG;

    if (strcmp("timeout", optionName) == 0) {
        unsigned int* timeout = (unsigned int*)malloc(sizeof(unsigned int));

        *timeout = *(const unsigned int*)value;

        *savedValue = &timeout;

        result = HTTPAPI_OK;
    }

    return result;
}
<|endoftext|>


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

#include <httprpc.h>

#include <chainparams.h>
#include <httpserver.h>
#include <key_io.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
#include <random.h>
#include <sync.h>
#include <util.h>
#include <utilstrencodings.h>
#include <ui_interface.h>
#include <crypto/hmac_sha256.h>
#include <stdio.h>

#include <memory>

#include <boost/algorithm/string.hpp> // boost::trim

/** WWW-Authenticate to present with 401 Unauthorized response */
static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";

/** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
 * re-lock the wallet.
 */
class HTTPRPCTimer : public RPCTimerBase
{
public:
    HTTPRPCTimer(struct event_base* eventBase, std::function<void(void)>& func, int64_t millis) :
        ev(eventBase, false, func)
    {
        struct timeval tv;
        tv.tv_sec = millis/1000;
        tv.tv_usec = (millis%1000)*1000;
        ev.trigger(&tv);
    }
private:
    HTTPEvent ev;
};

class HTTPRPCTimerInterface : public RPCTimerInterface
{
public:
    explicit HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
    {
    }
    const char* Name() override
    {
        return "HTTP";
    }
    RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) override
    {
        return new HTTPRPCTimer(base, func, millis);
    }
private:
    struct event_base* base;
};

/* Pre-base64-encoded authentication token */
static std::string strRPCUserColonPass;
/* Stored RPC timer interface (for unregistration) */
static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;

static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
{
    // Send error reply from json-rpc error object
    int nStatus = HTTP_INTERNAL_SERVER_ERROR;
    int code = find_value(objError, "code").get_int();

    if (code == RPC_INVALID_REQUEST)
        nStatus = HTTP_BAD_REQUEST;
    else if (code == RPC_METHOD_NOT_FOUND)
        nStatus = HTTP_NOT_FOUND;

    std::string strReply = JSONRPCReply(NullUniValue, objError, id);

    req->WriteHeader("Content-Type", "application/json");
    req->WriteReply(nStatus, strReply);
}

//This function checks username and password against -rpcauth
//entries from config file.
static bool multiUserAuthorized(std::string strUserPass)
{
    if (strUserPass.find(':') == std::string::npos) {
        return false;
    }
    std::string strUser = strUserPass.substr(0, strUserPass.find(':'));
    std::string strPass = strUserPass.substr(strUserPass.find(':') + 1);

    for (const std::string& strRPCAuth : gArgs.GetArgs("-rpcauth")) {
        //Search for multi-user login/pass "rpcauth" from config
        std::vector<std::string> vFields;
        boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
        if (vFields.size() != 3) {
            //Incorrect formatting in config file
            continue;
        }

        std::string strName = vFields[0];
        if (!TimingResistantEqual(strName, strUser)) {
            continue;
        }

        std::string strSalt = vFields[1];
        std::string strHash = vFields[2];

        static const unsigned int KEY_SIZE = 32;
        unsigned char out[KEY_SIZE];

        CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out);
        std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
        std::string strHashFromPass = HexStr(hexvec);

        if (TimingResistantEqual(strHashFromPass, strHash)) {
            return true;
        }
    }
    return false;
}

static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
{
    if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
        return false;
    if (strAuth.substr(0, 6) != "Basic ")
        return false;
    std::string strUserPass64 = strAuth.substr(6);
    boost::trim(strUserPass64);
    std::string strUserPass = DecodeBase64(strUserPass64);

    if (strUserPass.find(':') != std::string::npos)
        strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':'));

    //Check if authorized under single-user field
    if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
        return true;
    }
    return multiUserAuthorized(strUserPass);
}

static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
{
    // JSONRPC handles only POST
    if (req->GetRequestMethod() != HTTPRequest::POST) {
        req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
        return false;
    }
    // Check authorization
    std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
    if (!authHeader.first) {
        req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
        req->WriteReply(HTTP_UNAUTHORIZED);
        return false;
    }

    JSONRPCRequest jreq;
    jreq.peerAddr = req->GetPeer().ToString();
    if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
        LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr);

        /* Deter brute-forcing
           If this results in a DoS the user really
           shouldn't have their RPC port exposed. */
        MilliSleep(250);

        req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
        req->WriteReply(HTTP_UNAUTHORIZED);
        return false;
    }

    try {
        // Parse request
        UniValue valRequest;
        if (!valRequest.read(req->ReadBody()))
            throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");

        // Set the URI
        jreq.URI = req->GetURI();

        std::string strReply;
        // singleton request
        if (valRequest.isObject()) {
            jreq.parse(valRequest);

            UniValue result = tableRPC.execute(jreq);

            // Send reply
            strReply = JSONRPCReply(result, NullUniValue, jreq.id);

        // array of requests
        } else if (valRequest.isArray())
            strReply = JSONRPCExecBatch(jreq, valRequest.get_array());
        else
            throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");

        req->WriteHeader("Content-Type", "application/json");
        req->WriteReply(HTTP_OK, strReply);
    } catch (const UniValue& objError) {
        JSONErrorReply(req, objError, jreq.id);
        return false;
    } catch (const std::exception& e) {
        JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
        return false;
    }
    return true;
}

static bool InitRPCAuthentication()
{
    if (gArgs.GetArg("-rpcpassword", "") == "")
    {
        LogPrintf("No rpcpassword set - using random cookie authentication\n");
        if (!GenerateAuthCookie(&strRPCUserColonPass)) {
            uiInterface.ThreadSafeMessageBox(
                _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
                "", CClientUIInterface::MSG_ERROR);
            return false;
        }
    } else {
        LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation.\n");
        strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
    }
    return true;
}

bool StartHTTPRPC()
{
    LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
    if (!InitRPCAuthentication())
        return false;

    RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
#ifdef ENABLE_WALLET
    // ifdef can be removed once we switch to better endpoint support and API versioning
    RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
#endif
    assert(EventBase());
    httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(EventBase());
    RPCSetTimerInterface(httpRPCTimerInterface.get());
    return true;
}

void InterruptHTTPRPC()
{
    LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
}

void StopHTTPRPC()
{
    LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
    UnregisterHTTPHandler("/", true);
#ifdef ENABLE_WALLET
    UnregisterHTTPHandler("/wallet/", false);
#endif
    if (httpRPCTimerInterface) {
        RPCUnsetTimerInterface(httpRPCTimerInterface.get());
        httpRPCTimerInterface.reset();
    }
}
<commit_msg>RPCAuth Detection in Logs<commit_after>// Copyright (c) 2015-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <httprpc.h>

#include <chainparams.h>
#include <httpserver.h>
#include <key_io.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
#include <random.h>
#include <sync.h>
#include <util.h>
#include <utilstrencodings.h>
#include <ui_interface.h>
#include <crypto/hmac_sha256.h>
#include <stdio.h>

#include <memory>

#include <boost/algorithm/string.hpp> // boost::trim

/** WWW-Authenticate to present with 401 Unauthorized response */
static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";

/** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
 * re-lock the wallet.
 */
class HTTPRPCTimer : public RPCTimerBase
{
public:
    HTTPRPCTimer(struct event_base* eventBase, std::function<void(void)>& func, int64_t millis) :
        ev(eventBase, false, func)
    {
        struct timeval tv;
        tv.tv_sec = millis/1000;
        tv.tv_usec = (millis%1000)*1000;
        ev.trigger(&tv);
    }
private:
    HTTPEvent ev;
};

class HTTPRPCTimerInterface : public RPCTimerInterface
{
public:
    explicit HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
    {
    }
    const char* Name() override
    {
        return "HTTP";
    }
    RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) override
    {
        return new HTTPRPCTimer(base, func, millis);
    }
private:
    struct event_base* base;
};

/* Pre-base64-encoded authentication token */
static std::string strRPCUserColonPass;
/* Stored RPC timer interface (for unregistration) */
static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;

static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
{
    // Send error reply from json-rpc error object
    int nStatus = HTTP_INTERNAL_SERVER_ERROR;
    int code = find_value(objError, "code").get_int();

    if (code == RPC_INVALID_REQUEST)
        nStatus = HTTP_BAD_REQUEST;
    else if (code == RPC_METHOD_NOT_FOUND)
        nStatus = HTTP_NOT_FOUND;

    std::string strReply = JSONRPCReply(NullUniValue, objError, id);

    req->WriteHeader("Content-Type", "application/json");
    req->WriteReply(nStatus, strReply);
}

//This function checks username and password against -rpcauth
//entries from config file.
static bool multiUserAuthorized(std::string strUserPass)
{
    if (strUserPass.find(':') == std::string::npos) {
        return false;
    }
    std::string strUser = strUserPass.substr(0, strUserPass.find(':'));
    std::string strPass = strUserPass.substr(strUserPass.find(':') + 1);

    for (const std::string& strRPCAuth : gArgs.GetArgs("-rpcauth")) {
        //Search for multi-user login/pass "rpcauth" from config
        std::vector<std::string> vFields;
        boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
        if (vFields.size() != 3) {
            //Incorrect formatting in config file
            continue;
        }

        std::string strName = vFields[0];
        if (!TimingResistantEqual(strName, strUser)) {
            continue;
        }

        std::string strSalt = vFields[1];
        std::string strHash = vFields[2];

        static const unsigned int KEY_SIZE = 32;
        unsigned char out[KEY_SIZE];

        CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out);
        std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
        std::string strHashFromPass = HexStr(hexvec);

        if (TimingResistantEqual(strHashFromPass, strHash)) {
            return true;
        }
    }
    return false;
}

static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
{
    if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
        return false;
    if (strAuth.substr(0, 6) != "Basic ")
        return false;
    std::string strUserPass64 = strAuth.substr(6);
    boost::trim(strUserPass64);
    std::string strUserPass = DecodeBase64(strUserPass64);

    if (strUserPass.find(':') != std::string::npos)
        strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':'));

    //Check if authorized under single-user field
    if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
        return true;
    }
    return multiUserAuthorized(strUserPass);
}

static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
{
    // JSONRPC handles only POST
    if (req->GetRequestMethod() != HTTPRequest::POST) {
        req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
        return false;
    }
    // Check authorization
    std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
    if (!authHeader.first) {
        req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
        req->WriteReply(HTTP_UNAUTHORIZED);
        return false;
    }

    JSONRPCRequest jreq;
    jreq.peerAddr = req->GetPeer().ToString();
    if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
        LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr);

        /* Deter brute-forcing
           If this results in a DoS the user really
           shouldn't have their RPC port exposed. */
        MilliSleep(250);

        req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
        req->WriteReply(HTTP_UNAUTHORIZED);
        return false;
    }

    try {
        // Parse request
        UniValue valRequest;
        if (!valRequest.read(req->ReadBody()))
            throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");

        // Set the URI
        jreq.URI = req->GetURI();

        std::string strReply;
        // singleton request
        if (valRequest.isObject()) {
            jreq.parse(valRequest);

            UniValue result = tableRPC.execute(jreq);

            // Send reply
            strReply = JSONRPCReply(result, NullUniValue, jreq.id);

        // array of requests
        } else if (valRequest.isArray())
            strReply = JSONRPCExecBatch(jreq, valRequest.get_array());
        else
            throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");

        req->WriteHeader("Content-Type", "application/json");
        req->WriteReply(HTTP_OK, strReply);
    } catch (const UniValue& objError) {
        JSONErrorReply(req, objError, jreq.id);
        return false;
    } catch (const std::exception& e) {
        JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
        return false;
    }
    return true;
}

static bool InitRPCAuthentication()
{
    if (gArgs.GetArg("-rpcpassword", "") == "")
    {
        LogPrintf("No rpcpassword set - using random cookie authentication.\n");
        if (!GenerateAuthCookie(&strRPCUserColonPass)) {
            uiInterface.ThreadSafeMessageBox(
                _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
                "", CClientUIInterface::MSG_ERROR);
            return false;
        }
    } else {
        LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation.\n");
        strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
    }
    if (gArgs.GetArg("-rpcauth","") != "")
    {
        LogPrintf("Using rpcauth authentication.\n");
    }
    return true;
}

bool StartHTTPRPC()
{
    LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
    if (!InitRPCAuthentication())
        return false;

    RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
#ifdef ENABLE_WALLET
    // ifdef can be removed once we switch to better endpoint support and API versioning
    RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
#endif
    assert(EventBase());
    httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(EventBase());
    RPCSetTimerInterface(httpRPCTimerInterface.get());
    return true;
}

void InterruptHTTPRPC()
{
    LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
}

void StopHTTPRPC()
{
    LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
    UnregisterHTTPHandler("/", true);
#ifdef ENABLE_WALLET
    UnregisterHTTPHandler("/wallet/", false);
#endif
    if (httpRPCTimerInterface) {
        RPCUnsetTimerInterface(httpRPCTimerInterface.get());
        httpRPCTimerInterface.reset();
    }
}
<|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 "chrome_frame/com_message_event.h"

#include "base/logging.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"

ComMessageEvent::ComMessageEvent() {
}

ComMessageEvent::~ComMessageEvent() {
}

bool ComMessageEvent::Initialize(IOleContainer* container,
                                 const std::string& message,
                                 const std::string& origin,
                                 const std::string& event_type) {
  DCHECK(container);
  message_ = message;
  origin_ = origin;
  type_ = event_type;

  // May remain NULL if container not IE
  ScopedComPtr<IHTMLEventObj> basic_event;
  ScopedComPtr<IHTMLDocument2> doc;

  // Fetching doc may fail in non-IE containers.
  container->QueryInterface(doc.Receive());
  if (doc) {
    ScopedComPtr<IHTMLDocument4> doc4;
    doc4.QueryFrom(doc);
    DCHECK(doc4);  // supported by IE5.5 and higher
    if (doc4) {
      // IHTMLEventObj5 is only supported in IE8 and later, so we provide our
      // own (minimalistic) implementation of it.
      doc4->createEventObject(NULL, basic_event.Receive());
      DCHECK(basic_event);
    }
  }

  basic_event_ = basic_event;
  return true;
}

STDMETHODIMP ComMessageEvent::GetTypeInfoCount(UINT* info) {
  // Don't DCHECK as python scripts might still call this function
  // inadvertently.
  DLOG(WARNING) << "Not implemented: " << __FUNCTION__;
  return E_NOTIMPL;
}

STDMETHODIMP ComMessageEvent::GetTypeInfo(UINT which_info, LCID lcid,
                                          ITypeInfo** type_info) {
  DLOG(WARNING) << "Not implemented: " << __FUNCTION__;
  return E_NOTIMPL;
}

STDMETHODIMP ComMessageEvent::GetIDsOfNames(REFIID iid, LPOLESTR* names,
                                            UINT count_names, LCID lcid,
                                            DISPID* dispids) {
  HRESULT hr = S_OK;

  // Note that since we're using LowerCaseEqualsASCII for string comparison,
  // the second argument _must_ be all lower case.  I.e. we cannot compare
  // against L"messagePort" since it has a capital 'P'.
  for (UINT i = 0; SUCCEEDED(hr) && i < count_names; ++i) {
    const wchar_t* name_begin = names[i];
    const wchar_t* name_end = name_begin + wcslen(name_begin);
    if (LowerCaseEqualsASCII(name_begin, name_end, "data")) {
      dispids[i] = DISPID_MESSAGE_EVENT_DATA;
    } else if (LowerCaseEqualsASCII(name_begin, name_end, "origin")) {
      dispids[i] = DISPID_MESSAGE_EVENT_ORIGIN;
    } else if (LowerCaseEqualsASCII(name_begin, name_end, "lasteventid")) {
      dispids[i] = DISPID_MESSAGE_EVENT_LAST_EVENT_ID;
    } else if (LowerCaseEqualsASCII(name_begin, name_end, "source")) {
      dispids[i] = DISPID_MESSAGE_EVENT_SOURCE;
    } else if (LowerCaseEqualsASCII(name_begin, name_end, "messageport")) {
      dispids[i] = DISPID_MESSAGE_EVENT_MESSAGE_PORT;
    } else if (LowerCaseEqualsASCII(name_begin, name_end, "type")) {
      dispids[i] = DISPID_MESSAGE_EVENT_TYPE;
    } else {
      if (basic_event_) {
        hr = basic_event_->GetIDsOfNames(IID_IDispatch, &names[i], 1, lcid,
                                         &dispids[i]);
      } else {
        hr = DISP_E_MEMBERNOTFOUND;
      }

      if (FAILED(hr)) {
        DLOG(WARNING) << "member not found: " << names[i]
                      << StringPrintf(L"0x%08X", hr);
      }
    }
  }
  return hr;
}

STDMETHODIMP ComMessageEvent::Invoke(DISPID dispid, REFIID iid, LCID lcid,
                                     WORD flags, DISPPARAMS* params,
                                     VARIANT* result, EXCEPINFO* excepinfo,
                                     UINT* arg_err) {
  HRESULT hr = DISP_E_MEMBERNOTFOUND;
  switch (dispid) {
    case DISPID_MESSAGE_EVENT_DATA:
      hr = GetStringProperty(flags, UTF8ToWide(message_).c_str(), result);
      break;

    case DISPID_MESSAGE_EVENT_ORIGIN:
      hr = GetStringProperty(flags, UTF8ToWide(origin_).c_str(), result);
      break;

    case DISPID_MESSAGE_EVENT_TYPE:
      hr = GetStringProperty(flags, UTF8ToWide(type_).c_str(), result);
      break;

    case DISPID_MESSAGE_EVENT_LAST_EVENT_ID:
      hr = GetStringProperty(flags, L"", result);
      break;

    case DISPID_MESSAGE_EVENT_SOURCE:
    case DISPID_MESSAGE_EVENT_MESSAGE_PORT:
      if (flags & DISPATCH_PROPERTYGET) {
        result->vt = VT_NULL;
        hr = S_OK;
      } else {
        hr = DISP_E_TYPEMISMATCH;
      }
      break;

    default:
      if (basic_event_) {
        hr = basic_event_->Invoke(dispid, iid, lcid, flags, params, result,
                                  excepinfo, arg_err);
      }
      break;
  }

  return hr;
}

HRESULT ComMessageEvent::GetStringProperty(WORD flags, const wchar_t* value,
                                           VARIANT* result) {
  if (!result)
    return E_INVALIDARG;

  HRESULT hr;
  if (flags & DISPATCH_PROPERTYGET) {
    result->vt = VT_BSTR;
    result->bstrVal = ::SysAllocString(value);
    hr = S_OK;
  } else {
    hr = DISP_E_TYPEMISMATCH;
  }
  return hr;
}
<commit_msg>Check the container for NULLness before dereferencing.<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 "chrome_frame/com_message_event.h"

#include "base/logging.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"

ComMessageEvent::ComMessageEvent() {
}

ComMessageEvent::~ComMessageEvent() {
}

bool ComMessageEvent::Initialize(IOleContainer* container,
                                 const std::string& message,
                                 const std::string& origin,
                                 const std::string& event_type) {
  DLOG_IF(WARNING, !container) << __FUNCTION__ << " no container";
  message_ = message;
  origin_ = origin;
  type_ = event_type;

  // May remain NULL if container not IE
  ScopedComPtr<IHTMLEventObj> basic_event;
  ScopedComPtr<IHTMLDocument2> doc;

  // Fetching doc may fail in non-IE containers
  // and container might be NULL in some applications.
  if (container)
    container->QueryInterface(doc.Receive());
  if (doc) {
    ScopedComPtr<IHTMLDocument4> doc4;
    doc4.QueryFrom(doc);
    DCHECK(doc4);  // supported by IE5.5 and higher
    if (doc4) {
      // IHTMLEventObj5 is only supported in IE8 and later, so we provide our
      // own (minimalistic) implementation of it.
      doc4->createEventObject(NULL, basic_event.Receive());
      DCHECK(basic_event);
    }
  }

  basic_event_ = basic_event;
  return true;
}

STDMETHODIMP ComMessageEvent::GetTypeInfoCount(UINT* info) {
  // Don't DCHECK as python scripts might still call this function
  // inadvertently.
  DLOG(WARNING) << "Not implemented: " << __FUNCTION__;
  return E_NOTIMPL;
}

STDMETHODIMP ComMessageEvent::GetTypeInfo(UINT which_info, LCID lcid,
                                          ITypeInfo** type_info) {
  DLOG(WARNING) << "Not implemented: " << __FUNCTION__;
  return E_NOTIMPL;
}

STDMETHODIMP ComMessageEvent::GetIDsOfNames(REFIID iid, LPOLESTR* names,
                                            UINT count_names, LCID lcid,
                                            DISPID* dispids) {
  HRESULT hr = S_OK;

  // Note that since we're using LowerCaseEqualsASCII for string comparison,
  // the second argument _must_ be all lower case.  I.e. we cannot compare
  // against L"messagePort" since it has a capital 'P'.
  for (UINT i = 0; SUCCEEDED(hr) && i < count_names; ++i) {
    const wchar_t* name_begin = names[i];
    const wchar_t* name_end = name_begin + wcslen(name_begin);
    if (LowerCaseEqualsASCII(name_begin, name_end, "data")) {
      dispids[i] = DISPID_MESSAGE_EVENT_DATA;
    } else if (LowerCaseEqualsASCII(name_begin, name_end, "origin")) {
      dispids[i] = DISPID_MESSAGE_EVENT_ORIGIN;
    } else if (LowerCaseEqualsASCII(name_begin, name_end, "lasteventid")) {
      dispids[i] = DISPID_MESSAGE_EVENT_LAST_EVENT_ID;
    } else if (LowerCaseEqualsASCII(name_begin, name_end, "source")) {
      dispids[i] = DISPID_MESSAGE_EVENT_SOURCE;
    } else if (LowerCaseEqualsASCII(name_begin, name_end, "messageport")) {
      dispids[i] = DISPID_MESSAGE_EVENT_MESSAGE_PORT;
    } else if (LowerCaseEqualsASCII(name_begin, name_end, "type")) {
      dispids[i] = DISPID_MESSAGE_EVENT_TYPE;
    } else {
      if (basic_event_) {
        hr = basic_event_->GetIDsOfNames(IID_IDispatch, &names[i], 1, lcid,
                                         &dispids[i]);
      } else {
        hr = DISP_E_MEMBERNOTFOUND;
      }

      if (FAILED(hr)) {
        DLOG(WARNING) << "member not found: " << names[i]
                      << StringPrintf(L"0x%08X", hr);
      }
    }
  }
  return hr;
}

STDMETHODIMP ComMessageEvent::Invoke(DISPID dispid, REFIID iid, LCID lcid,
                                     WORD flags, DISPPARAMS* params,
                                     VARIANT* result, EXCEPINFO* excepinfo,
                                     UINT* arg_err) {
  HRESULT hr = DISP_E_MEMBERNOTFOUND;
  switch (dispid) {
    case DISPID_MESSAGE_EVENT_DATA:
      hr = GetStringProperty(flags, UTF8ToWide(message_).c_str(), result);
      break;

    case DISPID_MESSAGE_EVENT_ORIGIN:
      hr = GetStringProperty(flags, UTF8ToWide(origin_).c_str(), result);
      break;

    case DISPID_MESSAGE_EVENT_TYPE:
      hr = GetStringProperty(flags, UTF8ToWide(type_).c_str(), result);
      break;

    case DISPID_MESSAGE_EVENT_LAST_EVENT_ID:
      hr = GetStringProperty(flags, L"", result);
      break;

    case DISPID_MESSAGE_EVENT_SOURCE:
    case DISPID_MESSAGE_EVENT_MESSAGE_PORT:
      if (flags & DISPATCH_PROPERTYGET) {
        result->vt = VT_NULL;
        hr = S_OK;
      } else {
        hr = DISP_E_TYPEMISMATCH;
      }
      break;

    default:
      if (basic_event_) {
        hr = basic_event_->Invoke(dispid, iid, lcid, flags, params, result,
                                  excepinfo, arg_err);
      }
      break;
  }

  return hr;
}

HRESULT ComMessageEvent::GetStringProperty(WORD flags, const wchar_t* value,
                                           VARIANT* result) {
  if (!result)
    return E_INVALIDARG;

  HRESULT hr;
  if (flags & DISPATCH_PROPERTYGET) {
    result->vt = VT_BSTR;
    result->bstrVal = ::SysAllocString(value);
    hr = S_OK;
  } else {
    hr = DISP_E_TYPEMISMATCH;
  }
  return hr;
}
<|endoftext|>


<commit_before>/** @file editorviewmviface.cpp
 * 	@brief Класс, реализующий интерфейс представления в схеме Model/View
 * */
#include <QtGui>

#include "editorviewmviface.h"

#include "editorview.h"
#include "editorviewscene.h"

#include "realreporoles.h"

#include "uml_element.h"
#include "uml_guiobjectfactory.h"

EditorViewMViface::EditorViewMViface(EditorView *view, EditorViewScene *scene)
	: QAbstractItemView(0)
{
	this->view = view;
	this->scene = scene;

	//    view->mv_iface = this;
	scene->mv_iface = this;
	scene->view = view;
}

QRect EditorViewMViface::visualRect(const QModelIndex &) const
{
	return QRect();
}

void EditorViewMViface::scrollTo(const QModelIndex &, ScrollHint)
{
}

QModelIndex EditorViewMViface::indexAt(const QPoint &) const
{
	return QModelIndex();
}

QModelIndex EditorViewMViface::moveCursor(QAbstractItemView::CursorAction,
		Qt::KeyboardModifiers)
{
	return QModelIndex();
}

int EditorViewMViface::horizontalOffset() const
{
	return 0;
}

int EditorViewMViface::verticalOffset() const
{
	return 0;
}

bool EditorViewMViface::isIndexHidden(const QModelIndex &) const
{
	return false;
}

void EditorViewMViface::setSelection(const QRect&, QItemSelectionModel::SelectionFlags )
{
}

QRegion EditorViewMViface::visualRegionForSelection(const QItemSelection &) const
{
	return QRegion();
}
/*
void EditorViewMViface::raiseClick ( const QGraphicsItem * item )
{
	const UML::Element *e = qgraphicsitem_cast<const UML::Element *>(item);
	if (e)
		emit clicked(e->index());
}
*/

UML::Element* EditorViewMViface::getItem(int uuid)
{
	return items[uuid];
}

void EditorViewMViface::reset()
{
	items.clear();
	scene->clearScene();

	// so that our diagram be nicer
	scene->removeItem(scene->addRect(QRect(-1000,-1000,2000,2000)));

	if ( model() )
		rowsInserted(rootIndex(), 0, model()->rowCount(rootIndex()) - 1 );
}

void EditorViewMViface::setRootIndex(const QModelIndex &index)
{
	QAbstractItemView::setRootIndex(index);
	reset();
}

void EditorViewMViface::rowsInserted ( const QModelIndex & parent, int start, int end )
{
//	qDebug() << "========== rowsInserted" << parent << start << end;

//	if ( parent != rootIndex() )
//		return;
	if ( parent == QModelIndex() || parent.parent() == QModelIndex() )
		return;

//	qDebug() << "rowsInserted: adding items" << parent;
	for (int row = start; row <= end; ++row) {
		QPersistentModelIndex current = model()->index(row, 0, parent);
		int uuid = model()->index(row, 0, parent).data(Unreal::IdRole).toInt();
		int type = model()->index(row, 0, parent).data(Unreal::TypeRole).toInt();

		int parent_uuid;
		if ( parent != rootIndex() )
				parent_uuid = parent.data(Unreal::IdRole).toInt();
		else
				parent_uuid = -1;

//		qDebug() << uuid << type;

		if ( ! uuid )
			continue;

		if ( UML::Element *e = UML::GUIObjectFactory(type) ) {
			scene->addItem(e);
			e->setIndex(current);
			e->setPos(current.data(Unreal::PositionRole).toPointF());

			if ( parent_uuid != -1 )
				e->setParentItem(items[parent_uuid]);

			items[uuid] = e;
			e->connectToPort();
		}

		if ( model()->hasChildren(current) ) {
			rowsInserted( current, 0, model()->rowCount( current ) - 1 );
		}
	}

//	qDebug() << "rowsInserted: updating items";
	for (int row = start; row <= end; ++row) {
		int uuid = model()->index(row, 0, parent).data(Unreal::IdRole).toInt();
		if (items.contains(uuid))
			items[uuid]->updateData();
	}

	QAbstractItemView::rowsInserted(parent, start, end);
}

void EditorViewMViface::rowsAboutToBeRemoved ( const QModelIndex & parent, int start, int end )
{
	for (int row = start; row <= end; ++row) {
		int uuid = model()->index(row, 0, parent).data(Unreal::IdRole).toInt();

		scene->removeItem(items[uuid]);
		delete items[uuid];
		items.remove(uuid);
	}

	QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
}

void EditorViewMViface::dataChanged(const QModelIndex &topLeft,
		const QModelIndex &bottomRight)
{
	for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
		int uuid = topLeft.sibling(row, 0).data(Unreal::IdRole).toInt();

		if ( items.contains(uuid) )
			items[uuid]->updateData();
		else
			rowsInserted(topLeft.parent(),row,row);

	}
}

<commit_msg>Быстрый фикс к проблеме "отваливающихся" edgeElementов.<commit_after>/** @file editorviewmviface.cpp
 * 	@brief Класс, реализующий интерфейс представления в схеме Model/View
 * */
#include <QtGui>

#include "editorviewmviface.h"

#include "editorview.h"
#include "editorviewscene.h"

#include "realreporoles.h"

#include "uml_element.h"
#include "uml_guiobjectfactory.h"

EditorViewMViface::EditorViewMViface(EditorView *view, EditorViewScene *scene)
	: QAbstractItemView(0)
{
	this->view = view;
	this->scene = scene;

	//    view->mv_iface = this;
	scene->mv_iface = this;
	scene->view = view;
}

QRect EditorViewMViface::visualRect(const QModelIndex &) const
{
	return QRect();
}

void EditorViewMViface::scrollTo(const QModelIndex &, ScrollHint)
{
}

QModelIndex EditorViewMViface::indexAt(const QPoint &) const
{
	return QModelIndex();
}

QModelIndex EditorViewMViface::moveCursor(QAbstractItemView::CursorAction,
		Qt::KeyboardModifiers)
{
	return QModelIndex();
}

int EditorViewMViface::horizontalOffset() const
{
	return 0;
}

int EditorViewMViface::verticalOffset() const
{
	return 0;
}

bool EditorViewMViface::isIndexHidden(const QModelIndex &) const
{
	return false;
}

void EditorViewMViface::setSelection(const QRect&, QItemSelectionModel::SelectionFlags )
{
}

QRegion EditorViewMViface::visualRegionForSelection(const QItemSelection &) const
{
	return QRegion();
}
/*
void EditorViewMViface::raiseClick ( const QGraphicsItem * item )
{
	const UML::Element *e = qgraphicsitem_cast<const UML::Element *>(item);
	if (e)
		emit clicked(e->index());
}
*/

UML::Element* EditorViewMViface::getItem(int uuid)
{
	return items[uuid];
}

void EditorViewMViface::reset()
{
	items.clear();
	scene->clearScene();

	// so that our diagram be nicer
	scene->removeItem(scene->addRect(QRect(-1000,-1000,2000,2000)));

	if ( model() )
		rowsInserted(rootIndex(), 0, model()->rowCount(rootIndex()) - 1 );
}

void EditorViewMViface::setRootIndex(const QModelIndex &index)
{
	QAbstractItemView::setRootIndex(index);
	reset();
}

void EditorViewMViface::rowsInserted ( const QModelIndex & parent, int start, int end )
{
//	qDebug() << "========== rowsInserted" << parent << start << end;

//	if ( parent != rootIndex() )
//		return;
	if ( parent == QModelIndex() || parent.parent() == QModelIndex() )
		return;

//	qDebug() << "rowsInserted: adding items" << parent;
	for (int row = start; row <= end; ++row) {
		QPersistentModelIndex current = model()->index(row, 0, parent);
		int uuid = model()->index(row, 0, parent).data(Unreal::IdRole).toInt();
		int type = model()->index(row, 0, parent).data(Unreal::TypeRole).toInt();

		int parent_uuid;
		if ( parent != rootIndex() )
				parent_uuid = parent.data(Unreal::IdRole).toInt();
		else
				parent_uuid = -1;

//		qDebug() << uuid << type;

		if ( ! uuid )
			continue;

		if ( UML::Element *e = UML::GUIObjectFactory(type) ) {
			scene->addItem(e);
			e->setIndex(current);
			e->setPos(current.data(Unreal::PositionRole).toPointF());

			if ( parent_uuid != -1 )
				e->setParentItem(items[parent_uuid]);

			items[uuid] = e;

			// TODO: workaround for #92 - connectToPort sets old configuration
			// to a model, so we can not get it in updateData later.
			// More accurate fix needed.
			e->updateData();

			e->connectToPort();
		}

		if ( model()->hasChildren(current) ) {
			rowsInserted( current, 0, model()->rowCount( current ) - 1 );
		}
	}

//	qDebug() << "rowsInserted: updating items";
	for (int row = start; row <= end; ++row) {
		int uuid = model()->index(row, 0, parent).data(Unreal::IdRole).toInt();
		if (items.contains(uuid))
			items[uuid]->updateData();
	}

	QAbstractItemView::rowsInserted(parent, start, end);
}

void EditorViewMViface::rowsAboutToBeRemoved ( const QModelIndex & parent, int start, int end )
{
	for (int row = start; row <= end; ++row) {
		int uuid = model()->index(row, 0, parent).data(Unreal::IdRole).toInt();

		scene->removeItem(items[uuid]);
		delete items[uuid];
		items.remove(uuid);
	}

	QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
}

void EditorViewMViface::dataChanged(const QModelIndex &topLeft,
		const QModelIndex &bottomRight)
{
	for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
		int uuid = topLeft.sibling(row, 0).data(Unreal::IdRole).toInt();

		if ( items.contains(uuid) )
			items[uuid]->updateData();
		else
			rowsInserted(topLeft.parent(),row,row);

	}
}

<|endoftext|>


<commit_before><commit_msg>Reverted r702, also adding a comment stating why we shouldn't do this.<commit_after><|endoftext|>