544 lines
16 KiB
C++
544 lines
16 KiB
C++
#pragma region Includes
|
||
#include "ESignService.h"
|
||
#include "ThreadPool.h"
|
||
|
||
#include "esigner.h"
|
||
|
||
#include <cassert>
|
||
|
||
|
||
#include <zmq.h>
|
||
|
||
#pragma endregion
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// Utility
|
||
///////////////////////////////////////////////////////////
|
||
|
||
static std::string GetIniString(const char* key)
|
||
{
|
||
char val[_MAX_PATH];
|
||
::GetPrivateProfileStringA("Default", key, "", val, sizeof(val), "./esigner.ini");
|
||
return val;
|
||
}
|
||
|
||
static int GetIniInteger(const char* key)
|
||
{
|
||
char val[_MAX_PATH];
|
||
::GetPrivateProfileStringA("Default", key, "", val, sizeof(val), "./esigner.ini");
|
||
return atoi(val);
|
||
}
|
||
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// CEsigner
|
||
///////////////////////////////////////////////////////////
|
||
|
||
class CEsigner
|
||
{
|
||
HMODULE _hESigner;
|
||
ESignerSignProto _SignPDF;
|
||
ESignerVerifyProto _VerifyPDF;
|
||
bool m_bMark;
|
||
int m_nNum;
|
||
std::string m_strDLL, m_strPFX, m_strPEM, m_strCER, m_strEXT;
|
||
std::string m_strTSAurl, m_strTSAuser, m_strTSApwd, m_strTSApolicy, m_strTSAcoding;
|
||
std::string* _socket;
|
||
|
||
protected:
|
||
void Log(const char* fmt, ...) const;
|
||
bool EsitoZero(const CPathName& strResult) const;
|
||
|
||
public:
|
||
bool IsOk() const { return _hESigner != NULL; }
|
||
|
||
void SetOutputString(std::string& s) { _socket = &s; }
|
||
bool Sign(const CPathName& strInput, CPathName& strOutput, const CPathName& strBackup,
|
||
const std::string& strPin, const std::string& strExt) const;
|
||
|
||
CEsigner();
|
||
virtual ~CEsigner();
|
||
};
|
||
|
||
void CEsigner::Log(const char* fmt, ...) const
|
||
{
|
||
char buffer[512];
|
||
va_list ap;
|
||
va_start(ap, fmt);
|
||
vsprintf_s(buffer, sizeof(buffer), fmt, ap);
|
||
va_end(ap);
|
||
|
||
if (_socket != NULL)
|
||
{
|
||
*_socket += buffer;
|
||
*_socket += "\r\n";
|
||
}
|
||
else
|
||
printf("%s\n", buffer);
|
||
}
|
||
|
||
CEsigner::CEsigner() : m_bMark(false), m_nNum(0), _socket(NULL)
|
||
{
|
||
char full[_MAX_PATH]; ::GetModuleFileNameA(NULL, full, sizeof(full));
|
||
CPathName pn = full;
|
||
::SetCurrentDirectoryA(pn.Path().c_str());
|
||
|
||
_hESigner = ::LoadLibrary(TEXT("esigner.dll"));
|
||
if (_hESigner)
|
||
{
|
||
_SignPDF = (ESignerSignProto)::GetProcAddress(_hESigner, "Sign");
|
||
_VerifyPDF = (ESignerVerifyProto)::GetProcAddress(_hESigner, "Verify");
|
||
m_strCER = GetIniString("CER");
|
||
m_strDLL = GetIniString("DLL");
|
||
m_strPEM = GetIniString("PEM");
|
||
m_strPFX = GetIniString("PFX");
|
||
m_strEXT = GetIniString("EXT"); // PDF or P7M
|
||
if (m_strEXT.empty())
|
||
m_strEXT = "p7m";
|
||
|
||
const int nIdx = GetIniInteger("IDX");
|
||
if (nIdx > 0)
|
||
m_nNum = nIdx;
|
||
}
|
||
}
|
||
|
||
CEsigner::~CEsigner()
|
||
{
|
||
if (_hESigner)
|
||
::FreeLibrary(_hESigner);
|
||
}
|
||
|
||
bool CEsigner::EsitoZero(const CPathName& strResult) const
|
||
{
|
||
bool positive = false;
|
||
FILE* f = NULL;
|
||
if (fopen_s(&f, strResult.c_str(), "r") == 0)
|
||
{
|
||
char buffer[512];
|
||
while (!feof(f))
|
||
{
|
||
fgets(buffer, sizeof(buffer), f);
|
||
if (strstr(buffer, "ESITO=\"000\"") != NULL)
|
||
{
|
||
positive = true;
|
||
break;
|
||
}
|
||
}
|
||
fclose(f);
|
||
}
|
||
|
||
return positive;
|
||
}
|
||
|
||
|
||
bool CEsigner::Sign(const CPathName& strInput, CPathName& strOutput, const CPathName& strBackup,
|
||
const std::string& strPin, const std::string& strExt) const
|
||
{
|
||
if (_hESigner == NULL)
|
||
{
|
||
Log("Impossibile caricare esigner.dll");
|
||
return false;
|
||
}
|
||
|
||
std::string strMethod;
|
||
if (!m_strDLL.empty()) // Token
|
||
{
|
||
strMethod = "T";
|
||
Log("Firma tramite token");
|
||
} else
|
||
if (!m_strPFX.empty())
|
||
{
|
||
strMethod = "P";
|
||
Log("Firma tramite file PFX");
|
||
} else
|
||
if (!m_strCER.empty())
|
||
{
|
||
strMethod = "F";
|
||
Log("Firma tramite i file CER e PEM");
|
||
}
|
||
else
|
||
{
|
||
Log("Impossibile trovare certificati o token.\nVerificare i parametri in esigner.ini");
|
||
return false;
|
||
}
|
||
|
||
std::string strOperation = "S";
|
||
|
||
const bool bIsTSA = m_bMark && !m_strTSAurl.empty();
|
||
if (bIsTSA) // Firma con marcatura temporale
|
||
strOperation += "+T";
|
||
|
||
const bool bIsDir = strInput.IsDirectory();
|
||
if (bIsDir)
|
||
strOperation += "+D";
|
||
|
||
std::string strIndex;
|
||
strIndex << m_nNum;
|
||
|
||
char* operation = (char*)strOperation.c_str();
|
||
char* method = (char*)strMethod.c_str();
|
||
char* ext = (char*)(strExt.empty() ? m_strEXT.c_str() : strExt.c_str());
|
||
char* input = (char*)strInput.c_str();
|
||
char* output = (char*)strOutput.c_str();
|
||
|
||
char* cer = strMethod == "F" ? (char*)m_strCER.c_str() : NULL;
|
||
char* pem = strMethod == "F" ? (char*)m_strPEM.c_str() : NULL;
|
||
char* pfx = strMethod == "P" ? (char*)m_strPFX.c_str() : NULL;
|
||
char* idx = (char*)strIndex.c_str();
|
||
char* pin = (char*)strPin.c_str();
|
||
char* dll = strMethod == "T" ? (char*)m_strDLL.c_str() : NULL;
|
||
|
||
char* TSA_output = NULL;
|
||
char* TSA_url = NULL;
|
||
char* TSA_user = NULL;
|
||
char* TSA_pwd = NULL;
|
||
char* TSA_policy = NULL;
|
||
char* TSA_coding = NULL;
|
||
char* TSA_rootCA = NULL;
|
||
|
||
if (bIsTSA) // Firma con marcatura temporale
|
||
{
|
||
TSA_url = (char*)m_strTSAurl.c_str();
|
||
TSA_user = (char*)m_strTSAuser.c_str();
|
||
TSA_pwd = (char*)m_strTSApwd.c_str();
|
||
TSA_policy = (char*)m_strTSApolicy.c_str();
|
||
TSA_coding = (char*)m_strTSAcoding.c_str();
|
||
}
|
||
|
||
if (bIsDir)
|
||
{
|
||
if (strOutput.empty())
|
||
output = input;
|
||
}
|
||
else
|
||
{
|
||
//ext = strInput.Lower().EndsWith(".pdf") ? ".pdf.p7m" : ".p7m";
|
||
if (strOutput.empty())
|
||
{
|
||
strOutput = strInput;
|
||
if (strExt == "p7m")
|
||
strOutput += ".p7m";
|
||
}
|
||
::DeleteFileA(strOutput.c_str()); // Altrimenti la fantastica dll s'incazza come una biscia
|
||
|
||
while (!strOutput.Ext().empty())
|
||
strOutput.SetExt("");
|
||
output = (char*)strOutput.c_str();
|
||
}
|
||
|
||
int res = _SignPDF(operation, method,
|
||
input, output, NULL, ext,
|
||
cer, pem, pfx, dll, pin, NULL, idx,
|
||
TSA_url, TSA_user, TSA_pwd, TSA_policy, TSA_coding, NULL);
|
||
|
||
int nSigned = 0;
|
||
if (bIsDir) // +D only
|
||
{
|
||
std::vector<CPathName> infiles;
|
||
CPathName pnInput; pnInput.Set(strInput.c_str(), "*", "pdf");
|
||
const size_t n = pnInput.List(infiles);
|
||
if (n > 0)
|
||
{
|
||
Log("Trovati %d documenti da firmare in %s", n, strInput.c_str());
|
||
|
||
res = 0;
|
||
CPathName pnResult;
|
||
for (size_t i = 0; i < n; i++)
|
||
{
|
||
const CPathName& fi = infiles[i];
|
||
pnResult.Set(strOutput.c_str(), fi.FullName().c_str(), "result");
|
||
if (pnResult.IsFile() && EsitoZero(pnResult))
|
||
{
|
||
::DeleteFileA(pnResult.c_str());
|
||
nSigned++;
|
||
}
|
||
else
|
||
{
|
||
Log("Impossibile firmare il file %s", fi.c_str());
|
||
res = -99;
|
||
}
|
||
}
|
||
}
|
||
|
||
int mode = 0; // 0=nulla; 1=cancella; 2=backup
|
||
if (strBackup == "Cestino" || strBackup == "NULL" || strBackup == "Delete" ||
|
||
strBackup == "Basket" || strBackup == "Trash")
|
||
mode = 1; else
|
||
if (strBackup.IsDirectory())
|
||
mode = 2;
|
||
|
||
if (res == 0 && mode != 0 && n > 0)
|
||
{
|
||
std::string msg;
|
||
if (mode == 1)
|
||
{
|
||
msg = "Eliminazione file originali in ";
|
||
msg += strInput;
|
||
}
|
||
else
|
||
{
|
||
msg = "Spostamento file originali da ";
|
||
msg += strInput;
|
||
msg += " a ";
|
||
msg += strBackup;
|
||
}
|
||
|
||
for (size_t i = 0; i < n; i++)
|
||
{
|
||
const CPathName& fsrc = infiles[i];
|
||
CPathName fout = fsrc;
|
||
fout.SetPath(strBackup.c_str());
|
||
if (mode == 2)
|
||
::MoveFileA(fsrc.c_str(), fout.c_str());
|
||
else
|
||
::DeleteFileA(fsrc.c_str());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Log("Un documento da firmare in %s", strInput.c_str());
|
||
nSigned == res ? 0 : 1;
|
||
}
|
||
|
||
if (res == 0)
|
||
{
|
||
if (nSigned > 0)
|
||
Log("Sono stati firmati %d documenti in %s", nSigned, output);
|
||
else
|
||
Log("Non <20> stato firmato alcun documento");
|
||
}
|
||
else
|
||
{
|
||
const char* msg = NULL;
|
||
switch (res)
|
||
{
|
||
case -2: msg = "Errore di lettura del certificato"; break;
|
||
case -3: msg = "Errore di lettura della chiave privata"; break;
|
||
case -4: msg = "Errore di lettura file PFX"; break;
|
||
case -5: msg = "Errore recupero certificato da Token"; break;
|
||
case -6: msg = "Errore apertura file"; break;
|
||
case -7:
|
||
case -8:
|
||
case -9: msg = "Errore conatatto TSA"; break;
|
||
case -10: msg = "Accesso al token fallito"; break;
|
||
case -11: msg = "Impossibile trovare dll driver del token"; break;
|
||
case -14: msg = "Il file di output esiste gi<67>"; break;
|
||
case -15:
|
||
case -16: msg = "Errore di marcatura temporale"; break;
|
||
case -99: msg = "Impossibile trovare .result per tutti i file della cartella"; break;
|
||
default : msg = "Errore di accesso alle operazioni di firma"; break;
|
||
}
|
||
|
||
Log(msg);
|
||
}
|
||
|
||
return res == 0;
|
||
}
|
||
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// 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, std::string& str) const
|
||
{
|
||
str = "";
|
||
const char* s = equal;
|
||
for (; *s != '&' && *s > ' '; s++)
|
||
{
|
||
if (*s == '%')
|
||
{
|
||
const char h = ParseHex(s+1);
|
||
str += h;
|
||
s += 2;
|
||
}
|
||
else
|
||
str += *s;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
bool CESignService::ParseGET(const char* get, CPathName& src, CPathName& dst, CPathName& bck,
|
||
std::string& pin, std::string& ext) const
|
||
{
|
||
src = dst = bck = "";
|
||
pin = "";
|
||
ext = "";
|
||
while (get != NULL)
|
||
{
|
||
const char* equal = strchr(get, '=');
|
||
if (equal != NULL)
|
||
{
|
||
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 : break;
|
||
}
|
||
}
|
||
else
|
||
get = equal;
|
||
}
|
||
return !src.empty();
|
||
}
|
||
|
||
//
|
||
// 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 = 512;
|
||
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;
|
||
std::string pin, ext;
|
||
ParseGET(raw, src, dst, bck, pin, ext);
|
||
|
||
/* Sends the ID frame followed by the response */
|
||
zmq_send (socket, id, id_size, ZMQ_SNDMORE);
|
||
|
||
/* Prepares the response */
|
||
std::string log = "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n";
|
||
CEsigner esigner;
|
||
esigner.SetOutputString(log);
|
||
esigner.Sign(src, dst, bck, pin, ext);
|
||
zmq_send (socket, log.c_str(),log.length(), 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();
|
||
}
|