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 }