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 interface to the "testman" Web Service
5  * COPYRIGHT:   Copyright 2009-2020 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 static const WCHAR szHostname[] = L"reactos.org";
11 static const INTERNET_PORT ServerPort = 8443;
12 static const WCHAR szServerFile[] = L"testman/webservice/";
13 
14 /**
15  * Constructs a CWebService object and immediately establishes a connection to the "testman" Web Service.
16  */
CWebService()17 CWebService::CWebService()
18 {
19     /* Zero-initialize variables */
20     m_hHTTP = NULL;
21     m_hHTTPRequest = NULL;
22     m_TestID = NULL;
23 
24     /* Establish an internet connection to the "testman" server */
25     m_hInet = InternetOpenW(L"rosautotest", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
26 
27     if(!m_hInet)
28         FATAL("InternetOpenW failed\n");
29 
30     m_hHTTP = InternetConnectW(m_hInet, szHostname, ServerPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
31 
32     if(!m_hHTTP)
33         FATAL("InternetConnectW failed\n");
34 }
35 
36 /**
37  * Destructs a CWebService object and closes all connections to the Web Service.
38  */
~CWebService()39 CWebService::~CWebService()
40 {
41     if(m_hInet)
42         InternetCloseHandle(m_hInet);
43 
44     if(m_hHTTP)
45         InternetCloseHandle(m_hHTTP);
46 
47     if(m_hHTTPRequest)
48         InternetCloseHandle(m_hHTTPRequest);
49 
50     if(m_TestID)
51         delete m_TestID;
52 }
53 
54 /**
55  * Sends data to the Web Service.
56  *
57  * @param InputData
58  * A std::string containing all the data, which is going to be submitted as HTTP POST data.
59  *
60  * @return
61  * Returns a pointer to a char array containing the data received from the Web Service.
62  * The caller needs to free that pointer.
63  */
64 PCHAR
DoRequest(const string & InputData)65 CWebService::DoRequest(const string& InputData)
66 {
67     const WCHAR szHeaders[] = L"Content-Type: application/x-www-form-urlencoded";
68 
69     auto_array_ptr<char> Data;
70     DWORD DataLength;
71 
72     /* Post our test results to the web service */
73     m_hHTTPRequest = HttpOpenRequestW(m_hHTTP, L"POST", szServerFile, NULL, NULL, NULL, INTERNET_FLAG_SECURE | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);
74 
75     if(!m_hHTTPRequest)
76         FATAL("HttpOpenRequestW failed\n");
77 
78     Data.reset(new char[InputData.size() + 1]);
79     strcpy(Data, InputData.c_str());
80 
81     if(!HttpSendRequestW(m_hHTTPRequest, szHeaders, lstrlenW(szHeaders), Data, (DWORD)InputData.size()))
82         FATAL("HttpSendRequestW failed\n");
83 
84     /* Get the response */
85     if(!InternetQueryDataAvailable(m_hHTTPRequest, &DataLength, 0, 0))
86         FATAL("InternetQueryDataAvailable failed\n");
87 
88     Data.reset(new char[DataLength + 1]);
89 
90     if(!InternetReadFile(m_hHTTPRequest, Data, DataLength, &DataLength))
91         FATAL("InternetReadFile failed\n");
92 
93     Data[DataLength] = 0;
94 
95     return Data.release();
96 }
97 
98 /**
99 * Interface to other classes for finishing this test run
100 *
101 * @param TestType
102 * Constant pointer to a char array containing the test type to be run (i.e. "wine")
103 */
104 void
Finish(const char * TestType)105 CWebService::Finish(const char* TestType)
106 {
107     auto_array_ptr<char> Response;
108     string Data;
109     stringstream ss;
110 
111     if (!m_TestID)
112         EXCEPTION("CWebService::Finish was called, but not a single result had been submitted!\n");
113 
114     Data = "action=finish";
115     Data += Configuration.GetAuthenticationRequestString();
116     Data += "&testtype=";
117     Data += TestType;
118     Data += "&testid=";
119     Data += m_TestID;
120 
121     Response.reset(DoRequest(Data));
122 
123     if (strcmp(Response, "OK"))
124     {
125         ss << "When finishing the test run, the server responded:" << endl << Response << endl;
126         SSEXCEPTION;
127     }
128 }
129 
130 /**
131  * Requests a Test ID from the Web Service for our test run.
132  *
133  * @param TestType
134  * Constant pointer to a char array containing the test type to be run (i.e. "wine")
135  */
136 void
GetTestID(const char * TestType)137 CWebService::GetTestID(const char* TestType)
138 {
139     string Data;
140 
141     Data = "action=gettestid";
142     Data += Configuration.GetAuthenticationRequestString();
143     Data += Configuration.GetSystemInfoRequestString();
144     Data += "&testtype=";
145     Data += TestType;
146 
147     if(!Configuration.GetComment().empty())
148     {
149         Data += "&comment=";
150         Data += Configuration.GetComment();
151     }
152 
153     m_TestID = DoRequest(Data);
154 
155     /* Verify that this is really a number */
156     if(!IsNumber(m_TestID))
157     {
158         stringstream ss;
159 
160         ss << "Expected Test ID, but received:" << endl << m_TestID << endl;
161         SSEXCEPTION;
162     }
163 }
164 
165 /**
166  * Gets a Suite ID from the Web Service for this module/test combination.
167  *
168  * @param TestType
169  * Constant pointer to a char array containing the test type to be run (i.e. "wine")
170  *
171  * @param TestInfo
172  * Pointer to a CTestInfo object containing information about the test
173  *
174  * @return
175  * Returns a pointer to a char array containing the Suite ID received from the Web Service.
176  * The caller needs to free that pointer.
177  */
178 PCHAR
GetSuiteID(const char * TestType,CTestInfo * TestInfo)179 CWebService::GetSuiteID(const char* TestType, CTestInfo* TestInfo)
180 {
181     auto_array_ptr<char> SuiteID;
182     string Data;
183 
184     Data = "action=getsuiteid";
185     Data += Configuration.GetAuthenticationRequestString();
186     Data += "&testtype=";
187     Data += TestType;
188     Data += "&module=";
189     Data += TestInfo->Module;
190     Data += "&test=";
191     Data += TestInfo->Test;
192 
193     SuiteID.reset(DoRequest(Data));
194 
195     /* Verify that this is really a number */
196     if(!IsNumber(SuiteID))
197     {
198         stringstream ss;
199 
200         ss << "Expected Suite ID, but received:" << endl << SuiteID << endl;
201         SSEXCEPTION;
202     }
203 
204     return SuiteID.release();
205 }
206 
207 /**
208  * Interface to other classes for submitting a result of one test
209  *
210  * @param TestType
211  * Constant pointer to a char array containing the test type to be run (i.e. "wine")
212  *
213  * @param TestInfo
214  * Pointer to a CTestInfo object containing information about the test
215  */
216 void
Submit(const char * TestType,CTestInfo * TestInfo)217 CWebService::Submit(const char* TestType, CTestInfo* TestInfo)
218 {
219     auto_array_ptr<char> Response;
220     auto_array_ptr<char> SuiteID;
221     string Data;
222     stringstream ss;
223 
224     if(!m_TestID)
225         GetTestID(TestType);
226 
227     SuiteID.reset(GetSuiteID(TestType, TestInfo));
228 
229     Data = "action=submit";
230     Data += Configuration.GetAuthenticationRequestString();
231     Data += "&testtype=";
232     Data += TestType;
233     Data += "&testid=";
234     Data += m_TestID;
235     Data += "&suiteid=";
236     Data += SuiteID;
237     Data += "&log=";
238     Data += EscapeString(TestInfo->Log);
239 
240     Response.reset(DoRequest(Data));
241 
242     if (strcmp(Response, "OK"))
243     {
244         ss << "When submitting the result, the server responded:" << endl << Response << endl;
245         SSEXCEPTION;
246     }
247 }
248