1 /* 2 * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>. 3 * Copyright 2016-2018, Andrew Lindesay <apl@lindesay.co.nz>. 4 * All rights reserved. Distributed under the terms of the MIT License. 5 */ 6 7 #include "WebAppInterface.h" 8 9 #include <stdio.h> 10 11 #include <AppFileInfo.h> 12 #include <Application.h> 13 #include <AutoDeleter.h> 14 #include <Autolock.h> 15 #include <File.h> 16 #include <HttpHeaders.h> 17 #include <HttpRequest.h> 18 #include <Json.h> 19 #include <JsonTextWriter.h> 20 #include <Message.h> 21 #include <Roster.h> 22 #include <Url.h> 23 #include <UrlContext.h> 24 #include <UrlProtocolListener.h> 25 #include <UrlProtocolRoster.h> 26 27 #include "AutoLocker.h" 28 #include "DataIOUtils.h" 29 #include "HaikuDepotConstants.h" 30 #include "List.h" 31 #include "Logger.h" 32 #include "PackageInfo.h" 33 #include "ServerSettings.h" 34 #include "ServerHelper.h" 35 36 37 #define BASEURL_DEFAULT "https://depot.haiku-os.org" 38 #define USERAGENT_FALLBACK_VERSION "0.0.0" 39 #define LOG_PAYLOAD_LIMIT 8192 40 41 42 class JsonBuilder { 43 public: 44 JsonBuilder() 45 : 46 fString("{"), 47 fInList(false) 48 { 49 } 50 51 JsonBuilder& AddObject() 52 { 53 fString << '{'; 54 fInList = false; 55 return *this; 56 } 57 58 JsonBuilder& AddObject(const char* name) 59 { 60 _StartName(name); 61 fString << '{'; 62 fInList = false; 63 return *this; 64 } 65 66 JsonBuilder& EndObject() 67 { 68 fString << '}'; 69 fInList = true; 70 return *this; 71 } 72 73 JsonBuilder& AddArray(const char* name) 74 { 75 _StartName(name); 76 fString << '['; 77 fInList = false; 78 return *this; 79 } 80 81 JsonBuilder& EndArray() 82 { 83 fString << ']'; 84 fInList = true; 85 return *this; 86 } 87 88 JsonBuilder& AddStrings(const StringList& strings) 89 { 90 for (int i = 0; i < strings.CountItems(); i++) 91 AddItem(strings.ItemAtFast(i)); 92 return *this; 93 } 94 95 JsonBuilder& AddItem(const char* item) 96 { 97 return AddItem(item, false); 98 } 99 100 JsonBuilder& AddItem(const char* item, bool nullIfEmpty) 101 { 102 if (item == NULL || (nullIfEmpty && strlen(item) == 0)) { 103 if (fInList) 104 fString << ",null"; 105 else 106 fString << "null"; 107 } else { 108 if (fInList) 109 fString << ",\""; 110 else 111 fString << '"'; 112 fString << _EscapeString(item); 113 fString << '"'; 114 } 115 fInList = true; 116 return *this; 117 } 118 119 JsonBuilder& AddValue(const char* name, const char* value) 120 { 121 return AddValue(name, value, false); 122 } 123 124 JsonBuilder& AddValue(const char* name, const char* value, 125 bool nullIfEmpty) 126 { 127 _StartName(name); 128 if (value == NULL || (nullIfEmpty && strlen(value) == 0)) { 129 fString << "null"; 130 } else { 131 fString << '"'; 132 fString << _EscapeString(value); 133 fString << '"'; 134 } 135 fInList = true; 136 return *this; 137 } 138 139 JsonBuilder& AddValue(const char* name, int value) 140 { 141 _StartName(name); 142 fString << value; 143 fInList = true; 144 return *this; 145 } 146 147 JsonBuilder& AddValue(const char* name, bool value) 148 { 149 _StartName(name); 150 if (value) 151 fString << "true"; 152 else 153 fString << "false"; 154 fInList = true; 155 return *this; 156 } 157 158 const BString& End() 159 { 160 fString << "}\n"; 161 return fString; 162 } 163 164 private: 165 void _StartName(const char* name) 166 { 167 if (fInList) 168 fString << ",\""; 169 else 170 fString << '"'; 171 fString << _EscapeString(name); 172 fString << "\":"; 173 } 174 175 BString _EscapeString(const char* original) const 176 { 177 BString string(original); 178 string.ReplaceAll("\\", "\\\\"); 179 string.ReplaceAll("\"", "\\\""); 180 string.ReplaceAll("/", "\\/"); 181 string.ReplaceAll("\b", "\\b"); 182 string.ReplaceAll("\f", "\\f"); 183 string.ReplaceAll("\n", "\\n"); 184 string.ReplaceAll("\r", "\\r"); 185 string.ReplaceAll("\t", "\\t"); 186 return string; 187 } 188 189 private: 190 BString fString; 191 bool fInList; 192 }; 193 194 195 class ProtocolListener : public BUrlProtocolListener { 196 public: 197 ProtocolListener(bool traceLogging) 198 : 199 fDownloadIO(NULL), 200 fTraceLogging(traceLogging) 201 { 202 } 203 204 virtual ~ProtocolListener() 205 { 206 } 207 208 virtual void ConnectionOpened(BUrlRequest* caller) 209 { 210 } 211 212 virtual void HostnameResolved(BUrlRequest* caller, const char* ip) 213 { 214 } 215 216 virtual void ResponseStarted(BUrlRequest* caller) 217 { 218 } 219 220 virtual void HeadersReceived(BUrlRequest* caller, const BUrlResult& result) 221 { 222 } 223 224 virtual void DataReceived(BUrlRequest* caller, const char* data, 225 off_t position, ssize_t size) 226 { 227 if (fDownloadIO != NULL) 228 fDownloadIO->Write(data, size); 229 } 230 231 virtual void DownloadProgress(BUrlRequest* caller, ssize_t bytesReceived, 232 ssize_t bytesTotal) 233 { 234 } 235 236 virtual void UploadProgress(BUrlRequest* caller, ssize_t bytesSent, 237 ssize_t bytesTotal) 238 { 239 } 240 241 virtual void RequestCompleted(BUrlRequest* caller, bool success) 242 { 243 } 244 245 virtual void DebugMessage(BUrlRequest* caller, 246 BUrlProtocolDebugMessage type, const char* text) 247 { 248 if (fTraceLogging) 249 printf("jrpc: %s\n", text); 250 } 251 252 void SetDownloadIO(BDataIO* downloadIO) 253 { 254 fDownloadIO = downloadIO; 255 } 256 257 private: 258 BDataIO* fDownloadIO; 259 bool fTraceLogging; 260 }; 261 262 263 int 264 WebAppInterface::fRequestIndex = 0; 265 266 267 enum { 268 NEEDS_AUTHORIZATION = 1 << 0, 269 }; 270 271 272 WebAppInterface::WebAppInterface() 273 : 274 fLanguage("en") 275 { 276 } 277 278 279 WebAppInterface::WebAppInterface(const WebAppInterface& other) 280 : 281 fUsername(other.fUsername), 282 fPassword(other.fPassword), 283 fLanguage(other.fLanguage) 284 { 285 } 286 287 288 WebAppInterface::~WebAppInterface() 289 { 290 } 291 292 293 WebAppInterface& 294 WebAppInterface::operator=(const WebAppInterface& other) 295 { 296 if (this == &other) 297 return *this; 298 299 fUsername = other.fUsername; 300 fPassword = other.fPassword; 301 fLanguage = other.fLanguage; 302 303 return *this; 304 } 305 306 307 void 308 WebAppInterface::SetAuthorization(const BString& username, 309 const BString& password) 310 { 311 fUsername = username; 312 fPassword = password; 313 } 314 315 316 void 317 WebAppInterface::SetPreferredLanguage(const BString& language) 318 { 319 fLanguage = language; 320 } 321 322 323 status_t 324 WebAppInterface::GetChangelog(const BString& packageName, BMessage& message) 325 { 326 BString jsonString = JsonBuilder() 327 .AddValue("jsonrpc", "2.0") 328 .AddValue("id", ++fRequestIndex) 329 .AddValue("method", "getPkgChangelog") 330 .AddArray("params") 331 .AddObject() 332 .AddValue("pkgName", packageName) 333 .EndObject() 334 .EndArray() 335 .End(); 336 337 return _SendJsonRequest("pkg", jsonString, 0, message); 338 } 339 340 341 status_t 342 WebAppInterface::RetrieveUserRatings(const BString& packageName, 343 const BString& architecture, int resultOffset, int maxResults, 344 BMessage& message) 345 { 346 BString jsonString = JsonBuilder() 347 .AddValue("jsonrpc", "2.0") 348 .AddValue("id", ++fRequestIndex) 349 .AddValue("method", "searchUserRatings") 350 .AddArray("params") 351 .AddObject() 352 .AddValue("pkgName", packageName) 353 .AddValue("pkgVersionArchitectureCode", architecture) 354 .AddValue("offset", resultOffset) 355 .AddValue("limit", maxResults) 356 .EndObject() 357 .EndArray() 358 .End(); 359 360 return _SendJsonRequest("userrating", jsonString, 0, message); 361 } 362 363 364 status_t 365 WebAppInterface::RetrieveUserRating(const BString& packageName, 366 const BPackageVersion& version, const BString& architecture, 367 const BString &repositoryCode, const BString& username, 368 BMessage& message) 369 { 370 // BHttpRequest later takes ownership of this. 371 BMallocIO* requestEnvelopeData = new BMallocIO(); 372 BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); 373 374 requestEnvelopeWriter.WriteObjectStart(); 375 _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter, 376 "getUserRatingByUserAndPkgVersion"); 377 requestEnvelopeWriter.WriteObjectName("params"); 378 requestEnvelopeWriter.WriteArrayStart(); 379 380 requestEnvelopeWriter.WriteObjectStart(); 381 382 requestEnvelopeWriter.WriteObjectName("userNickname"); 383 requestEnvelopeWriter.WriteString(username.String()); 384 requestEnvelopeWriter.WriteObjectName("pkgName"); 385 requestEnvelopeWriter.WriteString(packageName.String()); 386 requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode"); 387 requestEnvelopeWriter.WriteString(architecture.String()); 388 requestEnvelopeWriter.WriteObjectName("repositoryCode"); 389 requestEnvelopeWriter.WriteString(repositoryCode.String()); 390 391 if (version.Major().Length() > 0) { 392 requestEnvelopeWriter.WriteObjectName("pkgVersionMajor"); 393 requestEnvelopeWriter.WriteString(version.Major().String()); 394 } 395 396 if (version.Minor().Length() > 0) { 397 requestEnvelopeWriter.WriteObjectName("pkgVersionMinor"); 398 requestEnvelopeWriter.WriteString(version.Minor().String()); 399 } 400 401 if (version.Micro().Length() > 0) { 402 requestEnvelopeWriter.WriteObjectName("pkgVersionMicro"); 403 requestEnvelopeWriter.WriteString(version.Micro().String()); 404 } 405 406 if (version.PreRelease().Length() > 0) { 407 requestEnvelopeWriter.WriteObjectName("pkgVersionPreRelease"); 408 requestEnvelopeWriter.WriteString(version.PreRelease().String()); 409 } 410 411 if (version.Revision() != 0) { 412 requestEnvelopeWriter.WriteObjectName("pkgVersionRevision"); 413 requestEnvelopeWriter.WriteInteger(version.Revision()); 414 } 415 416 requestEnvelopeWriter.WriteObjectEnd(); 417 requestEnvelopeWriter.WriteArrayEnd(); 418 requestEnvelopeWriter.WriteObjectEnd(); 419 420 return _SendJsonRequest("userrating", requestEnvelopeData, 421 requestEnvelopeData->Position(), NEEDS_AUTHORIZATION, message); 422 } 423 424 425 status_t 426 WebAppInterface::CreateUserRating(const BString& packageName, 427 const BPackageVersion& version, 428 const BString& architecture, const BString& repositoryCode, 429 const BString& languageCode, const BString& comment, 430 const BString& stability, int rating, BMessage& message) 431 { 432 // BHttpRequest later takes ownership of this. 433 BMallocIO* requestEnvelopeData = new BMallocIO(); 434 BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); 435 436 requestEnvelopeWriter.WriteObjectStart(); 437 _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter, 438 "createUserRating"); 439 requestEnvelopeWriter.WriteObjectName("params"); 440 requestEnvelopeWriter.WriteArrayStart(); 441 442 requestEnvelopeWriter.WriteObjectStart(); 443 requestEnvelopeWriter.WriteObjectName("pkgName"); 444 requestEnvelopeWriter.WriteString(packageName.String()); 445 requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode"); 446 requestEnvelopeWriter.WriteString(architecture.String()); 447 requestEnvelopeWriter.WriteObjectName("repositoryCode"); 448 requestEnvelopeWriter.WriteString(repositoryCode.String()); 449 requestEnvelopeWriter.WriteObjectName("naturalLanguageCode"); 450 requestEnvelopeWriter.WriteString(languageCode.String()); 451 requestEnvelopeWriter.WriteObjectName("pkgVersionType"); 452 requestEnvelopeWriter.WriteString("SPECIFIC"); 453 requestEnvelopeWriter.WriteObjectName("userNickname"); 454 requestEnvelopeWriter.WriteString(fUsername.String()); 455 456 if (!version.Major().IsEmpty()) { 457 requestEnvelopeWriter.WriteObjectName("pkgVersionMajor"); 458 requestEnvelopeWriter.WriteString(version.Major()); 459 } 460 461 if (!version.Minor().IsEmpty()) { 462 requestEnvelopeWriter.WriteObjectName("pkgVersionMinor"); 463 requestEnvelopeWriter.WriteString(version.Minor()); 464 } 465 466 if (!version.Micro().IsEmpty()) { 467 requestEnvelopeWriter.WriteObjectName("pkgVersionMicro"); 468 requestEnvelopeWriter.WriteString(version.Micro()); 469 } 470 471 if (!version.PreRelease().IsEmpty()) { 472 requestEnvelopeWriter.WriteObjectName("pkgVersionPreRelease"); 473 requestEnvelopeWriter.WriteString(version.PreRelease()); 474 } 475 476 if (version.Revision() != 0) { 477 requestEnvelopeWriter.WriteObjectName("pkgVersionRevision"); 478 requestEnvelopeWriter.WriteInteger(version.Revision()); 479 } 480 481 if (rating > 0.0f) { 482 requestEnvelopeWriter.WriteObjectName("rating"); 483 requestEnvelopeWriter.WriteInteger(rating); 484 } 485 486 if (stability.Length() > 0) { 487 requestEnvelopeWriter.WriteObjectName("userRatingStabilityCode"); 488 requestEnvelopeWriter.WriteString(stability); 489 } 490 491 if (comment.Length() > 0) { 492 requestEnvelopeWriter.WriteObjectName("comment"); 493 requestEnvelopeWriter.WriteString(comment.String()); 494 } 495 496 requestEnvelopeWriter.WriteObjectEnd(); 497 requestEnvelopeWriter.WriteArrayEnd(); 498 requestEnvelopeWriter.WriteObjectEnd(); 499 500 return _SendJsonRequest("userrating", requestEnvelopeData, 501 requestEnvelopeData->Position(), NEEDS_AUTHORIZATION, message); 502 } 503 504 505 status_t 506 WebAppInterface::UpdateUserRating(const BString& ratingID, 507 const BString& languageCode, const BString& comment, 508 const BString& stability, int rating, bool active, BMessage& message) 509 { 510 // BHttpRequest later takes ownership of this. 511 BMallocIO* requestEnvelopeData = new BMallocIO(); 512 BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); 513 514 requestEnvelopeWriter.WriteObjectStart(); 515 _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter, 516 "updateUserRating"); 517 518 requestEnvelopeWriter.WriteObjectName("params"); 519 requestEnvelopeWriter.WriteArrayStart(); 520 521 requestEnvelopeWriter.WriteObjectStart(); 522 523 requestEnvelopeWriter.WriteObjectName("code"); 524 requestEnvelopeWriter.WriteString(ratingID.String()); 525 requestEnvelopeWriter.WriteObjectName("naturalLanguageCode"); 526 requestEnvelopeWriter.WriteString(languageCode.String()); 527 requestEnvelopeWriter.WriteObjectName("active"); 528 requestEnvelopeWriter.WriteBoolean(active); 529 530 requestEnvelopeWriter.WriteObjectName("filter"); 531 requestEnvelopeWriter.WriteArrayStart(); 532 requestEnvelopeWriter.WriteString("ACTIVE"); 533 requestEnvelopeWriter.WriteString("NATURALLANGUAGE"); 534 requestEnvelopeWriter.WriteString("USERRATINGSTABILITY"); 535 requestEnvelopeWriter.WriteString("COMMENT"); 536 requestEnvelopeWriter.WriteString("RATING"); 537 requestEnvelopeWriter.WriteArrayEnd(); 538 539 if (rating >= 0) { 540 requestEnvelopeWriter.WriteObjectName("rating"); 541 requestEnvelopeWriter.WriteInteger(rating); 542 } 543 544 if (stability.Length() > 0) { 545 requestEnvelopeWriter.WriteObjectName("userRatingStabilityCode"); 546 requestEnvelopeWriter.WriteString(stability); 547 } 548 549 if (comment.Length() > 0) { 550 requestEnvelopeWriter.WriteObjectName("comment"); 551 requestEnvelopeWriter.WriteString(comment); 552 } 553 554 requestEnvelopeWriter.WriteObjectEnd(); 555 requestEnvelopeWriter.WriteArrayEnd(); 556 requestEnvelopeWriter.WriteObjectEnd(); 557 558 return _SendJsonRequest("userrating", requestEnvelopeData, 559 requestEnvelopeData->Position(), NEEDS_AUTHORIZATION, message); 560 } 561 562 563 status_t 564 WebAppInterface::RetrieveScreenshot(const BString& code, 565 int32 width, int32 height, BDataIO* stream) 566 { 567 BUrl url = ServerSettings::CreateFullUrl( 568 BString("/__pkgscreenshot/") << code << ".png" << "?tw=" 569 << width << "&th=" << height); 570 571 bool isSecure = url.Protocol() == "https"; 572 573 ProtocolListener listener(Logger::IsTraceEnabled()); 574 listener.SetDownloadIO(stream); 575 576 BHttpHeaders headers; 577 ServerSettings::AugmentHeaders(headers); 578 579 BHttpRequest request(url, isSecure, "HTTP", &listener); 580 request.SetMethod(B_HTTP_GET); 581 request.SetHeaders(headers); 582 583 thread_id thread = request.Run(); 584 wait_for_thread(thread, NULL); 585 586 const BHttpResult& result = dynamic_cast<const BHttpResult&>( 587 request.Result()); 588 589 int32 statusCode = result.StatusCode(); 590 591 if (statusCode == 200) 592 return B_OK; 593 594 fprintf(stderr, "failed to get screenshot from '%s': %" B_PRIi32 "\n", 595 url.UrlString().String(), statusCode); 596 return B_ERROR; 597 } 598 599 600 status_t 601 WebAppInterface::RequestCaptcha(BMessage& message) 602 { 603 BString jsonString = JsonBuilder() 604 .AddValue("jsonrpc", "2.0") 605 .AddValue("id", ++fRequestIndex) 606 .AddValue("method", "generateCaptcha") 607 .AddArray("params") 608 .AddObject() 609 .EndObject() 610 .EndArray() 611 .End(); 612 613 return _SendJsonRequest("captcha", jsonString, 0, message); 614 } 615 616 617 status_t 618 WebAppInterface::CreateUser(const BString& nickName, 619 const BString& passwordClear, const BString& email, 620 const BString& captchaToken, const BString& captchaResponse, 621 const BString& languageCode, BMessage& message) 622 { 623 JsonBuilder builder; 624 builder 625 .AddValue("jsonrpc", "2.0") 626 .AddValue("id", ++fRequestIndex) 627 .AddValue("method", "createUser") 628 .AddArray("params") 629 .AddObject() 630 .AddValue("nickname", nickName) 631 .AddValue("passwordClear", passwordClear); 632 633 if (!email.IsEmpty()) 634 builder.AddValue("email", email); 635 636 builder.AddValue("captchaToken", captchaToken) 637 .AddValue("captchaResponse", captchaResponse) 638 .AddValue("naturalLanguageCode", languageCode) 639 .EndObject() 640 .EndArray() 641 ; 642 643 BString jsonString = builder.End(); 644 645 return _SendJsonRequest("user", jsonString, 0, message); 646 } 647 648 649 status_t 650 WebAppInterface::AuthenticateUser(const BString& nickName, 651 const BString& passwordClear, BMessage& message) 652 { 653 BString jsonString = JsonBuilder() 654 .AddValue("jsonrpc", "2.0") 655 .AddValue("id", ++fRequestIndex) 656 .AddValue("method", "authenticateUser") 657 .AddArray("params") 658 .AddObject() 659 .AddValue("nickname", nickName) 660 .AddValue("passwordClear", passwordClear) 661 .EndObject() 662 .EndArray() 663 .End(); 664 665 return _SendJsonRequest("user", jsonString, 0, message); 666 } 667 668 669 /*! JSON-RPC invocations return a response. The response may be either 670 a result or it may be an error depending on the response structure. 671 If it is an error then there may be additional detail that is the 672 error code and message. This method will extract the error code 673 from the response. This method will return 0 if the payload does 674 not look like an error. 675 */ 676 677 int32 678 WebAppInterface::ErrorCodeFromResponse(BMessage& response) 679 { 680 BMessage error; 681 double code; 682 683 if (response.FindMessage("error", &error) == B_OK 684 && error.FindDouble("code", &code) == B_OK) { 685 return (int32) code; 686 } 687 688 return 0; 689 } 690 691 692 // #pragma mark - private 693 694 695 void 696 WebAppInterface::_WriteStandardJsonRpcEnvelopeValues(BJsonWriter& writer, 697 const char* methodName) 698 { 699 writer.WriteObjectName("jsonrpc"); 700 writer.WriteString("2.0"); 701 writer.WriteObjectName("id"); 702 writer.WriteInteger(++fRequestIndex); 703 writer.WriteObjectName("method"); 704 writer.WriteString(methodName); 705 } 706 707 708 status_t 709 WebAppInterface::_SendJsonRequest(const char* domain, BDataIO* requestData, 710 size_t requestDataSize, uint32 flags, BMessage& reply) const 711 { 712 if (!ServerHelper::IsNetworkAvailable()) { 713 if (Logger::IsDebugEnabled()) { 714 printf("jrpc; dropping request to ...[%s] as network is not " 715 "available\n", domain); 716 } 717 delete requestData; 718 return HD_NETWORK_INACCESSIBLE; 719 } 720 721 if (ServerSettings::IsClientTooOld()) { 722 if (Logger::IsDebugEnabled()) { 723 printf("jrpc; dropping request to ...[%s] as client is too " 724 "old\n", domain); 725 } 726 delete requestData; 727 return HD_CLIENT_TOO_OLD; 728 } 729 730 BUrl url = ServerSettings::CreateFullUrl(BString("/__api/v1/") << domain); 731 bool isSecure = url.Protocol() == "https"; 732 733 if (Logger::IsDebugEnabled()) { 734 printf("jrpc; will make request to [%s]\n", 735 url.UrlString().String()); 736 } 737 738 // If the request payload is logged then it must be copied to local memory 739 // from the stream. This then requires that the request data is then 740 // delivered from memory. 741 742 if (Logger::IsTraceEnabled()) { 743 BMallocIO *loggedRequestData = new BMallocIO(); 744 loggedRequestData->SetSize(requestDataSize); 745 status_t dataCopyResult = DataIOUtils::Copy(loggedRequestData, 746 requestData, requestDataSize); 747 delete requestData; 748 requestData = loggedRequestData; 749 750 if (dataCopyResult != B_OK) { 751 delete requestData; 752 return dataCopyResult; 753 } 754 755 printf("jrpc request; "); 756 _LogPayload(static_cast<const char *>(loggedRequestData->Buffer()), 757 loggedRequestData->BufferLength()); 758 printf("\n"); 759 } 760 761 ProtocolListener listener(Logger::IsTraceEnabled()); 762 BUrlContext context; 763 764 BHttpHeaders headers; 765 headers.AddHeader("Content-Type", "application/json"); 766 ServerSettings::AugmentHeaders(headers); 767 768 BHttpRequest request(url, isSecure, "HTTP", &listener, &context); 769 request.SetMethod(B_HTTP_POST); 770 request.SetHeaders(headers); 771 772 // Authentication via Basic Authentication 773 // The other way would be to obtain a token and then use the Token Bearer 774 // header. 775 if ((flags & NEEDS_AUTHORIZATION) != 0 776 && !fUsername.IsEmpty() && !fPassword.IsEmpty()) { 777 BHttpAuthentication authentication(fUsername, fPassword); 778 authentication.SetMethod(B_HTTP_AUTHENTICATION_BASIC); 779 context.AddAuthentication(url, authentication); 780 } 781 782 request.AdoptInputData(requestData, requestDataSize); 783 784 BMallocIO replyData; 785 listener.SetDownloadIO(&replyData); 786 787 thread_id thread = request.Run(); 788 wait_for_thread(thread, NULL); 789 790 const BHttpResult& result = dynamic_cast<const BHttpResult&>( 791 request.Result()); 792 793 int32 statusCode = result.StatusCode(); 794 795 if (Logger::IsDebugEnabled()) { 796 printf("jrpc; did receive http-status [%" B_PRId32 "] " 797 "from [%s]\n", statusCode, url.UrlString().String()); 798 } 799 800 switch (statusCode) { 801 case B_HTTP_STATUS_OK: 802 break; 803 804 case B_HTTP_STATUS_PRECONDITION_FAILED: 805 ServerHelper::NotifyClientTooOld(result.Headers()); 806 return HD_CLIENT_TOO_OLD; 807 808 default: 809 printf("json-rpc request to endpoint [.../%s] failed with http " 810 "status [%" B_PRId32 "]\n", domain, statusCode); 811 return B_ERROR; 812 } 813 814 if (Logger::IsTraceEnabled()) { 815 printf("jrpc response; "); 816 _LogPayload(static_cast<const char *>(replyData.Buffer()), 817 replyData.BufferLength()); 818 printf("\n"); 819 } 820 821 status_t status = BJson::Parse( 822 static_cast<const char *>(replyData.Buffer()), replyData.BufferLength(), 823 reply); 824 if (Logger::IsTraceEnabled() && status == B_BAD_DATA) { 825 BString resultString(static_cast<const char *>(replyData.Buffer()), 826 replyData.BufferLength()); 827 printf("Parser choked on JSON:\n%s\n", resultString.String()); 828 } 829 return status; 830 } 831 832 833 status_t 834 WebAppInterface::_SendJsonRequest(const char* domain, BString jsonString, 835 uint32 flags, BMessage& reply) const 836 { 837 // gets 'adopted' by the subsequent http request. 838 BMemoryIO* data = new BMemoryIO( 839 jsonString.String(), jsonString.Length() - 1); 840 841 return _SendJsonRequest(domain, data, jsonString.Length() - 1, flags, 842 reply); 843 } 844 845 846 void 847 WebAppInterface::_LogPayload(const char* data, ssize_t size) 848 { 849 if (size > LOG_PAYLOAD_LIMIT) 850 size = LOG_PAYLOAD_LIMIT; 851 852 for (int32 i = 0; i < size; i++) { 853 bool esc = data[i] > 126 || 854 (data[i] < 0x20 && data[i] != 0x0a); 855 856 if (esc) 857 printf("\\u%02x", data[i]); 858 else 859 putchar(data[i]); 860 } 861 862 if (size == LOG_PAYLOAD_LIMIT) 863 printf("...(continues)"); 864 } 865