#pragma region Includes #include "ESignService.h" #include "ThreadPool.h" #include "PathName.h" #include #include #include #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(); }