xref: /haiku/src/apps/haikudepot/server/WebAppInterface.cpp (revision d2d4866dd448fdcd8a336a904562a044060100d9)
119c15fecSAndrew Lindesay /*
219c15fecSAndrew Lindesay  * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
3f85e0300SAndrew Lindesay  * Copyright 2016-2019, Andrew Lindesay <apl@lindesay.co.nz>.
419c15fecSAndrew Lindesay  * All rights reserved. Distributed under the terms of the MIT License.
519c15fecSAndrew Lindesay  */
619c15fecSAndrew Lindesay 
719c15fecSAndrew Lindesay #include "WebAppInterface.h"
819c15fecSAndrew Lindesay 
919c15fecSAndrew Lindesay #include <stdio.h>
1019c15fecSAndrew Lindesay 
1119c15fecSAndrew Lindesay #include <AppFileInfo.h>
1219c15fecSAndrew Lindesay #include <Application.h>
13a9edb9bfSAndrew Lindesay #include <AutoDeleter.h>
1419c15fecSAndrew Lindesay #include <Autolock.h>
1519c15fecSAndrew Lindesay #include <File.h>
1619c15fecSAndrew Lindesay #include <HttpHeaders.h>
1719c15fecSAndrew Lindesay #include <HttpRequest.h>
1819c15fecSAndrew Lindesay #include <Json.h>
19a9edb9bfSAndrew Lindesay #include <JsonTextWriter.h>
2088575af1SAndrew Lindesay #include <JsonMessageWriter.h>
2119c15fecSAndrew Lindesay #include <Message.h>
2219c15fecSAndrew Lindesay #include <Roster.h>
2319c15fecSAndrew Lindesay #include <Url.h>
2419c15fecSAndrew Lindesay #include <UrlContext.h>
2519c15fecSAndrew Lindesay #include <UrlProtocolListener.h>
2619c15fecSAndrew Lindesay #include <UrlProtocolRoster.h>
2719c15fecSAndrew Lindesay 
2819c15fecSAndrew Lindesay #include "AutoLocker.h"
29cd417b96SAndrew Lindesay #include "DataIOUtils.h"
3054312619SAndrew Lindesay #include "HaikuDepotConstants.h"
3119c15fecSAndrew Lindesay #include "List.h"
32f0665db4SAndrew Lindesay #include "Logger.h"
3319c15fecSAndrew Lindesay #include "PackageInfo.h"
3419c15fecSAndrew Lindesay #include "ServerSettings.h"
3554312619SAndrew Lindesay #include "ServerHelper.h"
3619c15fecSAndrew Lindesay 
3719c15fecSAndrew Lindesay 
3819c15fecSAndrew Lindesay #define BASEURL_DEFAULT "https://depot.haiku-os.org"
3919c15fecSAndrew Lindesay #define USERAGENT_FALLBACK_VERSION "0.0.0"
40cd417b96SAndrew Lindesay #define LOG_PAYLOAD_LIMIT 8192
4119c15fecSAndrew Lindesay 
4219c15fecSAndrew Lindesay 
4319c15fecSAndrew Lindesay class JsonBuilder {
4419c15fecSAndrew Lindesay public:
4519c15fecSAndrew Lindesay 	JsonBuilder()
4619c15fecSAndrew Lindesay 		:
4719c15fecSAndrew Lindesay 		fString("{"),
4819c15fecSAndrew Lindesay 		fInList(false)
4919c15fecSAndrew Lindesay 	{
5019c15fecSAndrew Lindesay 	}
5119c15fecSAndrew Lindesay 
5219c15fecSAndrew Lindesay 	JsonBuilder& AddObject()
5319c15fecSAndrew Lindesay 	{
5419c15fecSAndrew Lindesay 		fString << '{';
5519c15fecSAndrew Lindesay 		fInList = false;
5619c15fecSAndrew Lindesay 		return *this;
5719c15fecSAndrew Lindesay 	}
5819c15fecSAndrew Lindesay 
5919c15fecSAndrew Lindesay 	JsonBuilder& AddObject(const char* name)
6019c15fecSAndrew Lindesay 	{
6119c15fecSAndrew Lindesay 		_StartName(name);
6219c15fecSAndrew Lindesay 		fString << '{';
6319c15fecSAndrew Lindesay 		fInList = false;
6419c15fecSAndrew Lindesay 		return *this;
6519c15fecSAndrew Lindesay 	}
6619c15fecSAndrew Lindesay 
6719c15fecSAndrew Lindesay 	JsonBuilder& EndObject()
6819c15fecSAndrew Lindesay 	{
6919c15fecSAndrew Lindesay 		fString << '}';
7019c15fecSAndrew Lindesay 		fInList = true;
7119c15fecSAndrew Lindesay 		return *this;
7219c15fecSAndrew Lindesay 	}
7319c15fecSAndrew Lindesay 
7419c15fecSAndrew Lindesay 	JsonBuilder& AddArray(const char* name)
7519c15fecSAndrew Lindesay 	{
7619c15fecSAndrew Lindesay 		_StartName(name);
7719c15fecSAndrew Lindesay 		fString << '[';
7819c15fecSAndrew Lindesay 		fInList = false;
7919c15fecSAndrew Lindesay 		return *this;
8019c15fecSAndrew Lindesay 	}
8119c15fecSAndrew Lindesay 
8219c15fecSAndrew Lindesay 	JsonBuilder& EndArray()
8319c15fecSAndrew Lindesay 	{
8419c15fecSAndrew Lindesay 		fString << ']';
8519c15fecSAndrew Lindesay 		fInList = true;
8619c15fecSAndrew Lindesay 		return *this;
8719c15fecSAndrew Lindesay 	}
8819c15fecSAndrew Lindesay 
8919c15fecSAndrew Lindesay 	JsonBuilder& AddStrings(const StringList& strings)
9019c15fecSAndrew Lindesay 	{
9119c15fecSAndrew Lindesay 		for (int i = 0; i < strings.CountItems(); i++)
9219c15fecSAndrew Lindesay 			AddItem(strings.ItemAtFast(i));
9319c15fecSAndrew Lindesay 		return *this;
9419c15fecSAndrew Lindesay 	}
9519c15fecSAndrew Lindesay 
9619c15fecSAndrew Lindesay 	JsonBuilder& AddItem(const char* item)
9719c15fecSAndrew Lindesay 	{
9819c15fecSAndrew Lindesay 		return AddItem(item, false);
9919c15fecSAndrew Lindesay 	}
10019c15fecSAndrew Lindesay 
10119c15fecSAndrew Lindesay 	JsonBuilder& AddItem(const char* item, bool nullIfEmpty)
10219c15fecSAndrew Lindesay 	{
10319c15fecSAndrew Lindesay 		if (item == NULL || (nullIfEmpty && strlen(item) == 0)) {
10419c15fecSAndrew Lindesay 			if (fInList)
10519c15fecSAndrew Lindesay 				fString << ",null";
10619c15fecSAndrew Lindesay 			else
10719c15fecSAndrew Lindesay 				fString << "null";
10819c15fecSAndrew Lindesay 		} else {
10919c15fecSAndrew Lindesay 			if (fInList)
11019c15fecSAndrew Lindesay 				fString << ",\"";
11119c15fecSAndrew Lindesay 			else
11219c15fecSAndrew Lindesay 				fString << '"';
11319c15fecSAndrew Lindesay 			fString << _EscapeString(item);
11419c15fecSAndrew Lindesay 			fString << '"';
11519c15fecSAndrew Lindesay 		}
11619c15fecSAndrew Lindesay 		fInList = true;
11719c15fecSAndrew Lindesay 		return *this;
11819c15fecSAndrew Lindesay 	}
11919c15fecSAndrew Lindesay 
12019c15fecSAndrew Lindesay 	JsonBuilder& AddValue(const char* name, const char* value)
12119c15fecSAndrew Lindesay 	{
12219c15fecSAndrew Lindesay 		return AddValue(name, value, false);
12319c15fecSAndrew Lindesay 	}
12419c15fecSAndrew Lindesay 
12519c15fecSAndrew Lindesay 	JsonBuilder& AddValue(const char* name, const char* value,
12619c15fecSAndrew Lindesay 		bool nullIfEmpty)
12719c15fecSAndrew Lindesay 	{
12819c15fecSAndrew Lindesay 		_StartName(name);
12919c15fecSAndrew Lindesay 		if (value == NULL || (nullIfEmpty && strlen(value) == 0)) {
13019c15fecSAndrew Lindesay 			fString << "null";
13119c15fecSAndrew Lindesay 		} else {
13219c15fecSAndrew Lindesay 			fString << '"';
13319c15fecSAndrew Lindesay 			fString << _EscapeString(value);
13419c15fecSAndrew Lindesay 			fString << '"';
13519c15fecSAndrew Lindesay 		}
13619c15fecSAndrew Lindesay 		fInList = true;
13719c15fecSAndrew Lindesay 		return *this;
13819c15fecSAndrew Lindesay 	}
13919c15fecSAndrew Lindesay 
14019c15fecSAndrew Lindesay 	JsonBuilder& AddValue(const char* name, int value)
14119c15fecSAndrew Lindesay 	{
14219c15fecSAndrew Lindesay 		_StartName(name);
14319c15fecSAndrew Lindesay 		fString << value;
14419c15fecSAndrew Lindesay 		fInList = true;
14519c15fecSAndrew Lindesay 		return *this;
14619c15fecSAndrew Lindesay 	}
14719c15fecSAndrew Lindesay 
14819c15fecSAndrew Lindesay 	JsonBuilder& AddValue(const char* name, bool value)
14919c15fecSAndrew Lindesay 	{
15019c15fecSAndrew Lindesay 		_StartName(name);
15119c15fecSAndrew Lindesay 		if (value)
15219c15fecSAndrew Lindesay 			fString << "true";
15319c15fecSAndrew Lindesay 		else
15419c15fecSAndrew Lindesay 			fString << "false";
15519c15fecSAndrew Lindesay 		fInList = true;
15619c15fecSAndrew Lindesay 		return *this;
15719c15fecSAndrew Lindesay 	}
15819c15fecSAndrew Lindesay 
15919c15fecSAndrew Lindesay 	const BString& End()
16019c15fecSAndrew Lindesay 	{
16119c15fecSAndrew Lindesay 		fString << "}\n";
16219c15fecSAndrew Lindesay 		return fString;
16319c15fecSAndrew Lindesay 	}
16419c15fecSAndrew Lindesay 
16519c15fecSAndrew Lindesay private:
16619c15fecSAndrew Lindesay 	void _StartName(const char* name)
16719c15fecSAndrew Lindesay 	{
16819c15fecSAndrew Lindesay 		if (fInList)
16919c15fecSAndrew Lindesay 			fString << ",\"";
17019c15fecSAndrew Lindesay 		else
17119c15fecSAndrew Lindesay 			fString << '"';
17219c15fecSAndrew Lindesay 		fString << _EscapeString(name);
17319c15fecSAndrew Lindesay 		fString << "\":";
17419c15fecSAndrew Lindesay 	}
17519c15fecSAndrew Lindesay 
17619c15fecSAndrew Lindesay 	BString _EscapeString(const char* original) const
17719c15fecSAndrew Lindesay 	{
17819c15fecSAndrew Lindesay 		BString string(original);
17919c15fecSAndrew Lindesay 		string.ReplaceAll("\\", "\\\\");
18019c15fecSAndrew Lindesay 		string.ReplaceAll("\"", "\\\"");
18119c15fecSAndrew Lindesay 		string.ReplaceAll("/", "\\/");
18219c15fecSAndrew Lindesay 		string.ReplaceAll("\b", "\\b");
18319c15fecSAndrew Lindesay 		string.ReplaceAll("\f", "\\f");
18419c15fecSAndrew Lindesay 		string.ReplaceAll("\n", "\\n");
18519c15fecSAndrew Lindesay 		string.ReplaceAll("\r", "\\r");
18619c15fecSAndrew Lindesay 		string.ReplaceAll("\t", "\\t");
18719c15fecSAndrew Lindesay 		return string;
18819c15fecSAndrew Lindesay 	}
18919c15fecSAndrew Lindesay 
19019c15fecSAndrew Lindesay private:
19119c15fecSAndrew Lindesay 	BString		fString;
19219c15fecSAndrew Lindesay 	bool		fInList;
19319c15fecSAndrew Lindesay };
19419c15fecSAndrew Lindesay 
19519c15fecSAndrew Lindesay 
19619c15fecSAndrew Lindesay class ProtocolListener : public BUrlProtocolListener {
19719c15fecSAndrew Lindesay public:
19819c15fecSAndrew Lindesay 	ProtocolListener(bool traceLogging)
19919c15fecSAndrew Lindesay 		:
20019c15fecSAndrew Lindesay 		fDownloadIO(NULL),
20119c15fecSAndrew Lindesay 		fTraceLogging(traceLogging)
20219c15fecSAndrew Lindesay 	{
20319c15fecSAndrew Lindesay 	}
20419c15fecSAndrew Lindesay 
20519c15fecSAndrew Lindesay 	virtual ~ProtocolListener()
20619c15fecSAndrew Lindesay 	{
20719c15fecSAndrew Lindesay 	}
20819c15fecSAndrew Lindesay 
20919c15fecSAndrew Lindesay 	virtual	void ConnectionOpened(BUrlRequest* caller)
21019c15fecSAndrew Lindesay 	{
21119c15fecSAndrew Lindesay 	}
21219c15fecSAndrew Lindesay 
21319c15fecSAndrew Lindesay 	virtual void HostnameResolved(BUrlRequest* caller, const char* ip)
21419c15fecSAndrew Lindesay 	{
21519c15fecSAndrew Lindesay 	}
21619c15fecSAndrew Lindesay 
21719c15fecSAndrew Lindesay 	virtual void ResponseStarted(BUrlRequest* caller)
21819c15fecSAndrew Lindesay 	{
21919c15fecSAndrew Lindesay 	}
22019c15fecSAndrew Lindesay 
221f9e1854fSAdrien Destugues 	virtual void HeadersReceived(BUrlRequest* caller, const BUrlResult& result)
22219c15fecSAndrew Lindesay 	{
22319c15fecSAndrew Lindesay 	}
22419c15fecSAndrew Lindesay 
22519c15fecSAndrew Lindesay 	virtual void DataReceived(BUrlRequest* caller, const char* data,
22619c15fecSAndrew Lindesay 		off_t position, ssize_t size)
22719c15fecSAndrew Lindesay 	{
22819c15fecSAndrew Lindesay 		if (fDownloadIO != NULL)
22919c15fecSAndrew Lindesay 			fDownloadIO->Write(data, size);
23019c15fecSAndrew Lindesay 	}
23119c15fecSAndrew Lindesay 
23219c15fecSAndrew Lindesay 	virtual	void DownloadProgress(BUrlRequest* caller, ssize_t bytesReceived,
23319c15fecSAndrew Lindesay 		ssize_t bytesTotal)
23419c15fecSAndrew Lindesay 	{
23519c15fecSAndrew Lindesay 	}
23619c15fecSAndrew Lindesay 
23719c15fecSAndrew Lindesay 	virtual void UploadProgress(BUrlRequest* caller, ssize_t bytesSent,
23819c15fecSAndrew Lindesay 		ssize_t bytesTotal)
23919c15fecSAndrew Lindesay 	{
24019c15fecSAndrew Lindesay 	}
24119c15fecSAndrew Lindesay 
24219c15fecSAndrew Lindesay 	virtual void RequestCompleted(BUrlRequest* caller, bool success)
24319c15fecSAndrew Lindesay 	{
24419c15fecSAndrew Lindesay 	}
24519c15fecSAndrew Lindesay 
24619c15fecSAndrew Lindesay 	virtual void DebugMessage(BUrlRequest* caller,
24719c15fecSAndrew Lindesay 		BUrlProtocolDebugMessage type, const char* text)
24819c15fecSAndrew Lindesay 	{
24919c15fecSAndrew Lindesay 		if (fTraceLogging)
25019c15fecSAndrew Lindesay 			printf("jrpc: %s\n", text);
25119c15fecSAndrew Lindesay 	}
25219c15fecSAndrew Lindesay 
25319c15fecSAndrew Lindesay 	void SetDownloadIO(BDataIO* downloadIO)
25419c15fecSAndrew Lindesay 	{
25519c15fecSAndrew Lindesay 		fDownloadIO = downloadIO;
25619c15fecSAndrew Lindesay 	}
25719c15fecSAndrew Lindesay 
25819c15fecSAndrew Lindesay private:
25919c15fecSAndrew Lindesay 	BDataIO*		fDownloadIO;
26019c15fecSAndrew Lindesay 	bool			fTraceLogging;
26119c15fecSAndrew Lindesay };
26219c15fecSAndrew Lindesay 
26319c15fecSAndrew Lindesay 
26419c15fecSAndrew Lindesay int
26519c15fecSAndrew Lindesay WebAppInterface::fRequestIndex = 0;
26619c15fecSAndrew Lindesay 
26719c15fecSAndrew Lindesay 
26819c15fecSAndrew Lindesay enum {
26919c15fecSAndrew Lindesay 	NEEDS_AUTHORIZATION = 1 << 0,
27019c15fecSAndrew Lindesay };
27119c15fecSAndrew Lindesay 
27219c15fecSAndrew Lindesay 
27319c15fecSAndrew Lindesay WebAppInterface::WebAppInterface()
27419c15fecSAndrew Lindesay {
27519c15fecSAndrew Lindesay }
27619c15fecSAndrew Lindesay 
27719c15fecSAndrew Lindesay 
27819c15fecSAndrew Lindesay WebAppInterface::WebAppInterface(const WebAppInterface& other)
27919c15fecSAndrew Lindesay 	:
280*d2d4866dSAndrew Lindesay 	fCredentials(other.fCredentials)
28119c15fecSAndrew Lindesay {
28219c15fecSAndrew Lindesay }
28319c15fecSAndrew Lindesay 
28419c15fecSAndrew Lindesay 
28519c15fecSAndrew Lindesay WebAppInterface::~WebAppInterface()
28619c15fecSAndrew Lindesay {
28719c15fecSAndrew Lindesay }
28819c15fecSAndrew Lindesay 
28919c15fecSAndrew Lindesay 
29019c15fecSAndrew Lindesay WebAppInterface&
29119c15fecSAndrew Lindesay WebAppInterface::operator=(const WebAppInterface& other)
29219c15fecSAndrew Lindesay {
29319c15fecSAndrew Lindesay 	if (this == &other)
29419c15fecSAndrew Lindesay 		return *this;
295*d2d4866dSAndrew Lindesay 	fCredentials = other.fCredentials;
29619c15fecSAndrew Lindesay 	return *this;
29719c15fecSAndrew Lindesay }
29819c15fecSAndrew Lindesay 
29919c15fecSAndrew Lindesay 
30019c15fecSAndrew Lindesay void
301*d2d4866dSAndrew Lindesay WebAppInterface::SetAuthorization(const UserCredentials& value)
30219c15fecSAndrew Lindesay {
303*d2d4866dSAndrew Lindesay 	fCredentials = value;
304*d2d4866dSAndrew Lindesay }
305*d2d4866dSAndrew Lindesay 
306*d2d4866dSAndrew Lindesay 
307*d2d4866dSAndrew Lindesay const BString&
308*d2d4866dSAndrew Lindesay WebAppInterface::Nickname() const
309*d2d4866dSAndrew Lindesay {
310*d2d4866dSAndrew Lindesay 	return fCredentials.Nickname();
31119c15fecSAndrew Lindesay }
31219c15fecSAndrew Lindesay 
31319c15fecSAndrew Lindesay 
31419c15fecSAndrew Lindesay status_t
31580a272eeSAndrew Lindesay WebAppInterface::GetChangelog(const BString& packageName, BMessage& message)
31619c15fecSAndrew Lindesay {
31719c15fecSAndrew Lindesay 	BString jsonString = JsonBuilder()
31819c15fecSAndrew Lindesay 		.AddValue("jsonrpc", "2.0")
31919c15fecSAndrew Lindesay 		.AddValue("id", ++fRequestIndex)
32080a272eeSAndrew Lindesay 		.AddValue("method", "getPkgChangelog")
32119c15fecSAndrew Lindesay 		.AddArray("params")
32219c15fecSAndrew Lindesay 			.AddObject()
32380a272eeSAndrew Lindesay 				.AddValue("pkgName", packageName)
32419c15fecSAndrew Lindesay 			.EndObject()
32519c15fecSAndrew Lindesay 		.EndArray()
32619c15fecSAndrew Lindesay 	.End();
32719c15fecSAndrew Lindesay 
32819c15fecSAndrew Lindesay 	return _SendJsonRequest("pkg", jsonString, 0, message);
32919c15fecSAndrew Lindesay }
33019c15fecSAndrew Lindesay 
33119c15fecSAndrew Lindesay 
33219c15fecSAndrew Lindesay status_t
333051ee9d8SAndrew Lindesay WebAppInterface::RetreiveUserRatingsForPackageForDisplay(
334051ee9d8SAndrew Lindesay 	const BString& packageName, const BString& webAppRepositoryCode,
335051ee9d8SAndrew Lindesay 	int resultOffset, int maxResults, BMessage& message)
33619c15fecSAndrew Lindesay {
337051ee9d8SAndrew Lindesay 		// BHttpRequest later takes ownership of this.
338051ee9d8SAndrew Lindesay 	BMallocIO* requestEnvelopeData = new BMallocIO();
339051ee9d8SAndrew Lindesay 	BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
34019c15fecSAndrew Lindesay 
341051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
342051ee9d8SAndrew Lindesay 	_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
343051ee9d8SAndrew Lindesay 		"searchUserRatings");
344051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("params");
345051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
346051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
347051ee9d8SAndrew Lindesay 
348051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgName");
349051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteString(packageName.String());
350051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("offset");
351051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteInteger(resultOffset);
352051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("limit");
353051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteInteger(maxResults);
354051ee9d8SAndrew Lindesay 
355051ee9d8SAndrew Lindesay 	if (!webAppRepositoryCode.IsEmpty()) {
356051ee9d8SAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("repositoryCode");
357051ee9d8SAndrew Lindesay 		requestEnvelopeWriter.WriteString(webAppRepositoryCode);
358051ee9d8SAndrew Lindesay 	}
359051ee9d8SAndrew Lindesay 
360051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
361051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
362051ee9d8SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
363051ee9d8SAndrew Lindesay 
364051ee9d8SAndrew Lindesay 	return _SendJsonRequest("userrating", requestEnvelopeData,
365051ee9d8SAndrew Lindesay 		_LengthAndSeekToZero(requestEnvelopeData), 0,
366051ee9d8SAndrew Lindesay 		message);
36719c15fecSAndrew Lindesay }
36819c15fecSAndrew Lindesay 
36919c15fecSAndrew Lindesay 
37019c15fecSAndrew Lindesay status_t
371051ee9d8SAndrew Lindesay WebAppInterface::RetreiveUserRatingForPackageAndVersionByUser(
372051ee9d8SAndrew Lindesay 	const BString& packageName, const BPackageVersion& version,
373051ee9d8SAndrew Lindesay 	const BString& architecture, const BString &repositoryCode,
374*d2d4866dSAndrew Lindesay 	const BString& userNickname, BMessage& message)
37519c15fecSAndrew Lindesay {
376a9edb9bfSAndrew Lindesay 		// BHttpRequest later takes ownership of this.
377a9edb9bfSAndrew Lindesay 	BMallocIO* requestEnvelopeData = new BMallocIO();
378a9edb9bfSAndrew Lindesay 	BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
37919c15fecSAndrew Lindesay 
380a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
381a9edb9bfSAndrew Lindesay 	_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
382a9edb9bfSAndrew Lindesay 		"getUserRatingByUserAndPkgVersion");
383a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("params");
384a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
385a9edb9bfSAndrew Lindesay 
386a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
387a9edb9bfSAndrew Lindesay 
388a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("userNickname");
389*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteString(userNickname.String());
390a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgName");
391a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(packageName.String());
392a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode");
393a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(architecture.String());
394a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("repositoryCode");
395a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(repositoryCode.String());
396a9edb9bfSAndrew Lindesay 
397a9edb9bfSAndrew Lindesay 	if (version.Major().Length() > 0) {
398a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMajor");
399a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Major().String());
400a9edb9bfSAndrew Lindesay 	}
401a9edb9bfSAndrew Lindesay 
402a9edb9bfSAndrew Lindesay 	if (version.Minor().Length() > 0) {
403a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMinor");
404a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Minor().String());
405a9edb9bfSAndrew Lindesay 	}
406a9edb9bfSAndrew Lindesay 
407a9edb9bfSAndrew Lindesay 	if (version.Micro().Length() > 0) {
408a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMicro");
409a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Micro().String());
410a9edb9bfSAndrew Lindesay 	}
411a9edb9bfSAndrew Lindesay 
412a9edb9bfSAndrew Lindesay 	if (version.PreRelease().Length() > 0) {
413a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionPreRelease");
414a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.PreRelease().String());
415a9edb9bfSAndrew Lindesay 	}
416a9edb9bfSAndrew Lindesay 
417a9edb9bfSAndrew Lindesay 	if (version.Revision() != 0) {
418a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionRevision");
419a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteInteger(version.Revision());
420a9edb9bfSAndrew Lindesay 	}
421a9edb9bfSAndrew Lindesay 
422a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
423a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
424a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
425a9edb9bfSAndrew Lindesay 
426a9edb9bfSAndrew Lindesay 	return _SendJsonRequest("userrating", requestEnvelopeData,
42788575af1SAndrew Lindesay 		_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
42888575af1SAndrew Lindesay 		message);
42919c15fecSAndrew Lindesay }
43019c15fecSAndrew Lindesay 
43119c15fecSAndrew Lindesay 
432*d2d4866dSAndrew Lindesay /*! This method will fill out the supplied UserDetail object with information
433*d2d4866dSAndrew Lindesay     about the user that is supplied in the credentials.  Importantly it will
434*d2d4866dSAndrew Lindesay     also authenticate the request with the details of the credentials and will
435*d2d4866dSAndrew Lindesay     not use the credentials that are configured in 'fCredentials'.
436*d2d4866dSAndrew Lindesay */
437*d2d4866dSAndrew Lindesay 
438*d2d4866dSAndrew Lindesay status_t
439*d2d4866dSAndrew Lindesay WebAppInterface::RetrieveUserDetailForCredentials(
440*d2d4866dSAndrew Lindesay 	const UserCredentials& credentials, UserDetail& userDetail)
441*d2d4866dSAndrew Lindesay {
442*d2d4866dSAndrew Lindesay 	if (!credentials.IsValid()) {
443*d2d4866dSAndrew Lindesay 		debugger("the credentials supplied are invalid so it is not possible "
444*d2d4866dSAndrew Lindesay 			"to obtain the user detail");
445*d2d4866dSAndrew Lindesay 	}
446*d2d4866dSAndrew Lindesay 
447*d2d4866dSAndrew Lindesay 		// BHttpRequest later takes ownership of this.
448*d2d4866dSAndrew Lindesay 	BMallocIO* requestEnvelopeData = new BMallocIO();
449*d2d4866dSAndrew Lindesay 	BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
450*d2d4866dSAndrew Lindesay 
451*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
452*d2d4866dSAndrew Lindesay 	_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter, "getUser");
453*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("params");
454*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
455*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
456*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("nickname");
457*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteString(credentials.Nickname().String());
458*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
459*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
460*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
461*d2d4866dSAndrew Lindesay 
462*d2d4866dSAndrew Lindesay 	BMessage responseEnvelopeMessage;
463*d2d4866dSAndrew Lindesay 	status_t result = _SendJsonRequest("user", credentials, requestEnvelopeData,
464*d2d4866dSAndrew Lindesay 		_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
465*d2d4866dSAndrew Lindesay 		responseEnvelopeMessage);
466*d2d4866dSAndrew Lindesay 		// note that the credentials used here are passed in as args.
467*d2d4866dSAndrew Lindesay 
468*d2d4866dSAndrew Lindesay 	if (result == B_OK)
469*d2d4866dSAndrew Lindesay 		result = _UnpackUserDetails(responseEnvelopeMessage, userDetail);
470*d2d4866dSAndrew Lindesay 
471*d2d4866dSAndrew Lindesay 	return result;
472*d2d4866dSAndrew Lindesay }
473*d2d4866dSAndrew Lindesay 
474*d2d4866dSAndrew Lindesay 
475*d2d4866dSAndrew Lindesay /*! This method will return the credentials for the currently authenticated
476*d2d4866dSAndrew Lindesay     user.
477*d2d4866dSAndrew Lindesay */
478*d2d4866dSAndrew Lindesay 
479*d2d4866dSAndrew Lindesay status_t
480*d2d4866dSAndrew Lindesay WebAppInterface::RetrieveCurrentUserDetail(UserDetail& userDetail)
481*d2d4866dSAndrew Lindesay {
482*d2d4866dSAndrew Lindesay 	return RetrieveUserDetailForCredentials(fCredentials, userDetail);
483*d2d4866dSAndrew Lindesay }
484*d2d4866dSAndrew Lindesay 
485*d2d4866dSAndrew Lindesay 
486*d2d4866dSAndrew Lindesay /*! When the user requests user detail, the server sends back an envelope of
487*d2d4866dSAndrew Lindesay     response data.  This method will unpack the data into a model object.
488*d2d4866dSAndrew Lindesay     \return Not B_OK if something went wrong.
489*d2d4866dSAndrew Lindesay */
490*d2d4866dSAndrew Lindesay 
491*d2d4866dSAndrew Lindesay /*static*/ status_t
492*d2d4866dSAndrew Lindesay WebAppInterface::_UnpackUserDetails(BMessage& responseEnvelopeMessage,
493*d2d4866dSAndrew Lindesay 	UserDetail& userDetail)
494*d2d4866dSAndrew Lindesay {
495*d2d4866dSAndrew Lindesay 	BMessage resultMessage;
496*d2d4866dSAndrew Lindesay 	status_t result = responseEnvelopeMessage.FindMessage(
497*d2d4866dSAndrew Lindesay 		"result", &resultMessage);
498*d2d4866dSAndrew Lindesay 
499*d2d4866dSAndrew Lindesay 	if (result != B_OK) {
500*d2d4866dSAndrew Lindesay 		fprintf(stderr, "bad response envelope missing 'result' entry\n");
501*d2d4866dSAndrew Lindesay 		return result;
502*d2d4866dSAndrew Lindesay 	}
503*d2d4866dSAndrew Lindesay 
504*d2d4866dSAndrew Lindesay 	BString nickname;
505*d2d4866dSAndrew Lindesay 	result = resultMessage.FindString("nickname", &nickname);
506*d2d4866dSAndrew Lindesay 	userDetail.SetNickname(nickname);
507*d2d4866dSAndrew Lindesay 
508*d2d4866dSAndrew Lindesay 	BMessage agreementMessage;
509*d2d4866dSAndrew Lindesay 	if (resultMessage.FindMessage("userUsageConditionsAgreement",
510*d2d4866dSAndrew Lindesay 		&agreementMessage) == B_OK) {
511*d2d4866dSAndrew Lindesay 		BString code;
512*d2d4866dSAndrew Lindesay 		BDateTime agreedToTimestamp;
513*d2d4866dSAndrew Lindesay 		BString userUsageConditionsCode;
514*d2d4866dSAndrew Lindesay 		UserUsageConditionsAgreement agreement = userDetail.Agreement();
515*d2d4866dSAndrew Lindesay 		bool isLatest;
516*d2d4866dSAndrew Lindesay 
517*d2d4866dSAndrew Lindesay 		if (agreementMessage.FindString("userUsageConditionsCode",
518*d2d4866dSAndrew Lindesay 			&userUsageConditionsCode) == B_OK) {
519*d2d4866dSAndrew Lindesay 			agreement.SetCode(userUsageConditionsCode);
520*d2d4866dSAndrew Lindesay 		}
521*d2d4866dSAndrew Lindesay 
522*d2d4866dSAndrew Lindesay 		double timestampAgreedMillis;
523*d2d4866dSAndrew Lindesay 		if (agreementMessage.FindDouble("timestampAgreed",
524*d2d4866dSAndrew Lindesay 			&timestampAgreedMillis) == B_OK) {
525*d2d4866dSAndrew Lindesay 			agreement.SetTimestampAgreed((uint64) timestampAgreedMillis);
526*d2d4866dSAndrew Lindesay 		}
527*d2d4866dSAndrew Lindesay 
528*d2d4866dSAndrew Lindesay 		if (agreementMessage.FindBool("isLatest", &isLatest)
529*d2d4866dSAndrew Lindesay 			== B_OK) {
530*d2d4866dSAndrew Lindesay 			agreement.SetIsLatest(isLatest);
531*d2d4866dSAndrew Lindesay 		}
532*d2d4866dSAndrew Lindesay 
533*d2d4866dSAndrew Lindesay 		userDetail.SetAgreement(agreement);
534*d2d4866dSAndrew Lindesay 	}
535*d2d4866dSAndrew Lindesay 
536*d2d4866dSAndrew Lindesay 	return result;
537*d2d4866dSAndrew Lindesay }
538*d2d4866dSAndrew Lindesay 
539*d2d4866dSAndrew Lindesay 
54001339a54SAndrew Lindesay /*! \brief Returns data relating to the user usage conditions
54101339a54SAndrew Lindesay 
54201339a54SAndrew Lindesay 	\param code defines the version of the data to return or if empty then the
54301339a54SAndrew Lindesay 		latest is returned.
54401339a54SAndrew Lindesay 
54501339a54SAndrew Lindesay     This method will go to the server and get details relating to the user usage
54601339a54SAndrew Lindesay     conditions.  It does this in two API calls; first gets the details (the
54701339a54SAndrew Lindesay     minimum age) and in the second call, the text of the conditions is returned.
54801339a54SAndrew Lindesay */
54901339a54SAndrew Lindesay 
55001339a54SAndrew Lindesay status_t
55101339a54SAndrew Lindesay WebAppInterface::RetrieveUserUsageConditions(const BString& code,
55201339a54SAndrew Lindesay 	UserUsageConditions& conditions)
55301339a54SAndrew Lindesay {
55401339a54SAndrew Lindesay 	BMessage responseEnvelopeMessage;
55501339a54SAndrew Lindesay 	status_t result = _RetrieveUserUsageConditionsMeta(code,
55601339a54SAndrew Lindesay 		responseEnvelopeMessage);
55701339a54SAndrew Lindesay 
55801339a54SAndrew Lindesay 	if (result != B_OK)
55901339a54SAndrew Lindesay 		return result;
56001339a54SAndrew Lindesay 
56101339a54SAndrew Lindesay 	BMessage resultMessage;
56201339a54SAndrew Lindesay 	if (responseEnvelopeMessage.FindMessage("result", &resultMessage) != B_OK) {
56301339a54SAndrew Lindesay 		fprintf(stderr, "bad response envelope missing 'result' entry\n");
56401339a54SAndrew Lindesay 		return B_BAD_DATA;
56501339a54SAndrew Lindesay 	}
56601339a54SAndrew Lindesay 
56701339a54SAndrew Lindesay 	BString metaDataCode;
56801339a54SAndrew Lindesay 	double metaDataMinimumAge;
56901339a54SAndrew Lindesay 	BString copyMarkdown;
57001339a54SAndrew Lindesay 
57101339a54SAndrew Lindesay 	if ( (resultMessage.FindString("code", &metaDataCode) != B_OK)
57201339a54SAndrew Lindesay 		|| (resultMessage.FindDouble(
57301339a54SAndrew Lindesay 			"minimumAge", &metaDataMinimumAge) != B_OK) ) {
57401339a54SAndrew Lindesay 		printf("unexpected response from server with missing user usage "
57501339a54SAndrew Lindesay 			"conditions data\n");
57601339a54SAndrew Lindesay 		return B_BAD_DATA;
57701339a54SAndrew Lindesay 	}
57801339a54SAndrew Lindesay 
57901339a54SAndrew Lindesay 	BMallocIO* copyMarkdownData = new BMallocIO();
58001339a54SAndrew Lindesay 	result = _RetrieveUserUsageConditionsCopy(metaDataCode, copyMarkdownData);
58101339a54SAndrew Lindesay 
58201339a54SAndrew Lindesay 	if (result != B_OK)
58301339a54SAndrew Lindesay 		return result;
58401339a54SAndrew Lindesay 
58501339a54SAndrew Lindesay 	conditions.SetCode(metaDataCode);
58601339a54SAndrew Lindesay 	conditions.SetMinimumAge(metaDataMinimumAge);
58701339a54SAndrew Lindesay 	conditions.SetCopyMarkdown(
58801339a54SAndrew Lindesay 		BString(static_cast<const char*>(copyMarkdownData->Buffer()),
58901339a54SAndrew Lindesay 			copyMarkdownData->BufferLength()));
59001339a54SAndrew Lindesay 
59101339a54SAndrew Lindesay 	return B_OK;
59201339a54SAndrew Lindesay }
59301339a54SAndrew Lindesay 
59401339a54SAndrew Lindesay 
59501339a54SAndrew Lindesay status_t
59601339a54SAndrew Lindesay WebAppInterface::_RetrieveUserUsageConditionsMeta(const BString& code,
59701339a54SAndrew Lindesay 	BMessage& message)
59801339a54SAndrew Lindesay {
59901339a54SAndrew Lindesay 	BMallocIO* requestEnvelopeData = new BMallocIO();
60001339a54SAndrew Lindesay 	BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
60101339a54SAndrew Lindesay 
60201339a54SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
60301339a54SAndrew Lindesay 	_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
60401339a54SAndrew Lindesay 		"getUserUsageConditions");
60501339a54SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("params");
60601339a54SAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
60701339a54SAndrew Lindesay 
60801339a54SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
60901339a54SAndrew Lindesay 
61001339a54SAndrew Lindesay 	if (!code.IsEmpty()) {
61101339a54SAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("code");
61201339a54SAndrew Lindesay 		requestEnvelopeWriter.WriteString(code.String());
61301339a54SAndrew Lindesay 	}
61401339a54SAndrew Lindesay 
61501339a54SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
61601339a54SAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
61701339a54SAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
61801339a54SAndrew Lindesay 
61901339a54SAndrew Lindesay 	// now fetch this information into an object.
62001339a54SAndrew Lindesay 
62101339a54SAndrew Lindesay 	return _SendJsonRequest("user", requestEnvelopeData,
62201339a54SAndrew Lindesay 		_LengthAndSeekToZero(requestEnvelopeData), 0, message);
62301339a54SAndrew Lindesay }
62401339a54SAndrew Lindesay 
62501339a54SAndrew Lindesay 
62601339a54SAndrew Lindesay status_t
62701339a54SAndrew Lindesay WebAppInterface::_RetrieveUserUsageConditionsCopy(const BString& code,
62801339a54SAndrew Lindesay 	BDataIO* stream)
62901339a54SAndrew Lindesay {
63001339a54SAndrew Lindesay 	return _SendRawGetRequest(
63101339a54SAndrew Lindesay 		BString("/__user/usageconditions/") << code << "/document.md",
63201339a54SAndrew Lindesay 		stream);
63301339a54SAndrew Lindesay }
63401339a54SAndrew Lindesay 
63501339a54SAndrew Lindesay 
63619c15fecSAndrew Lindesay status_t
63719c15fecSAndrew Lindesay WebAppInterface::CreateUserRating(const BString& packageName,
638a9edb9bfSAndrew Lindesay 	const BPackageVersion& version,
63919c15fecSAndrew Lindesay 	const BString& architecture, const BString& repositoryCode,
64019c15fecSAndrew Lindesay 	const BString& languageCode, const BString& comment,
64119c15fecSAndrew Lindesay 	const BString& stability, int rating, BMessage& message)
64219c15fecSAndrew Lindesay {
643a9edb9bfSAndrew Lindesay 		// BHttpRequest later takes ownership of this.
644a9edb9bfSAndrew Lindesay 	BMallocIO* requestEnvelopeData = new BMallocIO();
645a9edb9bfSAndrew Lindesay 	BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
64619c15fecSAndrew Lindesay 
647a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
648a9edb9bfSAndrew Lindesay 	_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
649a9edb9bfSAndrew Lindesay 		"createUserRating");
650a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("params");
651a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
652a9edb9bfSAndrew Lindesay 
653a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
654a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgName");
655a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(packageName.String());
656a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode");
657a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(architecture.String());
658a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("repositoryCode");
659a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(repositoryCode.String());
660a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
661a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(languageCode.String());
662a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgVersionType");
663a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("SPECIFIC");
664a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("userNickname");
665*d2d4866dSAndrew Lindesay 	requestEnvelopeWriter.WriteString(fCredentials.Nickname());
666a9edb9bfSAndrew Lindesay 
667a9edb9bfSAndrew Lindesay 	if (!version.Major().IsEmpty()) {
668a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMajor");
669a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Major());
670a9edb9bfSAndrew Lindesay 	}
671a9edb9bfSAndrew Lindesay 
672a9edb9bfSAndrew Lindesay 	if (!version.Minor().IsEmpty()) {
673a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMinor");
674a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Minor());
675a9edb9bfSAndrew Lindesay 	}
676a9edb9bfSAndrew Lindesay 
677a9edb9bfSAndrew Lindesay 	if (!version.Micro().IsEmpty()) {
678a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMicro");
679a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Micro());
680a9edb9bfSAndrew Lindesay 	}
681a9edb9bfSAndrew Lindesay 
682a9edb9bfSAndrew Lindesay 	if (!version.PreRelease().IsEmpty()) {
683a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionPreRelease");
684a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.PreRelease());
685a9edb9bfSAndrew Lindesay 	}
686a9edb9bfSAndrew Lindesay 
687a9edb9bfSAndrew Lindesay 	if (version.Revision() != 0) {
688a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionRevision");
689a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteInteger(version.Revision());
690a9edb9bfSAndrew Lindesay 	}
691a9edb9bfSAndrew Lindesay 
692a9edb9bfSAndrew Lindesay 	if (rating > 0.0f) {
693a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("rating");
694a9edb9bfSAndrew Lindesay     	requestEnvelopeWriter.WriteInteger(rating);
695a9edb9bfSAndrew Lindesay 	}
696a9edb9bfSAndrew Lindesay 
697a9edb9bfSAndrew Lindesay 	if (stability.Length() > 0) {
698a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("userRatingStabilityCode");
699a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(stability);
700a9edb9bfSAndrew Lindesay 	}
701a9edb9bfSAndrew Lindesay 
702a9edb9bfSAndrew Lindesay 	if (comment.Length() > 0) {
703a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("comment");
704a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(comment.String());
705a9edb9bfSAndrew Lindesay 	}
706a9edb9bfSAndrew Lindesay 
707a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
708a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
709a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
710a9edb9bfSAndrew Lindesay 
711a9edb9bfSAndrew Lindesay 	return _SendJsonRequest("userrating", requestEnvelopeData,
71288575af1SAndrew Lindesay 		_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
71388575af1SAndrew Lindesay 		message);
71419c15fecSAndrew Lindesay }
71519c15fecSAndrew Lindesay 
71619c15fecSAndrew Lindesay 
71719c15fecSAndrew Lindesay status_t
71819c15fecSAndrew Lindesay WebAppInterface::UpdateUserRating(const BString& ratingID,
71919c15fecSAndrew Lindesay 	const BString& languageCode, const BString& comment,
72019c15fecSAndrew Lindesay 	const BString& stability, int rating, bool active, BMessage& message)
72119c15fecSAndrew Lindesay {
722a9edb9bfSAndrew Lindesay 		// BHttpRequest later takes ownership of this.
723a9edb9bfSAndrew Lindesay 	BMallocIO* requestEnvelopeData = new BMallocIO();
724a9edb9bfSAndrew Lindesay 	BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
72519c15fecSAndrew Lindesay 
726a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
727a9edb9bfSAndrew Lindesay 	_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
728a9edb9bfSAndrew Lindesay 		"updateUserRating");
729a9edb9bfSAndrew Lindesay 
730a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("params");
731a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
732a9edb9bfSAndrew Lindesay 
733a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
734a9edb9bfSAndrew Lindesay 
735a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("code");
736a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(ratingID.String());
737a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
738a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(languageCode.String());
739a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("active");
740a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteBoolean(active);
741a9edb9bfSAndrew Lindesay 
742a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("filter");
743a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
744a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("ACTIVE");
745a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("NATURALLANGUAGE");
746a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("USERRATINGSTABILITY");
747a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("COMMENT");
748a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("RATING");
749a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
750a9edb9bfSAndrew Lindesay 
751a9edb9bfSAndrew Lindesay 	if (rating >= 0) {
752a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("rating");
753a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteInteger(rating);
754a9edb9bfSAndrew Lindesay 	}
755a9edb9bfSAndrew Lindesay 
756a9edb9bfSAndrew Lindesay 	if (stability.Length() > 0) {
757a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("userRatingStabilityCode");
758a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(stability);
759a9edb9bfSAndrew Lindesay 	}
760a9edb9bfSAndrew Lindesay 
761a9edb9bfSAndrew Lindesay 	if (comment.Length() > 0) {
762a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("comment");
763a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(comment);
764a9edb9bfSAndrew Lindesay 	}
765a9edb9bfSAndrew Lindesay 
766a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
767a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
768a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
769a9edb9bfSAndrew Lindesay 
770a9edb9bfSAndrew Lindesay 	return _SendJsonRequest("userrating", requestEnvelopeData,
77188575af1SAndrew Lindesay 		_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
77288575af1SAndrew Lindesay 		message);
77319c15fecSAndrew Lindesay }
77419c15fecSAndrew Lindesay 
77519c15fecSAndrew Lindesay 
77619c15fecSAndrew Lindesay status_t
77719c15fecSAndrew Lindesay WebAppInterface::RetrieveScreenshot(const BString& code,
77819c15fecSAndrew Lindesay 	int32 width, int32 height, BDataIO* stream)
77919c15fecSAndrew Lindesay {
78001339a54SAndrew Lindesay 	return _SendRawGetRequest(
7810c1bbfe5SAndrew Lindesay 		BString("/__pkgscreenshot/") << code << ".png" << "?tw="
78201339a54SAndrew Lindesay 			<< width << "&th=" << height, stream);
78319c15fecSAndrew Lindesay }
78419c15fecSAndrew Lindesay 
78519c15fecSAndrew Lindesay 
78619c15fecSAndrew Lindesay status_t
78719c15fecSAndrew Lindesay WebAppInterface::RequestCaptcha(BMessage& message)
78819c15fecSAndrew Lindesay {
78919c15fecSAndrew Lindesay 	BString jsonString = JsonBuilder()
79019c15fecSAndrew Lindesay 		.AddValue("jsonrpc", "2.0")
79119c15fecSAndrew Lindesay 		.AddValue("id", ++fRequestIndex)
79219c15fecSAndrew Lindesay 		.AddValue("method", "generateCaptcha")
79319c15fecSAndrew Lindesay 		.AddArray("params")
79419c15fecSAndrew Lindesay 			.AddObject()
79519c15fecSAndrew Lindesay 			.EndObject()
79619c15fecSAndrew Lindesay 		.EndArray()
79719c15fecSAndrew Lindesay 	.End();
79819c15fecSAndrew Lindesay 
79919c15fecSAndrew Lindesay 	return _SendJsonRequest("captcha", jsonString, 0, message);
80019c15fecSAndrew Lindesay }
80119c15fecSAndrew Lindesay 
80219c15fecSAndrew Lindesay 
80319c15fecSAndrew Lindesay status_t
80419c15fecSAndrew Lindesay WebAppInterface::CreateUser(const BString& nickName,
80519c15fecSAndrew Lindesay 	const BString& passwordClear, const BString& email,
80619c15fecSAndrew Lindesay 	const BString& captchaToken, const BString& captchaResponse,
8070c82f64bSAndrew Lindesay 	const BString& languageCode, const BString& userUsageConditionsCode,
8080c82f64bSAndrew Lindesay 	BMessage& message)
80919c15fecSAndrew Lindesay {
8100c82f64bSAndrew Lindesay 		// BHttpRequest later takes ownership of this.
8110c82f64bSAndrew Lindesay 	BMallocIO* requestEnvelopeData = new BMallocIO();
8120c82f64bSAndrew Lindesay 	BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
81319c15fecSAndrew Lindesay 
8140c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
8150c82f64bSAndrew Lindesay 	_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter, "createUser");
8160c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("params");
8170c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
81819c15fecSAndrew Lindesay 
8190c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
82019c15fecSAndrew Lindesay 
8210c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("nickname");
8220c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteString(nickName.String());
8230c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("passwordClear");
8240c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteString(passwordClear.String());
8250c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("captchaToken");
8260c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteString(captchaToken.String());
8270c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("captchaResponse");
8280c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteString(captchaResponse.String());
8290c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
8300c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteString(languageCode.String());
8310c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("userUsageConditionsCode");
8320c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteString(userUsageConditionsCode.String());
83319c15fecSAndrew Lindesay 
8340c82f64bSAndrew Lindesay 	if (!email.IsEmpty()) {
8350c82f64bSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("email");
8360c82f64bSAndrew Lindesay 		requestEnvelopeWriter.WriteString(email.String());
8370c82f64bSAndrew Lindesay 	}
8380c82f64bSAndrew Lindesay 
8390c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
8400c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
8410c82f64bSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
8420c82f64bSAndrew Lindesay 
8430c82f64bSAndrew Lindesay 	return _SendJsonRequest("user", requestEnvelopeData,
8440c82f64bSAndrew Lindesay 		_LengthAndSeekToZero(requestEnvelopeData), 0, message);
84519c15fecSAndrew Lindesay }
84619c15fecSAndrew Lindesay 
84719c15fecSAndrew Lindesay 
84819c15fecSAndrew Lindesay status_t
84919c15fecSAndrew Lindesay WebAppInterface::AuthenticateUser(const BString& nickName,
85019c15fecSAndrew Lindesay 	const BString& passwordClear, BMessage& message)
85119c15fecSAndrew Lindesay {
85219c15fecSAndrew Lindesay 	BString jsonString = JsonBuilder()
85319c15fecSAndrew Lindesay 		.AddValue("jsonrpc", "2.0")
85419c15fecSAndrew Lindesay 		.AddValue("id", ++fRequestIndex)
85519c15fecSAndrew Lindesay 		.AddValue("method", "authenticateUser")
85619c15fecSAndrew Lindesay 		.AddArray("params")
85719c15fecSAndrew Lindesay 			.AddObject()
85819c15fecSAndrew Lindesay 				.AddValue("nickname", nickName)
85919c15fecSAndrew Lindesay 				.AddValue("passwordClear", passwordClear)
86019c15fecSAndrew Lindesay 			.EndObject()
86119c15fecSAndrew Lindesay 		.EndArray()
86219c15fecSAndrew Lindesay 	.End();
86319c15fecSAndrew Lindesay 
86419c15fecSAndrew Lindesay 	return _SendJsonRequest("user", jsonString, 0, message);
86519c15fecSAndrew Lindesay }
86619c15fecSAndrew Lindesay 
86719c15fecSAndrew Lindesay 
868a9edb9bfSAndrew Lindesay /*! JSON-RPC invocations return a response.  The response may be either
869a9edb9bfSAndrew Lindesay     a result or it may be an error depending on the response structure.
870a9edb9bfSAndrew Lindesay     If it is an error then there may be additional detail that is the
871a9edb9bfSAndrew Lindesay     error code and message.  This method will extract the error code
872a9edb9bfSAndrew Lindesay     from the response.  This method will return 0 if the payload does
873a9edb9bfSAndrew Lindesay     not look like an error.
874a9edb9bfSAndrew Lindesay */
875a9edb9bfSAndrew Lindesay 
876a9edb9bfSAndrew Lindesay int32
877a9edb9bfSAndrew Lindesay WebAppInterface::ErrorCodeFromResponse(BMessage& response)
878a9edb9bfSAndrew Lindesay {
879a9edb9bfSAndrew Lindesay 	BMessage error;
880a9edb9bfSAndrew Lindesay 	double code;
881a9edb9bfSAndrew Lindesay 
882a9edb9bfSAndrew Lindesay 	if (response.FindMessage("error", &error) == B_OK
883a9edb9bfSAndrew Lindesay 		&& error.FindDouble("code", &code) == B_OK) {
884a9edb9bfSAndrew Lindesay 		return (int32) code;
885a9edb9bfSAndrew Lindesay 	}
886a9edb9bfSAndrew Lindesay 
887a9edb9bfSAndrew Lindesay 	return 0;
888a9edb9bfSAndrew Lindesay }
889a9edb9bfSAndrew Lindesay 
890a9edb9bfSAndrew Lindesay 
89119c15fecSAndrew Lindesay // #pragma mark - private
89219c15fecSAndrew Lindesay 
89319c15fecSAndrew Lindesay 
894a9edb9bfSAndrew Lindesay void
895a9edb9bfSAndrew Lindesay WebAppInterface::_WriteStandardJsonRpcEnvelopeValues(BJsonWriter& writer,
896a9edb9bfSAndrew Lindesay 	const char* methodName)
897a9edb9bfSAndrew Lindesay {
898a9edb9bfSAndrew Lindesay 	writer.WriteObjectName("jsonrpc");
899a9edb9bfSAndrew Lindesay 	writer.WriteString("2.0");
900a9edb9bfSAndrew Lindesay 	writer.WriteObjectName("id");
901a9edb9bfSAndrew Lindesay 	writer.WriteInteger(++fRequestIndex);
902a9edb9bfSAndrew Lindesay 	writer.WriteObjectName("method");
903a9edb9bfSAndrew Lindesay 	writer.WriteString(methodName);
904a9edb9bfSAndrew Lindesay }
905a9edb9bfSAndrew Lindesay 
906a9edb9bfSAndrew Lindesay 
90719c15fecSAndrew Lindesay status_t
90888575af1SAndrew Lindesay WebAppInterface::_SendJsonRequest(const char* domain, BPositionIO* requestData,
909b45e8b1eSAndrew Lindesay 	size_t requestDataSize, uint32 flags, BMessage& reply) const
91019c15fecSAndrew Lindesay {
911*d2d4866dSAndrew Lindesay 	return _SendJsonRequest(domain, fCredentials, requestData, requestDataSize,
912*d2d4866dSAndrew Lindesay 		flags, reply);
913*d2d4866dSAndrew Lindesay }
914*d2d4866dSAndrew Lindesay 
915*d2d4866dSAndrew Lindesay 
916*d2d4866dSAndrew Lindesay status_t
917*d2d4866dSAndrew Lindesay WebAppInterface::_SendJsonRequest(const char* domain,
918*d2d4866dSAndrew Lindesay 	UserCredentials credentials, BPositionIO* requestData,
919*d2d4866dSAndrew Lindesay 	size_t requestDataSize, uint32 flags, BMessage& reply) const
920*d2d4866dSAndrew Lindesay {
92188575af1SAndrew Lindesay 	if (requestDataSize == 0) {
92288575af1SAndrew Lindesay 		if (Logger::IsInfoEnabled())
92388575af1SAndrew Lindesay 			printf("jrpc; empty request payload\n");
92488575af1SAndrew Lindesay 		return B_ERROR;
92588575af1SAndrew Lindesay 	}
92688575af1SAndrew Lindesay 
927b45e8b1eSAndrew Lindesay 	if (!ServerHelper::IsNetworkAvailable()) {
928b45e8b1eSAndrew Lindesay 		if (Logger::IsDebugEnabled()) {
929cd417b96SAndrew Lindesay 			printf("jrpc; dropping request to ...[%s] as network is not "
930b45e8b1eSAndrew Lindesay 				"available\n", domain);
931b45e8b1eSAndrew Lindesay 		}
932cd417b96SAndrew Lindesay 		delete requestData;
93354312619SAndrew Lindesay 		return HD_NETWORK_INACCESSIBLE;
934b45e8b1eSAndrew Lindesay 	}
93554312619SAndrew Lindesay 
936b45e8b1eSAndrew Lindesay 	if (ServerSettings::IsClientTooOld()) {
937b45e8b1eSAndrew Lindesay 		if (Logger::IsDebugEnabled()) {
938cd417b96SAndrew Lindesay 			printf("jrpc; dropping request to ...[%s] as client is too "
939b45e8b1eSAndrew Lindesay 				"old\n", domain);
940b45e8b1eSAndrew Lindesay 		}
941cd417b96SAndrew Lindesay 		delete requestData;
94254312619SAndrew Lindesay 		return HD_CLIENT_TOO_OLD;
943b45e8b1eSAndrew Lindesay 	}
94419c15fecSAndrew Lindesay 
9450c1bbfe5SAndrew Lindesay 	BUrl url = ServerSettings::CreateFullUrl(BString("/__api/v1/") << domain);
9460c1bbfe5SAndrew Lindesay 	bool isSecure = url.Protocol() == "https";
94719c15fecSAndrew Lindesay 
948b45e8b1eSAndrew Lindesay 	if (Logger::IsDebugEnabled()) {
949cd417b96SAndrew Lindesay 		printf("jrpc; will make request to [%s]\n",
950b45e8b1eSAndrew Lindesay 			url.UrlString().String());
951b45e8b1eSAndrew Lindesay 	}
952b45e8b1eSAndrew Lindesay 
953cd417b96SAndrew Lindesay 	// If the request payload is logged then it must be copied to local memory
954cd417b96SAndrew Lindesay 	// from the stream.  This then requires that the request data is then
955cd417b96SAndrew Lindesay 	// delivered from memory.
956cd417b96SAndrew Lindesay 
957cd417b96SAndrew Lindesay 	if (Logger::IsTraceEnabled()) {
958cd417b96SAndrew Lindesay 		printf("jrpc request; ");
95988575af1SAndrew Lindesay 		_LogPayload(requestData, requestDataSize);
960cd417b96SAndrew Lindesay 		printf("\n");
961cd417b96SAndrew Lindesay 	}
962cd417b96SAndrew Lindesay 
963f0665db4SAndrew Lindesay 	ProtocolListener listener(Logger::IsTraceEnabled());
96419c15fecSAndrew Lindesay 	BUrlContext context;
96519c15fecSAndrew Lindesay 
96619c15fecSAndrew Lindesay 	BHttpHeaders headers;
96719c15fecSAndrew Lindesay 	headers.AddHeader("Content-Type", "application/json");
96819c15fecSAndrew Lindesay 	ServerSettings::AugmentHeaders(headers);
96919c15fecSAndrew Lindesay 
97019c15fecSAndrew Lindesay 	BHttpRequest request(url, isSecure, "HTTP", &listener, &context);
97119c15fecSAndrew Lindesay 	request.SetMethod(B_HTTP_POST);
97219c15fecSAndrew Lindesay 	request.SetHeaders(headers);
97319c15fecSAndrew Lindesay 
97419c15fecSAndrew Lindesay 	// Authentication via Basic Authentication
97519c15fecSAndrew Lindesay 	// The other way would be to obtain a token and then use the Token Bearer
97619c15fecSAndrew Lindesay 	// header.
977*d2d4866dSAndrew Lindesay 	if (((flags & NEEDS_AUTHORIZATION) != 0) && credentials.IsValid()) {
978*d2d4866dSAndrew Lindesay 		BHttpAuthentication authentication(credentials.Nickname(),
979*d2d4866dSAndrew Lindesay 			credentials.PasswordClear());
98019c15fecSAndrew Lindesay 		authentication.SetMethod(B_HTTP_AUTHENTICATION_BASIC);
98119c15fecSAndrew Lindesay 		context.AddAuthentication(url, authentication);
98219c15fecSAndrew Lindesay 	}
98319c15fecSAndrew Lindesay 
984b45e8b1eSAndrew Lindesay 	request.AdoptInputData(requestData, requestDataSize);
98519c15fecSAndrew Lindesay 
98619c15fecSAndrew Lindesay 	BMallocIO replyData;
98719c15fecSAndrew Lindesay 	listener.SetDownloadIO(&replyData);
98819c15fecSAndrew Lindesay 
98919c15fecSAndrew Lindesay 	thread_id thread = request.Run();
99019c15fecSAndrew Lindesay 	wait_for_thread(thread, NULL);
99119c15fecSAndrew Lindesay 
99219c15fecSAndrew Lindesay 	const BHttpResult& result = dynamic_cast<const BHttpResult&>(
99319c15fecSAndrew Lindesay 		request.Result());
99419c15fecSAndrew Lindesay 
99519c15fecSAndrew Lindesay 	int32 statusCode = result.StatusCode();
99654312619SAndrew Lindesay 
997b45e8b1eSAndrew Lindesay 	if (Logger::IsDebugEnabled()) {
998cd417b96SAndrew Lindesay 		printf("jrpc; did receive http-status [%" B_PRId32 "] "
999b45e8b1eSAndrew Lindesay 			"from [%s]\n", statusCode, url.UrlString().String());
1000b45e8b1eSAndrew Lindesay 	}
1001b45e8b1eSAndrew Lindesay 
100254312619SAndrew Lindesay 	switch (statusCode) {
100354312619SAndrew Lindesay 		case B_HTTP_STATUS_OK:
100454312619SAndrew Lindesay 			break;
100554312619SAndrew Lindesay 
100654312619SAndrew Lindesay 		case B_HTTP_STATUS_PRECONDITION_FAILED:
100754312619SAndrew Lindesay 			ServerHelper::NotifyClientTooOld(result.Headers());
100854312619SAndrew Lindesay 			return HD_CLIENT_TOO_OLD;
100954312619SAndrew Lindesay 
101054312619SAndrew Lindesay 		default:
101166f8dcb1SAndrew Lindesay 			printf("jrpc request to endpoint [.../%s] failed with http "
101254312619SAndrew Lindesay 				"status [%" B_PRId32 "]\n", domain, statusCode);
101319c15fecSAndrew Lindesay 			return B_ERROR;
101419c15fecSAndrew Lindesay 	}
101519c15fecSAndrew Lindesay 
101666f8dcb1SAndrew Lindesay 	replyData.Seek(0, SEEK_SET);
101766f8dcb1SAndrew Lindesay 
1018cd417b96SAndrew Lindesay 	if (Logger::IsTraceEnabled()) {
1019cd417b96SAndrew Lindesay 		printf("jrpc response; ");
102088575af1SAndrew Lindesay 		_LogPayload(&replyData, replyData.BufferLength());
1021cd417b96SAndrew Lindesay 		printf("\n");
1022cd417b96SAndrew Lindesay 	}
1023cd417b96SAndrew Lindesay 
102488575af1SAndrew Lindesay 	BJsonMessageWriter jsonMessageWriter(reply);
102588575af1SAndrew Lindesay 	BJson::Parse(&replyData, &jsonMessageWriter);
102688575af1SAndrew Lindesay 	status_t status = jsonMessageWriter.ErrorStatus();
102788575af1SAndrew Lindesay 
1028f0665db4SAndrew Lindesay 	if (Logger::IsTraceEnabled() && status == B_BAD_DATA) {
1029b45e8b1eSAndrew Lindesay 		BString resultString(static_cast<const char *>(replyData.Buffer()),
1030b45e8b1eSAndrew Lindesay 			replyData.BufferLength());
1031b45e8b1eSAndrew Lindesay 		printf("Parser choked on JSON:\n%s\n", resultString.String());
103219c15fecSAndrew Lindesay 	}
103319c15fecSAndrew Lindesay 	return status;
103419c15fecSAndrew Lindesay }
1035b45e8b1eSAndrew Lindesay 
1036b45e8b1eSAndrew Lindesay 
1037b45e8b1eSAndrew Lindesay status_t
103888575af1SAndrew Lindesay WebAppInterface::_SendJsonRequest(const char* domain, const BString& jsonString,
1039b45e8b1eSAndrew Lindesay 	uint32 flags, BMessage& reply) const
1040b45e8b1eSAndrew Lindesay {
1041a9edb9bfSAndrew Lindesay 	// gets 'adopted' by the subsequent http request.
104288575af1SAndrew Lindesay 	BMemoryIO* data = new BMemoryIO(jsonString.String(),
104388575af1SAndrew Lindesay 		jsonString.Length() - 1);
1044b45e8b1eSAndrew Lindesay 
1045b45e8b1eSAndrew Lindesay 	return _SendJsonRequest(domain, data, jsonString.Length() - 1, flags,
1046b45e8b1eSAndrew Lindesay 		reply);
1047b45e8b1eSAndrew Lindesay }
1048cd417b96SAndrew Lindesay 
1049cd417b96SAndrew Lindesay 
105001339a54SAndrew Lindesay status_t
105101339a54SAndrew Lindesay WebAppInterface::_SendRawGetRequest(const BString urlPathComponents,
105201339a54SAndrew Lindesay 	BDataIO* stream)
105301339a54SAndrew Lindesay {
105401339a54SAndrew Lindesay 	BUrl url = ServerSettings::CreateFullUrl(urlPathComponents);
105501339a54SAndrew Lindesay 	bool isSecure = url.Protocol() == "https";
105601339a54SAndrew Lindesay 
105701339a54SAndrew Lindesay 	ProtocolListener listener(Logger::IsTraceEnabled());
105801339a54SAndrew Lindesay 	listener.SetDownloadIO(stream);
105901339a54SAndrew Lindesay 
106001339a54SAndrew Lindesay 	BHttpHeaders headers;
106101339a54SAndrew Lindesay 	ServerSettings::AugmentHeaders(headers);
106201339a54SAndrew Lindesay 
106301339a54SAndrew Lindesay 	BHttpRequest request(url, isSecure, "HTTP", &listener);
106401339a54SAndrew Lindesay 	request.SetMethod(B_HTTP_GET);
106501339a54SAndrew Lindesay 	request.SetHeaders(headers);
106601339a54SAndrew Lindesay 
106701339a54SAndrew Lindesay 	thread_id thread = request.Run();
106801339a54SAndrew Lindesay 	wait_for_thread(thread, NULL);
106901339a54SAndrew Lindesay 
107001339a54SAndrew Lindesay 	const BHttpResult& result = dynamic_cast<const BHttpResult&>(
107101339a54SAndrew Lindesay 		request.Result());
107201339a54SAndrew Lindesay 
107301339a54SAndrew Lindesay 	int32 statusCode = result.StatusCode();
107401339a54SAndrew Lindesay 
107501339a54SAndrew Lindesay 	if (statusCode == 200)
107601339a54SAndrew Lindesay 		return B_OK;
107701339a54SAndrew Lindesay 
107801339a54SAndrew Lindesay 	fprintf(stderr, "failed to get data from '%s': %" B_PRIi32 "\n",
107901339a54SAndrew Lindesay 		url.UrlString().String(), statusCode);
108001339a54SAndrew Lindesay 	return B_ERROR;
108101339a54SAndrew Lindesay }
108201339a54SAndrew Lindesay 
108301339a54SAndrew Lindesay 
1084cd417b96SAndrew Lindesay void
108588575af1SAndrew Lindesay WebAppInterface::_LogPayload(BPositionIO* requestData, size_t size)
1086cd417b96SAndrew Lindesay {
108766f8dcb1SAndrew Lindesay 	off_t requestDataOffset = requestData->Position();
108888575af1SAndrew Lindesay 	char buffer[LOG_PAYLOAD_LIMIT];
108988575af1SAndrew Lindesay 
1090cd417b96SAndrew Lindesay 	if (size > LOG_PAYLOAD_LIMIT)
1091cd417b96SAndrew Lindesay 		size = LOG_PAYLOAD_LIMIT;
1092cd417b96SAndrew Lindesay 
109388575af1SAndrew Lindesay 	if (B_OK != requestData->ReadExactly(buffer, size)) {
109488575af1SAndrew Lindesay 		printf("jrpc; error logging payload\n");
109588575af1SAndrew Lindesay 	} else {
109688575af1SAndrew Lindesay 		for (uint32 i = 0; i < size; i++) {
109788575af1SAndrew Lindesay     		bool esc = buffer[i] > 126 ||
109888575af1SAndrew Lindesay     			(buffer[i] < 0x20 && buffer[i] != 0x0a);
1099cd417b96SAndrew Lindesay 
1100cd417b96SAndrew Lindesay     		if (esc)
110188575af1SAndrew Lindesay     			printf("\\u%02x", buffer[i]);
1102cd417b96SAndrew Lindesay     		else
110388575af1SAndrew Lindesay     			putchar(buffer[i]);
1104cd417b96SAndrew Lindesay     	}
1105cd417b96SAndrew Lindesay 
1106cd417b96SAndrew Lindesay     	if (size == LOG_PAYLOAD_LIMIT)
1107cd417b96SAndrew Lindesay     		printf("...(continues)");
1108cd417b96SAndrew Lindesay 	}
110966f8dcb1SAndrew Lindesay 
111066f8dcb1SAndrew Lindesay 	requestData->Seek(requestDataOffset, SEEK_SET);
111188575af1SAndrew Lindesay }
111288575af1SAndrew Lindesay 
111388575af1SAndrew Lindesay 
111488575af1SAndrew Lindesay /*! This will get the position of the data to get the length an then sets the
111588575af1SAndrew Lindesay     offset to zero so that it can be re-read for reading the payload in to log
111688575af1SAndrew Lindesay     or send.
111788575af1SAndrew Lindesay */
111888575af1SAndrew Lindesay 
111988575af1SAndrew Lindesay off_t
112088575af1SAndrew Lindesay WebAppInterface::_LengthAndSeekToZero(BPositionIO* data)
112188575af1SAndrew Lindesay {
112288575af1SAndrew Lindesay 	off_t dataSize = data->Position();
112388575af1SAndrew Lindesay     data->Seek(0, SEEK_SET);
112488575af1SAndrew Lindesay     return dataSize;
112588575af1SAndrew Lindesay }
1126