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