campo-sirio/SSAservice/ESignService.cpp
guy d40707a134 Versione 12
git-svn-id: svn://10.65.10.50/branches/R_10_00@23098 c028cbd2-c16b-5b4b-a496-9718f37d4682
2015-05-15 09:09:10 +00:00

392 lines
11 KiB
C++

#pragma region Includes
#include "ESignService.h"
#include "ThreadPool.h"
#include "PathName.h"
#include <cassert>
#include <zmq.h>
#include <Wtsapi32.h>
#pragma comment(lib, "Wtsapi32.lib")
#pragma endregion
///////////////////////////////////////////////////////////
// Utility
///////////////////////////////////////////////////////////
static errno_t GetIniString(const char* key, gt_string val)
{
::GetPrivateProfileStringA("Default", key, "", val, sizeof(gt_string), "./esigner.ini");
return *val ? 0 : EINVAL;
}
static int GetIniInteger(const char* key)
{
gt_string val = { 0 };
GetIniString(key, val);
return atoi(val);
}
///////////////////////////////////////////////////////////
// CESignService
///////////////////////////////////////////////////////////
CESignService::CESignService(LPCTSTR pszServiceName, BOOL fCanStop, BOOL fCanShutdown, BOOL fCanPauseContinue)
: CServiceBase(pszServiceName, fCanStop, fCanShutdown, fCanPauseContinue)
{
m_fStopping = FALSE;
// Create a manual-reset event that is not signaled at first to indicate
// the stopped signal of the service.
m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hStoppedEvent == NULL)
throw GetLastError();
}
CESignService::~CESignService(void)
{
if (m_hStoppedEvent)
{
CloseHandle(m_hStoppedEvent);
m_hStoppedEvent = NULL;
}
}
//
// FUNCTION: CESignService::OnStart(DWORD, LPWSTR *)
//
// PURPOSE: The function is executed when a Start command is sent to the
// service by the SCM or when the operating system starts (for a service
// that starts automatically). It specifies actions to take when the
// service starts. In this code sample, OnStart logs a service-start
// message to the Application log, and queues the main service function for
// execution in a thread pool worker thread.
//
// PARAMETERS:
// * dwArgc - number of command line arguments
// * lpszArgv - array of command line arguments
//
// NOTE: A service application is designed to be long running. Therefore,
// it usually polls or monitors something in the system. The monitoring is
// set up in the OnStart method. However, OnStart does not actually do the
// monitoring. The OnStart method must return to the operating system after
// the service's operation has begun. It must not loop forever or block. To
// set up a simple monitoring mechanism, one general solution is to create
// a timer in OnStart. The timer would then raise events in your code
// periodically, at which time your service could do its monitoring. The
// other solution is to spawn a new thread to perform the main service
// functions, which is demonstrated in this code sample.
//
void CESignService::OnStart(DWORD dwArgc, LPWSTR *lpszArgv)
{
// Log a service start message to the Application log.
WriteEventLogEntry(TEXT("ESignService Starting"));
// Queue the main service function for execution in a worker thread.
CThreadPool::QueueUserWorkItem(&CESignService::ServiceLoop, this);
}
char CESignService::ParseHex(const char* hex) const
{
int n = 0;
sscanf_s(hex, "%2X", &n);
return char(n);
}
const char* CESignService::ParseString(const char* equal, gt_string str) const
{
gt_strcpy(str, "");
const char* s = equal;
int i = 0;
for (; *s != '&' && *s > ' '; s++)
{
if (*s == '%')
{
const char h = ParseHex(s+1);
gt_chrcat(str, h);
s += 2;
}
else
gt_chrcat(str, *s);
}
return s;
}
const char* CESignService::ParseString(const char* equal, CPathName& path) const
{
gt_string str = { 0 };
const char* s = ParseString(equal, str);
path.Set(str);
return s;
}
bool CESignService::ParseGET(const char* get, CPathName& src, CPathName& dst, CPathName& bck,
gt_string pin, gt_string ext) const
{
src = dst = bck = "";
gt_strcpy(pin, "");
gt_strcpy(ext, "");
while (get != NULL)
{
const char* equal = strchr(get, '=');
if (equal != NULL)
{
if (*(equal-1) > 'Z')
break;
switch (*(equal-3))
{
case 'B': get = ParseString(equal+1, bck); break; // BCK
case 'D': get = ParseString(equal+1, dst); break; // DST
case 'E': get = ParseString(equal+1, ext); break; // EXT
case 'P': get = ParseString(equal+1, pin); break; // PIN
case 'S': get = ParseString(equal+1, src); break; // SRC
default : get = equal+1; break;
}
}
else
get = equal;
}
return !src.empty();
}
/*
DWORD CESignService::RunAsync(const char* strCmdLine) const
{
DWORD ret = ::WinExec(strCmdLine, SW_SHOWDEFAULT);
if (ret >= 32)
{
ret = 0;
}
else
{
LPWSTR messageBuffer = NULL;
ret = ::GetLastError();
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, ret, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL);
WriteEventLogEntry(messageBuffer, EVENTLOG_ERROR_TYPE, ret);
LocalFree(messageBuffer);
}
return ret;
}
*/
DWORD CESignService::RunSync(const char* strCmdLine) const
{
STARTUPINFOA si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(si);
si.lpDesktop = "winsta0\\default";
si.wShowWindow = SW_SHOW;
DWORD activeSessionId = ::WTSGetActiveConsoleSessionId();
HANDLE currentToken = NULL; ::WTSQueryUserToken(activeSessionId, &currentToken);
DWORD err = 0;
if (currentToken)
{
::CreateProcessAsUserA(currentToken, NULL,
(LPSTR)strCmdLine, // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
FALSE, // no Handle inheritance
NORMAL_PRIORITY_CLASS, // creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi);
}
else
{
::CreateProcessA(NULL,
(LPSTR)strCmdLine, // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
FALSE, // no Handle inheritance
NORMAL_PRIORITY_CLASS, // creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi);
}
if (pi.hProcess)
{
::WaitForSingleObject(pi.hProcess, INFINITE);
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);
}
else
err = ::GetLastError();
if (currentToken)
{
::CloseHandle(currentToken);
currentToken = NULL;
}
return err;
}
void AppendVar(gt_string cmd, const char* var, const char* val)
{
if (val && *val)
{
gt_chrcat(cmd, ' ');
gt_strcat(cmd, var);
gt_chrcat(cmd, '=');
if (*val != '"' && strchr(val, ' '))
{
gt_chrcat(cmd, '"');
gt_strcat(cmd, val);
gt_chrcat(cmd, '"');
}
else
gt_strcat(cmd, val);
}
}
//
// FUNCTION: CESignService::ServiceLoop(void)
//
// PURPOSE: The method performs the main function of the service. It runs
// on a thread pool worker thread.
//
void CESignService::ServiceLoop(void)
{
void *ctx = zmq_ctx_new ();
assert (ctx);
/* Create ZMQ_STREAM socket */
void *socket = zmq_socket (ctx, ZMQ_STREAM);
assert (socket);
int nPort = GetIniInteger("PORT");
if (nPort < 1000)
nPort = 8083;
char port[32]; sprintf_s(port, sizeof(port), "tcp://*:%d", nPort);
int rc = zmq_bind (socket, port);
assert (rc == 0);
const int timeout = 5000; // 5 secondi di timeout per verificare anche m_fStopping
zmq_setsockopt (socket, ZMQ_RCVTIMEO, &timeout, sizeof(timeout));
/* Data structure to hold the ZMQ_STREAM ID */
uint8_t id [256];
/* Data structure to hold the ZMQ_STREAM received data */
const size_t raw_max = 1024;
char* raw = new char[raw_max];
while (!m_fStopping)
{
/* Get HTTP request; ID frame and then request */
memset(id, 0, sizeof(id));
const int id_size = zmq_recv (socket, id, sizeof(id), 0);
if (id_size <= 0)
continue;
memset(raw, 0, raw_max);
const size_t raw_size = zmq_recv (socket, raw, raw_max, 0);
CPathName src, dst, bck;
gt_string pin = { 0 }, ext = { 0 };
ParseGET(raw, src, dst, bck, pin, ext);
/* Sends the ID frame followed by the response */
zmq_send (socket, id, id_size, ZMQ_SNDMORE);
gt_string log = {0};
gt_strcpy(log, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n");
CPathName pdf; pdf.Set(src, "*", "pdf");
const int nTotal = pdf.Count();
if (nTotal > 0)
{
const char* const cmdfile = "ESignCmd.exe";
const char* const logfile = "ESignCmd.log";
gt_string cmd = { 0 };
gt_strcpy(cmd, cmdfile);
AppendVar(cmd, "SRC", src);
AppendVar(cmd, "DST", dst);
AppendVar(cmd, "BCK", bck);
AppendVar(cmd, "LOG", logfile);
AppendVar(cmd, "PIN", pin);
AppendVar(cmd, "EXT", ext);
const DWORD err = RunSync(cmd);
memset(raw, 0, raw_max);
if (err == 0)
{
FILE* log = NULL;
if (fopen_s(&log, logfile, "r") == 0)
{
fread(raw, raw_max-1, 1, log);
fclose(log); log = NULL;
}
else
sprintf_s(raw, raw_max,
"%s: bloccato nella firma di %d documenti.",
cmdfile, nTotal);
}
else
sprintf_s(raw, raw_max,
"%s: errore 0x%X nella firma di %d documenti.",
cmdfile, err, nTotal);
gt_strcat(log, raw);
}
else
{
gt_strcat(log, "Non ci sono documenti da firmare!\r\n");
}
zmq_send (socket, log, strlen(log), ZMQ_SNDMORE);
/* Closes the connection by sending the ID frame followed by a zero response */
zmq_send (socket, id, id_size, ZMQ_SNDMORE);
zmq_send (socket, 0, 0, ZMQ_SNDMORE);
/* NOTE: If we don't use ZMQ_SNDMORE, then we won't be able to send more */
/* message to any client */
}
delete [] raw;
zmq_close (socket);
zmq_ctx_destroy (ctx);
// Signal the stopped event.
SetEvent(m_hStoppedEvent);
}
//
// FUNCTION: CESignService::OnStop(void)
//
// PURPOSE: The function is executed when a Stop command is sent to the
// service by SCM. It specifies actions to take when a service stops
// running. In this code sample, OnStop logs a service-stop message to the
// Application log, and waits for the finish of the main service function.
//
// COMMENTS:
// Be sure to periodically call ReportServiceStatus() with
// SERVICE_STOP_PENDING if the procedure is going to take long time.
//
void CESignService::OnStop()
{
// Log a service stop message to the Application log.
WriteEventLogEntry(L"ESignService Stopping");
// Indicate that the service is stopping and wait for the finish of the
// main service function (ServiceWorkerThread).
m_fStopping = TRUE;
if (::WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
throw ::GetLastError();
}