xref: /haiku/src/apps/haikudepot/server/WebAppInterface.cpp (revision 4b3c808edaaf626e7ee6cb0ee9d21dc96b173ee6)
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 			&timestampAgreedMillis) == 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