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