Reactos
1/*
2 * PROJECT: ReactOS Automatic Testing Utility
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Class implementing the curl interface to the "testman" Web Service
5 * COPYRIGHT: Copyright 2025 Mark Jansen <mark.jansen@reactos.org>
6 */
7
8#include "precomp.h"
9#include <curl/curl.h>
10
11static HMODULE g_hLibCurl = nullptr;
12static decltype(&curl_global_init) pcurl_global_init = nullptr;
13static decltype(&curl_easy_init) pcurl_easy_init = nullptr;
14static decltype(&curl_easy_setopt) pcurl_easy_setopt = nullptr;
15static decltype(&curl_easy_perform) pcurl_easy_perform = nullptr;
16static decltype(&curl_easy_strerror) pcurl_easy_strerror = nullptr;
17static decltype(&curl_easy_cleanup) pcurl_easy_cleanup = nullptr;
18static decltype(&curl_global_cleanup) pcurl_global_cleanup = nullptr;
19
20
21bool
22CWebServiceLibCurl::CanUseLibCurl()
23{
24 static bool initialized = false;
25 if (!initialized)
26 {
27 initialized = true;
28 g_hLibCurl = LoadLibraryA("___libcurl.dll");
29 if (!g_hLibCurl)
30 return false;
31
32 pcurl_global_init = (decltype(&curl_global_init))GetProcAddress(g_hLibCurl, "curl_global_init");
33 pcurl_easy_init = (decltype(&curl_easy_init))GetProcAddress(g_hLibCurl, "curl_easy_init");
34 pcurl_easy_setopt = (decltype(&curl_easy_setopt))GetProcAddress(g_hLibCurl, "curl_easy_setopt");
35 pcurl_easy_perform = (decltype(&curl_easy_perform))GetProcAddress(g_hLibCurl, "curl_easy_perform");
36 pcurl_easy_strerror = (decltype(&curl_easy_strerror))GetProcAddress(g_hLibCurl, "curl_easy_strerror");
37 pcurl_easy_cleanup = (decltype(&curl_easy_cleanup))GetProcAddress(g_hLibCurl, "curl_easy_cleanup");
38 pcurl_global_cleanup = (decltype(&curl_global_cleanup))GetProcAddress(g_hLibCurl, "curl_global_cleanup");
39 }
40 if (!pcurl_global_init || !pcurl_easy_init || !pcurl_easy_setopt || !pcurl_easy_perform || !pcurl_easy_strerror ||
41 !pcurl_easy_cleanup || !pcurl_global_cleanup)
42 {
43 return false;
44 }
45 return true;
46}
47
48/**
49 * Constructs a CWebServiceLibCurl object
50 */
51CWebServiceLibCurl::CWebServiceLibCurl()
52{
53 /* Initialize libcurl */
54 if (pcurl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
55 {
56 FATAL("Failed to initialize libcurl\n");
57 }
58
59 m_hCurl = pcurl_easy_init();
60 if (!m_hCurl)
61 {
62 FATAL("Failed to create a libcurl handle\n");
63 }
64
65 CHAR BaseName[MAX_PATH*2];
66 GetModuleFileNameA(g_hLibCurl, BaseName, _countof(BaseName));
67 PCHAR FileName = strrchr(BaseName, '\\');
68 FileName = FileName ? (FileName + 1) : BaseName;
69 strcpy(FileName, "curl-ca-bundle.crt");
70 pcurl_easy_setopt(m_hCurl, CURLOPT_CAINFO, BaseName);
71}
72
73/**
74 * Destructs a CWebServiceLibCurl object and closes all connections to the Web Service.
75 */
76CWebServiceLibCurl::~CWebServiceLibCurl()
77{
78 if (m_hCurl)
79 {
80 pcurl_easy_cleanup(m_hCurl);
81 m_hCurl = nullptr;
82 }
83
84 pcurl_global_cleanup();
85}
86
87
88static size_t
89callback_func(void *ptr, size_t size, size_t count, void *userdata)
90{
91 string *ResultData = (string *)userdata;
92 ResultData->append((const char*)ptr, count);
93 return count;
94}
95
96/**
97 * Sends data to the Web Service.
98 *
99 * @param InputData
100 * A std::string containing all the data, which is going to be submitted as HTTP POST data.
101 *
102 * @return
103 * Returns a pointer to a char array containing the data received from the Web Service.
104 * The caller needs to free that pointer.
105 */
106PCHAR
107CWebServiceLibCurl::DoRequest(const char *Hostname, INTERNET_PORT Port, const char *ServerFile, const string &InputData)
108{
109 char buffer[1024];
110 sprintf(buffer, "https://%s:%u/%s", Hostname, Port, ServerFile);
111
112 pcurl_easy_setopt(m_hCurl, CURLOPT_URL, buffer);
113 pcurl_easy_setopt(m_hCurl, CURLOPT_POSTFIELDS, InputData.c_str());
114 pcurl_easy_setopt(m_hCurl, CURLOPT_USERAGENT, "rosautotest/curl");
115
116 string ResultData;
117 pcurl_easy_setopt(m_hCurl, CURLOPT_WRITEFUNCTION, callback_func);
118 pcurl_easy_setopt(m_hCurl, CURLOPT_WRITEDATA, &ResultData);
119
120 CURLcode res = pcurl_easy_perform(m_hCurl);
121 if (res != CURLE_OK)
122 {
123 string errorMsg = string("curl_easy_perform failed: ") + pcurl_easy_strerror(res);
124 FATAL(errorMsg.c_str());
125 }
126
127 auto_array_ptr<char> Data;
128 Data.reset(new char[ResultData.size() + 1]);
129 strncpy(Data, ResultData.c_str(), ResultData.size());
130 Data[ResultData.size()] = '\0';
131
132 return Data.release();
133}
134