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