d40707a134
git-svn-id: svn://10.65.10.50/branches/R_10_00@23098 c028cbd2-c16b-5b4b-a496-9718f37d4682
392 lines
11 KiB
C++
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, ¤tToken);
|
|
|
|
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();
|
|
}
|