119c15fecSAndrew Lindesay /*
219c15fecSAndrew Lindesay * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
3bf866d5eSAndrew Lindesay * Copyright 2016-2024, Andrew Lindesay <apl@lindesay.co.nz>.
419c15fecSAndrew Lindesay * All rights reserved. Distributed under the terms of the MIT License.
519c15fecSAndrew Lindesay */
619c15fecSAndrew Lindesay #include "WebAppInterface.h"
719c15fecSAndrew Lindesay
819c15fecSAndrew Lindesay #include <Application.h>
94b347fccSAndrew Lindesay #include <Message.h>
104b347fccSAndrew Lindesay #include <Url.h>
114b347fccSAndrew Lindesay
124b347fccSAndrew Lindesay #include <AutoDeleter.h>
134b347fccSAndrew Lindesay #include <AutoLocker.h>
1419c15fecSAndrew Lindesay #include <HttpHeaders.h>
1519c15fecSAndrew Lindesay #include <HttpRequest.h>
1619c15fecSAndrew Lindesay #include <Json.h>
17a9edb9bfSAndrew Lindesay #include <JsonTextWriter.h>
1888575af1SAndrew Lindesay #include <JsonMessageWriter.h>
1919c15fecSAndrew Lindesay #include <UrlContext.h>
2019c15fecSAndrew Lindesay #include <UrlProtocolListener.h>
2119c15fecSAndrew Lindesay #include <UrlProtocolRoster.h>
2219c15fecSAndrew Lindesay
23cd417b96SAndrew Lindesay #include "DataIOUtils.h"
2454312619SAndrew Lindesay #include "HaikuDepotConstants.h"
254b347fccSAndrew Lindesay #include "JwtTokenHelper.h"
26f0665db4SAndrew Lindesay #include "Logger.h"
2719c15fecSAndrew Lindesay #include "ServerSettings.h"
2854312619SAndrew Lindesay #include "ServerHelper.h"
2919c15fecSAndrew Lindesay
3019c15fecSAndrew Lindesay
314080dbd6SNiels Sascha Reedijk using namespace BPrivate::Network;
324080dbd6SNiels Sascha Reedijk
334080dbd6SNiels Sascha Reedijk
3419c15fecSAndrew Lindesay #define BASEURL_DEFAULT "https://depot.haiku-os.org"
3519c15fecSAndrew Lindesay #define USERAGENT_FALLBACK_VERSION "0.0.0"
3621df7324SAndrew Lindesay #define PROTOCOL_NAME "post-json"
37cd417b96SAndrew Lindesay #define LOG_PAYLOAD_LIMIT 8192
3819c15fecSAndrew Lindesay
3919c15fecSAndrew Lindesay
4019c15fecSAndrew Lindesay class ProtocolListener : public BUrlProtocolListener {
4119c15fecSAndrew Lindesay public:
ProtocolListener()42f96d1f4dSAndrew Lindesay ProtocolListener()
4319c15fecSAndrew Lindesay {
4419c15fecSAndrew Lindesay }
4519c15fecSAndrew Lindesay
~ProtocolListener()4619c15fecSAndrew Lindesay virtual ~ProtocolListener()
4719c15fecSAndrew Lindesay {
4819c15fecSAndrew Lindesay }
4919c15fecSAndrew Lindesay
ConnectionOpened(BUrlRequest * caller)5019c15fecSAndrew Lindesay virtual void ConnectionOpened(BUrlRequest* caller)
5119c15fecSAndrew Lindesay {
5219c15fecSAndrew Lindesay }
5319c15fecSAndrew Lindesay
HostnameResolved(BUrlRequest * caller,const char * ip)5419c15fecSAndrew Lindesay virtual void HostnameResolved(BUrlRequest* caller, const char* ip)
5519c15fecSAndrew Lindesay {
5619c15fecSAndrew Lindesay }
5719c15fecSAndrew Lindesay
ResponseStarted(BUrlRequest * caller)5819c15fecSAndrew Lindesay virtual void ResponseStarted(BUrlRequest* caller)
5919c15fecSAndrew Lindesay {
6019c15fecSAndrew Lindesay }
6119c15fecSAndrew Lindesay
HeadersReceived(BUrlRequest * caller)623e27f8d5SLeorize virtual void HeadersReceived(BUrlRequest* caller)
6319c15fecSAndrew Lindesay {
6419c15fecSAndrew Lindesay }
6519c15fecSAndrew Lindesay
BytesWritten(BUrlRequest * caller,size_t bytesWritten)6678b14420SLeorize virtual void BytesWritten(BUrlRequest* caller, size_t bytesWritten)
6719c15fecSAndrew Lindesay {
6819c15fecSAndrew Lindesay }
6919c15fecSAndrew Lindesay
DownloadProgress(BUrlRequest * caller,off_t bytesReceived,off_t bytesTotal)70b72cc7f4SAndrew Lindesay virtual void DownloadProgress(BUrlRequest* caller, off_t bytesReceived, off_t bytesTotal)
7119c15fecSAndrew Lindesay {
7219c15fecSAndrew Lindesay }
7319c15fecSAndrew Lindesay
UploadProgress(BUrlRequest * caller,off_t bytesSent,off_t bytesTotal)74b72cc7f4SAndrew Lindesay virtual void UploadProgress(BUrlRequest* caller, off_t bytesSent, off_t bytesTotal)
7519c15fecSAndrew Lindesay {
7619c15fecSAndrew Lindesay }
7719c15fecSAndrew Lindesay
RequestCompleted(BUrlRequest * caller,bool success)7819c15fecSAndrew Lindesay virtual void RequestCompleted(BUrlRequest* caller, bool success)
7919c15fecSAndrew Lindesay {
8019c15fecSAndrew Lindesay }
8119c15fecSAndrew Lindesay
DebugMessage(BUrlRequest * caller,BUrlProtocolDebugMessage type,const char * text)8219c15fecSAndrew Lindesay virtual void DebugMessage(BUrlRequest* caller,
8319c15fecSAndrew Lindesay BUrlProtocolDebugMessage type, const char* text)
8419c15fecSAndrew Lindesay {
8521df7324SAndrew Lindesay HDTRACE("post-json: %s", text);
8619c15fecSAndrew Lindesay }
8719c15fecSAndrew Lindesay };
8819c15fecSAndrew Lindesay
8919c15fecSAndrew Lindesay
90e67a4284SLeorize static BHttpRequest*
make_http_request(const BUrl & url,BDataIO * output,BUrlProtocolListener * listener=NULL,BUrlContext * context=NULL)9178b14420SLeorize make_http_request(const BUrl& url, BDataIO* output,
9278b14420SLeorize BUrlProtocolListener* listener = NULL,
93e67a4284SLeorize BUrlContext* context = NULL)
94e67a4284SLeorize {
9578b14420SLeorize BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, output,
9678b14420SLeorize listener, context);
97e67a4284SLeorize BHttpRequest* httpRequest = dynamic_cast<BHttpRequest*>(request);
98e67a4284SLeorize if (httpRequest == NULL) {
99e67a4284SLeorize delete request;
100e67a4284SLeorize return NULL;
101e67a4284SLeorize }
102e67a4284SLeorize return httpRequest;
103e67a4284SLeorize }
104e67a4284SLeorize
105e67a4284SLeorize
10619c15fecSAndrew Lindesay enum {
10719c15fecSAndrew Lindesay NEEDS_AUTHORIZATION = 1 << 0,
10819c15fecSAndrew Lindesay };
10919c15fecSAndrew Lindesay
11019c15fecSAndrew Lindesay
WebAppInterface()11119c15fecSAndrew Lindesay WebAppInterface::WebAppInterface()
11219c15fecSAndrew Lindesay {
11319c15fecSAndrew Lindesay }
11419c15fecSAndrew Lindesay
11519c15fecSAndrew Lindesay
~WebAppInterface()11619c15fecSAndrew Lindesay WebAppInterface::~WebAppInterface()
11719c15fecSAndrew Lindesay {
11819c15fecSAndrew Lindesay }
11919c15fecSAndrew Lindesay
12019c15fecSAndrew Lindesay
12119c15fecSAndrew Lindesay void
SetCredentials(const UserCredentials & value)1224b347fccSAndrew Lindesay WebAppInterface::SetCredentials(const UserCredentials& value)
12319c15fecSAndrew Lindesay {
1244b347fccSAndrew Lindesay AutoLocker<BLocker> lock(&fLock);
1254b347fccSAndrew Lindesay if (fCredentials != value) {
126d2d4866dSAndrew Lindesay fCredentials = value;
1274b347fccSAndrew Lindesay fAccessToken.Clear();
1284b347fccSAndrew Lindesay }
129d2d4866dSAndrew Lindesay }
130d2d4866dSAndrew Lindesay
131d2d4866dSAndrew Lindesay
132d2d4866dSAndrew Lindesay const BString&
Nickname()1334b347fccSAndrew Lindesay WebAppInterface::Nickname()
134d2d4866dSAndrew Lindesay {
1354b347fccSAndrew Lindesay AutoLocker<BLocker> lock(&fLock);
136d2d4866dSAndrew Lindesay return fCredentials.Nickname();
13719c15fecSAndrew Lindesay }
13819c15fecSAndrew Lindesay
13919c15fecSAndrew Lindesay
14019c15fecSAndrew Lindesay status_t
GetChangelog(const BString & packageName,BMessage & message)14180a272eeSAndrew Lindesay WebAppInterface::GetChangelog(const BString& packageName, BMessage& message)
14219c15fecSAndrew Lindesay {
143835e7239SAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
144835e7239SAndrew Lindesay // BHttpRequest later takes ownership of this.
145835e7239SAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
14619c15fecSAndrew Lindesay
147835e7239SAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
148835e7239SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgName");
149835e7239SAndrew Lindesay requestEnvelopeWriter.WriteString(packageName.String());
150835e7239SAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
151835e7239SAndrew Lindesay
152*4b3c808eSAndrew Lindesay return _SendJsonRequest("pkg/get-pkg-changelog", requestEnvelopeData,
153*4b3c808eSAndrew Lindesay _LengthAndSeekToZero(requestEnvelopeData), 0, message);
154*4b3c808eSAndrew Lindesay }
155*4b3c808eSAndrew Lindesay
156*4b3c808eSAndrew Lindesay
157*4b3c808eSAndrew Lindesay /*! The summary is provided for by an algorithm on the server side. The
158*4b3c808eSAndrew Lindesay derivation is performed on the server because it follows an
159*4b3c808eSAndrew Lindesay algorithm that requires settings and rules.
160*4b3c808eSAndrew Lindesay */
161*4b3c808eSAndrew Lindesay
162*4b3c808eSAndrew Lindesay status_t
RetrieveUserRatingSummaryForPackage(const BString & packageName,const BString & webAppRepositoryCode,BMessage & message)163*4b3c808eSAndrew Lindesay WebAppInterface::RetrieveUserRatingSummaryForPackage(const BString& packageName,
164*4b3c808eSAndrew Lindesay const BString& webAppRepositoryCode, BMessage& message)
165*4b3c808eSAndrew Lindesay {
166*4b3c808eSAndrew Lindesay // BHttpRequest later takes ownership of this.
167*4b3c808eSAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
168*4b3c808eSAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
169*4b3c808eSAndrew Lindesay
170*4b3c808eSAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
171*4b3c808eSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgName");
172*4b3c808eSAndrew Lindesay requestEnvelopeWriter.WriteString(packageName.String());
173*4b3c808eSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("repositoryCode");
174*4b3c808eSAndrew Lindesay requestEnvelopeWriter.WriteString(webAppRepositoryCode);
175*4b3c808eSAndrew Lindesay
176*4b3c808eSAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
177*4b3c808eSAndrew Lindesay
178*4b3c808eSAndrew Lindesay return _SendJsonRequest("user-rating/get-summary-by-pkg", requestEnvelopeData,
179*4b3c808eSAndrew Lindesay _LengthAndSeekToZero(requestEnvelopeData), 0, message);
18019c15fecSAndrew Lindesay }
18119c15fecSAndrew Lindesay
18219c15fecSAndrew Lindesay
18319c15fecSAndrew Lindesay status_t
RetrieveUserRatingsForPackageForDisplay(const BString & packageName,const BString & webAppRepositoryCode,const BString & webAppRepositorySourceCode,int resultOffset,int maxResults,BMessage & message)1844b347fccSAndrew Lindesay WebAppInterface::RetrieveUserRatingsForPackageForDisplay(
185f6e22563SAndrew Lindesay const BString& packageName,
186f6e22563SAndrew Lindesay const BString& webAppRepositoryCode,
187f6e22563SAndrew Lindesay const BString& webAppRepositorySourceCode,
188051ee9d8SAndrew Lindesay int resultOffset, int maxResults, BMessage& message)
18919c15fecSAndrew Lindesay {
190051ee9d8SAndrew Lindesay // BHttpRequest later takes ownership of this.
191051ee9d8SAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
192051ee9d8SAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
19319c15fecSAndrew Lindesay
194051ee9d8SAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
195051ee9d8SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgName");
196051ee9d8SAndrew Lindesay requestEnvelopeWriter.WriteString(packageName.String());
197051ee9d8SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("offset");
198051ee9d8SAndrew Lindesay requestEnvelopeWriter.WriteInteger(resultOffset);
199051ee9d8SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("limit");
200051ee9d8SAndrew Lindesay requestEnvelopeWriter.WriteInteger(maxResults);
201051ee9d8SAndrew Lindesay
202f6e22563SAndrew Lindesay if (!webAppRepositorySourceCode.IsEmpty()) {
203f6e22563SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("repositorySourceCode");
204f6e22563SAndrew Lindesay requestEnvelopeWriter.WriteString(webAppRepositorySourceCode);
205f6e22563SAndrew Lindesay }
206f6e22563SAndrew Lindesay
207051ee9d8SAndrew Lindesay if (!webAppRepositoryCode.IsEmpty()) {
208051ee9d8SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("repositoryCode");
209051ee9d8SAndrew Lindesay requestEnvelopeWriter.WriteString(webAppRepositoryCode);
210051ee9d8SAndrew Lindesay }
211051ee9d8SAndrew Lindesay
212051ee9d8SAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
213051ee9d8SAndrew Lindesay
21421df7324SAndrew Lindesay return _SendJsonRequest("user-rating/search-user-ratings",
21521df7324SAndrew Lindesay requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
21621df7324SAndrew Lindesay 0, message);
21719c15fecSAndrew Lindesay }
21819c15fecSAndrew Lindesay
21919c15fecSAndrew Lindesay
22019c15fecSAndrew Lindesay status_t
RetrieveUserRatingForPackageAndVersionByUser(const BString & packageName,const BPackageVersion & version,const BString & architecture,const BString & webAppRepositoryCode,const BString & webAppRepositorySourceCode,const BString & userNickname,BMessage & message)2214b347fccSAndrew Lindesay WebAppInterface::RetrieveUserRatingForPackageAndVersionByUser(
222051ee9d8SAndrew Lindesay const BString& packageName, const BPackageVersion& version,
223f6e22563SAndrew Lindesay const BString& architecture,
224f6e22563SAndrew Lindesay const BString& webAppRepositoryCode,
225f6e22563SAndrew Lindesay const BString& webAppRepositorySourceCode,
226d2d4866dSAndrew Lindesay const BString& userNickname, BMessage& message)
22719c15fecSAndrew Lindesay {
228a9edb9bfSAndrew Lindesay // BHttpRequest later takes ownership of this.
229a9edb9bfSAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
230a9edb9bfSAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
23119c15fecSAndrew Lindesay
232a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
233a9edb9bfSAndrew Lindesay
234a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("userNickname");
235d2d4866dSAndrew Lindesay requestEnvelopeWriter.WriteString(userNickname.String());
236a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgName");
237a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(packageName.String());
238a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode");
239a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(architecture.String());
240a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("repositoryCode");
241f6e22563SAndrew Lindesay requestEnvelopeWriter.WriteString(webAppRepositoryCode.String());
242f6e22563SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("repositorySourceCode");
243f6e22563SAndrew Lindesay requestEnvelopeWriter.WriteString(webAppRepositorySourceCode.String());
244a9edb9bfSAndrew Lindesay
245a9edb9bfSAndrew Lindesay if (version.Major().Length() > 0) {
246a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionMajor");
247a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(version.Major().String());
248a9edb9bfSAndrew Lindesay }
249a9edb9bfSAndrew Lindesay
250a9edb9bfSAndrew Lindesay if (version.Minor().Length() > 0) {
251a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionMinor");
252a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(version.Minor().String());
253a9edb9bfSAndrew Lindesay }
254a9edb9bfSAndrew Lindesay
255a9edb9bfSAndrew Lindesay if (version.Micro().Length() > 0) {
256a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionMicro");
257a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(version.Micro().String());
258a9edb9bfSAndrew Lindesay }
259a9edb9bfSAndrew Lindesay
260a9edb9bfSAndrew Lindesay if (version.PreRelease().Length() > 0) {
261a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionPreRelease");
262a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(version.PreRelease().String());
263a9edb9bfSAndrew Lindesay }
264a9edb9bfSAndrew Lindesay
265a9edb9bfSAndrew Lindesay if (version.Revision() != 0) {
266a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionRevision");
267a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteInteger(version.Revision());
268a9edb9bfSAndrew Lindesay }
269a9edb9bfSAndrew Lindesay
270a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
271a9edb9bfSAndrew Lindesay
27221df7324SAndrew Lindesay return _SendJsonRequest(
27321df7324SAndrew Lindesay "user-rating/get-user-rating-by-user-and-pkg-version",
27421df7324SAndrew Lindesay requestEnvelopeData,
27588575af1SAndrew Lindesay _LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
27688575af1SAndrew Lindesay message);
27719c15fecSAndrew Lindesay }
27819c15fecSAndrew Lindesay
27919c15fecSAndrew Lindesay
280d2d4866dSAndrew Lindesay /*! This method will fill out the supplied UserDetail object with information
281d2d4866dSAndrew Lindesay about the user that is supplied in the credentials. Importantly it will
282d2d4866dSAndrew Lindesay also authenticate the request with the details of the credentials and will
283d2d4866dSAndrew Lindesay not use the credentials that are configured in 'fCredentials'.
284d2d4866dSAndrew Lindesay */
285d2d4866dSAndrew Lindesay
286d2d4866dSAndrew Lindesay status_t
RetrieveUserDetailForCredentials(const UserCredentials & credentials,BMessage & message)287d2d4866dSAndrew Lindesay WebAppInterface::RetrieveUserDetailForCredentials(
288d17c92f7SAndrew Lindesay const UserCredentials& credentials, BMessage& message)
289d2d4866dSAndrew Lindesay {
290d2d4866dSAndrew Lindesay if (!credentials.IsValid()) {
291d2d4866dSAndrew Lindesay debugger("the credentials supplied are invalid so it is not possible "
292d2d4866dSAndrew Lindesay "to obtain the user detail");
293d2d4866dSAndrew Lindesay }
294d2d4866dSAndrew Lindesay
2954b347fccSAndrew Lindesay status_t result = B_OK;
2964b347fccSAndrew Lindesay
2974b347fccSAndrew Lindesay // authenticate the user and obtain a token to use with the latter
2984b347fccSAndrew Lindesay // request.
2994b347fccSAndrew Lindesay
3004b347fccSAndrew Lindesay BMessage authenticateResponseEnvelopeMessage;
3014b347fccSAndrew Lindesay
3024b347fccSAndrew Lindesay if (result == B_OK) {
3034b347fccSAndrew Lindesay result = AuthenticateUser(
3044b347fccSAndrew Lindesay credentials.Nickname(),
3054b347fccSAndrew Lindesay credentials.PasswordClear(),
3064b347fccSAndrew Lindesay authenticateResponseEnvelopeMessage);
3074b347fccSAndrew Lindesay }
3084b347fccSAndrew Lindesay
3094b347fccSAndrew Lindesay AccessToken accessToken;
3104b347fccSAndrew Lindesay
3114b347fccSAndrew Lindesay if (result == B_OK)
3124b347fccSAndrew Lindesay result = UnpackAccessToken(authenticateResponseEnvelopeMessage, accessToken);
3134b347fccSAndrew Lindesay
3144b347fccSAndrew Lindesay if (result == B_OK) {
315d2d4866dSAndrew Lindesay // BHttpRequest later takes ownership of this.
316d2d4866dSAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
317d2d4866dSAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
318d2d4866dSAndrew Lindesay
319d2d4866dSAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
320d2d4866dSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("nickname");
321d2d4866dSAndrew Lindesay requestEnvelopeWriter.WriteString(credentials.Nickname().String());
322d2d4866dSAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
323d2d4866dSAndrew Lindesay
3244b347fccSAndrew Lindesay result = _SendJsonRequest("user/get-user", accessToken,
32521df7324SAndrew Lindesay requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
32621df7324SAndrew Lindesay NEEDS_AUTHORIZATION, message);
327d2d4866dSAndrew Lindesay // note that the credentials used here are passed in as args.
3284b347fccSAndrew Lindesay }
329d2d4866dSAndrew Lindesay
330d2d4866dSAndrew Lindesay return result;
331d2d4866dSAndrew Lindesay }
332d2d4866dSAndrew Lindesay
333d2d4866dSAndrew Lindesay
334d2d4866dSAndrew Lindesay /*! This method will return the credentials for the currently authenticated
335d2d4866dSAndrew Lindesay user.
336d2d4866dSAndrew Lindesay */
337d2d4866dSAndrew Lindesay
338d2d4866dSAndrew Lindesay status_t
RetrieveCurrentUserDetail(BMessage & message)339d17c92f7SAndrew Lindesay WebAppInterface::RetrieveCurrentUserDetail(BMessage& message)
340d2d4866dSAndrew Lindesay {
3414b347fccSAndrew Lindesay UserCredentials credentials = _Credentials();
3424b347fccSAndrew Lindesay return RetrieveUserDetailForCredentials(credentials, message);
343d2d4866dSAndrew Lindesay }
344d2d4866dSAndrew Lindesay
345d2d4866dSAndrew Lindesay
346d2d4866dSAndrew Lindesay /*! When the user requests user detail, the server sends back an envelope of
347d2d4866dSAndrew Lindesay response data. This method will unpack the data into a model object.
348d2d4866dSAndrew Lindesay \return Not B_OK if something went wrong.
349d2d4866dSAndrew Lindesay */
350d2d4866dSAndrew Lindesay
351d2d4866dSAndrew Lindesay /*static*/ status_t
UnpackUserDetail(BMessage & responseEnvelopeMessage,UserDetail & userDetail)352d17c92f7SAndrew Lindesay WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage,
353d2d4866dSAndrew Lindesay UserDetail& userDetail)
354d2d4866dSAndrew Lindesay {
355d2d4866dSAndrew Lindesay BMessage resultMessage;
356d2d4866dSAndrew Lindesay status_t result = responseEnvelopeMessage.FindMessage(
357d2d4866dSAndrew Lindesay "result", &resultMessage);
358d2d4866dSAndrew Lindesay
359d2d4866dSAndrew Lindesay if (result != B_OK) {
360f96d1f4dSAndrew Lindesay HDERROR("bad response envelope missing 'result' entry");
361d2d4866dSAndrew Lindesay return result;
362d2d4866dSAndrew Lindesay }
363d2d4866dSAndrew Lindesay
364d2d4866dSAndrew Lindesay BString nickname;
365d2d4866dSAndrew Lindesay result = resultMessage.FindString("nickname", &nickname);
366d2d4866dSAndrew Lindesay userDetail.SetNickname(nickname);
367d2d4866dSAndrew Lindesay
368d2d4866dSAndrew Lindesay BMessage agreementMessage;
369d2d4866dSAndrew Lindesay if (resultMessage.FindMessage("userUsageConditionsAgreement",
370d2d4866dSAndrew Lindesay &agreementMessage) == B_OK) {
371d2d4866dSAndrew Lindesay BString code;
372d2d4866dSAndrew Lindesay BDateTime agreedToTimestamp;
373d2d4866dSAndrew Lindesay BString userUsageConditionsCode;
374d2d4866dSAndrew Lindesay UserUsageConditionsAgreement agreement = userDetail.Agreement();
375d2d4866dSAndrew Lindesay bool isLatest;
376d2d4866dSAndrew Lindesay
377d2d4866dSAndrew Lindesay if (agreementMessage.FindString("userUsageConditionsCode",
378d2d4866dSAndrew Lindesay &userUsageConditionsCode) == B_OK) {
379d2d4866dSAndrew Lindesay agreement.SetCode(userUsageConditionsCode);
380d2d4866dSAndrew Lindesay }
381d2d4866dSAndrew Lindesay
382d2d4866dSAndrew Lindesay double timestampAgreedMillis;
383d2d4866dSAndrew Lindesay if (agreementMessage.FindDouble("timestampAgreed",
384d2d4866dSAndrew Lindesay ×tampAgreedMillis) == B_OK) {
385d2d4866dSAndrew Lindesay agreement.SetTimestampAgreed((uint64) timestampAgreedMillis);
386d2d4866dSAndrew Lindesay }
387d2d4866dSAndrew Lindesay
388d2d4866dSAndrew Lindesay if (agreementMessage.FindBool("isLatest", &isLatest)
389d2d4866dSAndrew Lindesay == B_OK) {
390d2d4866dSAndrew Lindesay agreement.SetIsLatest(isLatest);
391d2d4866dSAndrew Lindesay }
392d2d4866dSAndrew Lindesay
393d2d4866dSAndrew Lindesay userDetail.SetAgreement(agreement);
394d2d4866dSAndrew Lindesay }
395d2d4866dSAndrew Lindesay
396d2d4866dSAndrew Lindesay return result;
397d2d4866dSAndrew Lindesay }
398d2d4866dSAndrew Lindesay
399d2d4866dSAndrew Lindesay
4004b347fccSAndrew Lindesay /*! When an authentication API call is made, the response (if successful) will
4014b347fccSAndrew Lindesay return an access token in the response. This method will take the response
4024b347fccSAndrew Lindesay from the server and will parse out the access token data into the supplied
4034b347fccSAndrew Lindesay object.
4044b347fccSAndrew Lindesay */
4054b347fccSAndrew Lindesay
4064b347fccSAndrew Lindesay /*static*/ status_t
UnpackAccessToken(BMessage & responseEnvelopeMessage,AccessToken & accessToken)4074b347fccSAndrew Lindesay WebAppInterface::UnpackAccessToken(BMessage& responseEnvelopeMessage,
4084b347fccSAndrew Lindesay AccessToken& accessToken)
4094b347fccSAndrew Lindesay {
4104b347fccSAndrew Lindesay status_t result;
4114b347fccSAndrew Lindesay
4124b347fccSAndrew Lindesay BMessage resultMessage;
4134b347fccSAndrew Lindesay result = responseEnvelopeMessage.FindMessage(
4144b347fccSAndrew Lindesay "result", &resultMessage);
4154b347fccSAndrew Lindesay
4164b347fccSAndrew Lindesay if (result != B_OK) {
4174b347fccSAndrew Lindesay HDERROR("bad response envelope missing 'result' entry");
4184b347fccSAndrew Lindesay return result;
4194b347fccSAndrew Lindesay }
4204b347fccSAndrew Lindesay
4214b347fccSAndrew Lindesay BString token;
4224b347fccSAndrew Lindesay result = resultMessage.FindString("token", &token);
4234b347fccSAndrew Lindesay
4244b347fccSAndrew Lindesay if (result != B_OK || token.IsEmpty()) {
4254b347fccSAndrew Lindesay HDINFO("failure to authenticate");
426bf697877SAndrew Lindesay return B_PERMISSION_DENIED;
4274b347fccSAndrew Lindesay }
4284b347fccSAndrew Lindesay
4294b347fccSAndrew Lindesay // The token should be present in three parts; the header, the claims and
4304b347fccSAndrew Lindesay // then a digital signature. The logic here wants to extract some data
4314b347fccSAndrew Lindesay // from the claims part.
4324b347fccSAndrew Lindesay
4334b347fccSAndrew Lindesay BMessage claimsMessage;
4344b347fccSAndrew Lindesay result = JwtTokenHelper::ParseClaims(token, claimsMessage);
4354b347fccSAndrew Lindesay
4364b347fccSAndrew Lindesay if (Logger::IsTraceEnabled()) {
4374b347fccSAndrew Lindesay HDTRACE("start; token claims...");
4384b347fccSAndrew Lindesay claimsMessage.PrintToStream();
4394b347fccSAndrew Lindesay HDTRACE("...end; token claims");
4404b347fccSAndrew Lindesay }
4414b347fccSAndrew Lindesay
4424b347fccSAndrew Lindesay if (B_OK == result) {
4434b347fccSAndrew Lindesay accessToken.SetToken(token);
4444b347fccSAndrew Lindesay accessToken.SetExpiryTimestamp(0);
4454b347fccSAndrew Lindesay
4464b347fccSAndrew Lindesay double expiryTimestampDouble;
4474b347fccSAndrew Lindesay
4484b347fccSAndrew Lindesay // The claims should have parsed but it could transpire that there is
4494b347fccSAndrew Lindesay // no expiry. This should not be the case, but it is theoretically
4504b347fccSAndrew Lindesay // possible.
4514b347fccSAndrew Lindesay
4524b347fccSAndrew Lindesay if (claimsMessage.FindDouble("exp", &expiryTimestampDouble) == B_OK)
4534b347fccSAndrew Lindesay accessToken.SetExpiryTimestamp(1000 * static_cast<uint64>(expiryTimestampDouble));
4544b347fccSAndrew Lindesay }
4554b347fccSAndrew Lindesay
4564b347fccSAndrew Lindesay return result;
4574b347fccSAndrew Lindesay }
4584b347fccSAndrew Lindesay
4594b347fccSAndrew Lindesay
46001339a54SAndrew Lindesay /*! \brief Returns data relating to the user usage conditions
46101339a54SAndrew Lindesay
46201339a54SAndrew Lindesay \param code defines the version of the data to return or if empty then the
46301339a54SAndrew Lindesay latest is returned.
46401339a54SAndrew Lindesay
46501339a54SAndrew Lindesay This method will go to the server and get details relating to the user usage
46601339a54SAndrew Lindesay conditions. It does this in two API calls; first gets the details (the
46701339a54SAndrew Lindesay minimum age) and in the second call, the text of the conditions is returned.
46801339a54SAndrew Lindesay */
46901339a54SAndrew Lindesay
47001339a54SAndrew Lindesay status_t
RetrieveUserUsageConditions(const BString & code,UserUsageConditions & conditions)47101339a54SAndrew Lindesay WebAppInterface::RetrieveUserUsageConditions(const BString& code,
47201339a54SAndrew Lindesay UserUsageConditions& conditions)
47301339a54SAndrew Lindesay {
47401339a54SAndrew Lindesay BMessage responseEnvelopeMessage;
47501339a54SAndrew Lindesay status_t result = _RetrieveUserUsageConditionsMeta(code,
47601339a54SAndrew Lindesay responseEnvelopeMessage);
47701339a54SAndrew Lindesay
47801339a54SAndrew Lindesay if (result != B_OK)
47901339a54SAndrew Lindesay return result;
48001339a54SAndrew Lindesay
48101339a54SAndrew Lindesay BMessage resultMessage;
48201339a54SAndrew Lindesay if (responseEnvelopeMessage.FindMessage("result", &resultMessage) != B_OK) {
483fa5c8097SAndrew Lindesay HDERROR("bad response envelope missing 'result' entry");
48401339a54SAndrew Lindesay return B_BAD_DATA;
48501339a54SAndrew Lindesay }
48601339a54SAndrew Lindesay
48701339a54SAndrew Lindesay BString metaDataCode;
48801339a54SAndrew Lindesay double metaDataMinimumAge;
48901339a54SAndrew Lindesay BString copyMarkdown;
49001339a54SAndrew Lindesay
49101339a54SAndrew Lindesay if ( (resultMessage.FindString("code", &metaDataCode) != B_OK)
49201339a54SAndrew Lindesay || (resultMessage.FindDouble(
49301339a54SAndrew Lindesay "minimumAge", &metaDataMinimumAge) != B_OK) ) {
494f96d1f4dSAndrew Lindesay HDERROR("unexpected response from server with missing user usage "
495fa5c8097SAndrew Lindesay "conditions data");
49601339a54SAndrew Lindesay return B_BAD_DATA;
49701339a54SAndrew Lindesay }
49801339a54SAndrew Lindesay
49901339a54SAndrew Lindesay BMallocIO* copyMarkdownData = new BMallocIO();
50001339a54SAndrew Lindesay result = _RetrieveUserUsageConditionsCopy(metaDataCode, copyMarkdownData);
50101339a54SAndrew Lindesay
50201339a54SAndrew Lindesay if (result != B_OK)
50301339a54SAndrew Lindesay return result;
50401339a54SAndrew Lindesay
50501339a54SAndrew Lindesay conditions.SetCode(metaDataCode);
50601339a54SAndrew Lindesay conditions.SetMinimumAge(metaDataMinimumAge);
50701339a54SAndrew Lindesay conditions.SetCopyMarkdown(
50801339a54SAndrew Lindesay BString(static_cast<const char*>(copyMarkdownData->Buffer()),
50901339a54SAndrew Lindesay copyMarkdownData->BufferLength()));
51001339a54SAndrew Lindesay
51101339a54SAndrew Lindesay return B_OK;
51201339a54SAndrew Lindesay }
51301339a54SAndrew Lindesay
51401339a54SAndrew Lindesay
51501339a54SAndrew Lindesay status_t
AgreeUserUsageConditions(const BString & code,BMessage & responsePayload)51610cd325cSAndrew Lindesay WebAppInterface::AgreeUserUsageConditions(const BString& code,
51710cd325cSAndrew Lindesay BMessage& responsePayload)
51810cd325cSAndrew Lindesay {
51910cd325cSAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
52010cd325cSAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
52110cd325cSAndrew Lindesay
52210cd325cSAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
52310cd325cSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("userUsageConditionsCode");
52410cd325cSAndrew Lindesay requestEnvelopeWriter.WriteString(code.String());
52510cd325cSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("nickname");
5264b347fccSAndrew Lindesay requestEnvelopeWriter.WriteString(Nickname());
52710cd325cSAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
52810cd325cSAndrew Lindesay
52910cd325cSAndrew Lindesay // now fetch this information into an object.
53010cd325cSAndrew Lindesay
53121df7324SAndrew Lindesay return _SendJsonRequest("user/agree-user-usage-conditions",
53221df7324SAndrew Lindesay requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
53321df7324SAndrew Lindesay NEEDS_AUTHORIZATION, responsePayload);
53410cd325cSAndrew Lindesay }
53510cd325cSAndrew Lindesay
53610cd325cSAndrew Lindesay
53710cd325cSAndrew Lindesay status_t
_RetrieveUserUsageConditionsMeta(const BString & code,BMessage & message)53801339a54SAndrew Lindesay WebAppInterface::_RetrieveUserUsageConditionsMeta(const BString& code,
53901339a54SAndrew Lindesay BMessage& message)
54001339a54SAndrew Lindesay {
54101339a54SAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
54201339a54SAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
54301339a54SAndrew Lindesay
54401339a54SAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
54501339a54SAndrew Lindesay
54601339a54SAndrew Lindesay if (!code.IsEmpty()) {
54701339a54SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("code");
54801339a54SAndrew Lindesay requestEnvelopeWriter.WriteString(code.String());
54901339a54SAndrew Lindesay }
55001339a54SAndrew Lindesay
55101339a54SAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
55201339a54SAndrew Lindesay
55301339a54SAndrew Lindesay // now fetch this information into an object.
55401339a54SAndrew Lindesay
55521df7324SAndrew Lindesay return _SendJsonRequest("user/get-user-usage-conditions",
55621df7324SAndrew Lindesay requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
55721df7324SAndrew Lindesay 0, message);
55801339a54SAndrew Lindesay }
55901339a54SAndrew Lindesay
56001339a54SAndrew Lindesay
56101339a54SAndrew Lindesay status_t
_RetrieveUserUsageConditionsCopy(const BString & code,BDataIO * stream)56201339a54SAndrew Lindesay WebAppInterface::_RetrieveUserUsageConditionsCopy(const BString& code,
56301339a54SAndrew Lindesay BDataIO* stream)
56401339a54SAndrew Lindesay {
56501339a54SAndrew Lindesay return _SendRawGetRequest(
56601339a54SAndrew Lindesay BString("/__user/usageconditions/") << code << "/document.md",
56701339a54SAndrew Lindesay stream);
56801339a54SAndrew Lindesay }
56901339a54SAndrew Lindesay
57001339a54SAndrew Lindesay
57119c15fecSAndrew Lindesay status_t
CreateUserRating(const BString & packageName,const BPackageVersion & version,const BString & architecture,const BString & webAppRepositoryCode,const BString & webAppRepositorySourceCode,const BString & naturalLanguageCode,const BString & comment,const BString & stability,int rating,BMessage & message)57219c15fecSAndrew Lindesay WebAppInterface::CreateUserRating(const BString& packageName,
573a9edb9bfSAndrew Lindesay const BPackageVersion& version,
574f6e22563SAndrew Lindesay const BString& architecture,
575f6e22563SAndrew Lindesay const BString& webAppRepositoryCode,
576f6e22563SAndrew Lindesay const BString& webAppRepositorySourceCode,
577bf866d5eSAndrew Lindesay const BString& naturalLanguageCode,
578bf866d5eSAndrew Lindesay // This is the "ID" in the ICU system; the term `code` is used with the
579bf866d5eSAndrew Lindesay // server system.
580bf866d5eSAndrew Lindesay const BString& comment,
58119c15fecSAndrew Lindesay const BString& stability, int rating, BMessage& message)
58219c15fecSAndrew Lindesay {
583a9edb9bfSAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
58421df7324SAndrew Lindesay // BHttpRequest later takes ownership of this.
585a9edb9bfSAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
58619c15fecSAndrew Lindesay
587a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
588a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgName");
589a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(packageName.String());
590a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode");
591a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(architecture.String());
592a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("repositoryCode");
593f6e22563SAndrew Lindesay requestEnvelopeWriter.WriteString(webAppRepositoryCode.String());
594f6e22563SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("repositorySourceCode");
595f6e22563SAndrew Lindesay requestEnvelopeWriter.WriteString(webAppRepositorySourceCode.String());
596a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
597bf866d5eSAndrew Lindesay requestEnvelopeWriter.WriteString(naturalLanguageCode.String());
598a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionType");
599a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString("SPECIFIC");
600a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("userNickname");
6014b347fccSAndrew Lindesay requestEnvelopeWriter.WriteString(Nickname());
602a9edb9bfSAndrew Lindesay
603a9edb9bfSAndrew Lindesay if (!version.Major().IsEmpty()) {
604a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionMajor");
605a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(version.Major());
606a9edb9bfSAndrew Lindesay }
607a9edb9bfSAndrew Lindesay
608a9edb9bfSAndrew Lindesay if (!version.Minor().IsEmpty()) {
609a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionMinor");
610a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(version.Minor());
611a9edb9bfSAndrew Lindesay }
612a9edb9bfSAndrew Lindesay
613a9edb9bfSAndrew Lindesay if (!version.Micro().IsEmpty()) {
614a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionMicro");
615a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(version.Micro());
616a9edb9bfSAndrew Lindesay }
617a9edb9bfSAndrew Lindesay
618a9edb9bfSAndrew Lindesay if (!version.PreRelease().IsEmpty()) {
619a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionPreRelease");
620a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(version.PreRelease());
621a9edb9bfSAndrew Lindesay }
622a9edb9bfSAndrew Lindesay
623a9edb9bfSAndrew Lindesay if (version.Revision() != 0) {
624a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("pkgVersionRevision");
625a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteInteger(version.Revision());
626a9edb9bfSAndrew Lindesay }
627a9edb9bfSAndrew Lindesay
628a9edb9bfSAndrew Lindesay if (rating > 0.0f) {
629a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("rating");
630a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteInteger(rating);
631a9edb9bfSAndrew Lindesay }
632a9edb9bfSAndrew Lindesay
633a9edb9bfSAndrew Lindesay if (stability.Length() > 0) {
634a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("userRatingStabilityCode");
635a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(stability);
636a9edb9bfSAndrew Lindesay }
637a9edb9bfSAndrew Lindesay
638a9edb9bfSAndrew Lindesay if (comment.Length() > 0) {
639a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("comment");
640a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(comment.String());
641a9edb9bfSAndrew Lindesay }
642a9edb9bfSAndrew Lindesay
643a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
644a9edb9bfSAndrew Lindesay
64521df7324SAndrew Lindesay return _SendJsonRequest("user-rating/create-user-rating",
64621df7324SAndrew Lindesay requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
64721df7324SAndrew Lindesay NEEDS_AUTHORIZATION, message);
64819c15fecSAndrew Lindesay }
64919c15fecSAndrew Lindesay
65019c15fecSAndrew Lindesay
65119c15fecSAndrew Lindesay status_t
UpdateUserRating(const BString & ratingID,const BString & naturalLanguageCode,const BString & comment,const BString & stability,int rating,bool active,BMessage & message)65219c15fecSAndrew Lindesay WebAppInterface::UpdateUserRating(const BString& ratingID,
653bf866d5eSAndrew Lindesay const BString& naturalLanguageCode,
654bf866d5eSAndrew Lindesay // This is the "ID" in the ICU system; the term `code` is used with the
655bf866d5eSAndrew Lindesay // server system.
656bf866d5eSAndrew Lindesay const BString& comment,
65719c15fecSAndrew Lindesay const BString& stability, int rating, bool active, BMessage& message)
65819c15fecSAndrew Lindesay {
659a9edb9bfSAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
66021df7324SAndrew Lindesay // BHttpRequest later takes ownership of this.
661a9edb9bfSAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
66219c15fecSAndrew Lindesay
663a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
664a9edb9bfSAndrew Lindesay
665a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("code");
666a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(ratingID.String());
667a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
668bf866d5eSAndrew Lindesay requestEnvelopeWriter.WriteString(naturalLanguageCode.String());
669a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("active");
670a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteBoolean(active);
671a9edb9bfSAndrew Lindesay
672a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("filter");
673a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteArrayStart();
674a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString("ACTIVE");
675a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString("NATURALLANGUAGE");
676a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString("USERRATINGSTABILITY");
677a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString("COMMENT");
678a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString("RATING");
679a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteArrayEnd();
680a9edb9bfSAndrew Lindesay
681a9edb9bfSAndrew Lindesay if (rating >= 0) {
682a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("rating");
683a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteInteger(rating);
684a9edb9bfSAndrew Lindesay }
685a9edb9bfSAndrew Lindesay
686a9edb9bfSAndrew Lindesay if (stability.Length() > 0) {
687a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("userRatingStabilityCode");
688a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(stability);
689a9edb9bfSAndrew Lindesay }
690a9edb9bfSAndrew Lindesay
691a9edb9bfSAndrew Lindesay if (comment.Length() > 0) {
692a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("comment");
693a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteString(comment);
694a9edb9bfSAndrew Lindesay }
695a9edb9bfSAndrew Lindesay
696a9edb9bfSAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
697a9edb9bfSAndrew Lindesay
69821df7324SAndrew Lindesay return _SendJsonRequest("user-rating/update-user-rating",
69921df7324SAndrew Lindesay requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
70021df7324SAndrew Lindesay NEEDS_AUTHORIZATION, message);
70119c15fecSAndrew Lindesay }
70219c15fecSAndrew Lindesay
70319c15fecSAndrew Lindesay
70488af15cfSAndrew Lindesay /*! This method will call to the server to get a screenshot that will fit into
70588af15cfSAndrew Lindesay the specified width and height.
70688af15cfSAndrew Lindesay */
70788af15cfSAndrew Lindesay
70819c15fecSAndrew Lindesay status_t
RetrieveScreenshot(const BString & code,int32 width,int32 height,BDataIO * stream)70919c15fecSAndrew Lindesay WebAppInterface::RetrieveScreenshot(const BString& code,
71019c15fecSAndrew Lindesay int32 width, int32 height, BDataIO* stream)
71119c15fecSAndrew Lindesay {
71201339a54SAndrew Lindesay return _SendRawGetRequest(
7130c1bbfe5SAndrew Lindesay BString("/__pkgscreenshot/") << code << ".png" << "?tw="
71401339a54SAndrew Lindesay << width << "&th=" << height, stream);
71519c15fecSAndrew Lindesay }
71619c15fecSAndrew Lindesay
71719c15fecSAndrew Lindesay
71819c15fecSAndrew Lindesay status_t
RequestCaptcha(BMessage & message)71919c15fecSAndrew Lindesay WebAppInterface::RequestCaptcha(BMessage& message)
72019c15fecSAndrew Lindesay {
721835e7239SAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
722835e7239SAndrew Lindesay // BHttpRequest later takes ownership of this.
723835e7239SAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
72419c15fecSAndrew Lindesay
725835e7239SAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
726835e7239SAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
727835e7239SAndrew Lindesay
72821df7324SAndrew Lindesay return _SendJsonRequest("captcha/generate-captcha",
72921df7324SAndrew Lindesay requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
73021df7324SAndrew Lindesay 0, message);
73119c15fecSAndrew Lindesay }
73219c15fecSAndrew Lindesay
73319c15fecSAndrew Lindesay
73419c15fecSAndrew Lindesay status_t
CreateUser(const BString & nickName,const BString & passwordClear,const BString & email,const BString & captchaToken,const BString & captchaResponse,const BString & naturalLanguageCode,const BString & userUsageConditionsCode,BMessage & message)73519c15fecSAndrew Lindesay WebAppInterface::CreateUser(const BString& nickName,
736bf866d5eSAndrew Lindesay const BString& passwordClear,
737bf866d5eSAndrew Lindesay const BString& email,
738bf866d5eSAndrew Lindesay const BString& captchaToken,
739bf866d5eSAndrew Lindesay const BString& captchaResponse,
740bf866d5eSAndrew Lindesay const BString& naturalLanguageCode,
741bf866d5eSAndrew Lindesay // This is the "ID" in the ICU system; the term `code` is used with the
742bf866d5eSAndrew Lindesay // server system.
743bf866d5eSAndrew Lindesay const BString& userUsageConditionsCode,
7440c82f64bSAndrew Lindesay BMessage& message)
74519c15fecSAndrew Lindesay {
7460c82f64bSAndrew Lindesay // BHttpRequest later takes ownership of this.
7470c82f64bSAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
7480c82f64bSAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
74919c15fecSAndrew Lindesay
7500c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
75119c15fecSAndrew Lindesay
7520c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("nickname");
7530c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteString(nickName.String());
7540c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("passwordClear");
7550c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteString(passwordClear.String());
7560c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("captchaToken");
7570c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteString(captchaToken.String());
7580c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("captchaResponse");
7590c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteString(captchaResponse.String());
7600c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
761bf866d5eSAndrew Lindesay requestEnvelopeWriter.WriteString(naturalLanguageCode.String());
7620c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("userUsageConditionsCode");
7630c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteString(userUsageConditionsCode.String());
76419c15fecSAndrew Lindesay
7650c82f64bSAndrew Lindesay if (!email.IsEmpty()) {
7660c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteObjectName("email");
7670c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteString(email.String());
7680c82f64bSAndrew Lindesay }
7690c82f64bSAndrew Lindesay
7700c82f64bSAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
7710c82f64bSAndrew Lindesay
77221df7324SAndrew Lindesay return _SendJsonRequest("user/create-user", requestEnvelopeData,
7730c82f64bSAndrew Lindesay _LengthAndSeekToZero(requestEnvelopeData), 0, message);
77419c15fecSAndrew Lindesay }
77519c15fecSAndrew Lindesay
77619c15fecSAndrew Lindesay
7774b347fccSAndrew Lindesay /*! This method will authenticate the user set in the credentials and will
7784b347fccSAndrew Lindesay retain the resultant access token for authenticating any latter API calls.
7794b347fccSAndrew Lindesay */
7804b347fccSAndrew Lindesay
7814b347fccSAndrew Lindesay status_t
AuthenticateUserRetainingAccessToken()7824b347fccSAndrew Lindesay WebAppInterface::AuthenticateUserRetainingAccessToken()
7834b347fccSAndrew Lindesay {
7844b347fccSAndrew Lindesay UserCredentials userCredentials = _Credentials();
7854b347fccSAndrew Lindesay
7864b347fccSAndrew Lindesay if (!userCredentials.IsValid()) {
7874b347fccSAndrew Lindesay HDINFO("unable to get a new access token as there are no credentials");
7884b347fccSAndrew Lindesay return B_NOT_INITIALIZED;
7894b347fccSAndrew Lindesay }
7904b347fccSAndrew Lindesay
7914b347fccSAndrew Lindesay return _AuthenticateUserRetainingAccessToken(userCredentials.Nickname(),
7924b347fccSAndrew Lindesay userCredentials.PasswordClear());
7934b347fccSAndrew Lindesay }
7944b347fccSAndrew Lindesay
7954b347fccSAndrew Lindesay
7964b347fccSAndrew Lindesay status_t
_AuthenticateUserRetainingAccessToken(const BString & nickName,const BString & passwordClear)7974b347fccSAndrew Lindesay WebAppInterface::_AuthenticateUserRetainingAccessToken(const BString& nickName,
7984b347fccSAndrew Lindesay const BString& passwordClear) {
7994b347fccSAndrew Lindesay AutoLocker<BLocker> lock(&fLock);
8004b347fccSAndrew Lindesay
8014b347fccSAndrew Lindesay fAccessToken.Clear();
8024b347fccSAndrew Lindesay
8034b347fccSAndrew Lindesay BMessage responseEnvelopeMessage;
8044b347fccSAndrew Lindesay status_t result = AuthenticateUser(nickName, passwordClear, responseEnvelopeMessage);
8054b347fccSAndrew Lindesay
8064b347fccSAndrew Lindesay AccessToken accessToken;
8074b347fccSAndrew Lindesay
8084b347fccSAndrew Lindesay if (result == B_OK)
8094b347fccSAndrew Lindesay result = UnpackAccessToken(responseEnvelopeMessage, accessToken);
8104b347fccSAndrew Lindesay
8114b347fccSAndrew Lindesay if (result == B_OK)
8124b347fccSAndrew Lindesay fAccessToken = accessToken;
8134b347fccSAndrew Lindesay
8144b347fccSAndrew Lindesay return result;
8154b347fccSAndrew Lindesay }
8164b347fccSAndrew Lindesay
8174b347fccSAndrew Lindesay
81819c15fecSAndrew Lindesay status_t
AuthenticateUser(const BString & nickName,const BString & passwordClear,BMessage & message)81919c15fecSAndrew Lindesay WebAppInterface::AuthenticateUser(const BString& nickName,
82019c15fecSAndrew Lindesay const BString& passwordClear, BMessage& message)
82119c15fecSAndrew Lindesay {
822835e7239SAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
823835e7239SAndrew Lindesay // BHttpRequest later takes ownership of this.
824835e7239SAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
82519c15fecSAndrew Lindesay
826835e7239SAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
827835e7239SAndrew Lindesay
828835e7239SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("nickname");
829835e7239SAndrew Lindesay requestEnvelopeWriter.WriteString(nickName.String());
830835e7239SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("passwordClear");
831835e7239SAndrew Lindesay requestEnvelopeWriter.WriteString(passwordClear.String());
832835e7239SAndrew Lindesay
833835e7239SAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
834835e7239SAndrew Lindesay
83521df7324SAndrew Lindesay return _SendJsonRequest("user/authenticate-user",
83621df7324SAndrew Lindesay requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
83721df7324SAndrew Lindesay 0, message);
83819c15fecSAndrew Lindesay }
83919c15fecSAndrew Lindesay
84019c15fecSAndrew Lindesay
841133ebab6SAndrew Lindesay status_t
IncrementViewCounter(const PackageInfoRef package,const DepotInfoRef depot,BMessage & message)842133ebab6SAndrew Lindesay WebAppInterface::IncrementViewCounter(const PackageInfoRef package,
843133ebab6SAndrew Lindesay const DepotInfoRef depot, BMessage& message)
844133ebab6SAndrew Lindesay {
845133ebab6SAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
846133ebab6SAndrew Lindesay // BHttpRequest later takes ownership of this.
847133ebab6SAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
848133ebab6SAndrew Lindesay
849133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
850133ebab6SAndrew Lindesay
851133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("architectureCode");
852133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteString(package->Architecture());
853133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("repositoryCode");
854133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteString(depot->WebAppRepositoryCode());
855f6e22563SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("repositorySourceCode");
856f6e22563SAndrew Lindesay requestEnvelopeWriter.WriteString(depot->WebAppRepositorySourceCode());
857133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("name");
858133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteString(package->Name());
859133ebab6SAndrew Lindesay
860133ebab6SAndrew Lindesay const BPackageVersion version = package->Version();
861133ebab6SAndrew Lindesay if (!version.Major().IsEmpty()) {
862133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("major");
863133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteString(version.Major());
864133ebab6SAndrew Lindesay }
865133ebab6SAndrew Lindesay if (!version.Minor().IsEmpty()) {
866133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("minor");
867133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteString(version.Minor());
868133ebab6SAndrew Lindesay }
869133ebab6SAndrew Lindesay if (!version.Micro().IsEmpty()) {
870133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("micro");
871133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteString(version.Micro());
872133ebab6SAndrew Lindesay }
873133ebab6SAndrew Lindesay if (!version.PreRelease().IsEmpty()) {
874133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("preRelease");
875133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteString(version.PreRelease());
876133ebab6SAndrew Lindesay }
877133ebab6SAndrew Lindesay if (version.Revision() != 0) {
878133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteObjectName("revision");
879133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteInteger(
880133ebab6SAndrew Lindesay static_cast<int64>(version.Revision()));
881133ebab6SAndrew Lindesay }
882133ebab6SAndrew Lindesay
883133ebab6SAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
884133ebab6SAndrew Lindesay
88521df7324SAndrew Lindesay return _SendJsonRequest("pkg/increment-view-counter",
88621df7324SAndrew Lindesay requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
88721df7324SAndrew Lindesay 0, message);
888133ebab6SAndrew Lindesay }
889133ebab6SAndrew Lindesay
890133ebab6SAndrew Lindesay
891b6356b91SAndrew Lindesay status_t
RetrievePasswordRequirements(PasswordRequirements & passwordRequirements)892b6356b91SAndrew Lindesay WebAppInterface::RetrievePasswordRequirements(
893b6356b91SAndrew Lindesay PasswordRequirements& passwordRequirements)
894b6356b91SAndrew Lindesay {
895b6356b91SAndrew Lindesay BMessage responseEnvelopeMessage;
896b6356b91SAndrew Lindesay status_t result = _RetrievePasswordRequirementsMeta(
897b6356b91SAndrew Lindesay responseEnvelopeMessage);
898b6356b91SAndrew Lindesay
899b6356b91SAndrew Lindesay if (result != B_OK)
900b6356b91SAndrew Lindesay return result;
901b6356b91SAndrew Lindesay
902b6356b91SAndrew Lindesay BMessage resultMessage;
903b6356b91SAndrew Lindesay
904b6356b91SAndrew Lindesay result = responseEnvelopeMessage.FindMessage("result", &resultMessage);
905b6356b91SAndrew Lindesay
906b6356b91SAndrew Lindesay if (result != B_OK) {
907b6356b91SAndrew Lindesay HDERROR("bad response envelope missing 'result' entry");
908b6356b91SAndrew Lindesay return result;
909b6356b91SAndrew Lindesay }
910b6356b91SAndrew Lindesay
911b6356b91SAndrew Lindesay double value;
912b6356b91SAndrew Lindesay
913b6356b91SAndrew Lindesay if (resultMessage.FindDouble("minPasswordLength", &value) == B_OK)
914b6356b91SAndrew Lindesay passwordRequirements.SetMinPasswordLength((uint32) value);
915b6356b91SAndrew Lindesay
916b6356b91SAndrew Lindesay if (resultMessage.FindDouble("minPasswordUppercaseChar", &value) == B_OK)
917b6356b91SAndrew Lindesay passwordRequirements.SetMinPasswordUppercaseChar((uint32) value);
918b6356b91SAndrew Lindesay
919b6356b91SAndrew Lindesay if (resultMessage.FindDouble("minPasswordDigitsChar", &value) == B_OK)
920b6356b91SAndrew Lindesay passwordRequirements.SetMinPasswordDigitsChar((uint32) value);
921b6356b91SAndrew Lindesay
922b6356b91SAndrew Lindesay return result;
923b6356b91SAndrew Lindesay }
924b6356b91SAndrew Lindesay
925b6356b91SAndrew Lindesay
926b6356b91SAndrew Lindesay status_t
_RetrievePasswordRequirementsMeta(BMessage & message)927b6356b91SAndrew Lindesay WebAppInterface::_RetrievePasswordRequirementsMeta(BMessage& message)
928b6356b91SAndrew Lindesay {
929b6356b91SAndrew Lindesay BMallocIO* requestEnvelopeData = new BMallocIO();
930b6356b91SAndrew Lindesay // BHttpRequest later takes ownership of this.
931b6356b91SAndrew Lindesay BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
932b6356b91SAndrew Lindesay
933b6356b91SAndrew Lindesay requestEnvelopeWriter.WriteObjectStart();
934b6356b91SAndrew Lindesay requestEnvelopeWriter.WriteObjectEnd();
935b6356b91SAndrew Lindesay
936b6356b91SAndrew Lindesay return _SendJsonRequest("user/get-password-requirements",
937b6356b91SAndrew Lindesay requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
938b6356b91SAndrew Lindesay 0, message);
939b6356b91SAndrew Lindesay }
940b6356b91SAndrew Lindesay
941b6356b91SAndrew Lindesay
942a9edb9bfSAndrew Lindesay /*! JSON-RPC invocations return a response. The response may be either
943a9edb9bfSAndrew Lindesay a result or it may be an error depending on the response structure.
944a9edb9bfSAndrew Lindesay If it is an error then there may be additional detail that is the
945a9edb9bfSAndrew Lindesay error code and message. This method will extract the error code
946a9edb9bfSAndrew Lindesay from the response. This method will return 0 if the payload does
947a9edb9bfSAndrew Lindesay not look like an error.
948a9edb9bfSAndrew Lindesay */
949a9edb9bfSAndrew Lindesay
9504b347fccSAndrew Lindesay /*static*/ int32
ErrorCodeFromResponse(BMessage & responseEnvelopeMessage)951d17c92f7SAndrew Lindesay WebAppInterface::ErrorCodeFromResponse(BMessage& responseEnvelopeMessage)
952a9edb9bfSAndrew Lindesay {
953a9edb9bfSAndrew Lindesay BMessage error;
954a9edb9bfSAndrew Lindesay double code;
955a9edb9bfSAndrew Lindesay
956d17c92f7SAndrew Lindesay if (responseEnvelopeMessage.FindMessage("error", &error) == B_OK
957a9edb9bfSAndrew Lindesay && error.FindDouble("code", &code) == B_OK) {
958a9edb9bfSAndrew Lindesay return (int32) code;
959a9edb9bfSAndrew Lindesay }
960a9edb9bfSAndrew Lindesay
961a9edb9bfSAndrew Lindesay return 0;
962a9edb9bfSAndrew Lindesay }
963a9edb9bfSAndrew Lindesay
964a9edb9bfSAndrew Lindesay
96519c15fecSAndrew Lindesay // #pragma mark - private
96619c15fecSAndrew Lindesay
96719c15fecSAndrew Lindesay
96821df7324SAndrew Lindesay status_t
_SendJsonRequest(const char * urlPathComponents,BPositionIO * requestData,size_t requestDataSize,uint32 flags,BMessage & reply)96921df7324SAndrew Lindesay WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
97021df7324SAndrew Lindesay BPositionIO* requestData, size_t requestDataSize, uint32 flags,
9714b347fccSAndrew Lindesay BMessage& reply)
972a9edb9bfSAndrew Lindesay {
9734b347fccSAndrew Lindesay bool needsAuthorization = (flags & NEEDS_AUTHORIZATION) != 0;
9744b347fccSAndrew Lindesay AccessToken accessToken;
9754b347fccSAndrew Lindesay
9764b347fccSAndrew Lindesay if (needsAuthorization)
9774b347fccSAndrew Lindesay accessToken = _ObtainValidAccessToken();
9784b347fccSAndrew Lindesay
9794b347fccSAndrew Lindesay return _SendJsonRequest(urlPathComponents, accessToken, requestData,
98021df7324SAndrew Lindesay requestDataSize, flags, reply);
981a9edb9bfSAndrew Lindesay }
982a9edb9bfSAndrew Lindesay
983a9edb9bfSAndrew Lindesay
9844b347fccSAndrew Lindesay /*static*/ status_t
_SendJsonRequest(const char * urlPathComponents,const AccessToken & accessToken,BPositionIO * requestData,size_t requestDataSize,uint32 flags,BMessage & reply)98521df7324SAndrew Lindesay WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
9864b347fccSAndrew Lindesay const AccessToken& accessToken, BPositionIO* requestData,
9874b347fccSAndrew Lindesay size_t requestDataSize, uint32 flags, BMessage& reply)
988d2d4866dSAndrew Lindesay {
98988575af1SAndrew Lindesay if (requestDataSize == 0) {
99021df7324SAndrew Lindesay HDINFO("%s; empty request payload", PROTOCOL_NAME);
99188575af1SAndrew Lindesay return B_ERROR;
99288575af1SAndrew Lindesay }
99388575af1SAndrew Lindesay
994b45e8b1eSAndrew Lindesay if (!ServerHelper::IsNetworkAvailable()) {
99521df7324SAndrew Lindesay HDDEBUG("%s; dropping request to ...[%s] as network is not"
99621df7324SAndrew Lindesay " available", PROTOCOL_NAME, urlPathComponents);
997cd417b96SAndrew Lindesay delete requestData;
99854312619SAndrew Lindesay return HD_NETWORK_INACCESSIBLE;
999b45e8b1eSAndrew Lindesay }
100054312619SAndrew Lindesay
1001b45e8b1eSAndrew Lindesay if (ServerSettings::IsClientTooOld()) {
100221df7324SAndrew Lindesay HDDEBUG("%s; dropping request to ...[%s] as client is too old",
100321df7324SAndrew Lindesay PROTOCOL_NAME, urlPathComponents);
1004cd417b96SAndrew Lindesay delete requestData;
100554312619SAndrew Lindesay return HD_CLIENT_TOO_OLD;
1006b45e8b1eSAndrew Lindesay }
100719c15fecSAndrew Lindesay
10084b347fccSAndrew Lindesay bool needsAuthorization = (flags & NEEDS_AUTHORIZATION) != 0;
10094b347fccSAndrew Lindesay
10104b347fccSAndrew Lindesay if (needsAuthorization && !accessToken.IsValid()) {
10114b347fccSAndrew Lindesay HDDEBUG("%s; dropping request to ...[%s] as access token is not valid",
10124b347fccSAndrew Lindesay PROTOCOL_NAME, urlPathComponents);
10134b347fccSAndrew Lindesay delete requestData;
10144b347fccSAndrew Lindesay return B_NOT_ALLOWED;
10154b347fccSAndrew Lindesay }
10164b347fccSAndrew Lindesay
101721df7324SAndrew Lindesay BUrl url = ServerSettings::CreateFullUrl(BString("/__api/v2/")
101821df7324SAndrew Lindesay << urlPathComponents);
101921df7324SAndrew Lindesay HDDEBUG("%s; will make request to [%s]", PROTOCOL_NAME,
102021df7324SAndrew Lindesay url.UrlString().String());
1021b45e8b1eSAndrew Lindesay
1022cd417b96SAndrew Lindesay // If the request payload is logged then it must be copied to local memory
1023cd417b96SAndrew Lindesay // from the stream. This then requires that the request data is then
1024cd417b96SAndrew Lindesay // delivered from memory.
1025cd417b96SAndrew Lindesay
1026cd417b96SAndrew Lindesay if (Logger::IsTraceEnabled()) {
1027f96d1f4dSAndrew Lindesay HDLOGPREFIX(LOG_LEVEL_TRACE)
102821df7324SAndrew Lindesay printf("%s request; ", PROTOCOL_NAME);
102988575af1SAndrew Lindesay _LogPayload(requestData, requestDataSize);
1030cd417b96SAndrew Lindesay printf("\n");
1031cd417b96SAndrew Lindesay }
1032cd417b96SAndrew Lindesay
1033f96d1f4dSAndrew Lindesay ProtocolListener listener;
103419c15fecSAndrew Lindesay BUrlContext context;
103519c15fecSAndrew Lindesay
103619c15fecSAndrew Lindesay BHttpHeaders headers;
103719c15fecSAndrew Lindesay headers.AddHeader("Content-Type", "application/json");
103864a0ec2dSAndrew Lindesay headers.AddHeader("Accept", "application/json");
103919c15fecSAndrew Lindesay ServerSettings::AugmentHeaders(headers);
104019c15fecSAndrew Lindesay
104178b14420SLeorize BHttpRequest* request = make_http_request(url, NULL, &listener, &context);
1042e67a4284SLeorize ObjectDeleter<BHttpRequest> _(request);
1043e67a4284SLeorize if (request == NULL)
1044e67a4284SLeorize return B_ERROR;
1045e67a4284SLeorize request->SetMethod(B_HTTP_POST);
1046e67a4284SLeorize request->SetHeaders(headers);
104719c15fecSAndrew Lindesay
10484b347fccSAndrew Lindesay if (needsAuthorization) {
10494b347fccSAndrew Lindesay BHttpAuthentication authentication;
10504b347fccSAndrew Lindesay authentication.SetMethod(B_HTTP_AUTHENTICATION_BEARER);
10514b347fccSAndrew Lindesay authentication.SetToken(accessToken.Token());
105219c15fecSAndrew Lindesay context.AddAuthentication(url, authentication);
105319c15fecSAndrew Lindesay }
105419c15fecSAndrew Lindesay
1055e67a4284SLeorize request->AdoptInputData(requestData, requestDataSize);
105619c15fecSAndrew Lindesay
105719c15fecSAndrew Lindesay BMallocIO replyData;
105878b14420SLeorize request->SetOutput(&replyData);
105919c15fecSAndrew Lindesay
1060e67a4284SLeorize thread_id thread = request->Run();
106119c15fecSAndrew Lindesay wait_for_thread(thread, NULL);
106219c15fecSAndrew Lindesay
106319c15fecSAndrew Lindesay const BHttpResult& result = dynamic_cast<const BHttpResult&>(
1064e67a4284SLeorize request->Result());
106519c15fecSAndrew Lindesay
106619c15fecSAndrew Lindesay int32 statusCode = result.StatusCode();
106754312619SAndrew Lindesay
106821df7324SAndrew Lindesay HDDEBUG("%s; did receive http-status [%" B_PRId32 "] from [%s]",
106921df7324SAndrew Lindesay PROTOCOL_NAME, statusCode, url.UrlString().String());
1070b45e8b1eSAndrew Lindesay
107154312619SAndrew Lindesay switch (statusCode) {
107254312619SAndrew Lindesay case B_HTTP_STATUS_OK:
107354312619SAndrew Lindesay break;
107454312619SAndrew Lindesay
107554312619SAndrew Lindesay case B_HTTP_STATUS_PRECONDITION_FAILED:
107654312619SAndrew Lindesay ServerHelper::NotifyClientTooOld(result.Headers());
107754312619SAndrew Lindesay return HD_CLIENT_TOO_OLD;
107854312619SAndrew Lindesay
107954312619SAndrew Lindesay default:
108021df7324SAndrew Lindesay HDERROR("%s; request to endpoint [.../%s] failed with http "
108121df7324SAndrew Lindesay "status [%" B_PRId32 "]\n", PROTOCOL_NAME, urlPathComponents,
108221df7324SAndrew Lindesay statusCode);
108319c15fecSAndrew Lindesay return B_ERROR;
108419c15fecSAndrew Lindesay }
108519c15fecSAndrew Lindesay
108666f8dcb1SAndrew Lindesay replyData.Seek(0, SEEK_SET);
108766f8dcb1SAndrew Lindesay
1088cd417b96SAndrew Lindesay if (Logger::IsTraceEnabled()) {
1089f96d1f4dSAndrew Lindesay HDLOGPREFIX(LOG_LEVEL_TRACE)
109021df7324SAndrew Lindesay printf("%s; response; ", PROTOCOL_NAME);
109188575af1SAndrew Lindesay _LogPayload(&replyData, replyData.BufferLength());
1092cd417b96SAndrew Lindesay printf("\n");
1093cd417b96SAndrew Lindesay }
1094cd417b96SAndrew Lindesay
109588575af1SAndrew Lindesay BJsonMessageWriter jsonMessageWriter(reply);
109688575af1SAndrew Lindesay BJson::Parse(&replyData, &jsonMessageWriter);
109788575af1SAndrew Lindesay status_t status = jsonMessageWriter.ErrorStatus();
109888575af1SAndrew Lindesay
1099f0665db4SAndrew Lindesay if (Logger::IsTraceEnabled() && status == B_BAD_DATA) {
1100b45e8b1eSAndrew Lindesay BString resultString(static_cast<const char *>(replyData.Buffer()),
1101b45e8b1eSAndrew Lindesay replyData.BufferLength());
1102fa5c8097SAndrew Lindesay HDERROR("Parser choked on JSON:\n%s", resultString.String());
110319c15fecSAndrew Lindesay }
110419c15fecSAndrew Lindesay return status;
110519c15fecSAndrew Lindesay }
1106b45e8b1eSAndrew Lindesay
1107b45e8b1eSAndrew Lindesay
1108b45e8b1eSAndrew Lindesay status_t
_SendJsonRequest(const char * urlPathComponents,const BString & jsonString,uint32 flags,BMessage & reply)110921df7324SAndrew Lindesay WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
11104b347fccSAndrew Lindesay const BString& jsonString, uint32 flags, BMessage& reply)
1111b45e8b1eSAndrew Lindesay {
1112a9edb9bfSAndrew Lindesay // gets 'adopted' by the subsequent http request.
111388575af1SAndrew Lindesay BMemoryIO* data = new BMemoryIO(jsonString.String(),
111488575af1SAndrew Lindesay jsonString.Length() - 1);
1115b45e8b1eSAndrew Lindesay
111621df7324SAndrew Lindesay return _SendJsonRequest(urlPathComponents, data, jsonString.Length() - 1,
111721df7324SAndrew Lindesay flags, reply);
1118b45e8b1eSAndrew Lindesay }
1119cd417b96SAndrew Lindesay
1120cd417b96SAndrew Lindesay
112101339a54SAndrew Lindesay status_t
_SendRawGetRequest(const BString urlPathComponents,BDataIO * stream)112201339a54SAndrew Lindesay WebAppInterface::_SendRawGetRequest(const BString urlPathComponents,
112301339a54SAndrew Lindesay BDataIO* stream)
112401339a54SAndrew Lindesay {
112501339a54SAndrew Lindesay BUrl url = ServerSettings::CreateFullUrl(urlPathComponents);
112601339a54SAndrew Lindesay
1127409af934SAndrew Lindesay HDDEBUG("http-get; will make request to [%s]",
1128409af934SAndrew Lindesay url.UrlString().String());
1129409af934SAndrew Lindesay
1130f96d1f4dSAndrew Lindesay ProtocolListener listener;
113101339a54SAndrew Lindesay
113201339a54SAndrew Lindesay BHttpHeaders headers;
113301339a54SAndrew Lindesay ServerSettings::AugmentHeaders(headers);
113401339a54SAndrew Lindesay
113578b14420SLeorize BHttpRequest *request = make_http_request(url, stream, &listener);
1136e67a4284SLeorize ObjectDeleter<BHttpRequest> _(request);
1137e67a4284SLeorize if (request == NULL)
1138e67a4284SLeorize return B_ERROR;
1139e67a4284SLeorize request->SetMethod(B_HTTP_GET);
1140e67a4284SLeorize request->SetHeaders(headers);
114101339a54SAndrew Lindesay
1142e67a4284SLeorize thread_id thread = request->Run();
114301339a54SAndrew Lindesay wait_for_thread(thread, NULL);
114401339a54SAndrew Lindesay
114501339a54SAndrew Lindesay const BHttpResult& result = dynamic_cast<const BHttpResult&>(
1146e67a4284SLeorize request->Result());
114701339a54SAndrew Lindesay
114801339a54SAndrew Lindesay int32 statusCode = result.StatusCode();
114901339a54SAndrew Lindesay
1150409af934SAndrew Lindesay HDDEBUG("http-get; did receive http-status [%" B_PRId32 "] from [%s]",
1151409af934SAndrew Lindesay statusCode, url.UrlString().String());
1152409af934SAndrew Lindesay
115301339a54SAndrew Lindesay if (statusCode == 200)
115401339a54SAndrew Lindesay return B_OK;
115501339a54SAndrew Lindesay
1156f96d1f4dSAndrew Lindesay HDERROR("failed to get data from '%s': %" B_PRIi32 "",
1157fa5c8097SAndrew Lindesay url.UrlString().String(), statusCode);
115801339a54SAndrew Lindesay return B_ERROR;
115901339a54SAndrew Lindesay }
116001339a54SAndrew Lindesay
116101339a54SAndrew Lindesay
1162cd417b96SAndrew Lindesay void
_LogPayload(BPositionIO * requestData,size_t size)116388575af1SAndrew Lindesay WebAppInterface::_LogPayload(BPositionIO* requestData, size_t size)
1164cd417b96SAndrew Lindesay {
116566f8dcb1SAndrew Lindesay off_t requestDataOffset = requestData->Position();
116688575af1SAndrew Lindesay char buffer[LOG_PAYLOAD_LIMIT];
116788575af1SAndrew Lindesay
1168cd417b96SAndrew Lindesay if (size > LOG_PAYLOAD_LIMIT)
1169cd417b96SAndrew Lindesay size = LOG_PAYLOAD_LIMIT;
1170cd417b96SAndrew Lindesay
117188575af1SAndrew Lindesay if (B_OK != requestData->ReadExactly(buffer, size)) {
117221df7324SAndrew Lindesay printf("%s; error logging payload", PROTOCOL_NAME);
117388575af1SAndrew Lindesay } else {
117488575af1SAndrew Lindesay for (uint32 i = 0; i < size; i++) {
117588575af1SAndrew Lindesay bool esc = buffer[i] > 126 ||
117688575af1SAndrew Lindesay (buffer[i] < 0x20 && buffer[i] != 0x0a);
1177cd417b96SAndrew Lindesay
1178cd417b96SAndrew Lindesay if (esc)
117988575af1SAndrew Lindesay printf("\\u%02x", buffer[i]);
1180cd417b96SAndrew Lindesay else
118188575af1SAndrew Lindesay putchar(buffer[i]);
1182cd417b96SAndrew Lindesay }
1183cd417b96SAndrew Lindesay
1184cd417b96SAndrew Lindesay if (size == LOG_PAYLOAD_LIMIT)
1185cd417b96SAndrew Lindesay printf("...(continues)");
1186cd417b96SAndrew Lindesay }
118766f8dcb1SAndrew Lindesay
118866f8dcb1SAndrew Lindesay requestData->Seek(requestDataOffset, SEEK_SET);
118988575af1SAndrew Lindesay }
119088575af1SAndrew Lindesay
119188575af1SAndrew Lindesay
119288575af1SAndrew Lindesay /*! This will get the position of the data to get the length an then sets the
119388575af1SAndrew Lindesay offset to zero so that it can be re-read for reading the payload in to log
119488575af1SAndrew Lindesay or send.
119588575af1SAndrew Lindesay */
119688575af1SAndrew Lindesay
119788575af1SAndrew Lindesay off_t
_LengthAndSeekToZero(BPositionIO * data)119888575af1SAndrew Lindesay WebAppInterface::_LengthAndSeekToZero(BPositionIO* data)
119988575af1SAndrew Lindesay {
120088575af1SAndrew Lindesay off_t dataSize = data->Position();
120188575af1SAndrew Lindesay data->Seek(0, SEEK_SET);
120288575af1SAndrew Lindesay return dataSize;
120388575af1SAndrew Lindesay }
12044b347fccSAndrew Lindesay
12054b347fccSAndrew Lindesay
12064b347fccSAndrew Lindesay UserCredentials
_Credentials()12074b347fccSAndrew Lindesay WebAppInterface::_Credentials()
12084b347fccSAndrew Lindesay {
12094b347fccSAndrew Lindesay AutoLocker<BLocker> lock(&fLock);
12104b347fccSAndrew Lindesay return fCredentials;
12114b347fccSAndrew Lindesay }
12124b347fccSAndrew Lindesay
12134b347fccSAndrew Lindesay
12144b347fccSAndrew Lindesay AccessToken
_ObtainValidAccessToken()12154b347fccSAndrew Lindesay WebAppInterface::_ObtainValidAccessToken()
12164b347fccSAndrew Lindesay {
12174b347fccSAndrew Lindesay AutoLocker<BLocker> lock(&fLock);
12184b347fccSAndrew Lindesay
12194b347fccSAndrew Lindesay uint64 now = static_cast<uint64>(time(NULL)) * 1000;
12204b347fccSAndrew Lindesay
12214b347fccSAndrew Lindesay if (!fAccessToken.IsValid(now)) {
12224b347fccSAndrew Lindesay HDINFO("clearing cached access token as it is no longer valid");
12234b347fccSAndrew Lindesay fAccessToken.Clear();
12244b347fccSAndrew Lindesay }
12254b347fccSAndrew Lindesay
12264b347fccSAndrew Lindesay if (!fAccessToken.IsValid()) {
12274b347fccSAndrew Lindesay HDINFO("no cached access token present; will obtain a new one");
12284b347fccSAndrew Lindesay AuthenticateUserRetainingAccessToken();
12294b347fccSAndrew Lindesay }
12304b347fccSAndrew Lindesay
12314b347fccSAndrew Lindesay return fAccessToken;
12324b347fccSAndrew Lindesay }
1233