xref: /haiku/src/apps/haikudepot/server/ServerHelper.cpp (revision 268f99dd7dc4bd7474a8bd2742d3f1ec1de6752a)
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
NotifyServerJsonRpcError(BMessage & responsePayload)39 ServerHelper::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
AlertServerJsonRpcError(BMessage * responseEnvelopeMessage)48 ServerHelper::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
NotifyTransportError(status_t error)96 ServerHelper::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
AlertTransportError(BMessage * message)117 ServerHelper::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
NotifyClientTooOld(const BHttpHeaders & responseHeaders)151 ServerHelper::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
AlertClientTooOld(BMessage * message)169 ServerHelper::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
IsNetworkAvailable()194 ServerHelper::IsNetworkAvailable()
195 {
196 	return !ServerSettings::ForceNoNetwork() && IsPlatformNetworkAvailable();
197 }
198 
199 
200 /*static*/ bool
IsPlatformNetworkAvailable()201 ServerHelper::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
GetFailuresFromJsonRpcError(ValidationFailures & failures,BMessage & responseEnvelopeMessage)243 ServerHelper::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