<commit_before>#include "vlcmex.h"

enum Verb { INIT, RELEASE, OPEN, CLOSE, FRAME, PLAY, PAUSE, INFO, CLEANUP } verbs;

static void cleanup();

libvlc_instance_t* init();
void release(libvlc_instance_t*);
libvlc_media_player_t* open(libvlc_instance_t*, string);
void close(libvlc_media_player_t*);
double frame(libvlc_media_player_t*);
double frame(libvlc_media_player_t*, double);
void play(libvlc_media_player_t*, float);
void pause(libvlc_media_player_t*);
vector<double> info(libvlc_media_player_t*);

static instance_list instances;
static player_list players;
#ifdef OWN_WINDOW
static window_list windows;
#endif

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    if (!check_args(nrhs, 1, 3)) return;

    mexAtExit(cleanup);

    Verb verb = (Verb)(int)(mxGetPr(prhs[0])[0]);
    switch (verb) {
        case INIT:
            if (!check_args(nrhs, 1)) return;
            pack_pointer(plhs[0], init());
            break;
        case RELEASE:
            if (!check_args(nrhs, 2)) return;
            release(unpack_pointer(libvlc_instance_t, prhs[1]));
            break;
        case OPEN:
            if (!check_args(nrhs, 3)) return;
            pack_pointer(plhs[0], open(unpack_pointer(libvlc_instance_t, prhs[1]), arr2str(prhs[2])));
            break;
        case CLOSE:
            if (!check_args(nrhs, 2)) return;
            close(unpack_pointer(libvlc_media_player_t, prhs[1]));
            break;
        case FRAME:
            if (!check_args(nrhs, 2, 3)) return;
            switch (nrhs) {
                case 2:
                    pack_number(plhs[0], frame(unpack_pointer(libvlc_media_player_t, prhs[1])));
                    break;
                case 3:
                    pack_number(plhs[0], frame(unpack_pointer(libvlc_media_player_t, prhs[1]), mxGetPr(prhs[2])[0]));
                    break;
            }
            break;
        case PLAY:
            if (!check_args(nrhs, 3)) return;
            play(unpack_pointer(libvlc_media_player_t, prhs[1]), mxGetPr(prhs[2])[0]);
            break;
        case PAUSE:
            if (!check_args(nrhs, 2)) return;
            pause(unpack_pointer(libvlc_media_player_t, prhs[1]));
            break;
        case INFO:
            if (!check_args(nrhs, 2)) return;
            pack_number(plhs[0], info(unpack_pointer(libvlc_media_player_t, prhs[1])));
            break;
        case CLEANUP:
            if (!check_args(nrhs, 1)) return;
            cleanup();
            break;
        default:
            mexErrMsgIdAndTxt("VLC:badVerb", "Unsupported operation");
            break;
    }
}

static void cleanup()
{
    for (player_list::iterator i = players.begin(); i != players.end(); ++i)
    {
        if (libvlc_media_player_is_playing(*i)) {
            libvlc_media_player_stop(*i);
        }
        libvlc_media_player_release(*i);
    }
    players.clear();

    for (instance_list::iterator i = instances.begin(); i != instances.end(); ++i)
    {
        libvlc_release(*i);
    }
    instances.clear();

#ifdef OWN_WINDOW
    for (window_list::iterator i = windows.begin(); i != windows.end(); ++i)
    {
        close_window(i->second);
    }
#endif
}

libvlc_instance_t* init()
{
#ifdef __APPLE__
#include "apple_workaround.h"
    setenv("VLC_PLUGIN_PATH", _VLC_ "/Contents/MacOS/plugins", 1);
#endif
    libvlc_instance_t *vlc = libvlc_new(0, NULL);
    instances.push_back(vlc);

    if (vlc == NULL) {
        mexWarnMsgIdAndTxt("VLC:init:nullPointer", "VLC instance is NULL");
    }

    return vlc;
}

void release(libvlc_instance_t *vlc)
{
    instance_list::iterator iter = find(instances.begin(), instances.end(), vlc);
    if (iter == instances.end()) {
        mexErrMsgIdAndTxt("VLC:release:invalidHandle", "Not a VLC instance");
        return;
    }

    instances.erase(iter);
    libvlc_release(vlc);
}

libvlc_media_player_t* open(libvlc_instance_t *vlc, string filename)
{
    instance_list::iterator iter = find(instances.begin(), instances.end(), vlc);
    if (iter == instances.end()) {
        mexErrMsgIdAndTxt("VLC:open:invalidHandle", "Not a VLC instance");
        return NULL;
    }

    struct stat statbuf;
    if (stat(filename.c_str(), &statbuf) == -1) {
        mexErrMsgIdAndTxt("VLC:open:badFile", "Could not stat video file: %s", strerror(errno));
        return NULL;
    }

    libvlc_media_t *media = libvlc_media_new_path(vlc, filename.c_str());
    libvlc_media_player_t *player = libvlc_media_player_new_from_media(media);
    if (!media || !player) {
        mexErrMsgIdAndTxt("VLC:open:errorOpening", "Could not open video file");
        return NULL;
    }

    players.push_back(player);

    libvlc_media_parse(media);
    char *title = libvlc_media_get_meta(media, libvlc_meta_Title);

#ifdef __APPLE__
    // get size
    libvlc_media_track_t **tracks;
    int count = libvlc_media_tracks_get(media, &tracks);
    int w = 720, h = 480;
    for (int i = 0; i < count; ++i) {
        if (tracks[i]->i_type == libvlc_track_video) {
            w = tracks[i]->video->i_width > w
                    ? tracks[i]->video->i_width
                    : w;
            h = tracks[i]->video->i_height > h
                    ? tracks[i]->video->i_height
                    : h;
        }
    }
    libvlc_media_tracks_release(tracks, count);
#endif
#ifdef __APPLE__
    NSWindow *win;
    NSView *view = create_window(&win, 100, 100, w, h, title);
    windows[player] = win;

    libvlc_media_player_set_nsobject(player, view);
#endif
#ifdef _WIN32
	windows[player] = create_window(100, 100, 720, 480, title);
	libvlc_media_player_set_hwnd(player, windows[player]);
#endif

    libvlc_media_player_play(player);
    while (!libvlc_media_player_is_playing(player));
    libvlc_media_player_pause(player);

    libvlc_media_release(media);
    return player;
}

void close(libvlc_media_player_t *player)
{

    player_list::iterator iter = find(players.begin(), players.end(), player);
    if (iter == players.end()) {
        mexErrMsgIdAndTxt("VLC:close:invalidHandle", "Not a VLC media player instance");
        return;
    }

#ifdef OWN_WINDOW
    window_list::iterator witer = windows.find(player);
    if (witer != windows.end()) {
        close_window(witer->second);
        windows.erase(witer);
    }
#endif

    players.erase(iter);
    libvlc_media_player_release(player);
}

double frame(libvlc_media_player_t *player, double ms)
{
    if (find(players.begin(), players.end(), player) != players.end()) {
        libvlc_media_player_set_time(player, ms);
        return libvlc_media_player_get_time(player);
    }
    return -1;
}

double frame(libvlc_media_player_t *player)
{
    if (find(players.begin(), players.end(), player) != players.end()) {
        return libvlc_media_player_get_time(player);
    }
    return -1;
}

void play(libvlc_media_player_t *player, float rate)
{
    if (find(players.begin(), players.end(), player) != players.end())
    {
        libvlc_media_player_set_rate(player, rate);
        libvlc_media_player_play(player);
    }
}

void pause(libvlc_media_player_t *player)
{
    if (find(players.begin(), players.end(), player) != players.end())
        libvlc_media_player_pause(player);
}

vector<double> info(libvlc_media_player_t *player)
{
    vector<double> v;
    if (find(players.begin(), players.end(), player) != players.end()) {
        v.push_back(libvlc_media_player_get_fps(player));

        v.push_back(libvlc_media_get_duration(libvlc_media_player_get_media(player)));
    }
    return v;
}

<commit_msg>don't crash when cleaning up twice<commit_after>#include "vlcmex.h"

enum Verb { INIT, RELEASE, OPEN, CLOSE, FRAME, PLAY, PAUSE, INFO, CLEANUP } verbs;

static void cleanup();

libvlc_instance_t* init();
void release(libvlc_instance_t*);
libvlc_media_player_t* open(libvlc_instance_t*, string);
void close(libvlc_media_player_t*);
double frame(libvlc_media_player_t*);
double frame(libvlc_media_player_t*, double);
void play(libvlc_media_player_t*, float);
void pause(libvlc_media_player_t*);
vector<double> info(libvlc_media_player_t*);

static instance_list instances;
static player_list players;
#ifdef OWN_WINDOW
static window_list windows;
#endif

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    if (!check_args(nrhs, 1, 3)) return;

    mexAtExit(cleanup);

    Verb verb = (Verb)(int)(mxGetPr(prhs[0])[0]);
    switch (verb) {
        case INIT:
            if (!check_args(nrhs, 1)) return;
            pack_pointer(plhs[0], init());
            break;
        case RELEASE:
            if (!check_args(nrhs, 2)) return;
            release(unpack_pointer(libvlc_instance_t, prhs[1]));
            break;
        case OPEN:
            if (!check_args(nrhs, 3)) return;
            pack_pointer(plhs[0], open(unpack_pointer(libvlc_instance_t, prhs[1]), arr2str(prhs[2])));
            break;
        case CLOSE:
            if (!check_args(nrhs, 2)) return;
            close(unpack_pointer(libvlc_media_player_t, prhs[1]));
            break;
        case FRAME:
            if (!check_args(nrhs, 2, 3)) return;
            switch (nrhs) {
                case 2:
                    pack_number(plhs[0], frame(unpack_pointer(libvlc_media_player_t, prhs[1])));
                    break;
                case 3:
                    pack_number(plhs[0], frame(unpack_pointer(libvlc_media_player_t, prhs[1]), mxGetPr(prhs[2])[0]));
                    break;
            }
            break;
        case PLAY:
            if (!check_args(nrhs, 3)) return;
            play(unpack_pointer(libvlc_media_player_t, prhs[1]), mxGetPr(prhs[2])[0]);
            break;
        case PAUSE:
            if (!check_args(nrhs, 2)) return;
            pause(unpack_pointer(libvlc_media_player_t, prhs[1]));
            break;
        case INFO:
            if (!check_args(nrhs, 2)) return;
            pack_number(plhs[0], info(unpack_pointer(libvlc_media_player_t, prhs[1])));
            break;
        case CLEANUP:
            if (!check_args(nrhs, 1)) return;
            cleanup();
            break;
        default:
            mexErrMsgIdAndTxt("VLC:badVerb", "Unsupported operation");
            break;
    }
}

static void cleanup()
{
    for (player_list::iterator i = players.begin(); i != players.end(); ++i)
    {
        if (libvlc_media_player_is_playing(*i)) {
            libvlc_media_player_stop(*i);
        }
        libvlc_media_player_release(*i);
    }
    players.clear();

    for (instance_list::iterator i = instances.begin(); i != instances.end(); ++i)
    {
        libvlc_release(*i);
    }
    instances.clear();

#ifdef OWN_WINDOW
    for (window_list::iterator i = windows.begin(); i != windows.end(); ++i)
    {
        close_window(i->second);
    }
    windows.clear();
#endif
}

libvlc_instance_t* init()
{
#ifdef __APPLE__
#include "apple_workaround.h"
    setenv("VLC_PLUGIN_PATH", _VLC_ "/Contents/MacOS/plugins", 1);
#endif
    libvlc_instance_t *vlc = libvlc_new(0, NULL);
    instances.push_back(vlc);

    if (vlc == NULL) {
        mexWarnMsgIdAndTxt("VLC:init:nullPointer", "VLC instance is NULL");
    }

    return vlc;
}

void release(libvlc_instance_t *vlc)
{
    instance_list::iterator iter = find(instances.begin(), instances.end(), vlc);
    if (iter == instances.end()) {
        mexErrMsgIdAndTxt("VLC:release:invalidHandle", "Not a VLC instance");
        return;
    }

    instances.erase(iter);
    libvlc_release(vlc);
}

libvlc_media_player_t* open(libvlc_instance_t *vlc, string filename)
{
    instance_list::iterator iter = find(instances.begin(), instances.end(), vlc);
    if (iter == instances.end()) {
        mexErrMsgIdAndTxt("VLC:open:invalidHandle", "Not a VLC instance");
        return NULL;
    }

    struct stat statbuf;
    if (stat(filename.c_str(), &statbuf) == -1) {
        mexErrMsgIdAndTxt("VLC:open:badFile", "Could not stat video file: %s", strerror(errno));
        return NULL;
    }

    libvlc_media_t *media = libvlc_media_new_path(vlc, filename.c_str());
    libvlc_media_player_t *player = libvlc_media_player_new_from_media(media);
    if (!media || !player) {
        mexErrMsgIdAndTxt("VLC:open:errorOpening", "Could not open video file");
        return NULL;
    }

    players.push_back(player);

    libvlc_media_parse(media);
    char *title = libvlc_media_get_meta(media, libvlc_meta_Title);

#ifdef __APPLE__
    // get size
    libvlc_media_track_t **tracks;
    int count = libvlc_media_tracks_get(media, &tracks);
    int w = 720, h = 480;
    for (int i = 0; i < count; ++i) {
        if (tracks[i]->i_type == libvlc_track_video) {
            w = tracks[i]->video->i_width > w
                    ? tracks[i]->video->i_width
                    : w;
            h = tracks[i]->video->i_height > h
                    ? tracks[i]->video->i_height
                    : h;
        }
    }
    libvlc_media_tracks_release(tracks, count);
#endif
#ifdef __APPLE__
    NSWindow *win;
    NSView *view = create_window(&win, 100, 100, w, h, title);
    windows[player] = win;

    libvlc_media_player_set_nsobject(player, view);
#endif
#ifdef _WIN32
	windows[player] = create_window(100, 100, 720, 480, title);
	libvlc_media_player_set_hwnd(player, windows[player]);
#endif

    libvlc_media_player_play(player);
    while (!libvlc_media_player_is_playing(player));
    libvlc_media_player_pause(player);

    libvlc_media_release(media);
    return player;
}

void close(libvlc_media_player_t *player)
{

    player_list::iterator iter = find(players.begin(), players.end(), player);
    if (iter == players.end()) {
        mexErrMsgIdAndTxt("VLC:close:invalidHandle", "Not a VLC media player instance");
        return;
    }

#ifdef OWN_WINDOW
    window_list::iterator witer = windows.find(player);
    if (witer != windows.end()) {
        close_window(witer->second);
        windows.erase(witer);
    }
#endif

    players.erase(iter);
    libvlc_media_player_release(player);
}

double frame(libvlc_media_player_t *player, double ms)
{
    if (find(players.begin(), players.end(), player) != players.end()) {
        libvlc_media_player_set_time(player, ms);
        return libvlc_media_player_get_time(player);
    }
    return -1;
}

double frame(libvlc_media_player_t *player)
{
    if (find(players.begin(), players.end(), player) != players.end()) {
        return libvlc_media_player_get_time(player);
    }
    return -1;
}

void play(libvlc_media_player_t *player, float rate)
{
    if (find(players.begin(), players.end(), player) != players.end())
    {
        libvlc_media_player_set_rate(player, rate);
        libvlc_media_player_play(player);
    }
}

void pause(libvlc_media_player_t *player)
{
    if (find(players.begin(), players.end(), player) != players.end())
        libvlc_media_player_pause(player);
}

vector<double> info(libvlc_media_player_t *player)
{
    vector<double> v;
    if (find(players.begin(), players.end(), player) != players.end()) {
        v.push_back(libvlc_media_player_get_fps(player));

        v.push_back(libvlc_media_get_duration(libvlc_media_player_get_media(player)));
    }
    return v;
}

<|endoftext|>


<commit_before>// Include GLFW
#include <glfw3.h>
extern GLFWwindow* window; // The "extern" keyword here is to access the variable "window" declared in tutorialXXX.cpp. This is a hack to keep the tutorials simple. Please avoid this.

// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include "controls.hpp"

glm::mat4 ViewMatrix;
glm::mat4 ProjectionMatrix;

glm::mat4 getViewMatrix(){
	return ViewMatrix;
}
glm::mat4 getProjectionMatrix(){
	return ProjectionMatrix;
}

// Initial position : on +Z
glm::vec3 position = glm::vec3( 0, 0, -500 );
// Initial horizontal angle : toward -Z
float horizontalAngle = 0;
// Initial vertical angle : none
float verticalAngle = 0.0f;
// Initial Field of View
float initialFoV = 45.0f;

float speed = 300.f; // 3 units / second
float hRotateSpeed = 150.f;
float vRotateSpeed = 150.f;
float mouseSpeed = 0.005f;

glm::vec3 up = glm::vec3(0, 1, 0);

float dist = 500;
float hRot = 0;
float vRot = 0;

void computeMatricesFromInputs(){

	// glfwGetTime is called only once, the first time this function is called
	static double lastTime = glfwGetTime();

	// Compute time difference between current and last frame
	double currentTime = glfwGetTime();
	float deltaTime = float(currentTime - lastTime);

	// Get mouse position
	double xpos, ypos;
	glfwGetCursorPos(window, &xpos, &ypos);

	static double lastX =  xpos;
	static double lastY = ypos;

	// Reset mouse position for next frame
	//glfwSetCursorPos(window, 1024/2, 768/2);
	double dx = 0.0;
	double dy = 0.0;
	if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS)
	{
		dx = lastX - xpos ;
		dy = lastY - ypos ;
	}

	lastX = xpos;
	lastY = ypos;

	// Compute new orientation
	horizontalAngle += mouseSpeed * float(dx);
	verticalAngle   += mouseSpeed * float(dy );

	// Move forward
	if (glfwGetKey( window, GLFW_KEY_W ) == GLFW_PRESS){
		vRot -= deltaTime * vRotateSpeed;
	}
	// Move backward
	if (glfwGetKey( window, GLFW_KEY_S ) == GLFW_PRESS){
		vRot += deltaTime * vRotateSpeed;
	}
	// Strafe right
	if (glfwGetKey( window, GLFW_KEY_D ) == GLFW_PRESS){
		hRot += deltaTime * hRotateSpeed;
	}
	// Strafe left
	if (glfwGetKey( window, GLFW_KEY_A ) == GLFW_PRESS){
		hRot -= deltaTime * hRotateSpeed;
	}
	//Move towards planet
	if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
		dist -= speed * deltaTime;
		if (dist < 10) dist = 20;
	}
	//Move away from planet
	if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
		dist += speed * deltaTime;
		if (dist > 1000) dist = 1000;
	}

	double hRad = hRot * 3.1415 / 180;
	double vRad = vRot * 3.1415 / 180;

	double x = dist * sin(vRad) * cos(hRad);
	double y = dist * sin(vRad) * sin(hRad);
	double z = dist * cos(vRad);

	double buffer = y;
	y = -z;
	z = buffer;

	position = glm::vec3(x, y, z);

	float FoV = initialFoV;

	// Projection matrix : 45 Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
	ProjectionMatrix = glm::perspective(FoV, 4.0f / 3.0f, 1.0f, 10000.0f);
	// Camera matrix
	ViewMatrix = glm::lookAt(
		position,           // Camera is here
		glm::vec3(0, 0, 0), // and looks here : at the same position, plus "direction"
		up                  // Head is up (set to 0,-1,0 to look upside-down)
	);

	// For the next frame, the "last time" will be "now"
	lastTime = currentTime;
}<commit_msg>tests<commit_after>// Include GLFW
#include <glfw3.h>
extern GLFWwindow* window; // The "extern" keyword here is to access the variable "window" declared in tutorialXXX.cpp. This is a hack to keep the tutorials simple. Please avoid this.

// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include "controls.hpp"

glm::mat4 ViewMatrix;
glm::mat4 ProjectionMatrix;

glm::mat4 getViewMatrix(){
	return ViewMatrix;
}
glm::mat4 getProjectionMatrix(){
	return ProjectionMatrix;
}

// Initial position : on +Z
glm::vec3 position = glm::vec3( 0, 0, -500 );
// Initial horizontal angle : toward -Z
float horizontalAngle = 0;
// Initial vertical angle : none
float verticalAngle = 0.0f;
// Initial Field of View
float initialFoV = 45.0f;

float speed = 300.f; // 3 units / second
float hRotateSpeed = 150.f;
float vRotateSpeed = 150.f;
float mouseSpeed = 0.005f;

glm::vec3 up = glm::vec3(0, 1, 0);

float dist = 500;
float hRot = 0;
float vRot = 0;

void computeMatricesFromInputs(){

	// glfwGetTime is called only once, the first time this function is called
	static double lastTime = glfwGetTime();

	// Compute time difference between current and last frame
	double currentTime = glfwGetTime();
	float deltaTime = float(currentTime - lastTime);

	// Get mouse position
	double xpos, ypos;
	glfwGetCursorPos(window, &xpos, &ypos);

	static double lastX =  xpos;
	static double lastY = ypos;

	// Reset mouse position for next frame
	//glfwSetCursorPos(window, 1024/2, 768/2);
	double dx = 0.0;
	double dy = 0.0;
	if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS)
	{
		dx = lastX - xpos ;
		dy = lastY - ypos ;
	}

	lastX = xpos;
	lastY = ypos;

	// Compute new orientation
	horizontalAngle += mouseSpeed * float(dx);
	verticalAngle   += mouseSpeed * float(dy );

	// Move forward
	if (glfwGetKey( window, GLFW_KEY_W ) == GLFW_PRESS){
		vRot += deltaTime * vRotateSpeed;
		if (vRot > 90)
			vRot = 90;
	}
	// Move backward
	if (glfwGetKey( window, GLFW_KEY_S ) == GLFW_PRESS){
		vRot -= deltaTime * vRotateSpeed;
		if (vRot < -90)
			vRot = -90;
	}
	// Strafe right
	if (glfwGetKey( window, GLFW_KEY_D ) == GLFW_PRESS){
		hRot += deltaTime * hRotateSpeed;
	}
	// Strafe left
	if (glfwGetKey( window, GLFW_KEY_A ) == GLFW_PRESS){
		hRot -= deltaTime * hRotateSpeed;
	}
	//Move towards planet
	if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
		dist -= speed * deltaTime;
		if (dist < 10) dist = 10;
	}
	//Move away from planet
	if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
		dist += speed * deltaTime;
		if (dist > 1000) dist = 1000;
	}

	double hRad = hRot * 3.1415 / 180;
	double vRad = vRot * 3.1415 / 180;

	/*double x = dist * sin(vRad) * cos(hRad);
	double y = dist * sin(vRad) * sin(hRad);
	double z = dist * cos(vRad);

	glm::vec4 wrongPos = glm::vec4(x, y, z, 0);

	float aaa[16] = {
		1, 0, 0, 0,
		0, 0, 1, 0,
		0, 1, 0, 0,
		0, 0, 0, 1
	};
	glm::mat4 bbb;
	memcpy((&bbb), aaa, sizeof(aaa));

	wrongPos = (bbb * wrongPos);
	position = glm::vec3(wrongPos.x, wrongPos.y, wrongPos.z);*/

	double x = dist * cos(vRad) * sin(hRad);
	double y = dist * sin(vRad);
	double z = dist * cos(vRad) * cos(hRad);

	position = glm::vec3(x, y, z);

	glm::vec3 direction = -position;

	printf("\n(%6.4lf, %6.4lf, %6.4lf)", position.x, position.y, position.z);

	// Projection matrix : 45 Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
	ProjectionMatrix = glm::perspective(initialFoV, 4.0f / 3.0f, 0.1f, 10000.0f);
	// Camera matrix
	ViewMatrix = glm::lookAt(
		position,
		position+direction,
		up
	);

	// For the next frame, the "last time" will be "now"
	lastTime = currentTime;
}<|endoftext|>


<commit_before>/*
 * This file is part of signon
 *
 * Copyright (C) 2009-2010 Nokia Corporation.
 *
 * 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 "pluginproxy.h"

#include <sys/types.h>
#include <pwd.h>

#include <QStringList>
#include <QThreadStorage>
#include <QThread>

#include "SignOn/uisessiondata_priv.h"
#include "SignOn/signonplugincommon.h"

/*
 *   TODO: remove the "SignOn/authpluginif.h" include below after the removal
 *         of the deprecated error handling (needed here only for the deprecated
 *         AuthPluginError::PLUGIN_ERROR_GENERAL).
 */
#include "SignOn/authpluginif.h"

using namespace SignOn;

//TODO get this from config
#define REMOTEPLUGIN_BIN_PATH QLatin1String("/usr/bin/signonpluginprocess")
#define PLUGINPROCESS_START_TIMEOUT 5000
#define PLUGINPROCESS_STOP_TIMEOUT 1000

namespace SignonDaemonNS {

    PluginProcess::PluginProcess(QObject *parent) : QProcess(parent)
    {
    }

    PluginProcess::~PluginProcess()
    {
    }

    void PluginProcess::setupChildProcess()
    {
        // Drop all root privileges in the child process and switch to signon user
#ifndef NO_SIGNON_USER
        //get uid and gid
        struct passwd *passwdRecord = getpwnam("signon");
        if ( !passwdRecord ){
            fprintf(stderr, "failed to get user: signon\n");
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
#ifdef SIGNOND_TRACE
        //this is run in remote plugin process, so trace should go to stderr
        fprintf(stderr, "got user: %s with uid: %d\n", passwdRecord->pw_name, passwdRecord->pw_uid);
#endif
        if (( ::setgid(passwdRecord->pw_gid))
                || (::setuid(passwdRecord->pw_uid))
                || (::getuid() != passwdRecord->pw_uid)
                ) {
            fprintf(stderr, "failed to set user: %s with uid: %d", passwdRecord->pw_name, passwdRecord->pw_uid);
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
 #endif
    }

    PluginProxy::PluginProxy(QString type, QObject *parent)
            : QObject(parent)
    {
        TRACE();

        m_type = type;
        m_isProcessing = false;
        m_process = new PluginProcess(this);

        connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(onReadStandardError()));

        /*
         * TODO: some error handling should be added here, at least remove of current
         * request data from the top of the queue and reply an error code
         * */
        connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onExit(int, QProcess::ExitStatus)));
        connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
    }

    PluginProxy::~PluginProxy()
    {
        if (m_process != NULL &&
            m_process->state() != QProcess::NotRunning)
        {
            if (m_isProcessing)
                cancel();

            stop();

            if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT)) {
                qCritical() << "The signon plugin does not react on demand to stop: need to kill it!!!";
                m_process->kill();

                if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT))
                {
                    if (m_process->pid()) {
                        qCritical() << "The signon plugin seems to ignore kill(), killing it from command line";
                        QString killProcessCommand(QString::fromLatin1("kill -9 %1").arg(m_process->pid()));
                        QProcess::execute(killProcessCommand);
                    }
                }
            }
        }
    }

    PluginProxy* PluginProxy::createNewPluginProxy(const QString &type)
    {
        PluginProxy *pp = new PluginProxy(type);

        QStringList args = QStringList() << pp->m_type;
        pp->m_process->start(REMOTEPLUGIN_BIN_PATH, args);

        QByteArray tmp;

        if (!pp->waitForStarted(PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot be started";
            delete pp;
            return NULL;
        }

        if (!pp->readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot load plugin";
            delete pp;
            return NULL;
        }

        pp->m_type = pp->queryType();
        pp->m_mechanisms = pp->queryMechanisms();

        connect(pp->m_process, SIGNAL(readyReadStandardOutput()), pp, SLOT(onReadStandardOutput()));

        TRACE() << "The process is started";
        return pp;
    }

   bool PluginProxy::process(const QString &cancelKey, const QVariantMap &inData, const QString &mechanism)
   {
        TRACE();
        if (!restartIfRequired())
            return false;

        m_cancelKey = cancelKey;
        QVariant value = inData.value(SSOUI_KEY_UIPOLICY);
        m_uiPolicy = value.toInt();

        QDataStream in(m_process);
        in << (quint32)PLUGIN_OP_PROCESS;
        in << QVariant(inData);
        in << QVariant(mechanism);

        m_isProcessing = true;

        return true;
    }

   bool PluginProxy::processUi(const QString &cancelKey, const QVariantMap &inData)
   {
        TRACE() << inData;

        if (!restartIfRequired())
            return false;

        m_cancelKey = cancelKey;

        QDataStream in(m_process);

        in << (quint32)PLUGIN_OP_PROCESS_UI;
        in << QVariant(inData);

        m_isProcessing = true;

        return true;
    }

   bool PluginProxy::processRefresh(const QString &cancelKey, const QVariantMap &inData)
   {
        TRACE() << inData;

        if (!restartIfRequired())
            return false;

        m_cancelKey = cancelKey;

        QDataStream in(m_process);

        in << (quint32)PLUGIN_OP_REFRESH;
        in << QVariant(inData);

        m_isProcessing = true;

        return true;
    }

   void PluginProxy::cancel()
   {
       TRACE();
       QDataStream in(m_process);
       in << (quint32)PLUGIN_OP_CANCEL;
    }

   void PluginProxy::stop()
   {
       TRACE();
       QDataStream in(m_process);
       in << (quint32)PLUGIN_OP_STOP;
    }

    bool PluginProxy::readOnReady(QByteArray &buffer, int timeout)
    {
        bool ready = m_process->waitForReadyRead(timeout);

        if (ready) {
            if (!m_process->bytesAvailable())
                return false;

            while (m_process->bytesAvailable())
                buffer += m_process->readAllStandardOutput();
        }

        return ready;
    }

    bool PluginProxy::isProcessing()
    {
        return m_isProcessing;
    }

    void PluginProxy::onReadStandardOutput()
    {
        disconnect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadStandardOutput()));

        QByteArray token;

        QVariant infoVa;
        QVariantMap info;

        if (!m_process->bytesAvailable()) {
            qCritical() << "No information available on process";
            m_isProcessing = false;
            emit processError(m_cancelKey, Error::InternalServer, QString());
            return;
        }

        QByteArray buffer;

        while (m_process->bytesAvailable())
            buffer += m_process->readAllStandardOutput();

        /*
         * we need to analyze the whole buffer
         * and if it contains error then emit only error
         * otherwise process/emit the incoming information
         * one by one
         * */
        QDataStream out(buffer);
        bool isResultObtained = false;

        while (out.status() == QDataStream::Ok) {
            quint32 opres;
            out >> opres; //result of operation: error code

            TRACE() << opres;

            if (opres == PLUGIN_RESPONSE_RESULT) {
                TRACE() << "PLUGIN_RESPONSE_RESULT";
                out >> infoVa; //SessionData in QVariant
                info = infoVa.toMap();
                m_isProcessing = false;

                if (!isResultObtained)
                    emit processResultReply(m_cancelKey, info);
                else
                    BLAME() << "Unexpected plugin response: " << info;

                isResultObtained = true;
            } else if (opres == PLUGIN_RESPONSE_STORE) {
                TRACE() << "PLUGIN_RESPONSE_STORE";
                out >> infoVa; //SessionData in QVariant
                info = infoVa.toMap();

                if (!isResultObtained)
                    emit processStore(m_cancelKey, info);
                else
                    BLAME() << "Unexpected plugin store: " << info;

            } else if (opres == PLUGIN_RESPONSE_UI) {
                TRACE() << "PLUGIN_RESPONSE_UI";
                out >> infoVa; //UiSessionData in QVariant
                info = infoVa.toMap();

                if (!isResultObtained) {
                    bool allowed = true;

                    if (m_uiPolicy == NoUserInteractionPolicy)
                        allowed = false;

                    if (m_uiPolicy == ValidationPolicy) {
                        bool credentialsQueried =
                            (info.contains(SSOUI_KEY_QUERYUSERNAME)
                            || info.contains(SSOUI_KEY_QUERYPASSWORD));

                        bool captchaQueried  =
                            (info.contains(SSOUI_KEY_CAPTCHAIMG)
                             || info.contains(SSOUI_KEY_CAPTCHAURL));

                        if (credentialsQueried && !captchaQueried)
                            allowed = false;
                    }

                    if (!allowed) {
                        //set error and return;
                        TRACE() << "ui policy prevented ui launch";
                        info.insert(SSOUI_KEY_ERROR, QUERY_ERROR_FORBIDDEN);
                        processUi(m_cancelKey, info);
                    } else {
                        TRACE() << "open ui";
                        emit processUiRequest(m_cancelKey, info);
                    }
                } else {
                    BLAME() << "Unexpected plugin ui response: " << info;
                }
            } else if (opres == PLUGIN_RESPONSE_REFRESHED) {
                TRACE() << "PLUGIN_RESPONSE_REFRESHED";
                out >> infoVa; //UiSessionData in QVariant
                info = infoVa.toMap();

                if (!isResultObtained)
                    emit processRefreshRequest(m_cancelKey, info);
                else
                    BLAME() << "Unexpected plugin ui response: " << info;
            } else if (opres == PLUGIN_RESPONSE_ERROR) {
                TRACE() << "PLUGIN_RESPONSE_ERROR";
                quint32 err;
                QString errorMessage;
                out >> err;
                out >> errorMessage;
                m_isProcessing = false;

                if (!isResultObtained)
                    emit processError(m_cancelKey, (int)err, errorMessage);
                else
                    BLAME() << "Unexpected plugin error: " << errorMessage;

                isResultObtained = true;
            } else if (opres == PLUGIN_RESPONSE_SIGNAL) {
                TRACE() << "PLUGIN_RESPONSE_SIGNAL";
                quint32 state;
                QString message;

                out >> state;
                out >> message;

                if (!isResultObtained)
                    emit stateChanged(m_cancelKey, (int)state, message);
                else
                    BLAME() << "Unexpected plugin signal: " << state << " " << message;
            }
        }

        connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadStandardOutput()));
    }

    void PluginProxy::onReadStandardError()
    {
        QString ba = QString::fromLatin1(m_process->readAllStandardError());
        TRACE() << ba;
    }

    void PluginProxy::onExit(int exitCode, QProcess::ExitStatus exitStatus)
    {
        TRACE() << "Plugin process exit with code " << exitCode << " : " << exitStatus;

        if (m_isProcessing || exitStatus == QProcess::CrashExit) {
            qCritical() << "Challenge produces CRASH!";
            emit processError(m_cancelKey, Error::InternalServer, QLatin1String("plugin processed crashed"));
        }
        if (exitCode == 2) {
            TRACE() << "plugin process terminated because cannot change user";
        }

        m_isProcessing = false;
    }

    void PluginProxy::onError(QProcess::ProcessError err)
    {
        TRACE() << "Error: " << err;
    }

    QString PluginProxy::queryType()
    {
        TRACE();

        if (!restartIfRequired())
            return QString();

        QDataStream ds(m_process);
        ds << (quint32)PLUGIN_OP_TYPE;

        QByteArray typeBa, buffer;
        bool result;

        if ((result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) {
            QDataStream out(buffer);
            out >> typeBa;
        } else
            qCritical("PluginProxy returned NULL result");

        return QString::fromLatin1(typeBa);
    }

    QStringList PluginProxy::queryMechanisms()
    {
        TRACE();

        if (!restartIfRequired())
            return QStringList();

        QDataStream in(m_process);
        in << (quint32)PLUGIN_OP_MECHANISMS;

        QByteArray buffer;
        QStringList strList;
        bool result;

        if ((result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) {
            QVariant mechanismsVar;
            QDataStream out(buffer);

            out >> mechanismsVar;
            QVariantList varList = mechanismsVar.toList();

            for (int i = 0; i < varList.count(); i++)
                    strList << varList.at(i).toString();

            TRACE() << strList;
        } else
            qCritical("PluginProxy returned NULL result");

        return strList;
    }

    bool PluginProxy::waitForStarted(int timeout)
    {
        return m_process->waitForStarted(timeout);
    }

    bool PluginProxy::waitForFinished(int timeout)
    {
        return m_process->waitForFinished(timeout);
    }

    bool PluginProxy::restartIfRequired()
    {
        if (m_process->state() == QProcess::NotRunning) {
            TRACE() << "RESTART REQUIRED";
            m_process->start(REMOTEPLUGIN_BIN_PATH, QStringList(m_type));

            QByteArray tmp;
            if (!waitForStarted(PLUGINPROCESS_START_TIMEOUT) || !readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT))
                return false;
        }
        return true;
    }

} //namespace SignonDaemonNS
<commit_msg>Included common header.<commit_after>/*
 * This file is part of signon
 *
 * Copyright (C) 2009-2010 Nokia Corporation.
 *
 * 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 "pluginproxy.h"

#include <sys/types.h>
#include <pwd.h>

#include <QStringList>
#include <QThreadStorage>
#include <QThread>

#include "signond-common.h"
#include "SignOn/uisessiondata_priv.h"
#include "SignOn/signonplugincommon.h"

/*
 *   TODO: remove the "SignOn/authpluginif.h" include below after the removal
 *         of the deprecated error handling (needed here only for the deprecated
 *         AuthPluginError::PLUGIN_ERROR_GENERAL).
 */
#include "SignOn/authpluginif.h"

using namespace SignOn;

//TODO get this from config
#define REMOTEPLUGIN_BIN_PATH QLatin1String("/usr/bin/signonpluginprocess")
#define PLUGINPROCESS_START_TIMEOUT 5000
#define PLUGINPROCESS_STOP_TIMEOUT 1000

namespace SignonDaemonNS {

    PluginProcess::PluginProcess(QObject *parent) : QProcess(parent)
    {
    }

    PluginProcess::~PluginProcess()
    {
    }

    void PluginProcess::setupChildProcess()
    {
        // Drop all root privileges in the child process and switch to signon user
#ifndef NO_SIGNON_USER
        //get uid and gid
        struct passwd *passwdRecord = getpwnam("signon");
        if ( !passwdRecord ){
            fprintf(stderr, "failed to get user: signon\n");
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
#ifdef SIGNOND_TRACE
        //this is run in remote plugin process, so trace should go to stderr
        fprintf(stderr, "got user: %s with uid: %d\n", passwdRecord->pw_name, passwdRecord->pw_uid);
#endif
        if (( ::setgid(passwdRecord->pw_gid))
                || (::setuid(passwdRecord->pw_uid))
                || (::getuid() != passwdRecord->pw_uid)
                ) {
            fprintf(stderr, "failed to set user: %s with uid: %d", passwdRecord->pw_name, passwdRecord->pw_uid);
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
 #endif
    }

    PluginProxy::PluginProxy(QString type, QObject *parent)
            : QObject(parent)
    {
        TRACE();

        m_type = type;
        m_isProcessing = false;
        m_process = new PluginProcess(this);

        connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(onReadStandardError()));

        /*
         * TODO: some error handling should be added here, at least remove of current
         * request data from the top of the queue and reply an error code
         * */
        connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onExit(int, QProcess::ExitStatus)));
        connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
    }

    PluginProxy::~PluginProxy()
    {
        if (m_process != NULL &&
            m_process->state() != QProcess::NotRunning)
        {
            if (m_isProcessing)
                cancel();

            stop();

            if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT)) {
                qCritical() << "The signon plugin does not react on demand to stop: need to kill it!!!";
                m_process->kill();

                if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT))
                {
                    if (m_process->pid()) {
                        qCritical() << "The signon plugin seems to ignore kill(), killing it from command line";
                        QString killProcessCommand(QString::fromLatin1("kill -9 %1").arg(m_process->pid()));
                        QProcess::execute(killProcessCommand);
                    }
                }
            }
        }
    }

    PluginProxy* PluginProxy::createNewPluginProxy(const QString &type)
    {
        PluginProxy *pp = new PluginProxy(type);

        QStringList args = QStringList() << pp->m_type;
        pp->m_process->start(REMOTEPLUGIN_BIN_PATH, args);

        QByteArray tmp;

        if (!pp->waitForStarted(PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot be started";
            delete pp;
            return NULL;
        }

        if (!pp->readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot load plugin";
            delete pp;
            return NULL;
        }

        pp->m_type = pp->queryType();
        pp->m_mechanisms = pp->queryMechanisms();

        connect(pp->m_process, SIGNAL(readyReadStandardOutput()), pp, SLOT(onReadStandardOutput()));

        TRACE() << "The process is started";
        return pp;
    }

   bool PluginProxy::process(const QString &cancelKey, const QVariantMap &inData, const QString &mechanism)
   {
        TRACE();
        if (!restartIfRequired())
            return false;

        m_cancelKey = cancelKey;
        QVariant value = inData.value(SSOUI_KEY_UIPOLICY);
        m_uiPolicy = value.toInt();

        QDataStream in(m_process);
        in << (quint32)PLUGIN_OP_PROCESS;
        in << QVariant(inData);
        in << QVariant(mechanism);

        m_isProcessing = true;

        return true;
    }

   bool PluginProxy::processUi(const QString &cancelKey, const QVariantMap &inData)
   {
        TRACE() << inData;

        if (!restartIfRequired())
            return false;

        m_cancelKey = cancelKey;

        QDataStream in(m_process);

        in << (quint32)PLUGIN_OP_PROCESS_UI;
        in << QVariant(inData);

        m_isProcessing = true;

        return true;
    }

   bool PluginProxy::processRefresh(const QString &cancelKey, const QVariantMap &inData)
   {
        TRACE() << inData;

        if (!restartIfRequired())
            return false;

        m_cancelKey = cancelKey;

        QDataStream in(m_process);

        in << (quint32)PLUGIN_OP_REFRESH;
        in << QVariant(inData);

        m_isProcessing = true;

        return true;
    }

   void PluginProxy::cancel()
   {
       TRACE();
       QDataStream in(m_process);
       in << (quint32)PLUGIN_OP_CANCEL;
    }

   void PluginProxy::stop()
   {
       TRACE();
       QDataStream in(m_process);
       in << (quint32)PLUGIN_OP_STOP;
    }

    bool PluginProxy::readOnReady(QByteArray &buffer, int timeout)
    {
        bool ready = m_process->waitForReadyRead(timeout);

        if (ready) {
            if (!m_process->bytesAvailable())
                return false;

            while (m_process->bytesAvailable())
                buffer += m_process->readAllStandardOutput();
        }

        return ready;
    }

    bool PluginProxy::isProcessing()
    {
        return m_isProcessing;
    }

    void PluginProxy::onReadStandardOutput()
    {
        disconnect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadStandardOutput()));

        QByteArray token;

        QVariant infoVa;
        QVariantMap info;

        if (!m_process->bytesAvailable()) {
            qCritical() << "No information available on process";
            m_isProcessing = false;
            emit processError(m_cancelKey, Error::InternalServer, QString());
            return;
        }

        QByteArray buffer;

        while (m_process->bytesAvailable())
            buffer += m_process->readAllStandardOutput();

        /*
         * we need to analyze the whole buffer
         * and if it contains error then emit only error
         * otherwise process/emit the incoming information
         * one by one
         * */
        QDataStream out(buffer);
        bool isResultObtained = false;

        while (out.status() == QDataStream::Ok) {
            quint32 opres;
            out >> opres; //result of operation: error code

            TRACE() << opres;

            if (opres == PLUGIN_RESPONSE_RESULT) {
                TRACE() << "PLUGIN_RESPONSE_RESULT";
                out >> infoVa; //SessionData in QVariant
                info = infoVa.toMap();
                m_isProcessing = false;

                if (!isResultObtained)
                    emit processResultReply(m_cancelKey, info);
                else
                    BLAME() << "Unexpected plugin response: " << info;

                isResultObtained = true;
            } else if (opres == PLUGIN_RESPONSE_STORE) {
                TRACE() << "PLUGIN_RESPONSE_STORE";
                out >> infoVa; //SessionData in QVariant
                info = infoVa.toMap();

                if (!isResultObtained)
                    emit processStore(m_cancelKey, info);
                else
                    BLAME() << "Unexpected plugin store: " << info;

            } else if (opres == PLUGIN_RESPONSE_UI) {
                TRACE() << "PLUGIN_RESPONSE_UI";
                out >> infoVa; //UiSessionData in QVariant
                info = infoVa.toMap();

                if (!isResultObtained) {
                    bool allowed = true;

                    if (m_uiPolicy == NoUserInteractionPolicy)
                        allowed = false;

                    if (m_uiPolicy == ValidationPolicy) {
                        bool credentialsQueried =
                            (info.contains(SSOUI_KEY_QUERYUSERNAME)
                            || info.contains(SSOUI_KEY_QUERYPASSWORD));

                        bool captchaQueried  =
                            (info.contains(SSOUI_KEY_CAPTCHAIMG)
                             || info.contains(SSOUI_KEY_CAPTCHAURL));

                        if (credentialsQueried && !captchaQueried)
                            allowed = false;
                    }

                    if (!allowed) {
                        //set error and return;
                        TRACE() << "ui policy prevented ui launch";
                        info.insert(SSOUI_KEY_ERROR, QUERY_ERROR_FORBIDDEN);
                        processUi(m_cancelKey, info);
                    } else {
                        TRACE() << "open ui";
                        emit processUiRequest(m_cancelKey, info);
                    }
                } else {
                    BLAME() << "Unexpected plugin ui response: " << info;
                }
            } else if (opres == PLUGIN_RESPONSE_REFRESHED) {
                TRACE() << "PLUGIN_RESPONSE_REFRESHED";
                out >> infoVa; //UiSessionData in QVariant
                info = infoVa.toMap();

                if (!isResultObtained)
                    emit processRefreshRequest(m_cancelKey, info);
                else
                    BLAME() << "Unexpected plugin ui response: " << info;
            } else if (opres == PLUGIN_RESPONSE_ERROR) {
                TRACE() << "PLUGIN_RESPONSE_ERROR";
                quint32 err;
                QString errorMessage;
                out >> err;
                out >> errorMessage;
                m_isProcessing = false;

                if (!isResultObtained)
                    emit processError(m_cancelKey, (int)err, errorMessage);
                else
                    BLAME() << "Unexpected plugin error: " << errorMessage;

                isResultObtained = true;
            } else if (opres == PLUGIN_RESPONSE_SIGNAL) {
                TRACE() << "PLUGIN_RESPONSE_SIGNAL";
                quint32 state;
                QString message;

                out >> state;
                out >> message;

                if (!isResultObtained)
                    emit stateChanged(m_cancelKey, (int)state, message);
                else
                    BLAME() << "Unexpected plugin signal: " << state << " " << message;
            }
        }

        connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadStandardOutput()));
    }

    void PluginProxy::onReadStandardError()
    {
        QString ba = QString::fromLatin1(m_process->readAllStandardError());
        TRACE() << ba;
    }

    void PluginProxy::onExit(int exitCode, QProcess::ExitStatus exitStatus)
    {
        TRACE() << "Plugin process exit with code " << exitCode << " : " << exitStatus;

        if (m_isProcessing || exitStatus == QProcess::CrashExit) {
            qCritical() << "Challenge produces CRASH!";
            emit processError(m_cancelKey, Error::InternalServer, QLatin1String("plugin processed crashed"));
        }
        if (exitCode == 2) {
            TRACE() << "plugin process terminated because cannot change user";
        }

        m_isProcessing = false;
    }

    void PluginProxy::onError(QProcess::ProcessError err)
    {
        TRACE() << "Error: " << err;
    }

    QString PluginProxy::queryType()
    {
        TRACE();

        if (!restartIfRequired())
            return QString();

        QDataStream ds(m_process);
        ds << (quint32)PLUGIN_OP_TYPE;

        QByteArray typeBa, buffer;
        bool result;

        if ((result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) {
            QDataStream out(buffer);
            out >> typeBa;
        } else
            qCritical("PluginProxy returned NULL result");

        return QString::fromLatin1(typeBa);
    }

    QStringList PluginProxy::queryMechanisms()
    {
        TRACE();

        if (!restartIfRequired())
            return QStringList();

        QDataStream in(m_process);
        in << (quint32)PLUGIN_OP_MECHANISMS;

        QByteArray buffer;
        QStringList strList;
        bool result;

        if ((result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) {
            QVariant mechanismsVar;
            QDataStream out(buffer);

            out >> mechanismsVar;
            QVariantList varList = mechanismsVar.toList();

            for (int i = 0; i < varList.count(); i++)
                    strList << varList.at(i).toString();

            TRACE() << strList;
        } else
            qCritical("PluginProxy returned NULL result");

        return strList;
    }

    bool PluginProxy::waitForStarted(int timeout)
    {
        return m_process->waitForStarted(timeout);
    }

    bool PluginProxy::waitForFinished(int timeout)
    {
        return m_process->waitForFinished(timeout);
    }

    bool PluginProxy::restartIfRequired()
    {
        if (m_process->state() == QProcess::NotRunning) {
            TRACE() << "RESTART REQUIRED";
            m_process->start(REMOTEPLUGIN_BIN_PATH, QStringList(m_type));

            QByteArray tmp;
            if (!waitForStarted(PLUGINPROCESS_START_TIMEOUT) || !readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT))
                return false;
        }
        return true;
    }

} //namespace SignonDaemonNS
<|endoftext|>


<commit_before>#include "QOsgViewer.h"

#include <osgGA/TrackballManipulator>
#include <osgViewer/ViewerEventHandlers>
#include "simvis/osg_camera_man.h"
#include "xo/system/log.h"
#include "qevent.h"
#include "simvis/node.h"
#include "simvis/scene.h"
#include "osg/MatrixTransform"
#include "xo/geometry/quat.h"
#include "simvis/osg_tools.h"

// class for routing GUI events to QOsgViewer
// This is needed because QOsgViewer can't derive from GUIEventHandler directly
// because both are derived from osg::Object (basically, because of bad design)
class QOsgEventHandler : public osgGA::GUIEventHandler
{
public:
	QOsgEventHandler( QOsgViewer& viewer ) : viewer_( viewer ) {}
	virtual bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) { return viewer_.handle( ea, aa ); }
private:
	QOsgViewer& viewer_;
};

QOsgViewer::QOsgViewer( QWidget* parent /*= 0*/, Qt::WindowFlags f /*= 0*/, osgViewer::ViewerBase::ThreadingModel threadingModel/*=osgViewer::CompositeViewer::SingleThreaded*/ ) :
QWidget( parent, f ),
capture_handler_( nullptr ),
frame_count_( 0 ),
scene_light_offset_( -2, 8, 3 )
{
	QCoreApplication::instance()->installEventFilter( this );
	setThreadingModel( threadingModel );

	// disable the default setting of viewer.done() by pressing Escape.
	setKeyEventSetsDone( 0 );

	// setup viewer grid
	QWidget* widget1 = addViewWidget( createGraphicsWindow( 0, 0, 100, 100, "", true ) );
	auto* grid = new QGridLayout;
	grid->addWidget( widget1 );
	setLayout( grid );
	grid->setMargin( 1 );

	// start timer
	// TODO: remove this -- only update after something has changed
	connect( &timer_, SIGNAL( timeout() ), this, SLOT( update() ) );
	timer_.start( 1000 / 120 );
}

QWidget* QOsgViewer::addViewWidget( osgQt::GraphicsWindowQt* gw )
{
	view_ = new osgViewer::View;
	addView( view_ );

	osg::Camera* cam = view_->getCamera();
	cam->setGraphicsContext( gw );

	const osg::GraphicsContext::Traits* traits = gw->getTraits();

	cam->setClearColor( osg::Vec4( 0.55, 0.55, 0.55, 1.0 ) );
	cam->setViewport( new osg::Viewport( 0, 0, traits->width, traits->height ) );
	cam->setProjectionMatrixAsPerspective( 30.0f, static_cast<double>( traits->width ) / static_cast<double>( traits->height ), 1.0f, 10000.0f );

	// add event handlers
	view_->addEventHandler( new osgViewer::StatsHandler );
	view_->addEventHandler( new QOsgEventHandler( *this ) );

	// disable lighting by default
	view_->setLightingMode( osg::View::NO_LIGHT );

	// setup camera manipulator
	gw->setTouchEventsEnabled( true );

	camera_man_ = new vis::osg_camera_man();
	camera_man_->setVerticalAxisFixed( false );
	view_->setCameraManipulator( camera_man_ );

	return gw->getGLWidget();
}

osgQt::GraphicsWindowQt* QOsgViewer::createGraphicsWindow( int x, int y, int w, int h, const std::string& name/*=""*/, bool windowDecoration/*=false */ )
{
	osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
	ds->setNumMultiSamples( 2 );

	osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
	traits->windowName = name;
	traits->windowDecoration = windowDecoration;
	traits->x = x;
	traits->y = y;
	traits->width = w;
	traits->height = h;
	traits->doubleBuffer = true;
	traits->alpha = ds->getMinimumNumAlphaBits();
	traits->stencil = ds->getMinimumNumStencilBits();
	traits->sampleBuffers = ds->getMultiSamples();
	traits->samples = ds->getNumMultiSamples();

	width_ = w;
	height_ = h;

	return new osgQt::GraphicsWindowQt( traits.get() );
}

void QOsgViewer::paintEvent( QPaintEvent* event )
{
	// prevent capturing duplicate frames
	if ( isCapturing() && current_frame_time_ == last_drawn_frame_time_ )
		return; // this frame was already captured, skip

	updateLightPos();

	++frame_count_;
	frame();
	last_drawn_frame_time_ = current_frame_time_;
}

void QOsgViewer::setScene( vis::scene* s )
{
	scene_ = s;
	for ( size_t i = 0; i < getNumViews(); ++i )
		getView( i )->setSceneData( s->osg_node() );

	// init light
	scene_light_ = scene_->add_light( scene_light_offset_, vis::make_white( 1 ) );
	scene_light_.attenuation( 1.0f, 0.0f, 0.0f );
}

void QOsgViewer::setHud( const xo::path& file )
{
	hud_ = vis::plane( xo::vec3f( hud_size, 0, 0 ), xo::vec3f( 0, hud_size, 0 ), file, 1.0f, 1.0f );
	hud_.osg_trans_node().setReferenceFrame( osg::Transform::ABSOLUTE_RF );
	auto geostate = hud_.osg_group().getChild( 0 )->asGeode()->getDrawable( 0 )->getOrCreateStateSet();
	geostate->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
	geostate->setMode( GL_BLEND, osg::StateAttribute::ON );
	view_->getCamera()->addChild( hud_.osg_node() );
	//updateHudPos();
}

void QOsgViewer::updateHudPos()
{
	double fovy, aspect, nearplane, farplane;
	view_->getCamera()->getProjectionMatrixAsPerspective( fovy, aspect, nearplane, farplane );
	//xo::log::info( "aspect ratio = ", aspect );
	auto hh = tan( xo::deg_to_rad( fovy ) / 2 );
	auto hw = tan( atan( hh * aspect ) );
	hud_.pos( xo::vec3f( hw - 0.55f * hud_size, -hh + 0.55f * hud_size, -1 ) );
}

void QOsgViewer::updateLightPos()
{
	auto center = camera_man_->getCenter();
	auto ori = xo::quat_from_axis_angle( xo::vec3f::unit_y(), camera_man_->getYaw() );
	auto v = ori * scene_light_offset_;
	scene_light_.pos( vis::vec3f( vis::from_osg( center ) ) + v );
}

void QOsgViewer::setClearColor( const osg::Vec4& col )
{
	view_->getCamera()->setClearColor( col );
}

void QOsgViewer::moveCamera( const osg::Vec3d& delta_pos )
{
	camera_man_->setCenter( camera_man_->getCenter() + delta_pos );
}

bool QOsgViewer::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
{
	if ( ea.getEventType() == osgGA::GUIEventAdapter::RESIZE )
	{
		width_ = ea.getWindowWidth();
		height_ = ea.getWindowHeight();
		xo::log::info( "Viewer resized to ", width_, "x", height_ );
		updateHudPos();
		return true;
	}
	return false;
}

void QOsgViewer::startCapture( const std::string& filename )
{
	// create capture handler
	xo::log::info( "Started capturing video to ", filename );
	capture_handler_ = new osgViewer::ScreenCaptureHandler(
		new osgViewer::ScreenCaptureHandler::WriteToFile( filename, "png", osgViewer::ScreenCaptureHandler::WriteToFile::SEQUENTIAL_NUMBER ), -1 );
	view_->addEventHandler( capture_handler_ );
	capture_handler_->startCapture();
}

void QOsgViewer::stopCapture()
{
	last_drawn_frame_time_ = ~size_t ( 0 );
	if ( capture_handler_ )
	{
		xo::log::info( "Video capture stopped" );
		capture_handler_->stopCapture();
		capture_handler_ = nullptr;
	}
}

void QOsgViewer::captureCurrentFrame( const std::string& filename )
{
	stopCapture();

	capture_handler_ = new osgViewer::ScreenCaptureHandler(
		new osgViewer::ScreenCaptureHandler::WriteToFile( filename, "png", osgViewer::ScreenCaptureHandler::WriteToFile::OVERWRITE ), -1 );
	view_->addEventHandler( capture_handler_ );
	capture_handler_->startCapture();
	frame();
	capture_handler_->stopCapture();
	capture_handler_ = nullptr;
	xo::log::info( "Written image to ", filename );
}

void QOsgViewer::setFrameTime( double t )
{
	if ( current_frame_time_ != t )
	{
		current_frame_time_ = t;
		repaint();
	}
}

bool QOsgViewer::eventFilter( QObject* obj, QEvent* event )
{
	return QObject::eventFilter( obj, event );
}
<commit_msg>R: updated for new simvis<commit_after>#include "QOsgViewer.h"

#include <osgGA/TrackballManipulator>
#include <osgViewer/ViewerEventHandlers>
#include "simvis/osg_camera_man.h"
#include "xo/system/log.h"
#include "qevent.h"
#include "simvis/node.h"
#include "simvis/scene.h"
#include "osg/MatrixTransform"
#include "xo/geometry/quat.h"
#include "simvis/osg_tools.h"

// class for routing GUI events to QOsgViewer
// This is needed because QOsgViewer can't derive from GUIEventHandler directly
// because both are derived from osg::Object (basically, because of bad design)
class QOsgEventHandler : public osgGA::GUIEventHandler
{
public:
	QOsgEventHandler( QOsgViewer& viewer ) : viewer_( viewer ) {}
	virtual bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) { return viewer_.handle( ea, aa ); }
private:
	QOsgViewer& viewer_;
};

QOsgViewer::QOsgViewer( QWidget* parent /*= 0*/, Qt::WindowFlags f /*= 0*/, osgViewer::ViewerBase::ThreadingModel threadingModel/*=osgViewer::CompositeViewer::SingleThreaded*/ ) :
QWidget( parent, f ),
capture_handler_( nullptr ),
frame_count_( 0 ),
scene_light_offset_( -2, 8, 3 )
{
	QCoreApplication::instance()->installEventFilter( this );
	setThreadingModel( threadingModel );

	// disable the default setting of viewer.done() by pressing Escape.
	setKeyEventSetsDone( 0 );

	// setup viewer grid
	QWidget* widget1 = addViewWidget( createGraphicsWindow( 0, 0, 100, 100, "", true ) );
	auto* grid = new QGridLayout;
	grid->addWidget( widget1 );
	setLayout( grid );
	grid->setMargin( 1 );

	// start timer
	// TODO: remove this -- only update after something has changed
	connect( &timer_, SIGNAL( timeout() ), this, SLOT( update() ) );
	timer_.start( 1000 / 120 );
}

QWidget* QOsgViewer::addViewWidget( osgQt::GraphicsWindowQt* gw )
{
	view_ = new osgViewer::View;
	addView( view_ );

	osg::Camera* cam = view_->getCamera();
	cam->setGraphicsContext( gw );

	const osg::GraphicsContext::Traits* traits = gw->getTraits();

	cam->setClearColor( osg::Vec4( 0.55, 0.55, 0.55, 1.0 ) );
	cam->setViewport( new osg::Viewport( 0, 0, traits->width, traits->height ) );
	cam->setProjectionMatrixAsPerspective( 30.0f, static_cast<double>( traits->width ) / static_cast<double>( traits->height ), 1.0f, 10000.0f );

	// add event handlers
	view_->addEventHandler( new osgViewer::StatsHandler );
	view_->addEventHandler( new QOsgEventHandler( *this ) );

	// disable lighting by default
	view_->setLightingMode( osg::View::NO_LIGHT );

	// setup camera manipulator
	gw->setTouchEventsEnabled( true );

	camera_man_ = new vis::osg_camera_man();
	camera_man_->setVerticalAxisFixed( false );
	view_->setCameraManipulator( camera_man_ );

	return gw->getGLWidget();
}

osgQt::GraphicsWindowQt* QOsgViewer::createGraphicsWindow( int x, int y, int w, int h, const std::string& name/*=""*/, bool windowDecoration/*=false */ )
{
	osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
	ds->setNumMultiSamples( 2 );

	osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
	traits->windowName = name;
	traits->windowDecoration = windowDecoration;
	traits->x = x;
	traits->y = y;
	traits->width = w;
	traits->height = h;
	traits->doubleBuffer = true;
	traits->alpha = ds->getMinimumNumAlphaBits();
	traits->stencil = ds->getMinimumNumStencilBits();
	traits->sampleBuffers = ds->getMultiSamples();
	traits->samples = ds->getNumMultiSamples();

	width_ = w;
	height_ = h;

	return new osgQt::GraphicsWindowQt( traits.get() );
}

void QOsgViewer::paintEvent( QPaintEvent* event )
{
	// prevent capturing duplicate frames
	if ( isCapturing() && current_frame_time_ == last_drawn_frame_time_ )
		return; // this frame was already captured, skip

	updateLightPos();

	++frame_count_;
	frame();
	last_drawn_frame_time_ = current_frame_time_;
}

void QOsgViewer::setScene( vis::scene* s )
{
	scene_ = s;
	for ( size_t i = 0; i < getNumViews(); ++i )
		getView( i )->setSceneData( s->osg_node() );

	// init light
	scene_light_ = scene_->add_light( scene_light_offset_, vis::make_white( 1 ) );
	scene_light_.attenuation( 1.0f, 0.0f, 0.0f );
}

void QOsgViewer::setHud( const xo::path& file )
{
	hud_ = vis::plane( *scene_, xo::vec3f( hud_size, 0, 0 ), xo::vec3f( 0, hud_size, 0 ), file, 1.0f, 1.0f );
	hud_.osg_trans_node().setReferenceFrame( osg::Transform::ABSOLUTE_RF );
	auto geostate = hud_.osg_group().getChild( 0 )->asGeode()->getDrawable( 0 )->getOrCreateStateSet();
	geostate->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
	geostate->setMode( GL_BLEND, osg::StateAttribute::ON );
	view_->getCamera()->addChild( hud_.osg_node() );
	//updateHudPos();
}

void QOsgViewer::updateHudPos()
{
	double fovy, aspect, nearplane, farplane;
	view_->getCamera()->getProjectionMatrixAsPerspective( fovy, aspect, nearplane, farplane );
	//xo::log::info( "aspect ratio = ", aspect );
	auto hh = tan( xo::deg_to_rad( fovy ) / 2 );
	auto hw = tan( atan( hh * aspect ) );
	hud_.pos( xo::vec3f( hw - 0.55f * hud_size, -hh + 0.55f * hud_size, -1 ) );
}

void QOsgViewer::updateLightPos()
{
	auto center = camera_man_->getCenter();
	auto ori = xo::quat_from_axis_angle( xo::vec3f::unit_y(), camera_man_->getYaw() );
	auto v = ori * scene_light_offset_;
	scene_light_.pos( vis::vec3f( vis::from_osg( center ) ) + v );
}

void QOsgViewer::setClearColor( const osg::Vec4& col )
{
	view_->getCamera()->setClearColor( col );
}

void QOsgViewer::moveCamera( const osg::Vec3d& delta_pos )
{
	camera_man_->setCenter( camera_man_->getCenter() + delta_pos );
}

bool QOsgViewer::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
{
	if ( ea.getEventType() == osgGA::GUIEventAdapter::RESIZE )
	{
		width_ = ea.getWindowWidth();
		height_ = ea.getWindowHeight();
		xo::log::info( "Viewer resized to ", width_, "x", height_ );
		updateHudPos();
		return true;
	}
	return false;
}

void QOsgViewer::startCapture( const std::string& filename )
{
	// create capture handler
	xo::log::info( "Started capturing video to ", filename );
	capture_handler_ = new osgViewer::ScreenCaptureHandler(
		new osgViewer::ScreenCaptureHandler::WriteToFile( filename, "png", osgViewer::ScreenCaptureHandler::WriteToFile::SEQUENTIAL_NUMBER ), -1 );
	view_->addEventHandler( capture_handler_ );
	capture_handler_->startCapture();
}

void QOsgViewer::stopCapture()
{
	last_drawn_frame_time_ = ~size_t ( 0 );
	if ( capture_handler_ )
	{
		xo::log::info( "Video capture stopped" );
		capture_handler_->stopCapture();
		capture_handler_ = nullptr;
	}
}

void QOsgViewer::captureCurrentFrame( const std::string& filename )
{
	stopCapture();

	capture_handler_ = new osgViewer::ScreenCaptureHandler(
		new osgViewer::ScreenCaptureHandler::WriteToFile( filename, "png", osgViewer::ScreenCaptureHandler::WriteToFile::OVERWRITE ), -1 );
	view_->addEventHandler( capture_handler_ );
	capture_handler_->startCapture();
	frame();
	capture_handler_->stopCapture();
	capture_handler_ = nullptr;
	xo::log::info( "Written image to ", filename );
}

void QOsgViewer::setFrameTime( double t )
{
	if ( current_frame_time_ != t )
	{
		current_frame_time_ = t;
		repaint();
	}
}

bool QOsgViewer::eventFilter( QObject* obj, QEvent* event )
{
	return QObject::eventFilter( obj, event );
}
<|endoftext|>


<commit_before>#include <Windows.h> // curse you, monolithic headers!
#include <sys/stat.h>
#include <direct.h>

#include "XSCommon/XSCommon.h"
#include "XSCommon/XSConsole.h"
#include "XSCommon/XSGlobals.h"
#include "XSSystem/XSOS.h"

namespace XS {

	namespace OS {

		void GetCurrentWorkingDirectory( char *cwd, size_t bufferLen ) {
			_getcwd( cwd, (int)bufferLen - 1 );
			cwd[FILENAME_MAX - 1] = '\0';
		}

		bool MkDir( const char *path ) {
			if ( !CreateDirectory( path, nullptr ) ) {
				if ( GetLastError() != ERROR_ALREADY_EXISTS )
					return false;
			}

			return true;
		}

		bool ResolvePath( char *outPath, const char *inPath, size_t pathLen ) {
			SDL_assert( outPath && inPath && "OS::ResolvePath called with invalid parameters" );

			if ( /*Stat( inPath ) &&*/ !_fullpath( outPath, inPath, pathLen ) ) {
				if ( Common::com_developer->GetBool() ) {
					Console::Print( "Could not resolve path: \"%s\" (%s)\n", inPath, strerror( errno ) );
				}
				outPath[0] = '\0';

				return false;
			}

			return true;
		}

		bool Stat( const char *path ) {
			struct _stat buf;

			if ( _stat( path, &buf ) ) {
				return false;
			}

			return true;
		}

	} // namespace OS

} // namespace XS
<commit_msg>Fix Windows builds<commit_after>#include <Windows.h> // curse you, monolithic headers!
#include <sys/stat.h>
#include <direct.h>

#include "XSCommon/XSCommon.h"
#include "XSCommon/XSConsole.h"
#include "XSCommon/XSGlobals.h"
#include "XSSystem/XSOS.h"

namespace XS {

	namespace OS {

		void GetCurrentWorkingDirectory( char *cwd, size_t bufferLen ) {
			_getcwd( cwd, (int)bufferLen - 1 );
			cwd[FILENAME_MAX - 1] = '\0';
		}

		bool MkDir( const char *path ) {
			if ( !CreateDirectory( path, nullptr ) ) {
				if ( GetLastError() != ERROR_ALREADY_EXISTS )
					return false;
			}

			return true;
		}

		bool ResolvePath( char *outPath, const char *inPath, size_t pathLen ) {
			SDL_assert( outPath && inPath && "OS::ResolvePath called with invalid parameters" );

			if ( /*Stat( inPath ) &&*/ !_fullpath( outPath, inPath, pathLen ) ) {
				if ( Common::com_developer->GetBool() ) {
					console.Print( "Could not resolve path: \"%s\" (%s)\n", inPath, strerror( errno ) );
				}
				outPath[0] = '\0';

				return false;
			}

			return true;
		}

		bool Stat( const char *path ) {
			struct _stat buf;

			if ( _stat( path, &buf ) ) {
				return false;
			}

			return true;
		}

	} // namespace OS

} // namespace XS
<|endoftext|>


<commit_before>/** \file    generate_vufind_translation_files.cc
 *  \brief   A tool for creating the ".ini" files VuFind uses based on data in the SQL translations table.
 *  \author  Dr. Johannes Ruscheinski
 */

/*
    Copyright (C) 2016-2021, Library of the University of Tübingen

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

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <iostream>
#include <map>
#include <tuple>
#include <utility>
#include <vector>
#include <cstring>
#include "File.h"
#include "FileUtil.h"
#include "TranslationUtil.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "DbRow.h"
#include "IniFile.h"
#include "StringUtil.h"
#include "UBTools.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << " [--verbose] output_directory_path\n";
    std::exit(EXIT_FAILURE);
}

// Needed since no consistent convention was used for brackets
std::string NormalizeBrackets(const std::string string_to_normalize) {
    return StringUtil::Map(string_to_normalize, "<>", "()");
}

// Generates a XX.ini output file with entries like the original file.  The XX is a 2-letter language code.
void ProcessLanguage(const bool verbose, const std::string &output_file_path, const std::string &_3letter_code,
                     DbConnection * const db_connection)
{
    if (verbose)
        std::cerr << "Processing language code: " << _3letter_code << '\n';

    std::unordered_map<std::string, std::pair<unsigned, std::string>> token_to_line_no_and_other_map;
    if (::access(output_file_path.c_str(), R_OK) != 0)
        LOG_WARNING("\"" + output_file_path + "\" is not readable, maybe it doesn't exist?");
    else {
        TranslationUtil::ReadIniFile(output_file_path, &token_to_line_no_and_other_map);

        if (unlikely(not FileUtil::RenameFile(output_file_path, output_file_path + ".bak", /* remove_target = */true)))
            LOG_ERROR("failed to rename \"" + output_file_path + "\" to \"" + output_file_path + ".bak\"!");
    }

    File output(output_file_path, "w");
    if (unlikely(output.fail()))
        LOG_ERROR("failed to open \"" + output_file_path + "\" for writing!");

    db_connection->queryOrDie("SELECT token,translation FROM vufind_translations WHERE language_code='" + _3letter_code + "'");
    DbResultSet result_set(db_connection->getLastResultSet());
    if (unlikely(result_set.empty()))
        LOG_ERROR("found no translations for language code \"" + _3letter_code + "\"!");
    if (verbose)
        std::cerr << "\tFound " << result_set.size() << " (token,translation) pairs.\n";

    std::vector<std::tuple<unsigned, std::string, std::string>> line_nos_tokens_and_translations;
    while (const DbRow row = result_set.getNextRow()) {
        const auto &token_to_line_no_and_other(token_to_line_no_and_other_map.find(row[0]));
        if (token_to_line_no_and_other != token_to_line_no_and_other_map.cend())
            line_nos_tokens_and_translations.emplace_back(token_to_line_no_and_other->second.first, row[0], row[1]);
        else
            line_nos_tokens_and_translations.emplace_back(token_to_line_no_and_other_map.size() + 1, row[0], row[1]);
    }

    std::sort(line_nos_tokens_and_translations.begin(), line_nos_tokens_and_translations.end(),
              [](const std::tuple<unsigned, std::string, std::string> &left, const std::tuple<unsigned, std::string, std::string> &right)
                  { return std::get<0>(left) < std::get<0>(right); });

    for (const auto &line_no_token_and_translation : line_nos_tokens_and_translations) {
        const std::string token(std::get<1>(line_no_token_and_translation));
        const std::string translation(StringUtil::TrimWhite(std::get<2>(line_no_token_and_translation)));
        if (not translation.empty())
            output << token << " = \"" << StringUtil::TrimWhite(NormalizeBrackets(translation)) << "\"\n";
    }

    if (verbose)
        std::cerr << "Wrote " << line_nos_tokens_and_translations.size() << " language mappings to \"" << output_file_path << "\"\n";
}

void GetLanguageCodes(const bool verbose, DbConnection * const db_connection,
                      std::map<std::string, std::string> * language_codes)
{
    db_connection->queryOrDie("SELECT DISTINCT language_code FROM vufind_translations");
    DbResultSet language_codes_result_set(db_connection->getLastResultSet());
    if (unlikely(language_codes_result_set.empty()))
        LOG_ERROR("no language codes found, expected multiple!");

    while (const DbRow row = language_codes_result_set.getNextRow()) {
        const std::string german_language_code(
            TranslationUtil::MapFake3LetterEnglishLanguagesCodesToGermanLanguageCodes(row[0]));
        if (german_language_code == "???")
            continue;
        const std::string international_language_code(
            TranslationUtil::MapGerman3Or4LetterCodeToInternational2LetterCode(german_language_code));
        language_codes->emplace(international_language_code, row[0]);
    }
    if (verbose)
        std::cerr << "Found " << language_codes->size()
                  << " distinct language code in the \"vufind_translations\" table.\n";
}

const std::string CONF_FILE_PATH(UBTools::GetTuelibPath() + "translations.conf");

} // unnamed namespace

int Main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc < 2)
        Usage();
    bool verbose(false);
    if (std::strcmp(argv[1], "--verbose") == 0) {
        verbose = true;
        --argc, ++argv;
    }
    if (argc != 2)
        Usage();

    const std::string output_directory(argv[1]);
    if (unlikely(not FileUtil::IsDirectory(output_directory)))
        LOG_ERROR("\"" + output_directory + "\" is not a directory or can't be read!");

    const IniFile ini_file(CONF_FILE_PATH);
    const std::string sql_database(ini_file.getString("Database", "sql_database"));
    const std::string sql_username(ini_file.getString("Database", "sql_username"));
    const std::string sql_password(ini_file.getString("Database", "sql_password"));
    DbConnection db_connection(DbConnection::MySQLFactory(sql_database, sql_username, sql_password));

    std::map<std::string, std::string> _2letter_and_3letter_codes;
    GetLanguageCodes(verbose, &db_connection, &_2letter_and_3letter_codes);
    for (const auto &_2letter_intl_code_and_fake_3letter_english_code : _2letter_and_3letter_codes)
        ProcessLanguage(verbose,
                        output_directory + "/" + _2letter_intl_code_and_fake_3letter_english_code.first + ".ini",
                        _2letter_intl_code_and_fake_3letter_english_code.second, &db_connection);

    return EXIT_SUCCESS;
}
<commit_msg>changed script for transformation of translation from db to ini<commit_after>/** \file    generate_vufind_translation_files.cc
 *  \brief   A tool for creating the ".ini" files VuFind uses based on data in the SQL translations table.
 *  \author  Dr. Johannes Ruscheinski
 */

/*
    Copyright (C) 2016-2021, Library of the University of Tübingen

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

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <iostream>
#include <map>
#include <tuple>
#include <utility>
#include <vector>
#include <cstring>
#include "File.h"
#include "FileUtil.h"
#include "TranslationUtil.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "DbRow.h"
#include "IniFile.h"
#include "StringUtil.h"
#include "UBTools.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << " [--verbose] output_directory_path\n";
    std::exit(EXIT_FAILURE);
}

// Needed since no consistent convention was used for brackets
std::string NormalizeBrackets(const std::string string_to_normalize) {
    return StringUtil::Map(string_to_normalize, "<>", "()");
}

// Generates a XX.ini output file with entries like the original file.  The XX is a 2-letter language code.
void ProcessLanguage(const bool verbose, const std::string &output_file_path, const std::string &_3letter_code,
                     DbConnection * const db_connection)
{
    if (verbose)
        std::cerr << "Processing language code: " << _3letter_code << '\n';

    std::unordered_map<std::string, std::pair<unsigned, std::string>> token_to_line_no_and_other_map;
    if (::access(output_file_path.c_str(), R_OK) != 0)
        LOG_WARNING("\"" + output_file_path + "\" is not readable, maybe it doesn't exist?");
    else {
        TranslationUtil::ReadIniFile(output_file_path, &token_to_line_no_and_other_map);

        if (unlikely(not FileUtil::RenameFile(output_file_path, output_file_path + ".bak", /* remove_target = */true)))
            LOG_ERROR("failed to rename \"" + output_file_path + "\" to \"" + output_file_path + ".bak\"!");
    }

    File output(output_file_path, "w");
    if (unlikely(output.fail()))
        LOG_ERROR("failed to open \"" + output_file_path + "\" for writing!");

    db_connection->queryOrDie("SELECT token,translation FROM vufind_translations WHERE next_version_id IS NULL AND language_code='" + _3letter_code + "'");
    DbResultSet result_set(db_connection->getLastResultSet());
    if (unlikely(result_set.empty()))
        LOG_ERROR("found no translations for language code \"" + _3letter_code + "\"!");
    if (verbose)
        std::cerr << "\tFound " << result_set.size() << " (token,translation) pairs.\n";

    std::vector<std::tuple<unsigned, std::string, std::string>> line_nos_tokens_and_translations;
    while (const DbRow row = result_set.getNextRow()) {
        const auto &token_to_line_no_and_other(token_to_line_no_and_other_map.find(row[0]));
        if (token_to_line_no_and_other != token_to_line_no_and_other_map.cend())
            line_nos_tokens_and_translations.emplace_back(token_to_line_no_and_other->second.first, row[0], row[1]);
        else
            line_nos_tokens_and_translations.emplace_back(token_to_line_no_and_other_map.size() + 1, row[0], row[1]);
    }

    std::sort(line_nos_tokens_and_translations.begin(), line_nos_tokens_and_translations.end(),
              [](const std::tuple<unsigned, std::string, std::string> &left, const std::tuple<unsigned, std::string, std::string> &right)
                  { return std::get<0>(left) < std::get<0>(right); });

    for (const auto &line_no_token_and_translation : line_nos_tokens_and_translations) {
        const std::string token(std::get<1>(line_no_token_and_translation));
        const std::string translation(StringUtil::TrimWhite(std::get<2>(line_no_token_and_translation)));
        if (not translation.empty())
            output << token << " = \"" << StringUtil::TrimWhite(NormalizeBrackets(translation)) << "\"\n";
    }

    if (verbose)
        std::cerr << "Wrote " << line_nos_tokens_and_translations.size() << " language mappings to \"" << output_file_path << "\"\n";
}

void GetLanguageCodes(const bool verbose, DbConnection * const db_connection,
                      std::map<std::string, std::string> * language_codes)
{
    db_connection->queryOrDie("SELECT DISTINCT language_code FROM vufind_translations");
    DbResultSet language_codes_result_set(db_connection->getLastResultSet());
    if (unlikely(language_codes_result_set.empty()))
        LOG_ERROR("no language codes found, expected multiple!");

    while (const DbRow row = language_codes_result_set.getNextRow()) {
        const std::string german_language_code(
            TranslationUtil::MapFake3LetterEnglishLanguagesCodesToGermanLanguageCodes(row[0]));
        if (german_language_code == "???")
            continue;
        const std::string international_language_code(
            TranslationUtil::MapGerman3Or4LetterCodeToInternational2LetterCode(german_language_code));
        language_codes->emplace(international_language_code, row[0]);
    }
    if (verbose)
        std::cerr << "Found " << language_codes->size()
                  << " distinct language code in the \"vufind_translations\" table.\n";
}

const std::string CONF_FILE_PATH(UBTools::GetTuelibPath() + "translations.conf");

} // unnamed namespace

int Main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc < 2)
        Usage();
    bool verbose(false);
    if (std::strcmp(argv[1], "--verbose") == 0) {
        verbose = true;
        --argc, ++argv;
    }
    if (argc != 2)
        Usage();

    const std::string output_directory(argv[1]);
    if (unlikely(not FileUtil::IsDirectory(output_directory)))
        LOG_ERROR("\"" + output_directory + "\" is not a directory or can't be read!");

    const IniFile ini_file(CONF_FILE_PATH);
    const std::string sql_database(ini_file.getString("Database", "sql_database"));
    const std::string sql_username(ini_file.getString("Database", "sql_username"));
    const std::string sql_password(ini_file.getString("Database", "sql_password"));
    DbConnection db_connection(DbConnection::MySQLFactory(sql_database, sql_username, sql_password));

    std::map<std::string, std::string> _2letter_and_3letter_codes;
    GetLanguageCodes(verbose, &db_connection, &_2letter_and_3letter_codes);
    for (const auto &_2letter_intl_code_and_fake_3letter_english_code : _2letter_and_3letter_codes)
        ProcessLanguage(verbose,
                        output_directory + "/" + _2letter_intl_code_and_fake_3letter_english_code.first + ".ini",
                        _2letter_intl_code_and_fake_3letter_english_code.second, &db_connection);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/// @file   craftAction.cpp
/// @brief  Implementation of the class for a build action.
/// @author Enrico Fraccaroli
/// @date   Jul 14 2016
/// @copyright
/// Copyright (c) 2016 Enrico Fraccaroli <enrico.fraccaroli@gmail.com>
/// Permission to use, copy, modify, and distribute this software for any
/// purpose with or without fee is hereby granted, provided that the above
/// copyright notice and this permission notice appear in all copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
/// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
/// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
/// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
/// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
/// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
/// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include "craftAction.hpp"
#include "../logger.hpp"
#include "../character.hpp"
#include "../sqlite/SQLiteDbms.hpp"
#include "../formatter.hpp"
#include "../room.hpp"

using namespace std::chrono;

CraftAction::CraftAction(
    Character * _actor,
    Production * _production,
    Material * _material,
    ItemVector & _tools,
    ItemVector & _ingredients,
    unsigned int & _cooldown) :
        GeneralAction(_actor, system_clock::now() + seconds(_cooldown)),
        production(_production),
        material(_material),
        tools(_tools),
        ingredients(_ingredients)
{
    Logger::log(LogLevel::Debug, "Created crafting action.");
}

CraftAction::~CraftAction()
{
    Logger::log(LogLevel::Debug, "Deleted crafting action.");
}

bool CraftAction::check() const
{
    bool correct = GeneralAction::check();
    correct &= this->checkProduction();
    correct &= this->checkMaterial();
    correct &= this->checkTools();
    correct &= this->checkIngredients();
    return correct;
}

ActionType CraftAction::getType() const
{
    return ActionType::Crafting;
}

std::string CraftAction::getDescription() const
{
    return this->production->profession->action;
}

std::string CraftAction::stop()
{
    return this->production->profession->interruptMessage + ".";
}

bool CraftAction::perform()
{
    // Check the character stamina.
    unsigned int consumedStamina;
    if (!this->checkHasStamina(consumedStamina))
    {
        actor->sendMsg("\nYou are too tired right now.\n");
        // Set the status to Error.
        this->actionStatus = ActionStatus::Error;
        return false;
    }
    if (!checkProduction() || !checkMaterial() || !checkTools() || !checkIngredients())
    {
        actor->sendMsg("\nYou have failed your action.\n");
        // Set the status to Error.
        this->actionStatus = ActionStatus::Error;
        return false;
    }
    // Consume the stamina.
    actor->remStamina(consumedStamina, true);

    SQLiteDbms::instance().beginTransaction();
    ItemVector createdItems;
    for (unsigned int it = 0; it < production->quantity; ++it)
    {
        ItemModel * outcomeModel = production->outcome;
        // Create the item.
        Item * newItem = outcomeModel->createItem(actor->getName(), material, ItemQuality::Normal);
        if (newItem == nullptr)
        {
            // Log a warning.
            Logger::log(
                LogLevel::Warning,
                actor->getName() + ":craft = New item is a null pointer.");
            // Rollback the database.
            SQLiteDbms::instance().rollbackTransection();
            // Delete all the items created so far.
            for (auto createdItem : createdItems)
            {
                delete (createdItem);
            }
            // Notify the character.
            actor->sendMsg("\nYou have failed your action.\n");
            // Set the status to Error.
            this->actionStatus = ActionStatus::Error;
            return false;
        }
        createdItems.push_back(newItem);
    }
    SQLiteDbms::instance().endTransaction();

    // Add the created items into the character inventory.
    bool dropped = false;
    SQLiteDbms::instance().beginTransaction();
    for (auto createdItem : createdItems)
    {
        if (!actor->canCarry(createdItem))
        {
            actor->room->addItem(createdItem);
            dropped = true;
        }
        else
        {
            actor->addInventoryItem(createdItem);
        }
        createdItem->updateOnDB();
    }
    // Update the tools.
    ItemVector toDestroy;
    for (auto iterator : tools)
    {
        // Update the condition of the involved objects.
        if (iterator->triggerDecay())
        {
            actor->sendMsg(iterator->getName() + " falls into pieces.");
            toDestroy.push_back(iterator);
        }
    }
    for (auto it : ingredients)
    {
        toDestroy.push_back(it);
    }
    for (auto it : toDestroy)
    {
        it->destroy();
    }
    SQLiteDbms::instance().endTransaction();

    // Send conclusion message.
    actor->sendMsg(
        "%s %s.\n\n",
        production->profession->finishMessage,
        Formatter::yellow() + createdItems.back()->getName() + Formatter::reset());
    if (dropped)
    {
        actor->sendMsg(
            "Since you can't carry them, some of the items have been placed on the ground.\n\n");
    }
    return true;
}

bool CraftAction::checkHasStamina(unsigned int & consumed) const
{
    // Check if the actor has enough stamina to execute the action.
    if (!actor->hasStaminaFor(consumed, ActionType::Crafting))
    {
        return false;
    }
    return true;
}

bool CraftAction::checkProduction() const
{
    bool correct = true;
    if (production == nullptr)
    {
        Logger::log(LogLevel::Error, "The production is a null pointer.");
        correct = false;
    }
    else
    {
        if (production->outcome == nullptr)
        {
            Logger::log(LogLevel::Error, "The production outcome is a null pointer.");
            correct = false;
        }
        if (production->profession == nullptr)
        {
            Logger::log(LogLevel::Error, "The production profession is a null pointer.");
            correct = false;
        }
    }
    return correct;
}

bool CraftAction::checkMaterial() const
{
    if (material == nullptr)
    {
        Logger::log(LogLevel::Error, "The material is a null pointer.");
        return false;
    }
    return true;
}

bool CraftAction::checkIngredients() const
{
    if (ingredients.empty())
    {
        Logger::log(LogLevel::Error, "No used ingredients have been set.");
        return false;
    }
    for (auto iterator : ingredients)
    {
        // Check if the ingredient has been deleted.
        if (iterator == nullptr)
        {
            Logger::log(LogLevel::Error, "One of the ingredients is a null pointer.");
            return false;
        }
    }
    return true;
}

bool CraftAction::checkTools() const
{
    if (tools.empty())
    {
        Logger::log(LogLevel::Error, "No used tools have been set.");
        return false;
    }
    for (auto iterator : tools)
    {
        // Check if the tool has been deleted.
        if (iterator == nullptr)
        {
            Logger::log(LogLevel::Error, "One of the tools is a null pointer.");
            return false;
        }
    }
    return true;
}
<commit_msg>Fix crafting with no cooldown<commit_after>/// @file   craftAction.cpp
/// @brief  Implementation of the class for a build action.
/// @author Enrico Fraccaroli
/// @date   Jul 14 2016
/// @copyright
/// Copyright (c) 2016 Enrico Fraccaroli <enrico.fraccaroli@gmail.com>
/// Permission to use, copy, modify, and distribute this software for any
/// purpose with or without fee is hereby granted, provided that the above
/// copyright notice and this permission notice appear in all copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
/// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
/// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
/// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
/// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
/// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
/// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include "craftAction.hpp"
#include "../logger.hpp"
#include "../character.hpp"
#include "../sqlite/SQLiteDbms.hpp"
#include "../formatter.hpp"
#include "../room.hpp"

using namespace std::chrono;

CraftAction::CraftAction(
    Character * _actor,
    Production * _production,
    Material * _material,
    ItemVector & _tools,
    ItemVector & _ingredients,
    unsigned int & _cooldown) :
        GeneralAction(_actor, system_clock::now() + seconds(_cooldown)),
        production(_production),
        material(_material),
        tools(_tools),
        ingredients(_ingredients)
{
    Logger::log(LogLevel::Debug, "Created crafting action.");
}

CraftAction::~CraftAction()
{
    Logger::log(LogLevel::Debug, "Deleted crafting action.");
}

bool CraftAction::check() const
{
    bool correct = GeneralAction::check();
    correct &= this->checkProduction();
    correct &= this->checkMaterial();
    correct &= this->checkTools();
    correct &= this->checkIngredients();
    return correct;
}

ActionType CraftAction::getType() const
{
    return ActionType::Crafting;
}

std::string CraftAction::getDescription() const
{
    return this->production->profession->action;
}

std::string CraftAction::stop()
{
    return this->production->profession->interruptMessage + ".";
}

bool CraftAction::perform()
{
    // Check if the cooldown is ended.
    if (!this->checkElapsed())
    {
        return false;
    }
    // Check the character stamina.
    unsigned int consumedStamina;
    if (!this->checkHasStamina(consumedStamina))
    {
        actor->sendMsg("\nYou are too tired right now.\n");
        // Set the status to Error.
        this->actionStatus = ActionStatus::Error;
        return false;
    }
    if (!checkProduction() || !checkMaterial() || !checkTools() || !checkIngredients())
    {
        actor->sendMsg("\nYou have failed your action.\n");
        // Set the status to Error.
        this->actionStatus = ActionStatus::Error;
        return false;
    }
    // Consume the stamina.
    actor->remStamina(consumedStamina, true);

    SQLiteDbms::instance().beginTransaction();
    ItemVector createdItems;
    for (unsigned int it = 0; it < production->quantity; ++it)
    {
        ItemModel * outcomeModel = production->outcome;
        // Create the item.
        Item * newItem = outcomeModel->createItem(actor->getName(), material, ItemQuality::Normal);
        if (newItem == nullptr)
        {
            // Log a warning.
            Logger::log(
                LogLevel::Warning,
                actor->getName() + ":craft = New item is a null pointer.");
            // Rollback the database.
            SQLiteDbms::instance().rollbackTransection();
            // Delete all the items created so far.
            for (auto createdItem : createdItems)
            {
                delete (createdItem);
            }
            // Notify the character.
            actor->sendMsg("\nYou have failed your action.\n");
            // Set the status to Error.
            this->actionStatus = ActionStatus::Error;
            return false;
        }
        createdItems.push_back(newItem);
    }
    SQLiteDbms::instance().endTransaction();

    // Add the created items into the character inventory.
    bool dropped = false;
    SQLiteDbms::instance().beginTransaction();
    for (auto createdItem : createdItems)
    {
        if (!actor->canCarry(createdItem))
        {
            actor->room->addItem(createdItem);
            dropped = true;
        }
        else
        {
            actor->addInventoryItem(createdItem);
        }
        createdItem->updateOnDB();
    }
    // Update the tools.
    ItemVector toDestroy;
    for (auto iterator : tools)
    {
        // Update the condition of the involved objects.
        if (iterator->triggerDecay())
        {
            actor->sendMsg(iterator->getName() + " falls into pieces.");
            toDestroy.push_back(iterator);
        }
    }
    for (auto it : ingredients)
    {
        toDestroy.push_back(it);
    }
    for (auto it : toDestroy)
    {
        it->destroy();
    }
    SQLiteDbms::instance().endTransaction();

    // Send conclusion message.
    actor->sendMsg(
        "%s %s.\n\n",
        production->profession->finishMessage,
        Formatter::yellow() + createdItems.back()->getName() + Formatter::reset());
    if (dropped)
    {
        actor->sendMsg(
            "Since you can't carry them, some of the items have been placed on the ground.\n\n");
    }
    return true;
}

bool CraftAction::checkHasStamina(unsigned int & consumed) const
{
    // Check if the actor has enough stamina to execute the action.
    if (!actor->hasStaminaFor(consumed, ActionType::Crafting))
    {
        return false;
    }
    return true;
}

bool CraftAction::checkProduction() const
{
    bool correct = true;
    if (production == nullptr)
    {
        Logger::log(LogLevel::Error, "The production is a null pointer.");
        correct = false;
    }
    else
    {
        if (production->outcome == nullptr)
        {
            Logger::log(LogLevel::Error, "The production outcome is a null pointer.");
            correct = false;
        }
        if (production->profession == nullptr)
        {
            Logger::log(LogLevel::Error, "The production profession is a null pointer.");
            correct = false;
        }
    }
    return correct;
}

bool CraftAction::checkMaterial() const
{
    if (material == nullptr)
    {
        Logger::log(LogLevel::Error, "The material is a null pointer.");
        return false;
    }
    return true;
}

bool CraftAction::checkIngredients() const
{
    if (ingredients.empty())
    {
        Logger::log(LogLevel::Error, "No used ingredients have been set.");
        return false;
    }
    for (auto iterator : ingredients)
    {
        // Check if the ingredient has been deleted.
        if (iterator == nullptr)
        {
            Logger::log(LogLevel::Error, "One of the ingredients is a null pointer.");
            return false;
        }
    }
    return true;
}

bool CraftAction::checkTools() const
{
    if (tools.empty())
    {
        Logger::log(LogLevel::Error, "No used tools have been set.");
        return false;
    }
    for (auto iterator : tools)
    {
        // Check if the tool has been deleted.
        if (iterator == nullptr)
        {
            Logger::log(LogLevel::Error, "One of the tools is a null pointer.");
            return false;
        }
    }
    return true;
}
<|endoftext|>