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