1/*
2 * Copyright 2017-2021, Andrew Lindesay <apl@lindesay.co.nz>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6
7#include "ServerHelper.h"
8
9#include <stdio.h>
10#include <stdlib.h>
11
12#include <Alert.h>
13#include <Application.h>
14#include <Catalog.h>
15#include <NetworkInterface.h>
16#include <NetworkRoster.h>
17
18#include "Logger.h"
19#include "HaikuDepotConstants.h"
20#include "ServerSettings.h"
21#include "WebAppInterface.h"
22
23
24#undef B_TRANSLATION_CONTEXT
25#define B_TRANSLATION_CONTEXT "ServerHelper"
26
27#define KEY_MSG_MINIMUM_VERSION "minimumVersion"
28#define KEY_HEADER_MINIMUM_VERSION "X-Desktop-Application-Minimum-Version"
29
30
31/*! \brief This method will cause an alert to be shown to the user regarding a
32    JSON-RPC error that has been sent from the application server.  It will
33    send a message to the application looper which will then relay the message
34    to the looper and then onto the user to see.
35    \param responsePayload The top level payload returned from the server.
36*/
37
38/*static*/ void
39ServerHelper::NotifyServerJsonRpcError(BMessage& responsePayload)
40{
41	BMessage message(MSG_SERVER_ERROR);
42	message.AddMessage("error", &responsePayload);
43	be_app->PostMessage(&message);
44}
45
46
47/*static*/ void
48ServerHelper::AlertServerJsonRpcError(BMessage* responseEnvelopeMessage)
49{
50	BMessage errorMessage;
51	int32 errorCode = 0;
52
53	if (responseEnvelopeMessage->FindMessage("error", &errorMessage) == B_OK)
54		errorCode = WebAppInterface::ErrorCodeFromResponse(errorMessage);
55
56	BString alertText;
57
58	switch (errorCode) {
59		case ERROR_CODE_VALIDATION:
60				// TODO; expand on that message.
61			alertText = B_TRANSLATE("A validation error has occurred");
62			break;
63		case ERROR_CODE_OBJECTNOTFOUND:
64			alertText = B_TRANSLATE("A requested object or an object involved"
65				" in the request was not found on the server.");
66			break;
67		case ERROR_CODE_CAPTCHABADRESPONSE:
68			alertText = B_TRANSLATE("The response to the captcha was"
69				" incorrect.");
70			break;
71		case ERROR_CODE_AUTHORIZATIONFAILURE:
72		case ERROR_CODE_AUTHORIZATIONRULECONFLICT:
73			alertText = B_TRANSLATE("Authorization or security issue. Logout"
74				" and log back in again to check that your password is correct"
75				" and also check that you have agreed to the latest usage"
76				" conditions.");
77			break;
78		default:
79			alertText.SetToFormat(
80				B_TRANSLATE("An unexpected error has been sent from the"
81					" server [%" B_PRIi32 "]"), errorCode);
82			break;
83	}
84
85	BAlert* alert = new BAlert(
86		B_TRANSLATE("Server error"),
87		alertText,
88		B_TRANSLATE("OK"));
89
90	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
91	alert->Go();
92}
93
94
95/*static*/ void
96ServerHelper::NotifyTransportError(status_t error)
97{
98	switch (error) {
99		case HD_CLIENT_TOO_OLD:
100				// this is handled earlier on because it requires some
101				// information from the HTTP request to create a sensible
102				// error message.
103			break;
104
105		default:
106		{
107			BMessage message(MSG_NETWORK_TRANSPORT_ERROR);
108			message.AddInt64("errno", (int64) error);
109			be_app->PostMessage(&message);
110			break;
111		}
112	}
113}
114
115
116/*static*/ void
117ServerHelper::AlertTransportError(BMessage* message)
118{
119	status_t error = B_OK;
120	int64 errnoInt64;
121	message->FindInt64("errno", &errnoInt64);
122	error = (status_t) errnoInt64;
123
124	BString errorDescription("?");
125	BString alertText;
126
127	switch (error) {
128		case HD_NETWORK_INACCESSIBLE:
129			errorDescription = B_TRANSLATE("Network error");
130			break;
131		default:
132			errorDescription.SetTo(strerror(error));
133			break;
134	}
135
136	alertText.SetToFormat(B_TRANSLATE("A network transport error has arisen"
137		" communicating with the server system: %s"),
138		errorDescription.String());
139
140	BAlert* alert = new BAlert(
141		B_TRANSLATE("Network transport error"),
142		alertText,
143		B_TRANSLATE("OK"));
144
145	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
146	alert->Go();
147}
148
149
150/*static*/ void
151ServerHelper::NotifyClientTooOld(const BHttpHeaders& responseHeaders)
152{
153	if (!ServerSettings::IsClientTooOld()) {
154		ServerSettings::SetClientTooOld();
155
156		const char* minimumVersionC = responseHeaders[KEY_HEADER_MINIMUM_VERSION];
157		BMessage message(MSG_CLIENT_TOO_OLD);
158
159		if (minimumVersionC != NULL && strlen(minimumVersionC) != 0) {
160			message.AddString(KEY_MSG_MINIMUM_VERSION, minimumVersionC);
161		}
162
163		be_app->PostMessage(&message);
164	}
165}
166
167
168/*static*/ void
169ServerHelper::AlertClientTooOld(BMessage* message)
170{
171	BString minimumVersion;
172	BString alertText;
173
174	if (message->FindString(KEY_MSG_MINIMUM_VERSION, &minimumVersion) != B_OK)
175		minimumVersion = "???";
176
177	alertText.SetToFormat(
178		B_TRANSLATE("This application is too old to communicate with the"
179			" server system. Obtain a newer version of this application"
180			" by updating your system. The minimum required version of this"
181			" application is \"%s\"."), minimumVersion.String());
182
183	BAlert* alert = new BAlert(
184		B_TRANSLATE("Client version too old"),
185		alertText,
186		B_TRANSLATE("OK"));
187
188	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
189	alert->Go();
190}
191
192
193/*static*/ bool
194ServerHelper::IsNetworkAvailable()
195{
196	return !ServerSettings::ForceNoNetwork() && IsPlatformNetworkAvailable();
197}
198
199
200/*static*/ bool
201ServerHelper::IsPlatformNetworkAvailable()
202{
203	BNetworkRoster& roster = BNetworkRoster::Default();
204	BNetworkInterface interface;
205	uint32 cookie = 0;
206	while (roster.GetNextInterface(&cookie, interface) == B_OK) {
207		uint32 flags = interface.Flags();
208		if ((flags & IFF_LOOPBACK) == 0
209			&& (flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) {
210			return true;
211		}
212	}
213
214	return false;
215}
216
217
218/*! If the response is an error and the error is a validation failure then
219 * various validation errors may be carried in the error data.  These are
220 * copied into the supplied failures.  An abridged example input JSON structure
221 * would be;
222 *
223 * \code
224 * {
225 *   ...
226 *   "error": {
227 *     "code": -32800,
228 *     "data": { "nickname": "required" }
229       },
230       ...
231 *   }
232 * }
233 * \endcode
234 *
235 * \param failures is the object into which the validation failures are to be
236 *   written.
237 * \param responseEnvelopeMessage is a representation of the entire
238 *   response sent back from the server when the error occurred.
239 *
240 */
241
242/*static*/ void
243ServerHelper::GetFailuresFromJsonRpcError(
244	ValidationFailures& failures, BMessage& responseEnvelopeMessage)
245{
246	BMessage errorMessage;
247
248	if (responseEnvelopeMessage.FindMessage("error", &errorMessage) == B_OK) {
249		BMessage dataMessage;
250
251		if (errorMessage.FindMessage("data", &dataMessage) == B_OK) {
252
253			// the names and values (strings) are key-value pairs indicating
254			// the error.
255
256			int32 i = 0;
257			BMessage dataItemMessage;
258
259			while (dataMessage.FindMessage(BString() << i, &dataItemMessage)
260					== B_OK) {
261				BString key;
262				BString value;
263				if (dataItemMessage.FindString("key", &key) == B_OK
264					&& dataItemMessage.FindString("value", &value) == B_OK) {
265					failures.AddFailure(key, value);
266				} else {
267					HDERROR("possibly corrupt validation message missing key "
268						"or value");
269				}
270				i++;
271			}
272		}
273	}
274}
275