xref: /haiku/src/apps/haikudepot/server/WebAppInterface.cpp (revision f85e030047e96fc3ae57af2fff35e6059d71ea77)
119c15fecSAndrew Lindesay /*
219c15fecSAndrew Lindesay  * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
3*f85e0300SAndrew 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 	:
28019c15fecSAndrew Lindesay 	fUsername(other.fUsername),
281*f85e0300SAndrew Lindesay 	fPassword(other.fPassword)
28219c15fecSAndrew Lindesay {
28319c15fecSAndrew Lindesay }
28419c15fecSAndrew Lindesay 
28519c15fecSAndrew Lindesay 
28619c15fecSAndrew Lindesay WebAppInterface::~WebAppInterface()
28719c15fecSAndrew Lindesay {
28819c15fecSAndrew Lindesay }
28919c15fecSAndrew Lindesay 
29019c15fecSAndrew Lindesay 
29119c15fecSAndrew Lindesay WebAppInterface&
29219c15fecSAndrew Lindesay WebAppInterface::operator=(const WebAppInterface& other)
29319c15fecSAndrew Lindesay {
29419c15fecSAndrew Lindesay 	if (this == &other)
29519c15fecSAndrew Lindesay 		return *this;
29619c15fecSAndrew Lindesay 	fUsername = other.fUsername;
29719c15fecSAndrew Lindesay 	fPassword = other.fPassword;
29819c15fecSAndrew Lindesay 	return *this;
29919c15fecSAndrew Lindesay }
30019c15fecSAndrew Lindesay 
30119c15fecSAndrew Lindesay 
30219c15fecSAndrew Lindesay void
30319c15fecSAndrew Lindesay WebAppInterface::SetAuthorization(const BString& username,
30419c15fecSAndrew Lindesay 	const BString& password)
30519c15fecSAndrew Lindesay {
30619c15fecSAndrew Lindesay 	fUsername = username;
30719c15fecSAndrew Lindesay 	fPassword = password;
30819c15fecSAndrew Lindesay }
30919c15fecSAndrew Lindesay 
31019c15fecSAndrew Lindesay 
31119c15fecSAndrew Lindesay status_t
31280a272eeSAndrew Lindesay WebAppInterface::GetChangelog(const BString& packageName, BMessage& message)
31319c15fecSAndrew Lindesay {
31419c15fecSAndrew Lindesay 	BString jsonString = JsonBuilder()
31519c15fecSAndrew Lindesay 		.AddValue("jsonrpc", "2.0")
31619c15fecSAndrew Lindesay 		.AddValue("id", ++fRequestIndex)
31780a272eeSAndrew Lindesay 		.AddValue("method", "getPkgChangelog")
31819c15fecSAndrew Lindesay 		.AddArray("params")
31919c15fecSAndrew Lindesay 			.AddObject()
32080a272eeSAndrew Lindesay 				.AddValue("pkgName", packageName)
32119c15fecSAndrew Lindesay 			.EndObject()
32219c15fecSAndrew Lindesay 		.EndArray()
32319c15fecSAndrew Lindesay 	.End();
32419c15fecSAndrew Lindesay 
32519c15fecSAndrew Lindesay 	return _SendJsonRequest("pkg", jsonString, 0, message);
32619c15fecSAndrew Lindesay }
32719c15fecSAndrew Lindesay 
32819c15fecSAndrew Lindesay 
32919c15fecSAndrew Lindesay status_t
33019c15fecSAndrew Lindesay WebAppInterface::RetrieveUserRatings(const BString& packageName,
33119c15fecSAndrew Lindesay 	const BString& architecture, int resultOffset, int maxResults,
33219c15fecSAndrew Lindesay 	BMessage& message)
33319c15fecSAndrew Lindesay {
33419c15fecSAndrew Lindesay 	BString jsonString = JsonBuilder()
33519c15fecSAndrew Lindesay 		.AddValue("jsonrpc", "2.0")
33619c15fecSAndrew Lindesay 		.AddValue("id", ++fRequestIndex)
33719c15fecSAndrew Lindesay 		.AddValue("method", "searchUserRatings")
33819c15fecSAndrew Lindesay 		.AddArray("params")
33919c15fecSAndrew Lindesay 			.AddObject()
34019c15fecSAndrew Lindesay 				.AddValue("pkgName", packageName)
34119c15fecSAndrew Lindesay 				.AddValue("pkgVersionArchitectureCode", architecture)
34219c15fecSAndrew Lindesay 				.AddValue("offset", resultOffset)
34319c15fecSAndrew Lindesay 				.AddValue("limit", maxResults)
34419c15fecSAndrew Lindesay 			.EndObject()
34519c15fecSAndrew Lindesay 		.EndArray()
34619c15fecSAndrew Lindesay 	.End();
34719c15fecSAndrew Lindesay 
34819c15fecSAndrew Lindesay 	return _SendJsonRequest("userrating", jsonString, 0, message);
34919c15fecSAndrew Lindesay }
35019c15fecSAndrew Lindesay 
35119c15fecSAndrew Lindesay 
35219c15fecSAndrew Lindesay status_t
35319c15fecSAndrew Lindesay WebAppInterface::RetrieveUserRating(const BString& packageName,
35419c15fecSAndrew Lindesay 	const BPackageVersion& version, const BString& architecture,
35519c15fecSAndrew Lindesay 	const BString &repositoryCode, const BString& username,
35619c15fecSAndrew Lindesay 	BMessage& message)
35719c15fecSAndrew Lindesay {
358a9edb9bfSAndrew Lindesay 		// BHttpRequest later takes ownership of this.
359a9edb9bfSAndrew Lindesay 	BMallocIO* requestEnvelopeData = new BMallocIO();
360a9edb9bfSAndrew Lindesay 	BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
36119c15fecSAndrew Lindesay 
362a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
363a9edb9bfSAndrew Lindesay 	_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
364a9edb9bfSAndrew Lindesay 		"getUserRatingByUserAndPkgVersion");
365a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("params");
366a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
367a9edb9bfSAndrew Lindesay 
368a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
369a9edb9bfSAndrew Lindesay 
370a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("userNickname");
371a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(username.String());
372a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgName");
373a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(packageName.String());
374a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode");
375a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(architecture.String());
376a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("repositoryCode");
377a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(repositoryCode.String());
378a9edb9bfSAndrew Lindesay 
379a9edb9bfSAndrew Lindesay 	if (version.Major().Length() > 0) {
380a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMajor");
381a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Major().String());
382a9edb9bfSAndrew Lindesay 	}
383a9edb9bfSAndrew Lindesay 
384a9edb9bfSAndrew Lindesay 	if (version.Minor().Length() > 0) {
385a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMinor");
386a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Minor().String());
387a9edb9bfSAndrew Lindesay 	}
388a9edb9bfSAndrew Lindesay 
389a9edb9bfSAndrew Lindesay 	if (version.Micro().Length() > 0) {
390a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMicro");
391a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Micro().String());
392a9edb9bfSAndrew Lindesay 	}
393a9edb9bfSAndrew Lindesay 
394a9edb9bfSAndrew Lindesay 	if (version.PreRelease().Length() > 0) {
395a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionPreRelease");
396a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.PreRelease().String());
397a9edb9bfSAndrew Lindesay 	}
398a9edb9bfSAndrew Lindesay 
399a9edb9bfSAndrew Lindesay 	if (version.Revision() != 0) {
400a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionRevision");
401a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteInteger(version.Revision());
402a9edb9bfSAndrew Lindesay 	}
403a9edb9bfSAndrew Lindesay 
404a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
405a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
406a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
407a9edb9bfSAndrew Lindesay 
408a9edb9bfSAndrew Lindesay 	return _SendJsonRequest("userrating", requestEnvelopeData,
40988575af1SAndrew Lindesay 		_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
41088575af1SAndrew Lindesay 		message);
41119c15fecSAndrew Lindesay }
41219c15fecSAndrew Lindesay 
41319c15fecSAndrew Lindesay 
41419c15fecSAndrew Lindesay status_t
41519c15fecSAndrew Lindesay WebAppInterface::CreateUserRating(const BString& packageName,
416a9edb9bfSAndrew Lindesay 	const BPackageVersion& version,
41719c15fecSAndrew Lindesay 	const BString& architecture, const BString& repositoryCode,
41819c15fecSAndrew Lindesay 	const BString& languageCode, const BString& comment,
41919c15fecSAndrew Lindesay 	const BString& stability, int rating, BMessage& message)
42019c15fecSAndrew Lindesay {
421a9edb9bfSAndrew Lindesay 		// BHttpRequest later takes ownership of this.
422a9edb9bfSAndrew Lindesay 	BMallocIO* requestEnvelopeData = new BMallocIO();
423a9edb9bfSAndrew Lindesay 	BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
42419c15fecSAndrew Lindesay 
425a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
426a9edb9bfSAndrew Lindesay 	_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
427a9edb9bfSAndrew Lindesay 		"createUserRating");
428a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("params");
429a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
430a9edb9bfSAndrew Lindesay 
431a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
432a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgName");
433a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(packageName.String());
434a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode");
435a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(architecture.String());
436a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("repositoryCode");
437a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(repositoryCode.String());
438a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
439a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(languageCode.String());
440a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("pkgVersionType");
441a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("SPECIFIC");
442a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("userNickname");
443a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(fUsername.String());
444a9edb9bfSAndrew Lindesay 
445a9edb9bfSAndrew Lindesay 	if (!version.Major().IsEmpty()) {
446a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMajor");
447a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Major());
448a9edb9bfSAndrew Lindesay 	}
449a9edb9bfSAndrew Lindesay 
450a9edb9bfSAndrew Lindesay 	if (!version.Minor().IsEmpty()) {
451a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMinor");
452a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Minor());
453a9edb9bfSAndrew Lindesay 	}
454a9edb9bfSAndrew Lindesay 
455a9edb9bfSAndrew Lindesay 	if (!version.Micro().IsEmpty()) {
456a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionMicro");
457a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.Micro());
458a9edb9bfSAndrew Lindesay 	}
459a9edb9bfSAndrew Lindesay 
460a9edb9bfSAndrew Lindesay 	if (!version.PreRelease().IsEmpty()) {
461a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionPreRelease");
462a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(version.PreRelease());
463a9edb9bfSAndrew Lindesay 	}
464a9edb9bfSAndrew Lindesay 
465a9edb9bfSAndrew Lindesay 	if (version.Revision() != 0) {
466a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("pkgVersionRevision");
467a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteInteger(version.Revision());
468a9edb9bfSAndrew Lindesay 	}
469a9edb9bfSAndrew Lindesay 
470a9edb9bfSAndrew Lindesay 	if (rating > 0.0f) {
471a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("rating");
472a9edb9bfSAndrew Lindesay     	requestEnvelopeWriter.WriteInteger(rating);
473a9edb9bfSAndrew Lindesay 	}
474a9edb9bfSAndrew Lindesay 
475a9edb9bfSAndrew Lindesay 	if (stability.Length() > 0) {
476a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("userRatingStabilityCode");
477a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(stability);
478a9edb9bfSAndrew Lindesay 	}
479a9edb9bfSAndrew Lindesay 
480a9edb9bfSAndrew Lindesay 	if (comment.Length() > 0) {
481a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("comment");
482a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(comment.String());
483a9edb9bfSAndrew Lindesay 	}
484a9edb9bfSAndrew Lindesay 
485a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
486a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
487a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
488a9edb9bfSAndrew Lindesay 
489a9edb9bfSAndrew Lindesay 	return _SendJsonRequest("userrating", requestEnvelopeData,
49088575af1SAndrew Lindesay 		_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
49188575af1SAndrew Lindesay 		message);
49219c15fecSAndrew Lindesay }
49319c15fecSAndrew Lindesay 
49419c15fecSAndrew Lindesay 
49519c15fecSAndrew Lindesay status_t
49619c15fecSAndrew Lindesay WebAppInterface::UpdateUserRating(const BString& ratingID,
49719c15fecSAndrew Lindesay 	const BString& languageCode, const BString& comment,
49819c15fecSAndrew Lindesay 	const BString& stability, int rating, bool active, BMessage& message)
49919c15fecSAndrew Lindesay {
500a9edb9bfSAndrew Lindesay 		// BHttpRequest later takes ownership of this.
501a9edb9bfSAndrew Lindesay 	BMallocIO* requestEnvelopeData = new BMallocIO();
502a9edb9bfSAndrew Lindesay 	BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
50319c15fecSAndrew Lindesay 
504a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
505a9edb9bfSAndrew Lindesay 	_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
506a9edb9bfSAndrew Lindesay 		"updateUserRating");
507a9edb9bfSAndrew Lindesay 
508a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("params");
509a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
510a9edb9bfSAndrew Lindesay 
511a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectStart();
512a9edb9bfSAndrew Lindesay 
513a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("code");
514a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(ratingID.String());
515a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
516a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString(languageCode.String());
517a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("active");
518a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteBoolean(active);
519a9edb9bfSAndrew Lindesay 
520a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectName("filter");
521a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayStart();
522a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("ACTIVE");
523a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("NATURALLANGUAGE");
524a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("USERRATINGSTABILITY");
525a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("COMMENT");
526a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteString("RATING");
527a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
528a9edb9bfSAndrew Lindesay 
529a9edb9bfSAndrew Lindesay 	if (rating >= 0) {
530a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("rating");
531a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteInteger(rating);
532a9edb9bfSAndrew Lindesay 	}
533a9edb9bfSAndrew Lindesay 
534a9edb9bfSAndrew Lindesay 	if (stability.Length() > 0) {
535a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("userRatingStabilityCode");
536a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(stability);
537a9edb9bfSAndrew Lindesay 	}
538a9edb9bfSAndrew Lindesay 
539a9edb9bfSAndrew Lindesay 	if (comment.Length() > 0) {
540a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteObjectName("comment");
541a9edb9bfSAndrew Lindesay 		requestEnvelopeWriter.WriteString(comment);
542a9edb9bfSAndrew Lindesay 	}
543a9edb9bfSAndrew Lindesay 
544a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
545a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteArrayEnd();
546a9edb9bfSAndrew Lindesay 	requestEnvelopeWriter.WriteObjectEnd();
547a9edb9bfSAndrew Lindesay 
548a9edb9bfSAndrew Lindesay 	return _SendJsonRequest("userrating", requestEnvelopeData,
54988575af1SAndrew Lindesay 		_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
55088575af1SAndrew Lindesay 		message);
55119c15fecSAndrew Lindesay }
55219c15fecSAndrew Lindesay 
55319c15fecSAndrew Lindesay 
55419c15fecSAndrew Lindesay status_t
55519c15fecSAndrew Lindesay WebAppInterface::RetrieveScreenshot(const BString& code,
55619c15fecSAndrew Lindesay 	int32 width, int32 height, BDataIO* stream)
55719c15fecSAndrew Lindesay {
5580c1bbfe5SAndrew Lindesay 	BUrl url = ServerSettings::CreateFullUrl(
5590c1bbfe5SAndrew Lindesay 		BString("/__pkgscreenshot/") << code << ".png" << "?tw="
5600c1bbfe5SAndrew Lindesay 			<< width << "&th=" << height);
56119c15fecSAndrew Lindesay 
5620c1bbfe5SAndrew Lindesay 	bool isSecure = url.Protocol() == "https";
56319c15fecSAndrew Lindesay 
564f0665db4SAndrew Lindesay 	ProtocolListener listener(Logger::IsTraceEnabled());
56519c15fecSAndrew Lindesay 	listener.SetDownloadIO(stream);
56619c15fecSAndrew Lindesay 
56719c15fecSAndrew Lindesay 	BHttpHeaders headers;
56819c15fecSAndrew Lindesay 	ServerSettings::AugmentHeaders(headers);
56919c15fecSAndrew Lindesay 
57019c15fecSAndrew Lindesay 	BHttpRequest request(url, isSecure, "HTTP", &listener);
57119c15fecSAndrew Lindesay 	request.SetMethod(B_HTTP_GET);
57219c15fecSAndrew Lindesay 	request.SetHeaders(headers);
57319c15fecSAndrew Lindesay 
57419c15fecSAndrew Lindesay 	thread_id thread = request.Run();
57519c15fecSAndrew Lindesay 	wait_for_thread(thread, NULL);
57619c15fecSAndrew Lindesay 
57719c15fecSAndrew Lindesay 	const BHttpResult& result = dynamic_cast<const BHttpResult&>(
57819c15fecSAndrew Lindesay 		request.Result());
57919c15fecSAndrew Lindesay 
58019c15fecSAndrew Lindesay 	int32 statusCode = result.StatusCode();
58119c15fecSAndrew Lindesay 
58219c15fecSAndrew Lindesay 	if (statusCode == 200)
58319c15fecSAndrew Lindesay 		return B_OK;
58419c15fecSAndrew Lindesay 
58519c15fecSAndrew Lindesay 	fprintf(stderr, "failed to get screenshot from '%s': %" B_PRIi32 "\n",
5860c1bbfe5SAndrew Lindesay 		url.UrlString().String(), statusCode);
58719c15fecSAndrew Lindesay 	return B_ERROR;
58819c15fecSAndrew Lindesay }
58919c15fecSAndrew Lindesay 
59019c15fecSAndrew Lindesay 
59119c15fecSAndrew Lindesay status_t
59219c15fecSAndrew Lindesay WebAppInterface::RequestCaptcha(BMessage& message)
59319c15fecSAndrew Lindesay {
59419c15fecSAndrew Lindesay 	BString jsonString = JsonBuilder()
59519c15fecSAndrew Lindesay 		.AddValue("jsonrpc", "2.0")
59619c15fecSAndrew Lindesay 		.AddValue("id", ++fRequestIndex)
59719c15fecSAndrew Lindesay 		.AddValue("method", "generateCaptcha")
59819c15fecSAndrew Lindesay 		.AddArray("params")
59919c15fecSAndrew Lindesay 			.AddObject()
60019c15fecSAndrew Lindesay 			.EndObject()
60119c15fecSAndrew Lindesay 		.EndArray()
60219c15fecSAndrew Lindesay 	.End();
60319c15fecSAndrew Lindesay 
60419c15fecSAndrew Lindesay 	return _SendJsonRequest("captcha", jsonString, 0, message);
60519c15fecSAndrew Lindesay }
60619c15fecSAndrew Lindesay 
60719c15fecSAndrew Lindesay 
60819c15fecSAndrew Lindesay status_t
60919c15fecSAndrew Lindesay WebAppInterface::CreateUser(const BString& nickName,
61019c15fecSAndrew Lindesay 	const BString& passwordClear, const BString& email,
61119c15fecSAndrew Lindesay 	const BString& captchaToken, const BString& captchaResponse,
61219c15fecSAndrew Lindesay 	const BString& languageCode, BMessage& message)
61319c15fecSAndrew Lindesay {
61419c15fecSAndrew Lindesay 	JsonBuilder builder;
61519c15fecSAndrew Lindesay 	builder
61619c15fecSAndrew Lindesay 		.AddValue("jsonrpc", "2.0")
61719c15fecSAndrew Lindesay 		.AddValue("id", ++fRequestIndex)
61819c15fecSAndrew Lindesay 		.AddValue("method", "createUser")
61919c15fecSAndrew Lindesay 		.AddArray("params")
62019c15fecSAndrew Lindesay 			.AddObject()
62119c15fecSAndrew Lindesay 				.AddValue("nickname", nickName)
62219c15fecSAndrew Lindesay 				.AddValue("passwordClear", passwordClear);
62319c15fecSAndrew Lindesay 
62419c15fecSAndrew Lindesay 				if (!email.IsEmpty())
62519c15fecSAndrew Lindesay 					builder.AddValue("email", email);
62619c15fecSAndrew Lindesay 
62719c15fecSAndrew Lindesay 				builder.AddValue("captchaToken", captchaToken)
62819c15fecSAndrew Lindesay 				.AddValue("captchaResponse", captchaResponse)
62919c15fecSAndrew Lindesay 				.AddValue("naturalLanguageCode", languageCode)
63019c15fecSAndrew Lindesay 			.EndObject()
63119c15fecSAndrew Lindesay 		.EndArray()
63219c15fecSAndrew Lindesay 	;
63319c15fecSAndrew Lindesay 
63419c15fecSAndrew Lindesay 	BString jsonString = builder.End();
63519c15fecSAndrew Lindesay 
63619c15fecSAndrew Lindesay 	return _SendJsonRequest("user", jsonString, 0, message);
63719c15fecSAndrew Lindesay }
63819c15fecSAndrew Lindesay 
63919c15fecSAndrew Lindesay 
64019c15fecSAndrew Lindesay status_t
64119c15fecSAndrew Lindesay WebAppInterface::AuthenticateUser(const BString& nickName,
64219c15fecSAndrew Lindesay 	const BString& passwordClear, BMessage& message)
64319c15fecSAndrew Lindesay {
64419c15fecSAndrew Lindesay 	BString jsonString = JsonBuilder()
64519c15fecSAndrew Lindesay 		.AddValue("jsonrpc", "2.0")
64619c15fecSAndrew Lindesay 		.AddValue("id", ++fRequestIndex)
64719c15fecSAndrew Lindesay 		.AddValue("method", "authenticateUser")
64819c15fecSAndrew Lindesay 		.AddArray("params")
64919c15fecSAndrew Lindesay 			.AddObject()
65019c15fecSAndrew Lindesay 				.AddValue("nickname", nickName)
65119c15fecSAndrew Lindesay 				.AddValue("passwordClear", passwordClear)
65219c15fecSAndrew Lindesay 			.EndObject()
65319c15fecSAndrew Lindesay 		.EndArray()
65419c15fecSAndrew Lindesay 	.End();
65519c15fecSAndrew Lindesay 
65619c15fecSAndrew Lindesay 	return _SendJsonRequest("user", jsonString, 0, message);
65719c15fecSAndrew Lindesay }
65819c15fecSAndrew Lindesay 
65919c15fecSAndrew Lindesay 
660a9edb9bfSAndrew Lindesay /*! JSON-RPC invocations return a response.  The response may be either
661a9edb9bfSAndrew Lindesay     a result or it may be an error depending on the response structure.
662a9edb9bfSAndrew Lindesay     If it is an error then there may be additional detail that is the
663a9edb9bfSAndrew Lindesay     error code and message.  This method will extract the error code
664a9edb9bfSAndrew Lindesay     from the response.  This method will return 0 if the payload does
665a9edb9bfSAndrew Lindesay     not look like an error.
666a9edb9bfSAndrew Lindesay */
667a9edb9bfSAndrew Lindesay 
668a9edb9bfSAndrew Lindesay int32
669a9edb9bfSAndrew Lindesay WebAppInterface::ErrorCodeFromResponse(BMessage& response)
670a9edb9bfSAndrew Lindesay {
671a9edb9bfSAndrew Lindesay 	BMessage error;
672a9edb9bfSAndrew Lindesay 	double code;
673a9edb9bfSAndrew Lindesay 
674a9edb9bfSAndrew Lindesay 	if (response.FindMessage("error", &error) == B_OK
675a9edb9bfSAndrew Lindesay 		&& error.FindDouble("code", &code) == B_OK) {
676a9edb9bfSAndrew Lindesay 		return (int32) code;
677a9edb9bfSAndrew Lindesay 	}
678a9edb9bfSAndrew Lindesay 
679a9edb9bfSAndrew Lindesay 	return 0;
680a9edb9bfSAndrew Lindesay }
681a9edb9bfSAndrew Lindesay 
682a9edb9bfSAndrew Lindesay 
68319c15fecSAndrew Lindesay // #pragma mark - private
68419c15fecSAndrew Lindesay 
68519c15fecSAndrew Lindesay 
686a9edb9bfSAndrew Lindesay void
687a9edb9bfSAndrew Lindesay WebAppInterface::_WriteStandardJsonRpcEnvelopeValues(BJsonWriter& writer,
688a9edb9bfSAndrew Lindesay 	const char* methodName)
689a9edb9bfSAndrew Lindesay {
690a9edb9bfSAndrew Lindesay 	writer.WriteObjectName("jsonrpc");
691a9edb9bfSAndrew Lindesay 	writer.WriteString("2.0");
692a9edb9bfSAndrew Lindesay 	writer.WriteObjectName("id");
693a9edb9bfSAndrew Lindesay 	writer.WriteInteger(++fRequestIndex);
694a9edb9bfSAndrew Lindesay 	writer.WriteObjectName("method");
695a9edb9bfSAndrew Lindesay 	writer.WriteString(methodName);
696a9edb9bfSAndrew Lindesay }
697a9edb9bfSAndrew Lindesay 
698a9edb9bfSAndrew Lindesay 
69919c15fecSAndrew Lindesay status_t
70088575af1SAndrew Lindesay WebAppInterface::_SendJsonRequest(const char* domain, BPositionIO* requestData,
701b45e8b1eSAndrew Lindesay 	size_t requestDataSize, uint32 flags, BMessage& reply) const
70219c15fecSAndrew Lindesay {
70388575af1SAndrew Lindesay 	if (requestDataSize == 0) {
70488575af1SAndrew Lindesay 		if (Logger::IsInfoEnabled())
70588575af1SAndrew Lindesay 			printf("jrpc; empty request payload\n");
70688575af1SAndrew Lindesay 		return B_ERROR;
70788575af1SAndrew Lindesay 	}
70888575af1SAndrew Lindesay 
709b45e8b1eSAndrew Lindesay 	if (!ServerHelper::IsNetworkAvailable()) {
710b45e8b1eSAndrew Lindesay 		if (Logger::IsDebugEnabled()) {
711cd417b96SAndrew Lindesay 			printf("jrpc; dropping request to ...[%s] as network is not "
712b45e8b1eSAndrew Lindesay 				"available\n", domain);
713b45e8b1eSAndrew Lindesay 		}
714cd417b96SAndrew Lindesay 		delete requestData;
71554312619SAndrew Lindesay 		return HD_NETWORK_INACCESSIBLE;
716b45e8b1eSAndrew Lindesay 	}
71754312619SAndrew Lindesay 
718b45e8b1eSAndrew Lindesay 	if (ServerSettings::IsClientTooOld()) {
719b45e8b1eSAndrew Lindesay 		if (Logger::IsDebugEnabled()) {
720cd417b96SAndrew Lindesay 			printf("jrpc; dropping request to ...[%s] as client is too "
721b45e8b1eSAndrew Lindesay 				"old\n", domain);
722b45e8b1eSAndrew Lindesay 		}
723cd417b96SAndrew Lindesay 		delete requestData;
72454312619SAndrew Lindesay 		return HD_CLIENT_TOO_OLD;
725b45e8b1eSAndrew Lindesay 	}
72619c15fecSAndrew Lindesay 
7270c1bbfe5SAndrew Lindesay 	BUrl url = ServerSettings::CreateFullUrl(BString("/__api/v1/") << domain);
7280c1bbfe5SAndrew Lindesay 	bool isSecure = url.Protocol() == "https";
72919c15fecSAndrew Lindesay 
730b45e8b1eSAndrew Lindesay 	if (Logger::IsDebugEnabled()) {
731cd417b96SAndrew Lindesay 		printf("jrpc; will make request to [%s]\n",
732b45e8b1eSAndrew Lindesay 			url.UrlString().String());
733b45e8b1eSAndrew Lindesay 	}
734b45e8b1eSAndrew Lindesay 
735cd417b96SAndrew Lindesay 	// If the request payload is logged then it must be copied to local memory
736cd417b96SAndrew Lindesay 	// from the stream.  This then requires that the request data is then
737cd417b96SAndrew Lindesay 	// delivered from memory.
738cd417b96SAndrew Lindesay 
739cd417b96SAndrew Lindesay 	if (Logger::IsTraceEnabled()) {
740cd417b96SAndrew Lindesay 		printf("jrpc request; ");
74188575af1SAndrew Lindesay 		_LogPayload(requestData, requestDataSize);
742cd417b96SAndrew Lindesay 		printf("\n");
743cd417b96SAndrew Lindesay 	}
744cd417b96SAndrew Lindesay 
745f0665db4SAndrew Lindesay 	ProtocolListener listener(Logger::IsTraceEnabled());
74619c15fecSAndrew Lindesay 	BUrlContext context;
74719c15fecSAndrew Lindesay 
74819c15fecSAndrew Lindesay 	BHttpHeaders headers;
74919c15fecSAndrew Lindesay 	headers.AddHeader("Content-Type", "application/json");
75019c15fecSAndrew Lindesay 	ServerSettings::AugmentHeaders(headers);
75119c15fecSAndrew Lindesay 
75219c15fecSAndrew Lindesay 	BHttpRequest request(url, isSecure, "HTTP", &listener, &context);
75319c15fecSAndrew Lindesay 	request.SetMethod(B_HTTP_POST);
75419c15fecSAndrew Lindesay 	request.SetHeaders(headers);
75519c15fecSAndrew Lindesay 
75619c15fecSAndrew Lindesay 	// Authentication via Basic Authentication
75719c15fecSAndrew Lindesay 	// The other way would be to obtain a token and then use the Token Bearer
75819c15fecSAndrew Lindesay 	// header.
75919c15fecSAndrew Lindesay 	if ((flags & NEEDS_AUTHORIZATION) != 0
76019c15fecSAndrew Lindesay 		&& !fUsername.IsEmpty() && !fPassword.IsEmpty()) {
76119c15fecSAndrew Lindesay 		BHttpAuthentication authentication(fUsername, fPassword);
76219c15fecSAndrew Lindesay 		authentication.SetMethod(B_HTTP_AUTHENTICATION_BASIC);
76319c15fecSAndrew Lindesay 		context.AddAuthentication(url, authentication);
76419c15fecSAndrew Lindesay 	}
76519c15fecSAndrew Lindesay 
76688575af1SAndrew Lindesay 
767b45e8b1eSAndrew Lindesay 	request.AdoptInputData(requestData, requestDataSize);
76819c15fecSAndrew Lindesay 
76919c15fecSAndrew Lindesay 	BMallocIO replyData;
77019c15fecSAndrew Lindesay 	listener.SetDownloadIO(&replyData);
77119c15fecSAndrew Lindesay 
77219c15fecSAndrew Lindesay 	thread_id thread = request.Run();
77319c15fecSAndrew Lindesay 	wait_for_thread(thread, NULL);
77419c15fecSAndrew Lindesay 
77519c15fecSAndrew Lindesay 	const BHttpResult& result = dynamic_cast<const BHttpResult&>(
77619c15fecSAndrew Lindesay 		request.Result());
77719c15fecSAndrew Lindesay 
77819c15fecSAndrew Lindesay 	int32 statusCode = result.StatusCode();
77954312619SAndrew Lindesay 
780b45e8b1eSAndrew Lindesay 	if (Logger::IsDebugEnabled()) {
781cd417b96SAndrew Lindesay 		printf("jrpc; did receive http-status [%" B_PRId32 "] "
782b45e8b1eSAndrew Lindesay 			"from [%s]\n", statusCode, url.UrlString().String());
783b45e8b1eSAndrew Lindesay 	}
784b45e8b1eSAndrew Lindesay 
78554312619SAndrew Lindesay 	switch (statusCode) {
78654312619SAndrew Lindesay 		case B_HTTP_STATUS_OK:
78754312619SAndrew Lindesay 			break;
78854312619SAndrew Lindesay 
78954312619SAndrew Lindesay 		case B_HTTP_STATUS_PRECONDITION_FAILED:
79054312619SAndrew Lindesay 			ServerHelper::NotifyClientTooOld(result.Headers());
79154312619SAndrew Lindesay 			return HD_CLIENT_TOO_OLD;
79254312619SAndrew Lindesay 
79354312619SAndrew Lindesay 		default:
79466f8dcb1SAndrew Lindesay 			printf("jrpc request to endpoint [.../%s] failed with http "
79554312619SAndrew Lindesay 				"status [%" B_PRId32 "]\n", domain, statusCode);
79619c15fecSAndrew Lindesay 			return B_ERROR;
79719c15fecSAndrew Lindesay 	}
79819c15fecSAndrew Lindesay 
79966f8dcb1SAndrew Lindesay 	replyData.Seek(0, SEEK_SET);
80066f8dcb1SAndrew Lindesay 
801cd417b96SAndrew Lindesay 	if (Logger::IsTraceEnabled()) {
802cd417b96SAndrew Lindesay 		printf("jrpc response; ");
80388575af1SAndrew Lindesay 		_LogPayload(&replyData, replyData.BufferLength());
804cd417b96SAndrew Lindesay 		printf("\n");
805cd417b96SAndrew Lindesay 	}
806cd417b96SAndrew Lindesay 
80788575af1SAndrew Lindesay 	BJsonMessageWriter jsonMessageWriter(reply);
80888575af1SAndrew Lindesay 	BJson::Parse(&replyData, &jsonMessageWriter);
80988575af1SAndrew Lindesay 	status_t status = jsonMessageWriter.ErrorStatus();
81088575af1SAndrew Lindesay 
811f0665db4SAndrew Lindesay 	if (Logger::IsTraceEnabled() && status == B_BAD_DATA) {
812b45e8b1eSAndrew Lindesay 		BString resultString(static_cast<const char *>(replyData.Buffer()),
813b45e8b1eSAndrew Lindesay 			replyData.BufferLength());
814b45e8b1eSAndrew Lindesay 		printf("Parser choked on JSON:\n%s\n", resultString.String());
81519c15fecSAndrew Lindesay 	}
81619c15fecSAndrew Lindesay 	return status;
81719c15fecSAndrew Lindesay }
818b45e8b1eSAndrew Lindesay 
819b45e8b1eSAndrew Lindesay 
820b45e8b1eSAndrew Lindesay status_t
82188575af1SAndrew Lindesay WebAppInterface::_SendJsonRequest(const char* domain, const BString& jsonString,
822b45e8b1eSAndrew Lindesay 	uint32 flags, BMessage& reply) const
823b45e8b1eSAndrew Lindesay {
824a9edb9bfSAndrew Lindesay 	// gets 'adopted' by the subsequent http request.
82588575af1SAndrew Lindesay 	BMemoryIO* data = new BMemoryIO(jsonString.String(),
82688575af1SAndrew Lindesay 		jsonString.Length() - 1);
827b45e8b1eSAndrew Lindesay 
828b45e8b1eSAndrew Lindesay 	return _SendJsonRequest(domain, data, jsonString.Length() - 1, flags,
829b45e8b1eSAndrew Lindesay 		reply);
830b45e8b1eSAndrew Lindesay }
831cd417b96SAndrew Lindesay 
832cd417b96SAndrew Lindesay 
833cd417b96SAndrew Lindesay void
83488575af1SAndrew Lindesay WebAppInterface::_LogPayload(BPositionIO* requestData, size_t size)
835cd417b96SAndrew Lindesay {
83666f8dcb1SAndrew Lindesay 	off_t requestDataOffset = requestData->Position();
83788575af1SAndrew Lindesay 	char buffer[LOG_PAYLOAD_LIMIT];
83888575af1SAndrew Lindesay 
839cd417b96SAndrew Lindesay 	if (size > LOG_PAYLOAD_LIMIT)
840cd417b96SAndrew Lindesay 		size = LOG_PAYLOAD_LIMIT;
841cd417b96SAndrew Lindesay 
84288575af1SAndrew Lindesay 	if (B_OK != requestData->ReadExactly(buffer, size)) {
84388575af1SAndrew Lindesay 		printf("jrpc; error logging payload\n");
84488575af1SAndrew Lindesay 	} else {
84588575af1SAndrew Lindesay 		for (uint32 i = 0; i < size; i++) {
84688575af1SAndrew Lindesay     		bool esc = buffer[i] > 126 ||
84788575af1SAndrew Lindesay     			(buffer[i] < 0x20 && buffer[i] != 0x0a);
848cd417b96SAndrew Lindesay 
849cd417b96SAndrew Lindesay     		if (esc)
85088575af1SAndrew Lindesay     			printf("\\u%02x", buffer[i]);
851cd417b96SAndrew Lindesay     		else
85288575af1SAndrew Lindesay     			putchar(buffer[i]);
853cd417b96SAndrew Lindesay     	}
854cd417b96SAndrew Lindesay 
855cd417b96SAndrew Lindesay     	if (size == LOG_PAYLOAD_LIMIT)
856cd417b96SAndrew Lindesay     		printf("...(continues)");
857cd417b96SAndrew Lindesay 	}
85866f8dcb1SAndrew Lindesay 
85966f8dcb1SAndrew Lindesay 	requestData->Seek(requestDataOffset, SEEK_SET);
86088575af1SAndrew Lindesay }
86188575af1SAndrew Lindesay 
86288575af1SAndrew Lindesay 
86388575af1SAndrew Lindesay /*! This will get the position of the data to get the length an then sets the
86488575af1SAndrew Lindesay     offset to zero so that it can be re-read for reading the payload in to log
86588575af1SAndrew Lindesay     or send.
86688575af1SAndrew Lindesay */
86788575af1SAndrew Lindesay 
86888575af1SAndrew Lindesay off_t
86988575af1SAndrew Lindesay WebAppInterface::_LengthAndSeekToZero(BPositionIO* data)
87088575af1SAndrew Lindesay {
87188575af1SAndrew Lindesay 	off_t dataSize = data->Position();
87288575af1SAndrew Lindesay     data->Seek(0, SEEK_SET);
87388575af1SAndrew Lindesay     return dataSize;
87488575af1SAndrew Lindesay }
875