<commit_before>//
// vtTin3d.cpp
//
// Class which represents a Triangulated Irregular Network.
//
// Copyright (c) 2002-2003 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#include "vtlib/vtlib.h"
#include "vtTin3d.h"

vtTin3d::vtTin3d()
{
	m_pGeom = NULL;
	m_pDropGeom = NULL;
}

/**
 * Read the TIN from a file.  This can either be an old-style or new-style
 * .tin format (so far, a VTP-specific format)
 */
bool vtTin3d::Read(const char *fname)
{
	if (!vtTin::Read(fname))
		return false;

	Initialize(m_proj.GetUnits(), m_EarthExtents, m_fMinHeight, m_fMaxHeight);
	return true;
}

FPoint3 ComputeNormal(const FPoint3 &p1, const FPoint3 &p2, const FPoint3 &p3)
{
	FPoint3 d1 = (p2 - p1);
	FPoint3 d2 = (p3 - p1);
	FPoint3 cross = d1.Cross(d2);
	cross.Normalize();
	return cross;
}

#define MAX_CHUNK_VERTS	30000

vtGeom *vtTin3d::CreateGeometry(bool bDropShadowMesh)
{
	// set up materials
	vtMaterialArray *pMats = new vtMaterialArray();
	bool lighting = false;
	pMats->AddRGBMaterial1(RGBf(1, 1, 1), false, lighting, false);
	pMats->AddRGBMaterial1(RGBf(0.5f, 0.5f, 0.5f), false, false, false);
	pMats->AddRGBMaterial1(RGBf(0, 0, 0), false, false, false);

	m_pGeom = new vtGeom();
	m_pGeom->SetMaterials(pMats);
	pMats->Release();

	int i, j, k;
	int verts = NumVerts();

	// Break it up into a series of meshes - this is good for both
	// culling and memory management

	FPoint3 ep, wp;		// earth point, world point
	FPoint3 p[3], norm;
	FPoint3 light_dir(1, 1, 0);
	RGBf color;
	float r, g=1.0f, b=0.5f;

	// most TINs are larger in the horionztal dimension than the vertical, so
	// use horizontal extents as the basis of subdivision
	DRECT rect = m_EarthExtents;
	double sizex = rect.Width();
	double sizey = rect.Height();
	float height_range = (m_fMaxHeight - m_fMinHeight);

	// make it slightly larger avoid edge condition
	rect.left -= 0.000001;
	sizex += 0.000002;
	rect.bottom -= 0.000001;
	sizey += 0.000002;

	int divx, divy;		// number of x and y divisions
	int dsize;
	Bin *bins;
	int tris = NumTris();
	int bx, by;
	DPoint2 gp;

	int divs = 4;
	bool acceptable = false;
	while (!acceptable)
	{
		// take the smaller dimension and split in to ensure a minimum level
		// of subdivision, with the larger dimension proportional
		if (sizex < sizey)
		{
			divx = divs;
			divy = (int) (divx * sizey / sizex);
		}
		else
		{
			divy = divs;
			divx = (int) (divy * sizex / sizey);
		}

		// create a 2d array of Bins
		dsize = divx*divy;
		bins = new Bin[dsize];

		int most = -1, newsize;
		for (i = 0; i < tris; i++)
		{
			j = i * 3;
			gp = (m_vert[m_tri[j]] + m_vert[m_tri[j+1]] + m_vert[m_tri[j+2]]) / 3;
			bx = (int) (divx * (gp.x - rect.left) / sizex);
			by = (int) (divy * (gp.y - rect.bottom) / sizey);

			Bin &bref = bins[bx * divy + by];
			bref.Append(i);
			newsize = bref.GetSize();
			if (newsize > most)
				most = newsize;
		}
		if (most > 10000)
		{
			delete [] bins;
			divs = divs * 3 / 2;
		}
		else
			acceptable = true;
	}

	int in_bin, tri, vidx;

	for (i = 0; i < dsize; i++)
	{
		Bin &bref = bins[i];
		in_bin = bref.GetSize();
		if (!in_bin)
			continue;

		vtMesh *pMesh = new vtMesh(GL_TRIANGLES, VT_Normals|VT_Colors, in_bin * 3);

		for (j = 0; j < in_bin; j++)
		{
			tri = bref[j];
			int tribase = tri * 3;

			for (k = 0; k < 3; k++)
			{
				vidx = m_tri[tribase + k];
				ep.Set(m_vert[vidx].x, m_vert[vidx].y, m_z[vidx]);
				m_Conversion.ConvertFromEarth(ep, p[k]);
			}
			norm = ComputeNormal(p[0], p[1], p[2]);

			float shade = norm.Dot(light_dir);	// shading 0 (dark) to 1 (light)
			if (shade < 0)
				shade = -shade;

			int vert_base = pMesh->GetNumVertices();
			for (k = 0; k < 3; k++)
			{
				vidx = m_tri[tribase + k];

				r = (m_z[vidx] - m_fMinHeight) / height_range;
				int vert_index = pMesh->AddVertex(p[k]);
				pMesh->SetVtxNormal(vert_index, norm);

				color.Set(r, g, b);
				color *= shade;
				pMesh->SetVtxColor(vert_index, color);
			}
			pMesh->AddTri(vert_base, vert_base+1, vert_base+2);
		}
		m_pGeom->AddMesh(pMesh, 0);
		m_Meshes.Append(pMesh);
	}

	/*
	int base = 0;
	remaining = verts;
	while (remaining)
	{
		int chunk = remaining;
		if (chunk > MAX_CHUNK_VERTS)
			chunk = MAX_CHUNK_VERTS;
		int tris = chunk / 3;

		vtMesh *pMesh = new vtMesh(GL_TRIANGLES, VT_Normals|VT_Colors, chunk);

		for (i = 0; i < tris; i++)
		{
			for (j = 0; j < 3; j++)
				m_Conversion.ConvertFromEarth(m_vert[base + (i*3+j)], p[j]);
			norm = ComputeNormal(p[0], p[1], p[2]);

			float shade = norm.Dot(light_dir);	// shading 0 (dark) to 1 (light)

			for (j = 0; j < 3; j++)
			{
				r = (m_points[i*3+j].z - m_fMinHeight) / (m_fMaxHeight - m_fMinHeight);
				pMesh->AddVertex(p[j]);
				pMesh->SetVtxNormal(i*3+j, norm);

				color.Set(r, g, b);
				color *= shade;
				pMesh->SetVtxColor(i*3+j, color);
			}
		}
		m_pGeom->AddMesh(pMesh, 0);
		m_Meshes.Append(pMesh);

		if (bDropShadowMesh)
		{
			vtMesh *pShadowMesh = new vtMesh(GL_TRIANGLES, 0, chunk);
			for (i = 0; i < chunk; i++)
			{
				ep = m_points[base+i];
				ep.z = m_fMinHeight - 4.9;
				m_Conversion.ConvertFromEarth(ep, wp);
				pShadowMesh->AddVertex(wp);
			}
			m_pGeom->AddMesh(pShadowMesh, 2);
		}

		remaining -= chunk;
		base += chunk;
	}
		*/

	if (bDropShadowMesh)
	{
		vtMesh *pBaseMesh = new vtMesh(GL_TRIANGLE_FAN, 0, 4);

		ep.Set(m_EarthExtents.left - 10, m_EarthExtents.bottom - 10, m_fMinHeight - 5);
		m_Conversion.ConvertFromEarth(ep, wp);
		pBaseMesh->AddVertex(wp);

		ep.Set(m_EarthExtents.right + 10, m_EarthExtents.bottom - 10, m_fMinHeight - 5);
		m_Conversion.ConvertFromEarth(ep, wp);
		pBaseMesh->AddVertex(wp);

		ep.Set(m_EarthExtents.right + 10, m_EarthExtents.top + 10, m_fMinHeight - 5);
		m_Conversion.ConvertFromEarth(ep, wp);
		pBaseMesh->AddVertex(wp);

		ep.Set(m_EarthExtents.left - 10, m_EarthExtents.top + 10, m_fMinHeight - 5);
		m_Conversion.ConvertFromEarth(ep, wp);
		pBaseMesh->AddVertex(wp);

		pBaseMesh->AddFan(0, 1, 2, 3);
		m_pGeom->AddMesh(pBaseMesh, 1);
	}
	return m_pGeom;
}

/**
 * Returns true if the point was over the TIN, false otherwise.
 */
bool vtTin3d::FindAltitudeAtPoint(const FPoint3 &input, float &fAltitude,
								  FPoint3 *vNormal) const
{
	// First try to identify which triangle
	FPoint2 p(input.x, input.z);

	FPoint3 wp1, wp2, wp3;
	FPoint2 p1, p2, p3;
	bool good;

	for (unsigned int m = 0; m < m_Meshes.GetSize(); m++)
	{
		vtMesh *mesh = m_Meshes[m];
		int tris = mesh->GetNumVertices() / 3;
		for (int i = 0; i < tris; i++)
		{
			// get world points
			wp1 = mesh->GetVtxPos(i*3+0);
			wp2 = mesh->GetVtxPos(i*3+1);
			wp3 = mesh->GetVtxPos(i*3+2);
			// convert to 2d
			p1.Set(wp1.x, wp1.z);
			p2.Set(wp2.x, wp2.z);
			p3.Set(wp3.x, wp3.z);

			if (!PointInTriangle(p, p1, p2, p3))
				continue;

			// compute barycentric coordinates with respect to the triangle
			float bary[3], val;
			good = BarycentricCoords(p1, p2, p3, p, bary);
			if (!good)
				continue;

			// compute barycentric combination of function values at vertices
			val = bary[0]*wp1.y +
				  bary[1]*wp2.y +
				  bary[2]*wp3.y;
			fAltitude = val;
			return true;
		}
	}
	return false;
}

/*
 * Algorithm from 'Fast, Minimum Storage Ray-Triangle Intersection',
 * Thomas Mller and Ben Trumbore, 1997
 * Adapted to use C++ and the vtdata math classes.
 */
#define EPSILON 0.000001
bool intersect_triangle(const FPoint3 &orig, const FPoint3 &dir,
			const FPoint3 &vert0, const FPoint3 &vert1, const FPoint3 &vert2,
			float &t, float &u, float &v)
{
	FPoint3 edge1, edge2, tvec, pvec, qvec;
	float det,inv_det;

	/* find vectors for two edges sharing vert0 */
	edge1 = vert1 - vert0;
	edge2 = vert2 - vert0;

	/* begin calculating determinant - also used to calculate U parameter */
	pvec = dir.Cross(edge2);

	/* if determinant is near zero, ray lies in plane of triangle */
	det = edge1.Dot(pvec);

	if (det < EPSILON)
		return false;

	/* calculate distance from vert0 to ray origin */
	tvec = orig - vert0;

	/* calculate U parameter and test bounds */
	u = tvec.Dot(pvec);
	if (u < 0.0 || u > det)
		return false;

	/* prepare to test V parameter */
	qvec = tvec.Cross(edge1);

	 /* calculate V parameter and test bounds */
	v = dir.Dot(qvec);
	if (v < 0.0 || u + v > det)
		return false;

	/* calculate t, scale parameters, ray intersects triangle */
	t = edge2.Dot(qvec);
	inv_det = 1.0 / det;
	t *= inv_det;
	u *= inv_det;
	v *= inv_det;

	return true;
}

bool vtTin3d::CastRayToSurface(const FPoint3 &point, const FPoint3 &dir,
							   FPoint3 &result) const
{
	FPoint3 wp1, wp2, wp3;
	float t, u, v, closest = 1E9;
	int i;

	for (unsigned int m = 0; m < m_Meshes.GetSize(); m++)
	{
		vtMesh *mesh = m_Meshes[m];
		int tris = mesh->GetNumVertices() / 3;
		for (i = 0; i < tris; i++)
		{
			// get world points
			wp1 = mesh->GetVtxPos(i*3+0);
			wp2 = mesh->GetVtxPos(i*3+1);
			wp3 = mesh->GetVtxPos(i*3+2);
			if (intersect_triangle(point, dir, wp1, wp2, wp3, t, u, v))
			{
				if (t < closest)
					closest = t;
			}
		}
	}
	if (closest == 1E9)
		return false;

	result = point + (dir * closest);
	return true;
}

FPoint3 vtTin3d::FindVectorToClosestVertex(const FPoint3 &pos)
{
	FPoint3 vert, diff, closest_diff;
	float len, minlen = 1E9;

	for (unsigned int m = 0; m < m_Meshes.GetSize(); m++)
	{
		vtMesh *mesh = m_Meshes[m];
		int points = mesh->GetNumVertices();
		for (int i = 0; i < points; i++)
		{
			vert = mesh->GetVtxPos(i);
			diff = vert - pos;
			len = diff.Length();
			if (len < minlen)
			{
				minlen = len;
				closest_diff = diff;
			}
		}
	}
	return closest_diff;
}

<commit_msg>use RaySphereIntersection to speed up raycasting<commit_after>//
// vtTin3d.cpp
//
// Class which represents a Triangulated Irregular Network.
//
// Copyright (c) 2002-2004 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#include "vtlib/vtlib.h"
#include "vtTin3d.h"

vtTin3d::vtTin3d()
{
	m_pGeom = NULL;
	m_pDropGeom = NULL;
}

/**
 * Read the TIN from a file.  This can either be an old-style or new-style
 * .tin format (so far, a VTP-specific format)
 */
bool vtTin3d::Read(const char *fname)
{
	if (!vtTin::Read(fname))
		return false;

	Initialize(m_proj.GetUnits(), m_EarthExtents, m_fMinHeight, m_fMaxHeight);
	return true;
}

FPoint3 ComputeNormal(const FPoint3 &p1, const FPoint3 &p2, const FPoint3 &p3)
{
	FPoint3 d1 = (p2 - p1);
	FPoint3 d2 = (p3 - p1);
	FPoint3 cross = d1.Cross(d2);
	cross.Normalize();
	return cross;
}

#define MAX_CHUNK_VERTS	30000

vtGeom *vtTin3d::CreateGeometry(bool bDropShadowMesh)
{
	// set up materials
	vtMaterialArray *pMats = new vtMaterialArray();
	bool lighting = false;
	pMats->AddRGBMaterial1(RGBf(1, 1, 1), false, lighting, false);
	pMats->AddRGBMaterial1(RGBf(0.5f, 0.5f, 0.5f), false, false, false);
	pMats->AddRGBMaterial1(RGBf(0, 0, 0), false, false, false);

	m_pGeom = new vtGeom();
	m_pGeom->SetMaterials(pMats);
	pMats->Release();

	int i, j, k;
	int verts = NumVerts();

	// Break it up into a series of meshes - this is good for both
	// culling and memory management

	FPoint3 ep, wp;		// earth point, world point
	FPoint3 p[3], norm;
	FPoint3 light_dir(1, 1, 0);
	RGBf color;
	float r, g=1.0f, b=0.5f;

	// most TINs are larger in the horionztal dimension than the vertical, so
	// use horizontal extents as the basis of subdivision
	DRECT rect = m_EarthExtents;
	double sizex = rect.Width();
	double sizey = rect.Height();
	float height_range = (m_fMaxHeight - m_fMinHeight);

	// make it slightly larger avoid edge condition
	rect.left -= 0.000001;
	sizex += 0.000002;
	rect.bottom -= 0.000001;
	sizey += 0.000002;

	int divx, divy;		// number of x and y divisions
	int dsize;
	Bin *bins;
	int tris = NumTris();
	int bx, by;
	DPoint2 gp;

	int divs = 4;
	bool acceptable = false;
	while (!acceptable)
	{
		// take the smaller dimension and split in to ensure a minimum level
		// of subdivision, with the larger dimension proportional
		if (sizex < sizey)
		{
			divx = divs;
			divy = (int) (divx * sizey / sizex);
		}
		else
		{
			divy = divs;
			divx = (int) (divy * sizex / sizey);
		}

		// create a 2d array of Bins
		dsize = divx*divy;
		bins = new Bin[dsize];

		int most = -1, newsize;
		for (i = 0; i < tris; i++)
		{
			j = i * 3;
			gp = (m_vert[m_tri[j]] + m_vert[m_tri[j+1]] + m_vert[m_tri[j+2]]) / 3;
			bx = (int) (divx * (gp.x - rect.left) / sizex);
			by = (int) (divy * (gp.y - rect.bottom) / sizey);

			Bin &bref = bins[bx * divy + by];
			bref.Append(i);
			newsize = bref.GetSize();
			if (newsize > most)
				most = newsize;
		}
		if (most > 10000)
		{
			delete [] bins;
			divs = divs * 3 / 2;
		}
		else
			acceptable = true;
	}

	int in_bin, tri, vidx;

	for (i = 0; i < dsize; i++)
	{
		Bin &bref = bins[i];
		in_bin = bref.GetSize();
		if (!in_bin)
			continue;

		vtMesh *pMesh = new vtMesh(GL_TRIANGLES, VT_Normals|VT_Colors, in_bin * 3);

		for (j = 0; j < in_bin; j++)
		{
			tri = bref[j];
			int tribase = tri * 3;

			for (k = 0; k < 3; k++)
			{
				vidx = m_tri[tribase + k];
				ep.Set(m_vert[vidx].x, m_vert[vidx].y, m_z[vidx]);
				m_Conversion.ConvertFromEarth(ep, p[k]);
			}
			norm = ComputeNormal(p[0], p[1], p[2]);

			float shade = norm.Dot(light_dir);	// shading 0 (dark) to 1 (light)
			if (shade < 0)
				shade = -shade;

			int vert_base = pMesh->GetNumVertices();
			for (k = 0; k < 3; k++)
			{
				vidx = m_tri[tribase + k];

				r = (m_z[vidx] - m_fMinHeight) / height_range;
				int vert_index = pMesh->AddVertex(p[k]);
				pMesh->SetVtxNormal(vert_index, norm);

				color.Set(r, g, b);
				color *= shade;
				pMesh->SetVtxColor(vert_index, color);
			}
			pMesh->AddTri(vert_base, vert_base+1, vert_base+2);
		}
		m_pGeom->AddMesh(pMesh, 0);
		m_Meshes.Append(pMesh);
	}

	/*
	int base = 0;
	remaining = verts;
	while (remaining)
	{
		int chunk = remaining;
		if (chunk > MAX_CHUNK_VERTS)
			chunk = MAX_CHUNK_VERTS;
		int tris = chunk / 3;

		vtMesh *pMesh = new vtMesh(GL_TRIANGLES, VT_Normals|VT_Colors, chunk);

		for (i = 0; i < tris; i++)
		{
			for (j = 0; j < 3; j++)
				m_Conversion.ConvertFromEarth(m_vert[base + (i*3+j)], p[j]);
			norm = ComputeNormal(p[0], p[1], p[2]);

			float shade = norm.Dot(light_dir);	// shading 0 (dark) to 1 (light)

			for (j = 0; j < 3; j++)
			{
				r = (m_points[i*3+j].z - m_fMinHeight) / (m_fMaxHeight - m_fMinHeight);
				pMesh->AddVertex(p[j]);
				pMesh->SetVtxNormal(i*3+j, norm);

				color.Set(r, g, b);
				color *= shade;
				pMesh->SetVtxColor(i*3+j, color);
			}
		}
		m_pGeom->AddMesh(pMesh, 0);
		m_Meshes.Append(pMesh);

		if (bDropShadowMesh)
		{
			vtMesh *pShadowMesh = new vtMesh(GL_TRIANGLES, 0, chunk);
			for (i = 0; i < chunk; i++)
			{
				ep = m_points[base+i];
				ep.z = m_fMinHeight - 4.9;
				m_Conversion.ConvertFromEarth(ep, wp);
				pShadowMesh->AddVertex(wp);
			}
			m_pGeom->AddMesh(pShadowMesh, 2);
		}

		remaining -= chunk;
		base += chunk;
	}
		*/

	if (bDropShadowMesh)
	{
		vtMesh *pBaseMesh = new vtMesh(GL_TRIANGLE_FAN, 0, 4);

		ep.Set(m_EarthExtents.left - 10, m_EarthExtents.bottom - 10, m_fMinHeight - 5);
		m_Conversion.ConvertFromEarth(ep, wp);
		pBaseMesh->AddVertex(wp);

		ep.Set(m_EarthExtents.right + 10, m_EarthExtents.bottom - 10, m_fMinHeight - 5);
		m_Conversion.ConvertFromEarth(ep, wp);
		pBaseMesh->AddVertex(wp);

		ep.Set(m_EarthExtents.right + 10, m_EarthExtents.top + 10, m_fMinHeight - 5);
		m_Conversion.ConvertFromEarth(ep, wp);
		pBaseMesh->AddVertex(wp);

		ep.Set(m_EarthExtents.left - 10, m_EarthExtents.top + 10, m_fMinHeight - 5);
		m_Conversion.ConvertFromEarth(ep, wp);
		pBaseMesh->AddVertex(wp);

		pBaseMesh->AddFan(0, 1, 2, 3);
		m_pGeom->AddMesh(pBaseMesh, 1);
	}
	return m_pGeom;
}

/**
 * Returns true if the point was over the TIN, false otherwise.
 */
bool vtTin3d::FindAltitudeAtPoint(const FPoint3 &input, float &fAltitude,
								  FPoint3 *vNormal) const
{
	// First try to identify which triangle
	FPoint2 p(input.x, input.z);

	FPoint3 wp1, wp2, wp3;
	FPoint2 p1, p2, p3;
	bool good;

	for (unsigned int m = 0; m < m_Meshes.GetSize(); m++)
	{
		vtMesh *mesh = m_Meshes[m];
		int tris = mesh->GetNumVertices() / 3;
		for (int i = 0; i < tris; i++)
		{
			// get world points
			wp1 = mesh->GetVtxPos(i*3+0);
			wp2 = mesh->GetVtxPos(i*3+1);
			wp3 = mesh->GetVtxPos(i*3+2);
			// convert to 2d
			p1.Set(wp1.x, wp1.z);
			p2.Set(wp2.x, wp2.z);
			p3.Set(wp3.x, wp3.z);

			if (!PointInTriangle(p, p1, p2, p3))
				continue;

			// compute barycentric coordinates with respect to the triangle
			float bary[3], val;
			good = BarycentricCoords(p1, p2, p3, p, bary);
			if (!good)
				continue;

			// compute barycentric combination of function values at vertices
			val = bary[0]*wp1.y +
				  bary[1]*wp2.y +
				  bary[2]*wp3.y;
			fAltitude = val;
			return true;
		}
	}
	return false;
}

/*
 * Algorithm from 'Fast, Minimum Storage Ray-Triangle Intersection',
 * Thomas Mller and Ben Trumbore, 1997
 * Adapted to use C++ and the vtdata math classes.
 */
#define EPSILON 0.000001
bool intersect_triangle(const FPoint3 &orig, const FPoint3 &dir,
			const FPoint3 &vert0, const FPoint3 &vert1, const FPoint3 &vert2,
			float &t, float &u, float &v)
{
	FPoint3 edge1, edge2, tvec, pvec, qvec;
	float det,inv_det;

	/* find vectors for two edges sharing vert0 */
	edge1 = vert1 - vert0;
	edge2 = vert2 - vert0;

	/* begin calculating determinant - also used to calculate U parameter */
	pvec = dir.Cross(edge2);

	/* if determinant is near zero, ray lies in plane of triangle */
	det = edge1.Dot(pvec);

	if (det < EPSILON)
		return false;

	/* calculate distance from vert0 to ray origin */
	tvec = orig - vert0;

	/* calculate U parameter and test bounds */
	u = tvec.Dot(pvec);
	if (u < 0.0 || u > det)
		return false;

	/* prepare to test V parameter */
	qvec = tvec.Cross(edge1);

	 /* calculate V parameter and test bounds */
	v = dir.Dot(qvec);
	if (v < 0.0 || u + v > det)
		return false;

	/* calculate t, scale parameters, ray intersects triangle */
	t = edge2.Dot(qvec);
	inv_det = 1.0 / det;
	t *= inv_det;
	u *= inv_det;
	v *= inv_det;

	return true;
}

bool vtTin3d::CastRayToSurface(const FPoint3 &point, const FPoint3 &dir,
							   FPoint3 &result) const
{
	FPoint3 wp1, wp2, wp3;
	float t, u, v, closest = 1E9;
	int i;

	for (unsigned int m = 0; m < m_Meshes.GetSize(); m++)
	{
		vtMesh *mesh = m_Meshes[m];

		FBox3 box;
		mesh->GetBoundBox(box);
		FSphere sph(box);

		int iQuantity;
		FPoint3 interpoints[2];
		if (!RaySphereIntersection(point, dir, sph, iQuantity, interpoints))
		{
			continue;
		}

		int tris = mesh->GetNumVertices() / 3;
		for (i = 0; i < tris; i++)
		{
			// get world points
			wp1 = mesh->GetVtxPos(i*3+0);
			wp2 = mesh->GetVtxPos(i*3+1);
			wp3 = mesh->GetVtxPos(i*3+2);
			if (intersect_triangle(point, dir, wp1, wp2, wp3, t, u, v))
			{
				if (t < closest)
					closest = t;
			}
		}
	}
	if (closest == 1E9)
		return false;

	result = point + (dir * closest);
	return true;
}

FPoint3 vtTin3d::FindVectorToClosestVertex(const FPoint3 &pos)
{
	FPoint3 vert, diff, closest_diff;
	float len, minlen = 1E9;

	for (unsigned int m = 0; m < m_Meshes.GetSize(); m++)
	{
		vtMesh *mesh = m_Meshes[m];
		int points = mesh->GetNumVertices();
		for (int i = 0; i < points; i++)
		{
			vert = mesh->GetVtxPos(i);
			diff = vert - pos;
			len = diff.Length();
			if (len < minlen)
			{
				minlen = len;
				closest_diff = diff;
			}
		}
	}
	return closest_diff;
}

<|endoftext|>


<commit_before>#include <nan.h>
#include <stdint.h>
#if defined(__APPLE__)
#include <fcntl.h>
#include <sys/disk.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#elif defined(_WIN32)
#include <io.h>
#include <malloc.h>
#include <winioctl.h>
#else
#include <errno.h>
#include <linux/fs.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#endif

void free_aligned_buffer(char *buffer, void *hint) {
#if defined(_WIN32)
  _aligned_free(buffer);
#else
  free(buffer);
#endif
}

class GetBlockDeviceWorker : public Nan::AsyncWorker {
 public:
  GetBlockDeviceWorker(
    const int fd,
    Nan::Callback *callback
  ) : Nan::AsyncWorker(callback),
      fd(fd) {}

  ~GetBlockDeviceWorker() {}

  void Execute() {
#if !defined(_WIN32)
    // WIN32: We know of no way to assert that a handle is a block device.
    struct stat st;
    if (fstat(fd, &st) == -1) {
      return SetErrorMessage("fstat failed");
    }
    if ((st.st_mode & S_IFMT) != S_IFBLK) {
      return SetErrorMessage("fd is not a block device");
    }
#endif
#if defined(__APPLE__)
    if (ioctl(fd, DKIOCGETBLOCKSIZE, &logical_sector_size) == -1) {
      return SetErrorMessage("DKIOCGETBLOCKSIZE failed");
    }
    if (ioctl(fd, DKIOCGETPHYSICALBLOCKSIZE, &physical_sector_size) == -1) {
      return SetErrorMessage("DKIOCGETPHYSICALBLOCKSIZE failed");
    }
    uint64_t logical_sectors = 0;
    if (ioctl(fd, DKIOCGETBLOCKCOUNT, &logical_sectors) == -1) {
      return SetErrorMessage("DKIOCGETBLOCKCOUNT failed");
    }
    size = logical_sector_size * logical_sectors;
#elif defined(_WIN32)
    HANDLE handle = uv_get_osfhandle(fd);
    if (handle == INVALID_HANDLE_VALUE) {
      return SetErrorMessage("EBADF: bad file descriptor");
    }
    STORAGE_PROPERTY_QUERY query;
    STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR alignment;
    DISK_GEOMETRY_EX geometry;
    DWORD bytes;
    ZeroMemory(&query, sizeof(query));
    ZeroMemory(&alignment, sizeof(alignment));
    ZeroMemory(&geometry, sizeof(geometry));
    query.QueryType = PropertyStandardQuery;
    query.PropertyId = StorageAccessAlignmentProperty;
    if (
      DeviceIoControl(
        handle,
        IOCTL_STORAGE_QUERY_PROPERTY,
        &query,
        sizeof(query),
        &alignment,
        sizeof(alignment),
        &bytes,
        NULL
      )
    ) {
      logical_sector_size = (uint32_t) alignment.BytesPerLogicalSector;
      physical_sector_size = (uint32_t) alignment.BytesPerPhysicalSector;
    } else {
      return SetErrorMessage("IOCTL_STORAGE_QUERY_PROPERTY failed");
    }
    if (
      DeviceIoControl(
        handle,
        IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
        NULL,
        0,
        &geometry,
        sizeof(geometry),
        &bytes,
        NULL
      )
    ) {
      size = static_cast<uint64_t>(geometry.DiskSize.QuadPart);
    } else {
      return SetErrorMessage("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX failed");
    }
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel_)
    // See: pipermail/freebsd-hackers/2016-September/049974.html
    if (ioctl(fd, DIOCGSECTORSIZE, &logical_sector_size) == -1) {
      return SetErrorMessage("DIOCGSECTORSIZE failed");
    }
    if (ioctl(fd, DIOCGSTRIPESIZE, &physical_sector_size) == -1) {
      return SetErrorMessage("DIOCGSTRIPESIZE failed");
    }
    if (ioctl(fd, DIOCGMEDIASIZE, &size) == -1) {
      return SetErrorMessage("DIOCGMEDIASIZE failed");
    }
#else
    if (ioctl(fd, BLKSSZGET, &logical_sector_size) == -1) {
      return SetErrorMessage("BLKSSZGET failed");
    }
    if (ioctl(fd, BLKPSZGET, &physical_sector_size) == -1) {
      return SetErrorMessage("BLKPSZGET failed");
    }
    if (ioctl(fd, BLKGETSIZE64, &size) == -1) {
      return SetErrorMessage("BLKGETSIZE64 failed");
    }
#endif
  }

  void HandleOKCallback () {
    Nan::HandleScope scope;
    v8::Local<v8::Object> device = Nan::New<v8::Object>();
    Nan::Set(
      device,
      Nan::New<v8::String>("logicalSectorSize").ToLocalChecked(),
      Nan::New<v8::Number>(logical_sector_size)
    );
    Nan::Set(
      device,
      Nan::New<v8::String>("physicalSectorSize").ToLocalChecked(),
      Nan::New<v8::Number>(physical_sector_size)
    );
    Nan::Set(
      device,
      Nan::New<v8::String>("size").ToLocalChecked(),
      Nan::New<v8::Number>(static_cast<double>(size))
    );
    v8::Local<v8::Value> argv[] = {
      Nan::Undefined(),
      device
    };
    callback->Call(2, argv);
  }

 private:
  const int fd;
  uint32_t logical_sector_size;
  uint32_t physical_sector_size;
  uint64_t size;
};

#if defined(__APPLE__)
class SetF_NOCACHEWorker : public Nan::AsyncWorker {
 public:
  SetF_NOCACHEWorker(
    const int fd,
    const int value,
    Nan::Callback *callback
  ) : Nan::AsyncWorker(callback),
      fd(fd),
      value(value) {}

  ~SetF_NOCACHEWorker() {}

  void Execute() {
    if (fcntl(fd, F_NOCACHE, value) != 0) {
      if (errno == EBADF) {
        SetErrorMessage("EBADF: bad file descriptor, fcntl");
      } else {
        SetErrorMessage("unexpected error, fcntl");
      }
    };
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;
    v8::Local<v8::Value> argv[] = {
      Nan::Undefined()
    };
    callback->Call(1, argv);
  }

 private:
  const int fd;
  const int value;
};
#endif

#if defined(_WIN32)
class SetFSCTL_LOCK_VOLUMEWorker : public Nan::AsyncWorker {
 public:
  SetFSCTL_LOCK_VOLUMEWorker(
    const int fd,
    const int value,
    Nan::Callback *callback
  ) : Nan::AsyncWorker(callback),
      fd(fd),
      value(value) {}

  ~SetFSCTL_LOCK_VOLUMEWorker() {}

  void Execute() {
    HANDLE handle = uv_get_osfhandle(fd);
    if (handle == INVALID_HANDLE_VALUE) {
      return SetErrorMessage("EBADF: bad file descriptor");
    }
    DWORD bytes;
    if (
      !DeviceIoControl(
        handle,
        value ? FSCTL_LOCK_VOLUME : FSCTL_UNLOCK_VOLUME,
        NULL,
        0,
        NULL,
        0,
        &bytes,
        NULL
      )
    ) {
      if (value) {
        return SetErrorMessage("FSCTL_LOCK_VOLUME failed");
      } else {
        return SetErrorMessage("FSCTL_UNLOCK_VOLUME failed");
      }
    }
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;
    v8::Local<v8::Value> argv[] = {
      Nan::Undefined()
    };
    callback->Call(1, argv);
  }

 private:
  const int fd;
  const int value;
};
#endif

NAN_METHOD(getAlignedBuffer) {
  if (
    info.Length() != 2 ||
    !info[0]->IsUint32() ||
    !info[1]->IsUint32()
  ) {
    return Nan::ThrowError(
      "bad arguments, expected: (uint32 size, uint32 alignment)"
    );
  }
  const size_t size = info[0]->Uint32Value();
  const size_t alignment = info[1]->Uint32Value();
  if (size == 0) return Nan::ThrowError("size must not be 0");
  if (alignment == 0) return Nan::ThrowError("alignment must not be 0");
  if (alignment & (alignment - 1)) {
    return Nan::ThrowError("alignment must be a power of 2");
  }
  if (alignment % sizeof(void *)) {
    return Nan::ThrowError("alignment must be a multiple of pointer size");
  }
#if defined(_WIN32)
  void *ptr = _aligned_malloc(size, alignment);
  if (ptr == NULL) return Nan::ThrowError("insufficient memory");
#else
  void *ptr;
  int result = posix_memalign(&ptr, alignment, size);
  if (result == EINVAL) return Nan::ThrowError("bad alignment argument");
  if (result == ENOMEM) return Nan::ThrowError("insufficient memory");
  if (result != 0) return Nan::ThrowError("unexpected error, posix_memalign");
#endif
  info.GetReturnValue().Set(
    Nan::NewBuffer(
      (char *) ptr,
      size,
      free_aligned_buffer,
      NULL
    ).ToLocalChecked()
  );
}

NAN_METHOD(getBlockDevice) {
  if (
    info.Length() != 2 ||
    !info[0]->IsUint32() ||
    !info[1]->IsFunction()
  ) {
    return Nan::ThrowError(
      "bad arguments, expected: (uint32 fd, function callback)"
    );
  }
  const int fd = info[0]->Uint32Value();
  Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
  Nan::AsyncQueueWorker(new GetBlockDeviceWorker(fd, callback));
}

NAN_METHOD(setF_NOCACHE) {
#if defined(__APPLE__)
  if (
    info.Length() != 3 ||
    !info[0]->IsUint32() ||
    !info[1]->IsUint32() ||
    !info[2]->IsFunction()
  ) {
    return Nan::ThrowError(
      "bad arguments, expected: (uint32 fd, uint32 value, function callback)"
    );
  }
  const int fd = info[0]->Uint32Value();
  const int value = info[1]->Uint32Value();
  if (value != 0 && value != 1) return Nan::ThrowError("value must be 0 or 1");
  Nan::Callback *callback = new Nan::Callback(info[2].As<v8::Function>());
  Nan::AsyncQueueWorker(new SetF_NOCACHEWorker(fd, value, callback));
#else
  return Nan::ThrowError("only supported on mac os");
#endif
}

NAN_METHOD(setFSCTL_LOCK_VOLUME) {
#if defined(_WIN32)
  if (
    info.Length() != 3 ||
    !info[0]->IsUint32() ||
    !info[1]->IsUint32() ||
    !info[2]->IsFunction()
  ) {
    return Nan::ThrowError(
      "bad arguments, expected: (uint32 fd, uint32 value, function callback)"
    );
  }
  const int fd = info[0]->Uint32Value();
  const int value = info[1]->Uint32Value();
  if (value != 0 && value != 1) return Nan::ThrowError("value must be 0 or 1");
  Nan::Callback *callback = new Nan::Callback(info[2].As<v8::Function>());
  Nan::AsyncQueueWorker(new SetFSCTL_LOCK_VOLUMEWorker(fd, value, callback));
#else
  return Nan::ThrowError("only supported on windows");
#endif
}

NAN_MODULE_INIT(Init) {
#if defined(O_DIRECT)
  NODE_DEFINE_CONSTANT(target, O_DIRECT);
#endif
#if defined(O_DSYNC)
  NODE_DEFINE_CONSTANT(target, O_DSYNC);
#endif
#if defined(O_EXCL)
  NODE_DEFINE_CONSTANT(target, O_EXCL);
#endif
#if defined(O_EXLOCK)
  NODE_DEFINE_CONSTANT(target, O_EXLOCK);
#endif
#if defined(O_SYNC)
  NODE_DEFINE_CONSTANT(target, O_SYNC);
#endif
  NAN_EXPORT(target, getAlignedBuffer);
  NAN_EXPORT(target, getBlockDevice);
  NAN_EXPORT(target, setF_NOCACHE);
  NAN_EXPORT(target, setFSCTL_LOCK_VOLUME);
}

NODE_MODULE(binding, Init)

// S.D.G.
<commit_msg>fix unix types to match the various kernel headers<commit_after>#include <nan.h>
#include <stdint.h>
#if defined(__APPLE__)
#include <fcntl.h>
#include <sys/disk.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#elif defined(_WIN32)
#include <io.h>
#include <malloc.h>
#include <winioctl.h>
#else
#include <errno.h>
#include <linux/fs.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#endif

void free_aligned_buffer(char *buffer, void *hint) {
#if defined(_WIN32)
  _aligned_free(buffer);
#else
  free(buffer);
#endif
}

class GetBlockDeviceWorker : public Nan::AsyncWorker {
 public:
  GetBlockDeviceWorker(
    const int fd,
    Nan::Callback *callback
  ) : Nan::AsyncWorker(callback),
      fd(fd) {}

  ~GetBlockDeviceWorker() {}

  void Execute() {
#if !defined(_WIN32)
    // WIN32: We know of no way to assert that a handle is a block device.
    struct stat st;
    if (fstat(fd, &st) == -1) {
      return SetErrorMessage("fstat failed");
    }
    if ((st.st_mode & S_IFMT) != S_IFBLK) {
      return SetErrorMessage("fd is not a block device");
    }
#endif
#if defined(__APPLE__)
    // https://opensource.apple.com/source/xnu/xnu-1699.26.8/bsd/sys/disk.h
    uint32_t _logical_sector_size;
    uint32_t _physical_sector_size;
    uint64_t _logical_sectors;
    if (ioctl(fd, DKIOCGETBLOCKSIZE, &_logical_sector_size) == -1) {
      return SetErrorMessage("DKIOCGETBLOCKSIZE failed");
    }
    if (ioctl(fd, DKIOCGETPHYSICALBLOCKSIZE, &_physical_sector_size) == -1) {
      return SetErrorMessage("DKIOCGETPHYSICALBLOCKSIZE failed");
    }
    if (ioctl(fd, DKIOCGETBLOCKCOUNT, &_logical_sectors) == -1) {
      return SetErrorMessage("DKIOCGETBLOCKCOUNT failed");
    }
    logical_sector_size = (uint64_t) _logical_sector_size;
    physical_sector_size = (uint64_t) _physical_sector_size;
    size = logical_sector_size * _logical_sectors;
#elif defined(_WIN32)
    HANDLE handle = uv_get_osfhandle(fd);
    if (handle == INVALID_HANDLE_VALUE) {
      return SetErrorMessage("EBADF: bad file descriptor");
    }
    STORAGE_PROPERTY_QUERY query;
    STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR alignment;
    DISK_GEOMETRY_EX geometry;
    DWORD bytes;
    ZeroMemory(&query, sizeof(query));
    ZeroMemory(&alignment, sizeof(alignment));
    ZeroMemory(&geometry, sizeof(geometry));
    query.QueryType = PropertyStandardQuery;
    query.PropertyId = StorageAccessAlignmentProperty;
    if (
      DeviceIoControl(
        handle,
        IOCTL_STORAGE_QUERY_PROPERTY,
        &query,
        sizeof(query),
        &alignment,
        sizeof(alignment),
        &bytes,
        NULL
      )
    ) {
      logical_sector_size = (uint64_t) alignment.BytesPerLogicalSector;
      physical_sector_size = (uint64_t) alignment.BytesPerPhysicalSector;
    } else {
      return SetErrorMessage("IOCTL_STORAGE_QUERY_PROPERTY failed");
    }
    if (
      DeviceIoControl(
        handle,
        IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
        NULL,
        0,
        &geometry,
        sizeof(geometry),
        &bytes,
        NULL
      )
    ) {
      size = static_cast<uint64_t>(geometry.DiskSize.QuadPart);
    } else {
      return SetErrorMessage("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX failed");
    }
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel_)
    // https://github.com/freebsd/freebsd/blob/master/sys/sys/disk.h
    unsigned int _logical_sector_size;
    off_t _physical_sector_size;
    off_t _size;
    if (ioctl(fd, DIOCGSECTORSIZE, &_logical_sector_size) == -1) {
      return SetErrorMessage("DIOCGSECTORSIZE failed");
    }
    if (ioctl(fd, DIOCGSTRIPESIZE, &_physical_sector_size) == -1) {
      return SetErrorMessage("DIOCGSTRIPESIZE failed");
    }
    if (ioctl(fd, DIOCGMEDIASIZE, &_size) == -1) {
      return SetErrorMessage("DIOCGMEDIASIZE failed");
    }
    logical_sector_size = (uint64_t) _logical_sector_size;
    physical_sector_size = (uint64_t) _physical_sector_size;
    size = (uint64_t) _size;
#else
    // https://github.com/torvalds/linux/blob/master/block/ioctl.c
    // We must use the correct type according to the control code passed:
    // https://stackoverflow.com/questions/19747663/where-are-ioctl-
    // parameters-such-as-0x1268-blksszget-actually-specified
    int _logical_sector_size;
    unsigned int _physical_sector_size;
    if (ioctl(fd, BLKSSZGET, &_logical_sector_size) == -1) {
      return SetErrorMessage("BLKSSZGET failed");
    }
    if (ioctl(fd, BLKPBSZGET, &_physical_sector_size) == -1) {
      return SetErrorMessage("BLKPBSZGET failed");
    }
    // The kernel expects size to be a u64 and defines u64 as uint64_t:
    // https://github.com/torvalds/linux/blob/master/tools/include/linux/types.h
    if (ioctl(fd, BLKGETSIZE64, &size) == -1) {
      return SetErrorMessage("BLKGETSIZE64 failed");
    }
    logical_sector_size = (uint64_t) _logical_sector_size;
    physical_sector_size = (uint64_t) _physical_sector_size;
#endif
  }

  void HandleOKCallback () {
    Nan::HandleScope scope;
    v8::Local<v8::Object> device = Nan::New<v8::Object>();
    Nan::Set(
      device,
      Nan::New<v8::String>("logicalSectorSize").ToLocalChecked(),
      Nan::New<v8::Number>(static_cast<double>(logical_sector_size))
    );
    Nan::Set(
      device,
      Nan::New<v8::String>("physicalSectorSize").ToLocalChecked(),
      Nan::New<v8::Number>(static_cast<double>(physical_sector_size))
    );
    Nan::Set(
      device,
      Nan::New<v8::String>("size").ToLocalChecked(),
      Nan::New<v8::Number>(static_cast<double>(size))
    );
    v8::Local<v8::Value> argv[] = {
      Nan::Undefined(),
      device
    };
    callback->Call(2, argv);
  }

 private:
  const int fd;
  uint64_t logical_sector_size;
  uint64_t physical_sector_size;
  uint64_t size;
};

#if defined(__APPLE__)
class SetF_NOCACHEWorker : public Nan::AsyncWorker {
 public:
  SetF_NOCACHEWorker(
    const int fd,
    const int value,
    Nan::Callback *callback
  ) : Nan::AsyncWorker(callback),
      fd(fd),
      value(value) {}

  ~SetF_NOCACHEWorker() {}

  void Execute() {
    if (fcntl(fd, F_NOCACHE, value) != 0) {
      if (errno == EBADF) {
        SetErrorMessage("EBADF: bad file descriptor, fcntl");
      } else {
        SetErrorMessage("unexpected error, fcntl");
      }
    };
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;
    v8::Local<v8::Value> argv[] = {
      Nan::Undefined()
    };
    callback->Call(1, argv);
  }

 private:
  const int fd;
  const int value;
};
#endif

#if defined(_WIN32)
class SetFSCTL_LOCK_VOLUMEWorker : public Nan::AsyncWorker {
 public:
  SetFSCTL_LOCK_VOLUMEWorker(
    const int fd,
    const int value,
    Nan::Callback *callback
  ) : Nan::AsyncWorker(callback),
      fd(fd),
      value(value) {}

  ~SetFSCTL_LOCK_VOLUMEWorker() {}

  void Execute() {
    HANDLE handle = uv_get_osfhandle(fd);
    if (handle == INVALID_HANDLE_VALUE) {
      return SetErrorMessage("EBADF: bad file descriptor");
    }
    DWORD bytes;
    if (
      !DeviceIoControl(
        handle,
        value ? FSCTL_LOCK_VOLUME : FSCTL_UNLOCK_VOLUME,
        NULL,
        0,
        NULL,
        0,
        &bytes,
        NULL
      )
    ) {
      if (value) {
        return SetErrorMessage("FSCTL_LOCK_VOLUME failed");
      } else {
        return SetErrorMessage("FSCTL_UNLOCK_VOLUME failed");
      }
    }
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;
    v8::Local<v8::Value> argv[] = {
      Nan::Undefined()
    };
    callback->Call(1, argv);
  }

 private:
  const int fd;
  const int value;
};
#endif

NAN_METHOD(getAlignedBuffer) {
  if (
    info.Length() != 2 ||
    !info[0]->IsUint32() ||
    !info[1]->IsUint32()
  ) {
    return Nan::ThrowError(
      "bad arguments, expected: (uint32 size, uint32 alignment)"
    );
  }
  const size_t size = info[0]->Uint32Value();
  const size_t alignment = info[1]->Uint32Value();
  if (size == 0) return Nan::ThrowError("size must not be 0");
  if (alignment == 0) return Nan::ThrowError("alignment must not be 0");
  if (alignment & (alignment - 1)) {
    return Nan::ThrowError("alignment must be a power of 2");
  }
  if (alignment % sizeof(void *)) {
    return Nan::ThrowError("alignment must be a multiple of pointer size");
  }
#if defined(_WIN32)
  void *ptr = _aligned_malloc(size, alignment);
  if (ptr == NULL) return Nan::ThrowError("insufficient memory");
#else
  void *ptr;
  int result = posix_memalign(&ptr, alignment, size);
  if (result == EINVAL) return Nan::ThrowError("bad alignment argument");
  if (result == ENOMEM) return Nan::ThrowError("insufficient memory");
  if (result != 0) return Nan::ThrowError("unexpected error, posix_memalign");
#endif
  info.GetReturnValue().Set(
    Nan::NewBuffer(
      (char *) ptr,
      size,
      free_aligned_buffer,
      NULL
    ).ToLocalChecked()
  );
}

NAN_METHOD(getBlockDevice) {
  if (
    info.Length() != 2 ||
    !info[0]->IsUint32() ||
    !info[1]->IsFunction()
  ) {
    return Nan::ThrowError(
      "bad arguments, expected: (uint32 fd, function callback)"
    );
  }
  const int fd = info[0]->Uint32Value();
  Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
  Nan::AsyncQueueWorker(new GetBlockDeviceWorker(fd, callback));
}

NAN_METHOD(setF_NOCACHE) {
#if defined(__APPLE__)
  if (
    info.Length() != 3 ||
    !info[0]->IsUint32() ||
    !info[1]->IsUint32() ||
    !info[2]->IsFunction()
  ) {
    return Nan::ThrowError(
      "bad arguments, expected: (uint32 fd, uint32 value, function callback)"
    );
  }
  const int fd = info[0]->Uint32Value();
  const int value = info[1]->Uint32Value();
  if (value != 0 && value != 1) return Nan::ThrowError("value must be 0 or 1");
  Nan::Callback *callback = new Nan::Callback(info[2].As<v8::Function>());
  Nan::AsyncQueueWorker(new SetF_NOCACHEWorker(fd, value, callback));
#else
  return Nan::ThrowError("only supported on mac os");
#endif
}

NAN_METHOD(setFSCTL_LOCK_VOLUME) {
#if defined(_WIN32)
  if (
    info.Length() != 3 ||
    !info[0]->IsUint32() ||
    !info[1]->IsUint32() ||
    !info[2]->IsFunction()
  ) {
    return Nan::ThrowError(
      "bad arguments, expected: (uint32 fd, uint32 value, function callback)"
    );
  }
  const int fd = info[0]->Uint32Value();
  const int value = info[1]->Uint32Value();
  if (value != 0 && value != 1) return Nan::ThrowError("value must be 0 or 1");
  Nan::Callback *callback = new Nan::Callback(info[2].As<v8::Function>());
  Nan::AsyncQueueWorker(new SetFSCTL_LOCK_VOLUMEWorker(fd, value, callback));
#else
  return Nan::ThrowError("only supported on windows");
#endif
}

NAN_MODULE_INIT(Init) {
#if defined(O_DIRECT)
  NODE_DEFINE_CONSTANT(target, O_DIRECT);
#endif
#if defined(O_DSYNC)
  NODE_DEFINE_CONSTANT(target, O_DSYNC);
#endif
#if defined(O_EXCL)
  NODE_DEFINE_CONSTANT(target, O_EXCL);
#endif
#if defined(O_EXLOCK)
  NODE_DEFINE_CONSTANT(target, O_EXLOCK);
#endif
#if defined(O_SYNC)
  NODE_DEFINE_CONSTANT(target, O_SYNC);
#endif
  NAN_EXPORT(target, getAlignedBuffer);
  NAN_EXPORT(target, getBlockDevice);
  NAN_EXPORT(target, setF_NOCACHE);
  NAN_EXPORT(target, setFSCTL_LOCK_VOLUME);
}

NODE_MODULE(binding, Init)

// S.D.G.
<|endoftext|>


<commit_before>#include <node.h>
#include <v8.h>
#include <rpcsvc/ypclnt.h>

using namespace v8;

Handle<Value> CreateObject(const Arguments& args) {
    HandleScope scope;
    char* domp;
    int error;

    Local<Object> obj = Object::New();
    error = yp_get_default_domain(&domp);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }
    obj->Set(String::NewSymbol("domain_name"), String::New(domp));
    return scope.Close(obj);
}

void init(Handle<Object> exports, Handle<Object> module) {
    module->Set(
        String::NewSymbol("exports"),
        FunctionTemplate::New(CreateObject)->GetFunction()
    );
}

NODE_MODULE(nis, init);
<commit_msg>Implement yp.all<commit_after>#include <node.h>
#include <v8.h>
#include <rpcsvc/ypclnt.h>

using namespace v8;

extern "C" {
    int foreach_all(unsigned long instatus, char *inkey, int inkeylen, char *inval, int invallen, void *data);
}

Handle<Value> All(const Arguments& args) {
    HandleScope scope;
    int error;

    if (args.Length() < 2) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }
    if (!args[1]->IsFunction()) {
        ThrowException(Exception::TypeError(String::New("Second argument must be function")));
        return scope.Close(Undefined());
    }
    Local<Function> user_cb = Local<Function>::Cast(args[1]);
    struct ypall_callback cb;
    cb.foreach = foreach_all;
    cb.data = (char *) &user_cb;

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value domain(args.Holder()->Get(String::NewSymbol("domain_name")));

    error = yp_all(*domain, *mapname, &cb);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    return scope.Close(Undefined());
}

extern "C"
int foreach_all(unsigned long instatus, char *inkey, int inkeylen, char *inval, int invallen, void *data) {
    Local<Function> *cb = static_cast<Local<Function> *>(data);
    const int argc = 3;
    Local<Value> argv[argc] = {
        Local<Value>::New(Integer::NewFromUnsigned(instatus)),
        Local<Value>::New(String::New(inkey, inkeylen)),
        Local<Value>::New(String::New(inval, invallen))
    };
    Local<Value> ret = (*cb)->Call(Context::GetCurrent()->Global(), argc, argv);
    return ret->Int32Value();
}

Handle<Value> CreateObject(const Arguments& args) {
    HandleScope scope;
    char* domp;
    int error;

    Local<Object> obj = Object::New();
    error = yp_get_default_domain(&domp);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }
    obj->Set(String::NewSymbol("domain_name"), String::New(domp));
    obj->Set(
        String::NewSymbol("all"),
        FunctionTemplate::New(All)->GetFunction()
    );
    return scope.Close(obj);
}

void init(Handle<Object> exports, Handle<Object> module) {
    module->Set(
        String::NewSymbol("exports"),
        FunctionTemplate::New(CreateObject)->GetFunction()
    );
}

NODE_MODULE(nis, init);
<|endoftext|>


<commit_before>/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of signon
 *
 * Copyright (C) 2009-2010 Nokia Corporation.
 *
 * Contact: Aurel Popirtac <mailto:ext-Aurel.Popirtac@nokia.com>
 * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "credentialsaccessmanager.h"

#include "signond-common.h"
#include "ui-key-authorizer.h"
#include "misc.h"

#include <QFile>
#include <QBuffer>

#define RETURN_IF_NOT_INITIALIZED(return_value)                  \
    do {                                                         \
        if (!m_isInitialized) {                                  \
            m_error = NotInitialized;                            \
            TRACE() << "CredentialsAccessManager not initialized."; \
            return return_value;                                \
        }                                                       \
    } while (0)

using namespace SignonDaemonNS;

/* ---------------------- CAMConfiguration ---------------------- */

CAMConfiguration::CAMConfiguration()
        : m_storagePath(QLatin1String(signonDefaultStoragePath)),
          m_dbName(QLatin1String(signonDefaultDbName)),
          m_useEncryption(signonDefaultUseEncryption),
          m_fileSystemType(QLatin1String(signonDefaultFileSystemType)),
          m_fileSystemSize(signonMinumumDbSize),
          m_encryptionPassphrase(QByteArray())
{}

void CAMConfiguration::serialize(QIODevice *device)
{
    if (device == NULL)
        return;

    if (!device->open(QIODevice::ReadWrite)) {
        return;
    }

    QString buffer;
    QTextStream stream(&buffer);
    stream << "\n\n====== Credentials Access Manager Configuration ======\n\n";
    stream << "File system mount name " << encryptedFSPath() << '\n';
    stream << "File system format: " << m_fileSystemType << '\n';
    stream << "File system size:" << m_fileSystemSize << "megabytes\n";

    const char *usingEncryption = m_useEncryption ? "true" : "false";
    stream << "Using encryption: " << usingEncryption << '\n';
    stream << "Credentials database name: " << m_dbName << '\n';
    stream << "======================================================\n\n";
    device->write(buffer.toUtf8());
    device->close();
}

QString CAMConfiguration::metadataDBPath() const
{
    return m_storagePath + QDir::separator() + m_dbName;
}

QString CAMConfiguration::encryptedFSPath() const
{
    return m_storagePath +
        QDir::separator() +
        QLatin1String(signonDefaultFileSystemName);
}

/* ---------------------- CredentialsAccessManager ---------------------- */

CredentialsAccessManager *CredentialsAccessManager::m_pInstance = NULL;

CredentialsAccessManager::CredentialsAccessManager(QObject *parent)
        : QObject(parent),
          m_isInitialized(false),
          m_systemOpened(false),
          m_error(NoError),
          keyManagers(),
          m_pCredentialsDB(NULL),
          m_pCryptoFileSystemManager(NULL),
          m_keyHandler(NULL),
          m_keyAuthorizer(NULL),
          m_CAMConfiguration(CAMConfiguration())
{
}

CredentialsAccessManager::~CredentialsAccessManager()
{
    closeCredentialsSystem();

    m_pInstance = NULL;
}

CredentialsAccessManager *CredentialsAccessManager::instance(QObject *parent)
{
    if (!m_pInstance)
        m_pInstance = new CredentialsAccessManager(parent);

    return m_pInstance;
}

void CredentialsAccessManager::finalize()
{
    if (m_systemOpened)
        closeCredentialsSystem();

    if (m_pCryptoFileSystemManager)
        delete m_pCryptoFileSystemManager;

    // Disconnect all key managers
    foreach (SignOn::AbstractKeyManager *keyManager, keyManagers)
        keyManager->disconnect();

    m_isInitialized = false;
    m_error = NoError;
}

bool CredentialsAccessManager::init(const CAMConfiguration &camConfiguration)
{
    if (m_isInitialized) {
        TRACE() << "CAM already initialized.";
        m_error = AlreadyInitialized;
        return false;
    }

    m_CAMConfiguration = camConfiguration;

    QBuffer config;
    m_CAMConfiguration.serialize(&config);
    TRACE() << "\n\nInitualizing CredentialsAccessManager with configuration: " << config.data();

    if (!createStorageDir()) {
        BLAME() << "Failed to create storage directory.";
        return false;
    }

    if (m_CAMConfiguration.m_useEncryption) {
        //Initialize CryptoManager
        m_pCryptoFileSystemManager = new CryptoManager(this);
        QObject::connect(m_pCryptoFileSystemManager, SIGNAL(fileSystemMounted()),
                         this, SLOT(onEncryptedFSMounted()));
        QObject::connect(m_pCryptoFileSystemManager, SIGNAL(fileSystemUnmounting()),
                         this, SLOT(onEncryptedFSUnmounting()));
        m_pCryptoFileSystemManager->setFileSystemPath(m_CAMConfiguration.encryptedFSPath());
        m_pCryptoFileSystemManager->setFileSystemSize(m_CAMConfiguration.m_fileSystemSize);
        m_pCryptoFileSystemManager->setFileSystemType(m_CAMConfiguration.m_fileSystemType);

        m_keyHandler = new KeyHandler(this);

        m_keyAuthorizer = new UiKeyAuthorizer(m_keyHandler, this);
        QObject::connect(m_keyAuthorizer,
                         SIGNAL(keyAuthorizationQueried(const SignOn::Key,int)),
                         this,
                         SLOT(onKeyAuthorizationQueried(const SignOn::Key,int)));

        /* These signal connections should be done after instantiating the
         * KeyAuthorizer, so that the KeyAuthorizer's slot will be called
         * first (or we could connect to them in queued mode)
         */
        QObject::connect(m_keyHandler, SIGNAL(ready()),
                         this, SIGNAL(credentialsSystemReady()));
        QObject::connect(m_keyHandler, SIGNAL(keyInserted(SignOn::Key)),
                         this, SLOT(onKeyInserted(SignOn::Key)));
        QObject::connect(m_keyHandler,
                         SIGNAL(lastAuthorizedKeyRemoved(SignOn::Key)),
                         this,
                         SLOT(onLastAuthorizedKeyRemoved(SignOn::Key)));
        QObject::connect(m_keyHandler, SIGNAL(keyRemoved(SignOn::Key)),
                         this, SLOT(onKeyRemoved(SignOn::Key)));
        m_keyHandler->initialize(m_pCryptoFileSystemManager, keyManagers);
    }

    m_isInitialized = true;
    m_error = NoError;

    TRACE() << "CredentialsAccessManager successfully initialized...";
    return true;
}

void CredentialsAccessManager::addKeyManager(
    SignOn::AbstractKeyManager *keyManager)
{
    keyManagers.append(keyManager);
}

bool CredentialsAccessManager::openSecretsDB()
{
    //todo remove this variable after LUKS implementation becomes stable.
    QString dbPath;

    if (m_CAMConfiguration.m_useEncryption) {
        dbPath = m_pCryptoFileSystemManager->fileSystemMountPath()
            + QDir::separator()
            + m_CAMConfiguration.m_dbName;

        if (!m_pCryptoFileSystemManager->fileSystemIsMounted()) {
            /* Do not attempt to mount the FS; we know that it will be mounted
             * automatically, as soon as some encryption keys are provided */
            m_error = CredentialsDbNotMounted;
            return false;
        }
    } else {
        dbPath = m_CAMConfiguration.metadataDBPath() + QLatin1String(".creds");
    }

    TRACE() << "Database name: [" << dbPath << "]";

    if (!m_pCredentialsDB->openSecretsDB(dbPath))
        return false;

    m_error = NoError;
    return true;
}

bool CredentialsAccessManager::isSecretsDBOpen()
{
    return m_pCredentialsDB->isSecretsDBOpen();
}

bool CredentialsAccessManager::closeSecretsDB()
{
    m_pCredentialsDB->closeSecretsDB();

    if (m_CAMConfiguration.m_useEncryption) {
        if (!m_pCryptoFileSystemManager->unmountFileSystem()) {
            m_error = CredentialsDbUnmountFailed;
            return false;
        }
    }

    return true;
}

bool CredentialsAccessManager::createStorageDir()
{
    QString dbPath = m_CAMConfiguration.metadataDBPath();

    QFileInfo fileInfo(dbPath);
    if (!fileInfo.exists()) {
        QDir storageDir(fileInfo.dir());
        if (!storageDir.mkpath(storageDir.path())) {
            BLAME() << "Could not create storage directory:" <<
                storageDir.path();
            m_error = CredentialsDbSetupFailed;
            return false;
        }
        setUserOwnership(storageDir.path());
    }
    return true;

}
bool CredentialsAccessManager::openMetaDataDB()
{
    QString dbPath = m_CAMConfiguration.metadataDBPath();

    m_pCredentialsDB = new CredentialsDB(dbPath);

    if (!m_pCredentialsDB->init()) {
        m_error = CredentialsDbConnectionError;
        return false;
    }

    return true;
}

void CredentialsAccessManager::closeMetaDataDB()
{
    if (m_pCredentialsDB) {
        delete m_pCredentialsDB;
        m_pCredentialsDB = NULL;
    }
}

bool CredentialsAccessManager::openCredentialsSystem()
{
    RETURN_IF_NOT_INITIALIZED(false);

    if (!openMetaDataDB()) {
        BLAME() << "Couldn't open metadata DB!";
        return false;
    }

    if (!openSecretsDB()) {
        BLAME() << "Failed to open secrets DB.";
        /* Even if the secrets DB couldn't be opened, signond is still usable:
         * that's why we return "true" anyways. */
    }

    m_systemOpened = true;
    return true;
}

bool CredentialsAccessManager::closeCredentialsSystem()
{
    RETURN_IF_NOT_INITIALIZED(false);

    if (!credentialsSystemOpened())
        return true;

    bool allClosed = true;
    if (isSecretsDBOpen() && !closeSecretsDB())
        allClosed = false;

    closeMetaDataDB();

    m_error = NoError;
    m_systemOpened = false;
    return allClosed;
}

bool CredentialsAccessManager::deleteCredentialsSystem()
{
    RETURN_IF_NOT_INITIALIZED(false);

    if (m_systemOpened && !closeCredentialsSystem()) {
        /* The close operation failed: we cannot proceed */
        return false;
    }

    m_error = NoError;

    if (m_CAMConfiguration.m_useEncryption) {
        if (!m_pCryptoFileSystemManager->deleteFileSystem())
            m_error = CredentialsDbDeletionFailed;
    } else {
        QFile dbFile(m_CAMConfiguration.m_dbName);
        if (dbFile.exists()) {
            if (!dbFile.remove())
                m_error = CredentialsDbDeletionFailed;
        }
    }

    return m_error == NoError;
}

CredentialsDB *CredentialsAccessManager::credentialsDB() const
{
    RETURN_IF_NOT_INITIALIZED(NULL);

    return m_pCredentialsDB;
}

bool CredentialsAccessManager::isCredentialsSystemReady() const
{
    return (m_keyHandler != 0) ? m_keyHandler->isReady() : true;
}

void CredentialsAccessManager::onKeyInserted(const SignOn::Key key)
{
    TRACE() << "Key inserted.";

    if (!m_keyHandler->keyIsAuthorized(key))
        m_keyAuthorizer->queryKeyAuthorization(
            key, AbstractKeyAuthorizer::KeyInserted);
}

void CredentialsAccessManager::onLastAuthorizedKeyRemoved(const SignOn::Key key)
{
    Q_UNUSED(key);
    TRACE() << "All keys disabled. Closing secure storage.";
    if (isSecretsDBOpen() || m_pCryptoFileSystemManager->fileSystemIsMounted())
        if (!closeSecretsDB())
            BLAME() << "Error occurred while closing secure storage.";
}

void CredentialsAccessManager::onKeyRemoved(const SignOn::Key key)
{
    TRACE() << "Key removed.";

    if (m_keyHandler->keyIsAuthorized(key)) {
        if (!m_keyHandler->revokeKeyAuthorization(key)) {
            BLAME() << "Revoking key authorization failed";
        }
    }
}

void CredentialsAccessManager::onKeyAuthorizationQueried(const SignOn::Key key,
                                                         int result)
{
    TRACE() << "result:" << result;

    if (result != AbstractKeyAuthorizer::Denied) {
        KeyHandler::AuthorizeFlags flags = KeyHandler::None;
        if (result == AbstractKeyAuthorizer::Exclusive) {
            TRACE() << "Reformatting secure storage.";
            flags |= KeyHandler::FormatStorage;
        }

        if (!m_keyHandler->authorizeKey(key, flags)) {
            BLAME() << "Authorization failed";
        }
    }

    replyToSecureStorageEventNotifiers();
}

bool CredentialsAccessManager::keysAvailable() const
{
    if (m_keyHandler == 0) return false;
    return !m_keyHandler->insertedKeys().isEmpty();
}

void CredentialsAccessManager::replyToSecureStorageEventNotifiers()
{
    TRACE();
    //Notify secure storage notifiers if any.
    int eventType = SIGNON_SECURE_STORAGE_NOT_AVAILABLE;
    if ((m_pCredentialsDB != 0) && m_pCredentialsDB->isSecretsDBOpen())
        eventType = SIGNON_SECURE_STORAGE_AVAILABLE;

    // Signal objects that posted secure storage not available events
    foreach (EventSender object, m_secureStorageEventNotifiers) {
        if (object.isNull())
            continue;

        SecureStorageEvent *secureStorageEvent =
            new SecureStorageEvent((QEvent::Type)eventType);

        QCoreApplication::postEvent(
            object.data(),
            secureStorageEvent,
            Qt::HighEventPriority);
    }

    m_secureStorageEventNotifiers.clear();
}

void CredentialsAccessManager::customEvent(QEvent *event)
{
    TRACE() << "Custom event received.";
    if (event->type() != SIGNON_SECURE_STORAGE_NOT_AVAILABLE) {
        QObject::customEvent(event);
        return;
    }

    SecureStorageEvent *localEvent =
        static_cast<SecureStorageEvent *>(event);

    /* All senders of this event will receive a reply when
     * the secure storage becomes available or an error occurs. */
    m_secureStorageEventNotifiers.append(localEvent->m_sender);

    TRACE() << "Processing secure storage not available event.";
    if ((localEvent == 0) || (m_pCredentialsDB == 0)) {
        replyToSecureStorageEventNotifiers();
        QObject::customEvent(event);
        return;
    }

    //Double check if the secrets DB is indeed unavailable
    if (m_pCredentialsDB->isSecretsDBOpen()) {
        replyToSecureStorageEventNotifiers();
        QObject::customEvent(event);
        return;
    }

    SignOn::Key key; /* we don't specity any key */
    m_keyAuthorizer->queryKeyAuthorization(key,
                                           AbstractKeyAuthorizer::StorageNeeded);

    QObject::customEvent(event);
}

void CredentialsAccessManager::onEncryptedFSMounted()
{
    TRACE();
    if (!credentialsSystemOpened()) return;

    if (!isSecretsDBOpen()) {
        if (openSecretsDB()) {
            TRACE() << "Secrets DB opened.";
        } else {
            BLAME() << "Failed to open secrets DB.";
        }
    } else {
        BLAME() << "Secrets DB already opened?";
    }
}

void CredentialsAccessManager::onEncryptedFSUnmounting()
{
    TRACE();
    if (!credentialsSystemOpened()) return;

    if (isSecretsDBOpen()) {
        m_pCredentialsDB->closeSecretsDB();
    }
}

<commit_msg>CAM: use default key authorizer<commit_after>/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of signon
 *
 * Copyright (C) 2009-2010 Nokia Corporation.
 *
 * Contact: Aurel Popirtac <mailto:ext-Aurel.Popirtac@nokia.com>
 * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "credentialsaccessmanager.h"

#include "default-key-authorizer.h"
#include "signond-common.h"
#include "misc.h"

#include <QFile>
#include <QBuffer>

#define RETURN_IF_NOT_INITIALIZED(return_value)                  \
    do {                                                         \
        if (!m_isInitialized) {                                  \
            m_error = NotInitialized;                            \
            TRACE() << "CredentialsAccessManager not initialized."; \
            return return_value;                                \
        }                                                       \
    } while (0)

using namespace SignonDaemonNS;

/* ---------------------- CAMConfiguration ---------------------- */

CAMConfiguration::CAMConfiguration()
        : m_storagePath(QLatin1String(signonDefaultStoragePath)),
          m_dbName(QLatin1String(signonDefaultDbName)),
          m_useEncryption(signonDefaultUseEncryption),
          m_fileSystemType(QLatin1String(signonDefaultFileSystemType)),
          m_fileSystemSize(signonMinumumDbSize),
          m_encryptionPassphrase(QByteArray())
{}

void CAMConfiguration::serialize(QIODevice *device)
{
    if (device == NULL)
        return;

    if (!device->open(QIODevice::ReadWrite)) {
        return;
    }

    QString buffer;
    QTextStream stream(&buffer);
    stream << "\n\n====== Credentials Access Manager Configuration ======\n\n";
    stream << "File system mount name " << encryptedFSPath() << '\n';
    stream << "File system format: " << m_fileSystemType << '\n';
    stream << "File system size:" << m_fileSystemSize << "megabytes\n";

    const char *usingEncryption = m_useEncryption ? "true" : "false";
    stream << "Using encryption: " << usingEncryption << '\n';
    stream << "Credentials database name: " << m_dbName << '\n';
    stream << "======================================================\n\n";
    device->write(buffer.toUtf8());
    device->close();
}

QString CAMConfiguration::metadataDBPath() const
{
    return m_storagePath + QDir::separator() + m_dbName;
}

QString CAMConfiguration::encryptedFSPath() const
{
    return m_storagePath +
        QDir::separator() +
        QLatin1String(signonDefaultFileSystemName);
}

/* ---------------------- CredentialsAccessManager ---------------------- */

CredentialsAccessManager *CredentialsAccessManager::m_pInstance = NULL;

CredentialsAccessManager::CredentialsAccessManager(QObject *parent)
        : QObject(parent),
          m_isInitialized(false),
          m_systemOpened(false),
          m_error(NoError),
          keyManagers(),
          m_pCredentialsDB(NULL),
          m_pCryptoFileSystemManager(NULL),
          m_keyHandler(NULL),
          m_keyAuthorizer(NULL),
          m_CAMConfiguration(CAMConfiguration())
{
}

CredentialsAccessManager::~CredentialsAccessManager()
{
    closeCredentialsSystem();

    m_pInstance = NULL;
}

CredentialsAccessManager *CredentialsAccessManager::instance(QObject *parent)
{
    if (!m_pInstance)
        m_pInstance = new CredentialsAccessManager(parent);

    return m_pInstance;
}

void CredentialsAccessManager::finalize()
{
    if (m_systemOpened)
        closeCredentialsSystem();

    if (m_pCryptoFileSystemManager)
        delete m_pCryptoFileSystemManager;

    // Disconnect all key managers
    foreach (SignOn::AbstractKeyManager *keyManager, keyManagers)
        keyManager->disconnect();

    m_isInitialized = false;
    m_error = NoError;
}

bool CredentialsAccessManager::init(const CAMConfiguration &camConfiguration)
{
    if (m_isInitialized) {
        TRACE() << "CAM already initialized.";
        m_error = AlreadyInitialized;
        return false;
    }

    m_CAMConfiguration = camConfiguration;

    QBuffer config;
    m_CAMConfiguration.serialize(&config);
    TRACE() << "\n\nInitualizing CredentialsAccessManager with configuration: " << config.data();

    if (!createStorageDir()) {
        BLAME() << "Failed to create storage directory.";
        return false;
    }

    if (m_CAMConfiguration.m_useEncryption) {
        //Initialize CryptoManager
        m_pCryptoFileSystemManager = new CryptoManager(this);
        QObject::connect(m_pCryptoFileSystemManager, SIGNAL(fileSystemMounted()),
                         this, SLOT(onEncryptedFSMounted()));
        QObject::connect(m_pCryptoFileSystemManager, SIGNAL(fileSystemUnmounting()),
                         this, SLOT(onEncryptedFSUnmounting()));
        m_pCryptoFileSystemManager->setFileSystemPath(m_CAMConfiguration.encryptedFSPath());
        m_pCryptoFileSystemManager->setFileSystemSize(m_CAMConfiguration.m_fileSystemSize);
        m_pCryptoFileSystemManager->setFileSystemType(m_CAMConfiguration.m_fileSystemType);

        m_keyHandler = new KeyHandler(this);

        m_keyAuthorizer = new DefaultKeyAuthorizer(m_keyHandler, this);
        QObject::connect(m_keyAuthorizer,
                         SIGNAL(keyAuthorizationQueried(const SignOn::Key,int)),
                         this,
                         SLOT(onKeyAuthorizationQueried(const SignOn::Key,int)));

        /* These signal connections should be done after instantiating the
         * KeyAuthorizer, so that the KeyAuthorizer's slot will be called
         * first (or we could connect to them in queued mode)
         */
        QObject::connect(m_keyHandler, SIGNAL(ready()),
                         this, SIGNAL(credentialsSystemReady()));
        QObject::connect(m_keyHandler, SIGNAL(keyInserted(SignOn::Key)),
                         this, SLOT(onKeyInserted(SignOn::Key)));
        QObject::connect(m_keyHandler,
                         SIGNAL(lastAuthorizedKeyRemoved(SignOn::Key)),
                         this,
                         SLOT(onLastAuthorizedKeyRemoved(SignOn::Key)));
        QObject::connect(m_keyHandler, SIGNAL(keyRemoved(SignOn::Key)),
                         this, SLOT(onKeyRemoved(SignOn::Key)));
        m_keyHandler->initialize(m_pCryptoFileSystemManager, keyManagers);
    }

    m_isInitialized = true;
    m_error = NoError;

    TRACE() << "CredentialsAccessManager successfully initialized...";
    return true;
}

void CredentialsAccessManager::addKeyManager(
    SignOn::AbstractKeyManager *keyManager)
{
    keyManagers.append(keyManager);
}

bool CredentialsAccessManager::openSecretsDB()
{
    //todo remove this variable after LUKS implementation becomes stable.
    QString dbPath;

    if (m_CAMConfiguration.m_useEncryption) {
        dbPath = m_pCryptoFileSystemManager->fileSystemMountPath()
            + QDir::separator()
            + m_CAMConfiguration.m_dbName;

        if (!m_pCryptoFileSystemManager->fileSystemIsMounted()) {
            /* Do not attempt to mount the FS; we know that it will be mounted
             * automatically, as soon as some encryption keys are provided */
            m_error = CredentialsDbNotMounted;
            return false;
        }
    } else {
        dbPath = m_CAMConfiguration.metadataDBPath() + QLatin1String(".creds");
    }

    TRACE() << "Database name: [" << dbPath << "]";

    if (!m_pCredentialsDB->openSecretsDB(dbPath))
        return false;

    m_error = NoError;
    return true;
}

bool CredentialsAccessManager::isSecretsDBOpen()
{
    return m_pCredentialsDB->isSecretsDBOpen();
}

bool CredentialsAccessManager::closeSecretsDB()
{
    m_pCredentialsDB->closeSecretsDB();

    if (m_CAMConfiguration.m_useEncryption) {
        if (!m_pCryptoFileSystemManager->unmountFileSystem()) {
            m_error = CredentialsDbUnmountFailed;
            return false;
        }
    }

    return true;
}

bool CredentialsAccessManager::createStorageDir()
{
    QString dbPath = m_CAMConfiguration.metadataDBPath();

    QFileInfo fileInfo(dbPath);
    if (!fileInfo.exists()) {
        QDir storageDir(fileInfo.dir());
        if (!storageDir.mkpath(storageDir.path())) {
            BLAME() << "Could not create storage directory:" <<
                storageDir.path();
            m_error = CredentialsDbSetupFailed;
            return false;
        }
        setUserOwnership(storageDir.path());
    }
    return true;

}
bool CredentialsAccessManager::openMetaDataDB()
{
    QString dbPath = m_CAMConfiguration.metadataDBPath();

    m_pCredentialsDB = new CredentialsDB(dbPath);

    if (!m_pCredentialsDB->init()) {
        m_error = CredentialsDbConnectionError;
        return false;
    }

    return true;
}

void CredentialsAccessManager::closeMetaDataDB()
{
    if (m_pCredentialsDB) {
        delete m_pCredentialsDB;
        m_pCredentialsDB = NULL;
    }
}

bool CredentialsAccessManager::openCredentialsSystem()
{
    RETURN_IF_NOT_INITIALIZED(false);

    if (!openMetaDataDB()) {
        BLAME() << "Couldn't open metadata DB!";
        return false;
    }

    if (!openSecretsDB()) {
        BLAME() << "Failed to open secrets DB.";
        /* Even if the secrets DB couldn't be opened, signond is still usable:
         * that's why we return "true" anyways. */
    }

    m_systemOpened = true;
    return true;
}

bool CredentialsAccessManager::closeCredentialsSystem()
{
    RETURN_IF_NOT_INITIALIZED(false);

    if (!credentialsSystemOpened())
        return true;

    bool allClosed = true;
    if (isSecretsDBOpen() && !closeSecretsDB())
        allClosed = false;

    closeMetaDataDB();

    m_error = NoError;
    m_systemOpened = false;
    return allClosed;
}

bool CredentialsAccessManager::deleteCredentialsSystem()
{
    RETURN_IF_NOT_INITIALIZED(false);

    if (m_systemOpened && !closeCredentialsSystem()) {
        /* The close operation failed: we cannot proceed */
        return false;
    }

    m_error = NoError;

    if (m_CAMConfiguration.m_useEncryption) {
        if (!m_pCryptoFileSystemManager->deleteFileSystem())
            m_error = CredentialsDbDeletionFailed;
    } else {
        QFile dbFile(m_CAMConfiguration.m_dbName);
        if (dbFile.exists()) {
            if (!dbFile.remove())
                m_error = CredentialsDbDeletionFailed;
        }
    }

    return m_error == NoError;
}

CredentialsDB *CredentialsAccessManager::credentialsDB() const
{
    RETURN_IF_NOT_INITIALIZED(NULL);

    return m_pCredentialsDB;
}

bool CredentialsAccessManager::isCredentialsSystemReady() const
{
    return (m_keyHandler != 0) ? m_keyHandler->isReady() : true;
}

void CredentialsAccessManager::onKeyInserted(const SignOn::Key key)
{
    TRACE() << "Key inserted.";

    if (!m_keyHandler->keyIsAuthorized(key))
        m_keyAuthorizer->queryKeyAuthorization(
            key, AbstractKeyAuthorizer::KeyInserted);
}

void CredentialsAccessManager::onLastAuthorizedKeyRemoved(const SignOn::Key key)
{
    Q_UNUSED(key);
    TRACE() << "All keys disabled. Closing secure storage.";
    if (isSecretsDBOpen() || m_pCryptoFileSystemManager->fileSystemIsMounted())
        if (!closeSecretsDB())
            BLAME() << "Error occurred while closing secure storage.";
}

void CredentialsAccessManager::onKeyRemoved(const SignOn::Key key)
{
    TRACE() << "Key removed.";

    if (m_keyHandler->keyIsAuthorized(key)) {
        if (!m_keyHandler->revokeKeyAuthorization(key)) {
            BLAME() << "Revoking key authorization failed";
        }
    }
}

void CredentialsAccessManager::onKeyAuthorizationQueried(const SignOn::Key key,
                                                         int result)
{
    TRACE() << "result:" << result;

    if (result != AbstractKeyAuthorizer::Denied) {
        KeyHandler::AuthorizeFlags flags = KeyHandler::None;
        if (result == AbstractKeyAuthorizer::Exclusive) {
            TRACE() << "Reformatting secure storage.";
            flags |= KeyHandler::FormatStorage;
        }

        if (!m_keyHandler->authorizeKey(key, flags)) {
            BLAME() << "Authorization failed";
        }
    }

    replyToSecureStorageEventNotifiers();
}

bool CredentialsAccessManager::keysAvailable() const
{
    if (m_keyHandler == 0) return false;
    return !m_keyHandler->insertedKeys().isEmpty();
}

void CredentialsAccessManager::replyToSecureStorageEventNotifiers()
{
    TRACE();
    //Notify secure storage notifiers if any.
    int eventType = SIGNON_SECURE_STORAGE_NOT_AVAILABLE;
    if ((m_pCredentialsDB != 0) && m_pCredentialsDB->isSecretsDBOpen())
        eventType = SIGNON_SECURE_STORAGE_AVAILABLE;

    // Signal objects that posted secure storage not available events
    foreach (EventSender object, m_secureStorageEventNotifiers) {
        if (object.isNull())
            continue;

        SecureStorageEvent *secureStorageEvent =
            new SecureStorageEvent((QEvent::Type)eventType);

        QCoreApplication::postEvent(
            object.data(),
            secureStorageEvent,
            Qt::HighEventPriority);
    }

    m_secureStorageEventNotifiers.clear();
}

void CredentialsAccessManager::customEvent(QEvent *event)
{
    TRACE() << "Custom event received.";
    if (event->type() != SIGNON_SECURE_STORAGE_NOT_AVAILABLE) {
        QObject::customEvent(event);
        return;
    }

    SecureStorageEvent *localEvent =
        static_cast<SecureStorageEvent *>(event);

    /* All senders of this event will receive a reply when
     * the secure storage becomes available or an error occurs. */
    m_secureStorageEventNotifiers.append(localEvent->m_sender);

    TRACE() << "Processing secure storage not available event.";
    if ((localEvent == 0) || (m_pCredentialsDB == 0)) {
        replyToSecureStorageEventNotifiers();
        QObject::customEvent(event);
        return;
    }

    //Double check if the secrets DB is indeed unavailable
    if (m_pCredentialsDB->isSecretsDBOpen()) {
        replyToSecureStorageEventNotifiers();
        QObject::customEvent(event);
        return;
    }

    SignOn::Key key; /* we don't specity any key */
    m_keyAuthorizer->queryKeyAuthorization(key,
                                           AbstractKeyAuthorizer::StorageNeeded);

    QObject::customEvent(event);
}

void CredentialsAccessManager::onEncryptedFSMounted()
{
    TRACE();
    if (!credentialsSystemOpened()) return;

    if (!isSecretsDBOpen()) {
        if (openSecretsDB()) {
            TRACE() << "Secrets DB opened.";
        } else {
            BLAME() << "Failed to open secrets DB.";
        }
    } else {
        BLAME() << "Secrets DB already opened?";
    }
}

void CredentialsAccessManager::onEncryptedFSUnmounting()
{
    TRACE();
    if (!credentialsSystemOpened()) return;

    if (isSecretsDBOpen()) {
        m_pCredentialsDB->closeSecretsDB();
    }
}

<|endoftext|>


<commit_before>#include "handleSerial.h"

#include <SPI.h>
#include <SD.h>
#include "state.h"
#include "powerSleep.h"

void handleSerial(State_t* State)
{
  Serial.print(F(">> User:   "));
  char input;
  if(Serial.available() > 0)    // Check if serial data is available.
  {
    input = Serial.read();
    if (input != '\n')          // Ignore newline characters.
      Serial.println(input);    // For the Arduino IDE Serial Monitor. Other serial monitors probably don't need this.

    switch(input)               // Switch statement for all defined inputs
    {
      case 'c':                                         // Delete files on the SD Card. Protected from deleting while in use.
        break;

      case 'd':
        break;

      case 'e':
        Serial.print(F(">> Logger: Exitting... "));
        State->serialOn = false;
        Serial.print(F(">> Logger: Finished."));
        power_usart0_disable();
        break;

      case 'E':
        break;

      case 'h':                                                                 // Help list
        Serial.print(F(">> Logger: List of commands:\n"));
        Serial.print(F("           c  -- Clean SD card\n"));
        Serial.print(F("           d  -- View date/time\n"));
        Serial.print(F("           e  -- Exit serial interface\n"));
        Serial.print(F("           E  -- Eject SD card\n"));
        Serial.print(F("           h  -- Display help\n"));
        Serial.print(F("           i  -- Initialize the SD card\n"));
        Serial.print(F("           s  -- Start datalogging (will overwrite old datalog.csv)\n"));
        Serial.print(F("           S  -- Stop datalogging\n"));
        Serial.print(F("           u  -- Update date/time\n"));
        break;

      case 'i':
        break;

      case 's':
        State->logging = true;
        break;

      case 'S':
        break;

      case 'u':
        break;

      case '\n':
        break;

      default:                                  // If the command is invalid, prompt the user and introduce 'h' option.
        Serial.print(F(">> Logger: Unknown command. Use the command \"h\" for a list of commands.\n"));
        break;
    }
  }
}

void serialPowerUp()
{
  power_usart0_enable();
  Serial.begin(9600);
  while(!Serial);
  Serial.print(F(">> Logger: Logger ready.\n"));

  return;
}
<commit_msg>Update handleSerial.cpp<commit_after>#include "handleSerial.h"

#include <SPI.h>
#include <SD.h>
#include "state.h"
#include "powerSleep.h"

void handleSerial(State_t* State)
{
  char input;
  if(Serial.available() > 0)    // Check if serial data is available.
  {
    input = Serial.read();
    if (input != '\n')          // Ignore newline characters.
      Serial.println(input);    // For the Arduino IDE Serial Monitor. Other serial monitors probably don't need this.

    switch(input)               // Switch statement for all defined inputs
    {
      case 'c':                                         // Delete files on the SD Card. Protected from deleting while in use.
        break;

      case 'd':
        break;

      case 'e':
        Serial.print(F(">> Logger: Exitting... "));
        State->serialOn = false;
        Serial.print(F(">> Logger: Finished."));
        power_usart0_disable();
        break;

      case 'E':
        break;

      case 'h':                                                                 // Help list
        Serial.print(F(">> Logger: List of commands:\n"));
        Serial.print(F("           c  -- Clean SD card\n"));
        Serial.print(F("           d  -- View date/time\n"));
        Serial.print(F("           e  -- Exit serial interface\n"));
        Serial.print(F("           E  -- Eject SD card\n"));
        Serial.print(F("           h  -- Display help\n"));
        Serial.print(F("           i  -- Initialize the SD card\n"));
        Serial.print(F("           s  -- Start datalogging (will overwrite old datalog.csv)\n"));
        Serial.print(F("           S  -- Stop datalogging\n"));
        Serial.print(F("           u  -- Update date/time\n"));
        Serial.print(F(">> User:   "));
        break;

      case 'i':
        break;

      case 's':
        State->logging = true;
        break;

      case 'S':
        break;

      case 'u':
        break;

      case '\n':
        break;

      default:                                  // If the command is invalid, prompt the user and introduce 'h' option.
        Serial.print(F(">> Logger: Unknown command. Use the command \"h\" for a list of commands.\n>> User:   "));
        break;
    }
  }
}

void serialPowerUp()
{
  power_usart0_enable();
  Serial.begin(9600);
  while(!Serial);
  Serial.print(F(">> Logger: Logger ready.\n>> User:   "));

  return;
}
<|endoftext|>


<commit_before>#include "Pointer.hpp"
#include <Windows.h>
#include <TlHelp32.h> //GetModuleBase
#include <memory>
#include <string>

namespace Util
{
    Pointer Pointer::GetModuleBase()
    {
        static void* Base = nullptr;

        if( Base == nullptr )
        {
            HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
            if( hSnapShot == INVALID_HANDLE_VALUE )
                return nullptr;
            MODULEENTRY32 lpModuleEntry;
            lpModuleEntry.dwSize = sizeof(MODULEENTRY32);
            int bRet = Module32First(hSnapShot, &lpModuleEntry);
            CloseHandle(hSnapShot);
            Base = (bRet != 0) ? (void*)lpModuleEntry.modBaseAddr : nullptr;
        }

        return Pointer(Base);
    }

    Pointer Pointer::GetModuleBase(const char* ModuleName)
    {
        HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
        if( hSnapShot == INVALID_HANDLE_VALUE )
            return nullptr;

        MODULEENTRY32 lpModuleEntry;
        lpModuleEntry.dwSize = sizeof(MODULEENTRY32);
        BOOL bModule = Module32First(hSnapShot, &lpModuleEntry);
        while( bModule )
        {
            //If module name matches: return it
            if( !std::strcmp(ModuleName, lpModuleEntry.szModule) )
            {
                CloseHandle(hSnapShot);
                return (void*)lpModuleEntry.modBaseAddr;
            }
            bModule = Module32Next(hSnapShot, &lpModuleEntry);
        }
        CloseHandle(hSnapShot);
        return nullptr;
    }
}<commit_msg>Pointer Module refactor<commit_after>#include "Pointer.hpp"
#include <Windows.h>
#include <TlHelp32.h> //GetModuleBase
#include <memory>
#include <string>

namespace Util
{
    Pointer Pointer::GetModuleBase()
    {
        // Cache base pointer
        static void* CacheBase = nullptr;

        if( CacheBase == nullptr )
        {
            void* hSnapShot = CreateToolhelp32Snapshot(
                TH32CS_SNAPMODULE,
                GetCurrentProcessId()
            );

            if( hSnapShot == INVALID_HANDLE_VALUE )
            {
                return nullptr;
            }

            MODULEENTRY32 lpModuleEntry = { 0 };
            lpModuleEntry.dwSize = sizeof(MODULEENTRY32);
            int bRet = Module32First(hSnapShot, &lpModuleEntry);
            CloseHandle(hSnapShot);
            CacheBase =
                (bRet != 0) ?
                reinterpret_cast<void*>(lpModuleEntry.modBaseAddr)
                : nullptr;
        }

        return Pointer(Base);
    }

    Pointer Pointer::GetModuleBase(const char* ModuleName)
    {
        void* hSnapShot = CreateToolhelp32Snapshot(
            TH32CS_SNAPMODULE,
            GetCurrentProcessId()
        );

        if( hSnapShot == INVALID_HANDLE_VALUE )
        {
            return nullptr;
        }

        MODULEENTRY32 lpModuleEntry = { 0 };
        lpModuleEntry.dwSize = sizeof(MODULEENTRY32);
        int32_t bModule = Module32First(hSnapShot, &lpModuleEntry);
        while( bModule )
        {
            //If module name matches: return it
            if( !std::strcmp(ModuleName, lpModuleEntry.szModule) )
            {
                CloseHandle(hSnapShot);
                return reinterpret_cast<void*>(lpModuleEntry.modBaseAddr);
            }
            bModule = Module32Next(hSnapShot, &lpModuleEntry);
        }
        CloseHandle(hSnapShot);
        return nullptr;
    }
}<|endoftext|>


<commit_before>#include <ShaderCompiler.h>
#include <ShaderDB.h>

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
{
	if (fdwReason == DLL_PROCESS_DETACH && !lpvReserved)
	{
		CloseDB();
	}

	return TRUE;
}
//---------------------------------------------------------------------
<commit_msg>Close DB connection on DLL unloading<commit_after>#include <ShaderCompiler.h>
#include <ShaderDB.h>

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
{
	if (fdwReason == DLL_PROCESS_DETACH)
	{
		// DLL is being unloaded for the current process
		CloseDB();
	}

	return TRUE;
}
//---------------------------------------------------------------------
<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "hotel/planning.h"

class HotelPlanning : public testing::Test
{
public:
  boost::gregorian::date makeDate(int day)
  {
    using namespace boost::gregorian;
    return date(2017, 1, 1) + days(day);
  }

  hotel::Reservation makeReservation(int room, int from, int to)
  {
    using namespace boost::gregorian;
    return hotel::Reservation("", room, date_period(makeDate(from), makeDate(to)));
  }
};

class MockPlanningBoardObserver : public hotel::PlanningBoardObserver
{
public:
  MOCK_METHOD1(reservationsAdded, void(const std::vector<const hotel::Reservation*>& reservations));
  MOCK_METHOD1(reservationsRemoved, void(const std::vector<const hotel::Reservation*>& reservations));
  MOCK_METHOD1(initialUpdate, void(const hotel::PlanningBoard&));
  MOCK_METHOD0(allReservationsRemoved, void());
};

TEST_F(HotelPlanning, Planning)
{
  using namespace boost::gregorian;
  auto reservation1 = makeReservation(1, 1, 3);
  auto reservation2 = makeReservation(1, 3, 5);
  auto reservation3 = makeReservation(1, 4, 5);
  auto reservation4 = makeReservation(3, 4, 9);
  auto reservation5 = makeReservation(3, 2, 2);

  hotel::PlanningBoard board;
  ASSERT_EQ(0, board.reservations().size());
  ASSERT_EQ(0, board.getReservationsInPeriod(date_period(date(2016, 1, 1), date(2018, 1, 1))).size());
  ASSERT_TRUE(board.getPlanningExtent().is_null());

  // No rooms
  ASSERT_FALSE(board.hasRoom(1));
  ASSERT_FALSE(board.isFree(1, reservation1.dateRange()));
  ASSERT_FALSE(board.canAddReservation(reservation1));
  ASSERT_EQ(0, board.getAvailableDaysFrom(1, date(2016, 1, 1)));

  // Adding rooms
  board.addRoomId(1);
  board.addRoomId(2);
  ASSERT_TRUE(board.hasRoom(1));
  ASSERT_TRUE(board.hasRoom(2));
  ASSERT_FALSE(board.hasRoom(3));
  ASSERT_TRUE(board.isFree(1, reservation1.dateRange()));
  ASSERT_TRUE(board.canAddReservation(reservation1));
  ASSERT_EQ(std::numeric_limits<int>::max(), board.getAvailableDaysFrom(1, makeDate(-100)));

  // Adding reservations
  board.addReservation(std::make_unique<hotel::Reservation>(reservation1));
  board.addReservation(std::make_unique<hotel::Reservation>(reservation2));
  ASSERT_ANY_THROW(board.addReservation(nullptr));
  ASSERT_ANY_THROW(board.addReservation(std::make_unique<hotel::Reservation>(reservation2)));
  ASSERT_ANY_THROW(board.addReservation(std::make_unique<hotel::Reservation>(reservation3)));
  ASSERT_ANY_THROW(board.addReservation(std::make_unique<hotel::Reservation>(reservation4)));
  ASSERT_ANY_THROW(board.addReservation(std::make_unique<hotel::Reservation>(reservation5)));
  ASSERT_EQ(2, board.reservations().size());
  ASSERT_EQ(date_period(makeDate(1), makeDate(5)), board.getPlanningExtent());

  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 2, 4)));
  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 6, 8)));
  ASSERT_EQ(4, board.reservations().size());
  ASSERT_EQ(date_period(makeDate(1), makeDate(8)), board.getPlanningExtent());
  // After the above insertions we have the following situation:
  // Room   1 2 3 4 5 6 7 8 9
  //    1   [###|###]
  //    2     [###]   [###]
  ASSERT_EQ(11, board.getAvailableDaysFrom(1, makeDate(-10)));
  ASSERT_EQ(1, board.getAvailableDaysFrom(1, makeDate(0)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(1, makeDate(1)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(1, makeDate(3)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(1, makeDate(4)));
  ASSERT_EQ(std::numeric_limits<int>::max(), board.getAvailableDaysFrom(1, makeDate(5)));
  ASSERT_EQ(12, board.getAvailableDaysFrom(2, makeDate(-10)));
  ASSERT_EQ(2, board.getAvailableDaysFrom(2, makeDate(0)));
  ASSERT_EQ(1, board.getAvailableDaysFrom(2, makeDate(1)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(2, makeDate(3)));
  ASSERT_EQ(2, board.getAvailableDaysFrom(2, makeDate(4)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(2, makeDate(6)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(2, makeDate(7)));
  ASSERT_EQ(std::numeric_limits<int>::max(), board.getAvailableDaysFrom(2, makeDate(8)));
  ASSERT_TRUE(board.canAddReservation(makeReservation(1, 0, 1)));
  ASSERT_FALSE(board.canAddReservation(makeReservation(1, 0, 2)));
  ASSERT_TRUE(board.canAddReservation(makeReservation(1, 5, 7)));
  ASSERT_TRUE(board.canAddReservation(makeReservation(2, 4, 5)));
  ASSERT_TRUE(board.canAddReservation(makeReservation(2, 4, 6)));
  ASSERT_FALSE(board.canAddReservation(makeReservation(2, 4, 7)));
  ASSERT_FALSE(board.canAddReservation(makeReservation(2, 10, 10)));

  ASSERT_EQ(0, board.getReservationsInPeriod(date_period(makeDate(0), makeDate(1))).size());
  ASSERT_EQ(1, board.getReservationsInPeriod(date_period(makeDate(1), makeDate(2))).size());
  ASSERT_EQ(2, board.getReservationsInPeriod(date_period(makeDate(1), makeDate(3))).size());
  ASSERT_EQ(3, board.getReservationsInPeriod(date_period(makeDate(1), makeDate(4))).size());
  ASSERT_EQ(4, board.getReservationsInPeriod(date_period(makeDate(1), makeDate(7))).size());
  ASSERT_EQ(1, board.getReservationsInPeriod(date_period(makeDate(5), makeDate(7))).size());

  // Test removal of reservations
  auto allReservations = board.getReservationsInPeriod(board.getPlanningExtent());
  ASSERT_EQ(4, allReservations.size());
  board.removeReservation(allReservations[0]);
  ASSERT_EQ(3, board.getReservationsInPeriod(board.getPlanningExtent()).size());
  board.removeReservation(allReservations[1]);
  ASSERT_EQ(2, board.getReservationsInPeriod(board.getPlanningExtent()).size());
  board.removeReservation(allReservations[2]);
  ASSERT_EQ(1, board.getReservationsInPeriod(board.getPlanningExtent()).size());
  board.removeReservation(allReservations[3]);
  ASSERT_EQ(0, board.getReservationsInPeriod(board.getPlanningExtent()).size());
  ASSERT_ANY_THROW(board.removeReservation(nullptr));

  /*
    void addObserver(PlanningBoardObserver* observer);
    void removeObserver(PlanningBoardObserver* observer);
  */
}

TEST_F(HotelPlanning, PlanningBoardObserver)
{
  hotel::PlanningBoard board;
  board.addRoomId(2);
  MockPlanningBoardObserver observer;

  EXPECT_CALL(observer, initialUpdate(testing::_)).Times(1);
  observer.setObservedPlanningBoard(&board);
  testing::Mock::VerifyAndClear(&observer);

  // Add reservations
  EXPECT_CALL(observer, reservationsAdded(testing::_)).Times(2);
  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 2, 4)));
  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 6, 8)));
  testing::Mock::VerifyAndClear(&observer);

  // Remove reservations
  EXPECT_CALL(observer, reservationsRemoved(testing::_)).Times(1);
  auto allReservations = board.getReservationsInPeriod(board.getPlanningExtent());
  board.removeReservation(allReservations[0]);
  testing::Mock::VerifyAndClear(&observer);

  // Reset observed board to nullptr
  EXPECT_CALL(observer, allReservationsRemoved()).Times(1);
  observer.setObservedPlanningBoard(nullptr);
  testing::Mock::VerifyAndClear(&observer);

  // Add reservations while no observer attached
  EXPECT_CALL(observer, initialUpdate(testing::_)).Times(1);
  EXPECT_CALL(observer, reservationsAdded(testing::_)).Times(0);
  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 10, 11)));
  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 11, 12)));
  observer.setObservedPlanningBoard(&board);
  testing::Mock::VerifyAndClear(&observer);
}
<commit_msg>Removed leftover comment in test<commit_after>#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "hotel/planning.h"

class HotelPlanning : public testing::Test
{
public:
  boost::gregorian::date makeDate(int day)
  {
    using namespace boost::gregorian;
    return date(2017, 1, 1) + days(day);
  }

  hotel::Reservation makeReservation(int room, int from, int to)
  {
    using namespace boost::gregorian;
    return hotel::Reservation("", room, date_period(makeDate(from), makeDate(to)));
  }
};

class MockPlanningBoardObserver : public hotel::PlanningBoardObserver
{
public:
  MOCK_METHOD1(reservationsAdded, void(const std::vector<const hotel::Reservation*>& reservations));
  MOCK_METHOD1(reservationsRemoved, void(const std::vector<const hotel::Reservation*>& reservations));
  MOCK_METHOD1(initialUpdate, void(const hotel::PlanningBoard&));
  MOCK_METHOD0(allReservationsRemoved, void());
};

TEST_F(HotelPlanning, Planning)
{
  using namespace boost::gregorian;
  auto reservation1 = makeReservation(1, 1, 3);
  auto reservation2 = makeReservation(1, 3, 5);
  auto reservation3 = makeReservation(1, 4, 5);
  auto reservation4 = makeReservation(3, 4, 9);
  auto reservation5 = makeReservation(3, 2, 2);

  hotel::PlanningBoard board;
  ASSERT_EQ(0, board.reservations().size());
  ASSERT_EQ(0, board.getReservationsInPeriod(date_period(date(2016, 1, 1), date(2018, 1, 1))).size());
  ASSERT_TRUE(board.getPlanningExtent().is_null());

  // No rooms
  ASSERT_FALSE(board.hasRoom(1));
  ASSERT_FALSE(board.isFree(1, reservation1.dateRange()));
  ASSERT_FALSE(board.canAddReservation(reservation1));
  ASSERT_EQ(0, board.getAvailableDaysFrom(1, date(2016, 1, 1)));

  // Adding rooms
  board.addRoomId(1);
  board.addRoomId(2);
  ASSERT_TRUE(board.hasRoom(1));
  ASSERT_TRUE(board.hasRoom(2));
  ASSERT_FALSE(board.hasRoom(3));
  ASSERT_TRUE(board.isFree(1, reservation1.dateRange()));
  ASSERT_TRUE(board.canAddReservation(reservation1));
  ASSERT_EQ(std::numeric_limits<int>::max(), board.getAvailableDaysFrom(1, makeDate(-100)));

  // Adding reservations
  board.addReservation(std::make_unique<hotel::Reservation>(reservation1));
  board.addReservation(std::make_unique<hotel::Reservation>(reservation2));
  ASSERT_ANY_THROW(board.addReservation(nullptr));
  ASSERT_ANY_THROW(board.addReservation(std::make_unique<hotel::Reservation>(reservation2)));
  ASSERT_ANY_THROW(board.addReservation(std::make_unique<hotel::Reservation>(reservation3)));
  ASSERT_ANY_THROW(board.addReservation(std::make_unique<hotel::Reservation>(reservation4)));
  ASSERT_ANY_THROW(board.addReservation(std::make_unique<hotel::Reservation>(reservation5)));
  ASSERT_EQ(2, board.reservations().size());
  ASSERT_EQ(date_period(makeDate(1), makeDate(5)), board.getPlanningExtent());

  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 2, 4)));
  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 6, 8)));
  ASSERT_EQ(4, board.reservations().size());
  ASSERT_EQ(date_period(makeDate(1), makeDate(8)), board.getPlanningExtent());
  // After the above insertions we have the following situation:
  // Room   1 2 3 4 5 6 7 8 9
  //    1   [###|###]
  //    2     [###]   [###]
  ASSERT_EQ(11, board.getAvailableDaysFrom(1, makeDate(-10)));
  ASSERT_EQ(1, board.getAvailableDaysFrom(1, makeDate(0)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(1, makeDate(1)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(1, makeDate(3)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(1, makeDate(4)));
  ASSERT_EQ(std::numeric_limits<int>::max(), board.getAvailableDaysFrom(1, makeDate(5)));
  ASSERT_EQ(12, board.getAvailableDaysFrom(2, makeDate(-10)));
  ASSERT_EQ(2, board.getAvailableDaysFrom(2, makeDate(0)));
  ASSERT_EQ(1, board.getAvailableDaysFrom(2, makeDate(1)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(2, makeDate(3)));
  ASSERT_EQ(2, board.getAvailableDaysFrom(2, makeDate(4)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(2, makeDate(6)));
  ASSERT_EQ(0, board.getAvailableDaysFrom(2, makeDate(7)));
  ASSERT_EQ(std::numeric_limits<int>::max(), board.getAvailableDaysFrom(2, makeDate(8)));
  ASSERT_TRUE(board.canAddReservation(makeReservation(1, 0, 1)));
  ASSERT_FALSE(board.canAddReservation(makeReservation(1, 0, 2)));
  ASSERT_TRUE(board.canAddReservation(makeReservation(1, 5, 7)));
  ASSERT_TRUE(board.canAddReservation(makeReservation(2, 4, 5)));
  ASSERT_TRUE(board.canAddReservation(makeReservation(2, 4, 6)));
  ASSERT_FALSE(board.canAddReservation(makeReservation(2, 4, 7)));
  ASSERT_FALSE(board.canAddReservation(makeReservation(2, 10, 10)));

  ASSERT_EQ(0, board.getReservationsInPeriod(date_period(makeDate(0), makeDate(1))).size());
  ASSERT_EQ(1, board.getReservationsInPeriod(date_period(makeDate(1), makeDate(2))).size());
  ASSERT_EQ(2, board.getReservationsInPeriod(date_period(makeDate(1), makeDate(3))).size());
  ASSERT_EQ(3, board.getReservationsInPeriod(date_period(makeDate(1), makeDate(4))).size());
  ASSERT_EQ(4, board.getReservationsInPeriod(date_period(makeDate(1), makeDate(7))).size());
  ASSERT_EQ(1, board.getReservationsInPeriod(date_period(makeDate(5), makeDate(7))).size());

  // Test removal of reservations
  auto allReservations = board.getReservationsInPeriod(board.getPlanningExtent());
  ASSERT_EQ(4, allReservations.size());
  board.removeReservation(allReservations[0]);
  ASSERT_EQ(3, board.getReservationsInPeriod(board.getPlanningExtent()).size());
  board.removeReservation(allReservations[1]);
  ASSERT_EQ(2, board.getReservationsInPeriod(board.getPlanningExtent()).size());
  board.removeReservation(allReservations[2]);
  ASSERT_EQ(1, board.getReservationsInPeriod(board.getPlanningExtent()).size());
  board.removeReservation(allReservations[3]);
  ASSERT_EQ(0, board.getReservationsInPeriod(board.getPlanningExtent()).size());
  ASSERT_ANY_THROW(board.removeReservation(nullptr));
}

TEST_F(HotelPlanning, PlanningBoardObserver)
{
  hotel::PlanningBoard board;
  board.addRoomId(2);
  MockPlanningBoardObserver observer;

  EXPECT_CALL(observer, initialUpdate(testing::_)).Times(1);
  observer.setObservedPlanningBoard(&board);
  testing::Mock::VerifyAndClear(&observer);

  // Add reservations
  EXPECT_CALL(observer, reservationsAdded(testing::_)).Times(2);
  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 2, 4)));
  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 6, 8)));
  testing::Mock::VerifyAndClear(&observer);

  // Remove reservations
  EXPECT_CALL(observer, reservationsRemoved(testing::_)).Times(1);
  auto allReservations = board.getReservationsInPeriod(board.getPlanningExtent());
  board.removeReservation(allReservations[0]);
  testing::Mock::VerifyAndClear(&observer);

  // Reset observed board to nullptr
  EXPECT_CALL(observer, allReservationsRemoved()).Times(1);
  observer.setObservedPlanningBoard(nullptr);
  testing::Mock::VerifyAndClear(&observer);

  // Add reservations while no observer attached
  EXPECT_CALL(observer, initialUpdate(testing::_)).Times(1);
  EXPECT_CALL(observer, reservationsAdded(testing::_)).Times(0);
  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 10, 11)));
  board.addReservation(std::make_unique<hotel::Reservation>(makeReservation(2, 11, 12)));
  observer.setObservedPlanningBoard(&board);
  testing::Mock::VerifyAndClear(&observer);
}
<|endoftext|>