2using System.Collections.Generic;
5using System.Threading.Tasks;
37 private readonly
string userVariable;
38 private readonly
string[] userPrivileges;
51 HttpServer WebServer,
string UserVariable, params
string[] UserPrivileges)
55 this.nlClient = NLClient;
56 this.provider = Provider;
57 this.webServer = WebServer;
58 this.userVariable = UserVariable;
59 this.userPrivileges = UserPrivileges;
96 if (this.nlClient.ClientDisposed)
106 int i = s.IndexOf(
'/');
108 s = s.Substring(0, i);
117 throw new ForbiddenException(
"Access to block not granted: Accessing blocks can only be done by approved peers, or via the administrative portal.");
121 if (Session is
null ||
123 !(v.ValueObject is
IUser User))
125 throw new ForbiddenException(
"Access to block not granted: User needs to be logged in.");
129 foreach (
string Privilege
in this.userPrivileges)
131 if (!User.HasPrivilege(Privilege))
132 throw new ForbiddenException(
"Access to block not granted: User lacks sufficient privileges.");
140 throw new ForbiddenException(
"Access to block not granted: Presence subscription not approved.");
158 if (this.nlClient.ClientDisposed)
165 string FileName = this.provider.GetFullFileName(Ref.
FileName);
167 if (
string.IsNullOrEmpty(Ref.
FileName) || !File.Exists(FileName))
171 foreach (
string Source
in Ref.
Sources)
173 if (this.nlClient.ClientDisposed)
176 if (await this.nlClient.RetrieveBlock(Ref, Source,
null,
true))
184 FileName = this.provider.GetFullFileName(Ref.
FileName);
185 if (
string.IsNullOrEmpty(Ref.
FileName) || !File.Exists(FileName))
187 if (Ref.
Creator ==
this.provider.ExternalIdentity)
200 case "application/octet-stream":
201 Response.StatusCode = 200;
202 Response.ContentType = Alternative;
206 MemoryStream Header =
new MemoryStream();
207 int CollectionLen = f.ReadByte();
208 Header.WriteByte((
byte)CollectionLen);
210 if (CollectionLen == 0)
213 byte[] CollectionBin =
new byte[CollectionLen];
214 await f.ReadAllAsync(CollectionBin, 0, CollectionLen);
216 Header.Write(CollectionBin, 0, CollectionLen);
218 string CollectionName = Encoding.UTF8.GetString(CollectionBin);
219 Response.
SetHeader(
"X-Collection", CollectionName);
221 int SigLen = f.ReadByte();
222 Header.WriteByte((
byte)SigLen);
227 byte[] Signature =
new byte[SigLen];
228 await f.ReadAllAsync(Signature, 0, SigLen);
230 Header.Write(Signature, 0, SigLen);
231 Response.
SetHeader(
"X-Signature", Convert.ToBase64String(Signature));
233 byte[] ContentLen =
new byte[8];
234 await f.ReadAllAsync(ContentLen, 0, 8);
236 Header.Write(ContentLen, 0, 8);
238 long InputLength = BitConverter.ToInt64(ContentLen, 0);
239 byte[] Buf = Header.ToArray();
241 Response.ContentLength = Header.Length + InputLength;
242 await Response.
Write(Buf, 0, Buf.Length);
244 Buf =
new byte[(int)Math.Min(32768, InputLength)];
246 while (InputLength > 0)
248 int i = (int)Math.Min(32768, InputLength);
250 await f.ReadAllAsync(Buf, 0, i);
252 await Response.
Write(Buf, 0, i);
261 StringBuilder Xml =
new StringBuilder();
262 XmlWriter w = XmlWriter.Create(Xml, Settings);
264 w.WriteStartDocument();
265 w.WriteStartElement(
"Block",
"http://waher.se/NLB");
266 w.WriteAttributeString(
"digest", Convert.ToBase64String(Ref.
Digest));
267 w.WriteAttributeString(
"status", Ref.
Status.ToString());
268 w.WriteAttributeString(
"signature", Convert.ToBase64String(Ref.
Signature));
269 w.WriteAttributeString(
"collection", Ref.
Collection);
271 w.WriteAttributeString(
"creator", Ref.
Creator);
273 if (Ref.
Expires != DateTime.MaxValue)
276 if (Ref.
Updated != DateTime.MinValue)
279 if (!(Ref.
Link is
null))
280 w.WriteAttributeString(
"link", Convert.ToBase64String(Ref.
Link));
291 w.WriteStartElement(
"Add");
295 w.WriteStartElement(
"Update");
299 w.WriteStartElement(
"Delete");
303 w.WriteStartElement(
"Clear");
315 w.WriteAttributeString(
"type", Obj.
TypeName);
316 w.WriteAttributeString(
"id", Obj.
ObjectId.ToString());
318 foreach (KeyValuePair<string, object> P
in Obj)
319 this.WriteProperty(w, P.Key, P.Value);
327 w.WriteEndDocument();
330 Response.StatusCode = 200;
331 Response.ContentType = Alternative;
332 await Response.
Write(Xml.ToString());
337 LinkedList<KeyValuePair<string, object>> Result =
new LinkedList<KeyValuePair<string, object>>();
339 Result.AddLast(
new KeyValuePair<string, object>(
"digest", Convert.ToBase64String(Ref.
Digest)));
340 Result.AddLast(
new KeyValuePair<string, object>(
"status", Ref.
Status.ToString()));
341 Result.AddLast(
new KeyValuePair<string, object>(
"signature", Convert.ToBase64String(Ref.
Signature)));
342 Result.AddLast(
new KeyValuePair<string, object>(
"collection", Ref.
Collection));
343 Result.AddLast(
new KeyValuePair<string, object>(
"created",
XML.
Encode(Ref.
Created)));
344 Result.AddLast(
new KeyValuePair<string, object>(
"creator", Ref.
Creator));
346 if (Ref.
Expires != DateTime.MaxValue)
347 Result.AddLast(
new KeyValuePair<string, object>(
"expires",
XML.
Encode(Ref.
Expires)));
349 if (Ref.
Updated != DateTime.MinValue)
350 Result.AddLast(
new KeyValuePair<string, object>(
"updated",
XML.
Encode(Ref.
Updated)));
352 if (!(Ref.
Link is
null))
353 Result.AddLast(
new KeyValuePair<string, object>(
"link", Convert.ToBase64String(Ref.
Link)));
355 LinkedList<object> Entries =
new LinkedList<object>();
356 Result.AddLast(
new KeyValuePair<string, object>(
"entries", Entries));
364 LinkedList<KeyValuePair<string, object>> P1 =
new LinkedList<KeyValuePair<string, object>>();
366 P1.AddLast(
new KeyValuePair<string, object>(
"type", e.
CurrentEntry.
Type.ToString()));
374 LinkedList<KeyValuePair<string, object>> P2 =
new LinkedList<KeyValuePair<string, object>>();
377 P1.AddLast(
new KeyValuePair<string, object>(
"obj", P2));
379 P2.AddLast(
new KeyValuePair<string, object>(
"type", Obj.TypeName));
380 P2.AddLast(
new KeyValuePair<string, object>(
"id", Obj.ObjectId.ToString()));
382 foreach (KeyValuePair<string, object> P
in Obj)
383 P2.AddLast(
new KeyValuePair<string, object>(P.Key, P.Value));
393 Response.StatusCode = 200;
394 Response.ContentType = Alternative;
399 throw new NotAcceptableException(
"Desired format not acceptable. Use the Accept header field to select either text/xml, application/json or application/octet-stream.");
410 private void WriteProperty(XmlWriter w,
string Name,
object Value)
414 w.WriteStartElement(
"Null");
416 w.WriteAttributeString(
"n",
string.Empty, Name);
419 else if (Value is Enum)
421 w.WriteStartElement(
"En");
423 w.WriteAttributeString(
"n",
string.Empty, Name);
424 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
429 switch (Type.GetTypeCode(Value.GetType()))
431 case TypeCode.Boolean:
432 w.WriteStartElement(
"Bl");
434 w.WriteAttributeString(
"n",
string.Empty, Name);
440 w.WriteStartElement(
"B");
442 w.WriteAttributeString(
"n",
string.Empty, Name);
443 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
448 w.WriteStartElement(
"Ch");
450 w.WriteAttributeString(
"n",
string.Empty, Name);
451 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
455 case TypeCode.DateTime:
456 w.WriteStartElement(
"DT");
458 w.WriteAttributeString(
"n",
string.Empty, Name);
459 w.WriteAttributeString(
"v",
string.Empty,
XML.
Encode((DateTime)Value));
463 case TypeCode.Decimal:
464 w.WriteStartElement(
"Dc");
466 w.WriteAttributeString(
"n",
string.Empty, Name);
471 case TypeCode.Double:
472 w.WriteStartElement(
"Db");
474 w.WriteAttributeString(
"n",
string.Empty, Name);
480 w.WriteStartElement(
"I2");
482 w.WriteAttributeString(
"n",
string.Empty, Name);
483 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
488 w.WriteStartElement(
"I4");
490 w.WriteAttributeString(
"n",
string.Empty, Name);
491 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
496 w.WriteStartElement(
"I8");
498 w.WriteAttributeString(
"n",
string.Empty, Name);
499 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
504 w.WriteStartElement(
"I1");
506 w.WriteAttributeString(
"n",
string.Empty, Name);
507 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
511 case TypeCode.Single:
512 w.WriteStartElement(
"Fl");
514 w.WriteAttributeString(
"n",
string.Empty, Name);
519 case TypeCode.String:
520 string s = Value.ToString();
523 XmlConvert.VerifyXmlChars(s);
524 w.WriteStartElement(
"S");
526 w.WriteAttributeString(
"n",
string.Empty, Name);
527 w.WriteAttributeString(
"v",
string.Empty, s);
532 byte[] Bin = Encoding.UTF8.GetBytes(s);
533 s = Convert.ToBase64String(Bin);
534 w.WriteStartElement(
"S64");
536 w.WriteAttributeString(
"n",
string.Empty, Name);
537 w.WriteAttributeString(
"v",
string.Empty, s);
542 case TypeCode.UInt16:
543 w.WriteStartElement(
"U2");
545 w.WriteAttributeString(
"n",
string.Empty, Name);
546 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
550 case TypeCode.UInt32:
551 w.WriteStartElement(
"U4");
553 w.WriteAttributeString(
"n",
string.Empty, Name);
554 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
558 case TypeCode.UInt64:
559 w.WriteStartElement(
"U8");
561 w.WriteAttributeString(
"n",
string.Empty, Name);
562 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
566 case TypeCode.DBNull:
568 w.WriteStartElement(
"Null");
570 w.WriteAttributeString(
"n",
string.Empty, Name);
574 case TypeCode.Object:
575 if (Value is TimeSpan)
577 w.WriteStartElement(
"TS");
579 w.WriteAttributeString(
"n",
string.Empty, Name);
580 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
583 else if (Value is DateTimeOffset DTO)
585 w.WriteStartElement(
"DTO");
587 w.WriteAttributeString(
"n",
string.Empty, Name);
588 w.WriteAttributeString(
"v",
string.Empty,
XML.
Encode(DTO));
596 XmlConvert.VerifyXmlChars(s);
597 w.WriteStartElement(
"CIS");
599 w.WriteAttributeString(
"n",
string.Empty, Name);
600 w.WriteAttributeString(
"v",
string.Empty, s);
605 byte[] Bin = Encoding.UTF8.GetBytes(s);
606 s = Convert.ToBase64String(Bin);
607 w.WriteStartElement(
"CIS64");
609 w.WriteAttributeString(
"n",
string.Empty, Name);
610 w.WriteAttributeString(
"v",
string.Empty, s);
614 else if (Value is
byte[] Bin)
616 w.WriteStartElement(
"Bin");
618 w.WriteAttributeString(
"n",
string.Empty, Name);
636 if (i == 0 && j == c)
643 Array.Copy(Bin, i, Buf, 0, j);
645 w.WriteElementString(
"Chunk", Convert.ToBase64String(Buf, 0, j, Base64FormattingOptions.None));
651 else if (Value is Guid)
653 w.WriteStartElement(
"ID");
655 w.WriteAttributeString(
"n",
string.Empty, Name);
656 w.WriteAttributeString(
"v",
string.Empty, Value.ToString());
659 else if (Value is Array A)
661 w.WriteStartElement(
"Array");
663 w.WriteAttributeString(
"n",
string.Empty, Name);
664 w.WriteAttributeString(
"elementType",
string.Empty, Value.GetType().GetElementType().FullName);
666 foreach (
object Obj
in A)
667 this.WriteProperty(w,
null, Obj);
673 w.WriteStartElement(
"Obj");
675 w.WriteAttributeString(
"n",
string.Empty, Name);
676 w.WriteAttributeString(
"type",
string.Empty, Obj.TypeName);
678 foreach (KeyValuePair<string, object> P
in Obj)
679 this.WriteProperty(w, P.Key, P.Value);
684 throw new Exception(
"Unhandled property value type: " + Value.GetType().FullName);
688 throw new Exception(
"Unhandled property value type: " + Value.GetType().FullName);
Static class that does BASE64URL encoding (using URL and filename safe alphabet), as defined in RFC46...
static byte[] Decode(string Base64Url)
Converts a Base64URL-encoded string to its binary representation.
Helps with parsing of commong data types.
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Helps with common JSON-related tasks.
static string Encode(string s)
Encodes a string for inclusion in JSON.
const string DefaultContentType
application/json
const string DefaultContentType
Default content type for XML documents.
const string SchemaContentType
Default content type for XML schema documents.
Helps with common XML-related tasks.
static string Encode(string s)
Encodes a string for use in XML.
static XmlWriterSettings WriterSettings(bool Indent, bool OmitXmlDeclaration)
Gets an XML writer settings object.
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
The request failed due to failure of a previous request (e.g., a PROPPATCH).
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
Base class for all asynchronous HTTP resources. An asynchronous resource responds outside of the meth...
Represents an HTTP request.
HttpRequestHeader Header
Request header.
string RemoteEndPoint
Remote end-point.
string SubPath
Sub-path. If a resource is found handling the request, this property contains the trailing sub-path o...
static string GetSessionId(HttpRequest Request, HttpResponse Response)
Gets the session ID used for a request.
const string HttpSessionID
The Cookie Key for HTTP Session Identifiers: "HttpSessionID"
string ResourceName
Name of resource.
Represets a response of an HTTP client request.
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
void SetHeader(string FieldName, string Value)
Sets a custom header field value.
async Task Write(byte[] Data)
Returns binary data in the response.
Implements an HTTP server.
The resource identified by the request is only capable of generating response entities which have con...
The server has not found anything matching the Request-URI. No indication is given of whether the con...
The server is currently unable to handle the request due to a temporary overloading or maintenance of...
Provides authenticated and authorized clients with binary blocks.
override bool HandlesSubPaths
If the resource handles sub-paths.
async Task GET(HttpRequest Request, HttpResponse Response)
Executes the GET method on the resource.
const string ErrorMsg_BlockFileHasBeenRemoved
Block file has been removed.
bool AllowsGET
If the GET method is allowed.
async Task ProcessRequest(HttpFieldAccept Accept, HttpResponse Response, BlockReference Ref)
Processes a block request.
BlockResource(string ResourceName, NeuroLedgerProvider Provider, XmppClient Client, NeuroLedgerClient NLClient, HttpServer WebServer, string UserVariable, params string[] UserPrivileges)
Provides authenticated and authorized clients with binary blocks.
override bool UserSessions
If the resource uses user sessions.
Maintains information about an item in the roster.
bool IsInGroup(string Group)
Checks if the roster item is in a specific group.
SubscriptionState State
roup Current subscription state.
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Represents a case-insensitive string.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
static async Task Delete(object Object)
Deletes an object in the database.
Reads objects in a block.
static Stream GetStream(string FileName, NeuroLedgerProvider Provider)
Gets a stream to the contents of the block.
DateTime Timestamp
Timestamp of entry.
static Task< BlockReference > FindReference(byte[] Digest)
Finds a BlockReference object related to a block, given its digest.
Enumeratres through objects available in a series of blocks.
T Current
Gets the element in the collection at the current position of the enumerator.
async Task< bool > MoveNextAsync()
Advances the enumerator to the next element of the collection.
Entry CurrentEntry
Current encoded entry.
static async Task< ObjectEnumerator< T > > Create(IAsyncEnumerator< BlockReference > BlockEnumerator, NeuroLedgerProvider Provider)
Creates an object enumerator from a block enumerator.
Contains a reference to a block in the ledger.
bool AccessDenied
If access to the block was denied.
string[] Sources
Sources of block
byte[] Digest
Digest of block
byte[] Signature
Signature of block
string FileName
Local filename of block
Generic object. Contains a sequence of properties.
string TypeName
Type name.
Contains information about a variable.
virtual bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
GET Interface for HTTP resources.
Basic interface for a user.
SubscriptionState
State of a presence subscription.
EntryType
Ledger entry type.