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 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 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 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 117 ServerHelper::AlertTransportError(BMessage* message) 118 { 119 status_t errno = B_OK; 120 int64 errnoInt64; 121 message->FindInt64("errno", &errnoInt64); 122 errno = (status_t) errnoInt64; 123 124 BString errorDescription("?"); 125 BString alertText; 126 127 switch (errno) { 128 case HD_NETWORK_INACCESSIBLE: 129 errorDescription = B_TRANSLATE("Network error"); 130 break; 131 default: 132 errorDescription.SetTo(strerror(errno)); 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 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 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 194 ServerHelper::IsNetworkAvailable() 195 { 196 return !ServerSettings::ForceNoNetwork() && IsPlatformNetworkAvailable(); 197 } 198 199 200 /*static*/ bool 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 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