From bd21c49ff49c37327b97a01144b14bb5a7ba9047 Mon Sep 17 00:00:00 2001 From: tong Date: Thu, 11 Apr 2024 14:42:22 +0200 Subject: [PATCH] Apply formatting --- src/xmpp/Attention.hx | 25 +- src/xmpp/Block.hx | 46 +- src/xmpp/DataForm.hx | 227 +++++----- src/xmpp/DelayedDelivery.hx | 46 +- src/xmpp/HttpFileUpload.hx | 48 +-- src/xmpp/IQ.hx | 123 +++--- src/xmpp/Jid.hx | 81 ++-- src/xmpp/LastActivity.hx | 9 +- src/xmpp/LastMessageCorrection.hx | 8 +- src/xmpp/Message.hx | 51 ++- src/xmpp/Muc.hx | 410 +++++++++--------- src/xmpp/Ping.hx | 9 +- src/xmpp/Presence.hx | 100 ++--- src/xmpp/PrivateStorage.hx | 25 +- src/xmpp/ServiceDiscovery.hx | 47 +- src/xmpp/Stanza.hx | 110 ++--- src/xmpp/Stream.hx | 363 ++++++++-------- src/xmpp/Uuid.hx | 45 +- src/xmpp/VCard.hx | 15 +- src/xmpp/XML.hx | 240 ++++++----- src/xmpp/client/Authentication.hx | 115 ++--- src/xmpp/client/CSI.hx | 21 +- src/xmpp/client/Roster.hx | 79 ++-- src/xmpp/client/Stream.hx | 11 +- src/xmpp/component/Stream.hx | 50 +-- src/xmpp/net/BOSH.hx | 412 ++++++++++-------- src/xmpp/xml/Printer.hx | 18 +- src/xmpp/xml/Schema.hx | 696 +++++++++++++++--------------- 28 files changed, 1788 insertions(+), 1642 deletions(-) diff --git a/src/xmpp/Attention.hx b/src/xmpp/Attention.hx index 7daf5615..cd33e786 100644 --- a/src/xmpp/Attention.hx +++ b/src/xmpp/Attention.hx @@ -1,27 +1,26 @@ package xmpp; /** - Extension for getting the attention of another user. + Extension for getting the attention of another user. - [XEP-0224: Attention](https://xmpp.org/extensions/xep-0224.html) + [XEP-0224: Attention](https://xmpp.org/extensions/xep-0224.html) **/ @xep(224) class Attention { - public static inline var XMLNS = "urn:xmpp:attention:0"; /** - Sends a message packet to the given entity inluding a property to get attention. + Sends a message packet to the given entity inluding a property to get attention. **/ - public static inline function captureAttention(message : xmpp.Message) : xmpp.Message { - message.properties.push(XML.create("attention").set('xmlns', XMLNS)); - return message; + public static inline function captureAttention(message:xmpp.Message):xmpp.Message { + message.properties.push(XML.create("attention").set('xmlns', XMLNS)); + return message; } - /** - Return `true` if the message stanza includes an `` element. - **/ - public static inline function wantsAttention(message : xmpp.Message) : Bool { - return message.properties.filter(e -> return e.is(XMLNS)).length > 0; - } + /** + Return `true` if the message stanza includes an `` element. + **/ + public static inline function wantsAttention(message:xmpp.Message):Bool { + return message.properties.filter(e -> return e.is(XMLNS)).length > 0; + } } diff --git a/src/xmpp/Block.hx b/src/xmpp/Block.hx index 62e4d658..7675c80a 100644 --- a/src/xmpp/Block.hx +++ b/src/xmpp/Block.hx @@ -3,35 +3,35 @@ package xmpp; import xmpp.IQ; /** - Extension for communicatin blocking. + Extension for communicatin blocking. - [XEP-0191: Blocking Command](https://xmpp.org/extensions/xep-0191.html) + [XEP-0191: Blocking Command](https://xmpp.org/extensions/xep-0191.html) **/ @xep(191) class Block { - public static inline var XMLNS = 'urn:xmpp:blocking'; - /** - Load list of blocked entities. - **/ - public static inline function getBlocklist(stream: xmpp.Stream, handler: xmpp.Response->Void) : IQ { - return stream.get(Payload.create(XMLNS, "blocklist"), handler); - } + /** + Load list of blocked entities. + **/ + public static inline function getBlocklist(stream:xmpp.Stream, handler:xmpp.Response->Void):IQ { + return stream.get(Payload.create(XMLNS, "blocklist"), handler); + } - /** - Block recieving stanzas from entity. - **/ - public static inline function block(stream: xmpp.Stream, jid: Jid, handler: xmpp.Response->Void) : IQ { - return stream.set(Payload.create(XMLNS, "block").append(XML.create('item').set('jid', jid)), handler); - } + /** + Block recieving stanzas from entity. + **/ + public static inline function block(stream:xmpp.Stream, jid:Jid, handler:xmpp.Response->Void):IQ { + return stream.set(Payload.create(XMLNS, "block").append(XML.create('item').set('jid', jid)), handler); + } - /** - Unblock recieving stanzas from entity. - **/ - public static function unblock(stream: Stream, ?jid: Jid, handler: xmpp.Response->Void) : IQ { - var x = IQ.Payload.create(XMLNS, "unblock"); - if(jid != null) x.append(XML.create('item').set('jid', jid)); - return stream.set(x, handler); - } + /** + Unblock recieving stanzas from entity. + **/ + public static function unblock(stream:Stream, ?jid:Jid, handler:xmpp.Response->Void):IQ { + var x = IQ.Payload.create(XMLNS, "unblock"); + if (jid != null) + x.append(XML.create('item').set('jid', jid)); + return stream.set(x, handler); + } } diff --git a/src/xmpp/DataForm.hx b/src/xmpp/DataForm.hx index bfbf1c5d..0f45fd97 100644 --- a/src/xmpp/DataForm.hx +++ b/src/xmpp/DataForm.hx @@ -1,7 +1,6 @@ package xmpp; enum abstract FormType(String) from String to String { - /** The form-submitting entity has cancelled submission of data to the form-processing entity. **/ @@ -27,12 +26,11 @@ enum abstract FormType(String) from String to String { } enum abstract FieldType(String) from String to String { - /** The field enables an entity to gather or provide an either-or choice between two options. The default value is `false`. **/ - var boolean; //TODO java throws error + var boolean; // TODO java throws error /** The field is intended for data description (e.g., human-readable text such as "section" headers) rather than data gathering or provision. @@ -74,7 +72,7 @@ enum abstract FieldType(String) from String to String { var list_single = "list-single"; /** - The field enables an entity to gather or provide multiple lines of text. + The field enables an entity to gather or provide multiple lines of text. **/ var text_multi = "text-multi"; @@ -92,126 +90,143 @@ enum abstract FieldType(String) from String to String { } private typedef FieldOption = { - var ?label : String; - var value : String; + var ?label:String; + var value:String; } typedef Field = { + /** **/ + var ?label:String; - /** **/ - var ?label : String; + /** **/ + var ?type:FieldType; - /** **/ - var ?type : FieldType; + /** **/ + @:native('var') var ?variable:String; - /** **/ - @:native('var') var ?variable : String; + /** **/ + var ?options:Array; - /** **/ - var ?options : Array; + /** Provides a natural-language description of the field. **/ + var ?desc:String; - /** Provides a natural-language description of the field. **/ - var ?desc : String; + /** **/ + var ?values:Array; - /** **/ - var ?values : Array; - - /** Flags the field as required in order for the form to be considered valid. **/ - var ?required : Bool; + /** Flags the field as required in order for the form to be considered valid. **/ + var ?required:Bool; } @:structInit private class TDataForm { - public var type : FormType; - public var title : String; - public var instructions : String; - public var fields : Array; - public var reported : Array; - public var items : Array>; - public function new(type:FormType, ?title:String, ?instructions:String, ?fields:Array, ?reported:Array, ?items:Array>) { - this.type = type; - this.title = title; - this.instructions = instructions; - this.fields = fields ?? []; - this.reported = reported ?? []; - this.items = items ?? []; - } + public var type:FormType; + public var title:String; + public var instructions:String; + public var fields:Array; + public var reported:Array; + public var items:Array>; + + public function new(type:FormType, ?title:String, ?instructions:String, ?fields:Array, ?reported:Array, ?items:Array>) { + this.type = type; + this.title = title; + this.instructions = instructions; + this.fields = fields ?? []; + this.reported = reported ?? []; + this.items = items ?? []; + } } /** - Data forms that can be used in workflows such as service configuration as well as for application-specific data description and reporting. + Data forms that can be used in workflows such as service configuration as well as for application-specific data description and reporting. - [XEP-0004: Data Forms](https://xmpp.org/extensions/xep-0004.html) + [XEP-0004: Data Forms](https://xmpp.org/extensions/xep-0004.html) **/ @:forward abstract DataForm(TDataForm) from TDataForm to TDataForm { - - public static inline var XMLNS = "jabber:x:data"; - - public inline function new(type:FormType, ?title:String, ?instructions:String, ?fields:Array, ?items:Array>) - this = new TDataForm(type, title, instructions, fields, items); - - @:to public function toXML() : XML { - function fieldToXML(f:Field) : XML { - final c = XML.create("field"); - if(f.variable != null) c.set("var", f.variable); - if(f.label != null) c.set("label", f.label); - if(f.type != null) c.set("type", f.type); - if(f.options != null) for(o in f.options) { - var e = XML.create("option"); - if(o.label != null) e.set("label", o.label); - e.append(XML.create("value", o.value)); - c.append(e); - } - if(f.desc != null) c.set("desc", f.desc); - if(f.values != null) for(v in f.values) c.append(XML.create("value",v)); - return c; - } - final xml = XML.create("x").set("xmlns", XMLNS).set("type", this.type); - if(this.title != null) xml.append(XML.create("title", this.title)); - if(this.instructions != null) xml.append(XML.create("instructions", this.instructions)); - for(f in this.fields) xml.append(fieldToXML(f)); - if(this.reported.length > 0) { - final e = XML.create("reported"); - for(f in this.reported) e.append(fieldToXML(f)); - xml.append(e); - } - return xml; - } - - @:from public static function fromXML(xml:XML) : DataForm { - function parseField(e:XML) : Field { - final f : Field = { - type: e["type"], - label: e["label"], - variable: e["var"], - options: [] - }; - for(e in e.elements) { - switch e.name { - case "desc": f.desc = e.text; - case "option": f.options.push({ - label: e["label"], - value: e.firstElement.text - }); - case "required": f.required = true; - } - } - return f; - } - final form = new DataForm(xml.get("type")); - for(e in xml.elements) { + public static inline var XMLNS = "jabber:x:data"; + + public inline function new(type:FormType, ?title:String, ?instructions:String, ?fields:Array, ?items:Array>) + this = new TDataForm(type, title, instructions, fields, items); + + @:to public function toXML():XML { + function fieldToXML(f:Field):XML { + final c = XML.create("field"); + if (f.variable != null) + c.set("var", f.variable); + if (f.label != null) + c.set("label", f.label); + if (f.type != null) + c.set("type", f.type); + if (f.options != null) + for (o in f.options) { + var e = XML.create("option"); + if (o.label != null) + e.set("label", o.label); + e.append(XML.create("value", o.value)); + c.append(e); + } + if (f.desc != null) + c.set("desc", f.desc); + if (f.values != null) + for (v in f.values) + c.append(XML.create("value", v)); + return c; + } + final xml = XML.create("x").set("xmlns", XMLNS).set("type", this.type); + if (this.title != null) + xml.append(XML.create("title", this.title)); + if (this.instructions != null) + xml.append(XML.create("instructions", this.instructions)); + for (f in this.fields) + xml.append(fieldToXML(f)); + if (this.reported.length > 0) { + final e = XML.create("reported"); + for (f in this.reported) + e.append(fieldToXML(f)); + xml.append(e); + } + return xml; + } + + @:from public static function fromXML(xml:XML):DataForm { + function parseField(e:XML):Field { + final f:Field = { + type: e["type"], + label: e["label"], + variable: e["var"], + options: [] + }; + for (e in e.elements) { + switch e.name { + case "desc": + f.desc = e.text; + case "option": + f.options.push({ + label: e["label"], + value: e.firstElement.text + }); + case "required": + f.required = true; + } + } + return f; + } + final form = new DataForm(xml.get("type")); + for (e in xml.elements) { switch e.name { - case "title": form.title = e.text; - case "field": form.fields.push(parseField(e)); - case "item": - for(e in e.elements) - form.items.push([for(e in e.elements) parseField(e)]); - case "instructions": form.instructions = e.text; - case "reported": - for(e in e.elements) - form.reported.push(parseField(e)); - } - } - return form; - } + case "title": + form.title = e.text; + case "field": + form.fields.push(parseField(e)); + case "item": + for (e in e.elements) + form.items.push([for (e in e.elements) parseField(e)]); + case "instructions": + form.instructions = e.text; + case "reported": + for (e in e.elements) + form.reported.push(parseField(e)); + } + } + return form; + } } diff --git a/src/xmpp/DelayedDelivery.hx b/src/xmpp/DelayedDelivery.hx index 090c49ea..938b903a 100644 --- a/src/xmpp/DelayedDelivery.hx +++ b/src/xmpp/DelayedDelivery.hx @@ -1,37 +1,35 @@ package xmpp; typedef TDelay = { - - /** - The Jabber ID of the entity that originally sent the XML stanza or that delayed the delivery of the stanza (e.g., the address of a multi-user chat room). - **/ - from : String, - - /** - The time when the XML stanza was originally sent. - **/ - stamp: String, - - /** - Natural-language description of the reason for the delay. - **/ - ?description: String + /** + The Jabber ID of the entity that originally sent the XML stanza or that delayed the delivery of the stanza (e.g., the address of a multi-user chat room). + **/ + from:String, + + /** + The time when the XML stanza was originally sent. + **/ + stamp:String, + + /** + Natural-language description of the reason for the delay. + **/ + ?description:String } /** - Communicate the fact that an XML stanza has been delivered with a delay. + Communicate the fact that an XML stanza has been delivered with a delay. - [XEP-0203: Delayed Delivery](https://xmpp.org/extensions/xep-0203.html) + [XEP-0203: Delayed Delivery](https://xmpp.org/extensions/xep-0203.html) **/ @xep(203) class DelayedDelivery { - public static inline var XMLNS = "urn:xmpp:delay"; - public static function delay(stanza: xmpp.Message) : TDelay { - for(e in stanza.properties) - if(e.is(XMLNS)) - return { from: e["from"], stamp: e["stamp"], description: e.text }; - return null; - } + public static function delay(stanza:xmpp.Message):TDelay { + for (e in stanza.properties) + if (e.is(XMLNS)) + return {from: e["from"], stamp: e["stamp"], description: e.text}; + return null; + } } diff --git a/src/xmpp/HttpFileUpload.hx b/src/xmpp/HttpFileUpload.hx index 8de4d54c..c6a0f3e7 100644 --- a/src/xmpp/HttpFileUpload.hx +++ b/src/xmpp/HttpFileUpload.hx @@ -3,34 +3,34 @@ package xmpp; import xmpp.IQ; /** - Allows to request permissions from another entity to upload a file to a specific path on an HTTP server and at the same time receive a URL from which that file can later be downloaded again. + Allows to request permissions from another entity to upload a file to a specific path on an HTTP server and at the same time receive a URL from which that file can later be downloaded again. - [XEP-00363: HTTP File Upload](https://xmpp.org/extensions/xep-0363.html) + [XEP-00363: HTTP File Upload](https://xmpp.org/extensions/xep-0363.html) **/ @xep(363) class HttpFileUpload { + public static inline var XMLNS = "urn:xmpp:http:upload:0"; - public static inline var XMLNS = "urn:xmpp:http:upload:0"; - - /** - **/ - public static function requestHttpUploadSlot(stream: Stream, jid: String, filename: String, size: Int, ?contentType: haxe.io.Mime, handler: Response->Void) : IQ { - final xml = Payload.create(XMLNS, "request") - .set("filename", filename) - .set("size", Std.string(size)); - if(contentType != null) xml.set("content-type", contentType); - return stream.get(xml, jid, handler); - } - - /** - **/ - public static function sendHttpUploadSlot(stream: Stream, req: IQ, urlPut: String, urlGet: String, ?headers: Map) { - final put = XML.create("put").set("url", urlPut); - if(headers != null) - for(k=>v in headers) put.append(XML.create("heaader").set(k,v)); - stream.send(req.createResult(XML.create("slot").set("xmlns", XMLNS) - .append(put) - .append(XML.create("get").set("url", urlGet)))); - } + /** + **/ + public static function requestHttpUploadSlot(stream:Stream, jid:String, filename:String, size:Int, ?contentType:haxe.io.Mime, + handler:Response->Void):IQ { + final xml = Payload.create(XMLNS, "request").set("filename", filename).set("size", Std.string(size)); + if (contentType != null) + xml.set("content-type", contentType); + return stream.get(xml, jid, handler); + } + /** + **/ + public static function sendHttpUploadSlot(stream:Stream, req:IQ, urlPut:String, urlGet:String, ?headers:Map) { + final put = XML.create("put").set("url", urlPut); + if (headers != null) + for (k => v in headers) + put.append(XML.create("heaader").set(k, v)); + stream.send(req.createResult(XML.create("slot") + .set("xmlns", XMLNS) + .append(put) + .append(XML.create("get").set("url", urlGet)))); + } } diff --git a/src/xmpp/IQ.hx b/src/xmpp/IQ.hx index 98633a07..f2708ff0 100644 --- a/src/xmpp/IQ.hx +++ b/src/xmpp/IQ.hx @@ -3,7 +3,6 @@ package xmpp; import xmpp.Stanza; enum abstract IQType(String) from String to String { - /** The stanza requests information, inquires about what data is needed in order to complete further operations. **/ @@ -35,7 +34,7 @@ enum abstract IQType(String) from String to String { } /* -enum abstract IQRequestType(String) to String to IQType { + enum abstract IQRequestType(String) to String to IQType { var Get = "get"; var Set = "set"; // @:from public static function fromString(s:String) @@ -44,9 +43,9 @@ enum abstract IQRequestType(String) to String to IQType { // case 'set': Set; // case null, _: null; // } -} + } -enum abstract IQResponseType(String) to String { + enum abstract IQResponseType(String) to String { var Result = "result"; var Error = "error"; // @:from public static function fromString(s:String) @@ -55,47 +54,45 @@ enum abstract IQResponseType(String) to String { // case 'error': Error; // case null, _: null; // } -} -*/ - + } + */ typedef Reply = xmpp.Response; /** - Info/Query *request-response* mechanism stanza. + Info/Query *request-response* mechanism stanza. The semantics of IQ enable an entity to make a request of, and receive a response from, another entity. - The data content of the request and response is defined by the schema or other structural definition associated with the XML namespace that qualifies the direct child element of the `IQ` element, and the interaction is tracked by the requesting entity through use of the `id` attribute. + The data content of the request and response is defined by the schema or other structural definition associated with the XML namespace that qualifies the direct child element of the `IQ` element, and the interaction is tracked by the requesting entity through use of the `id` attribute. Thus, `IQ` interactions follow a common pattern of structured data exchange such as *get/result* or *set/result* (although an error can be returned in reply to a request if appropriate). - Requesting Responding - Entity Entity - ---------- ---------- - | | - | | - | [ ... payload ... ] | - | | - | -------------------------> | - | | - | | - | [ ... payload ... ] | - | | - | <------------------------- | - | | - | | - | [ ... payload ... ] | - | | - | -------------------------> | - | | - | | - | [ ... condition ... ] | - | | - | <------------------------- | - | | + Requesting Responding + Entity Entity + ---------- ---------- + | | + | | + | [ ... payload ... ] | + | | + | -------------------------> | + | | + | | + | [ ... payload ... ] | + | | + | <------------------------- | + | | + | | + | [ ... payload ... ] | + | | + | -------------------------> | + | | + | | + | [ ... condition ... ] | + | | + | <------------------------- | + | | **/ -@:forward(from,to,id,lang,error,type,payload,xmlns,createResult,createError) +@:forward(from, to, id, lang, error, type, payload, xmlns, createResult, createError) abstract IQ(IQStanza) to Stanza { - public static inline var NAME = 'iq'; public inline function new(?payload:Payload, type = IQType.Get, ?id:String, ?to:String, ?from:String) @@ -115,16 +112,16 @@ abstract IQ(IQStanza) to Stanza { } private class IQStanza extends Stanza { - /** Either: get/set/result/error **/ - public var type: IQType; + public var type:IQType; /** The exclusive child element (mostly: ``) **/ - public var payload: Null; + public var payload:Null; + + /** Payload namespace **/ + public var xmlns(get, never):Null; - /** Payload namespace **/ - public var xmlns(get,never): Null; - inline function get_xmlns(): Null + inline function get_xmlns():Null return (payload != null) ? payload.xmlns : null; @:allow(xmpp.IQ) @@ -136,55 +133,63 @@ private class IQStanza extends Stanza { public function toXML():XML { final xml = Stanza.createXML(this, IQ.NAME).set("type", type); - if(payload != null) xml.append(payload); - if(error != null) xml.append(error.toXML()); + if (payload != null) + xml.append(payload); + if (error != null) + xml.append(error.toXML()); return xml; } public static function parse(xml:XML):IQ { final iq = Stanza.parseAttributes(new IQ(null, xml.get('type')), xml); switch iq.type { - case Error: iq.error = xmpp.Stanza.Error.fromXML(xml.firstElement); - case Get, Set, Result: iq.payload = xml.firstElement; + case Error: + iq.error = xmpp.Stanza.Error.fromXML(xml.firstElement); + case Get, Set, Result: + iq.payload = xml.firstElement; } return iq; } - public inline function createResult(?payload: Payload) : IQ - return new IQ(payload, Result, this.id, this.from); + public inline function createResult(?payload:Payload):IQ + return new IQ(payload, Result, this.id, this.from); - public function createError(e: xmpp.Stanza.Error) : IQ { - var r = new IQ(Error, this.id, this.from); - r.error = e; - return r; - } + public function createError(e:xmpp.Stanza.Error):IQ { + var r = new IQ(Error, this.id, this.from); + r.error = e; + return r; + } } @:forward @:forwardStatics abstract Payload(XML) from XML to XML { + public var xmlns(get, set):String; - public var xmlns(get,set):String; inline function get_xmlns():String return this.get('xmlns'); + inline function set_xmlns(s:String):String return this.set('xmlns', s); - public var content(get,never):XML; + public var content(get, never):XML; + inline function get_content():XML return this.firstElement; + // inline function set_content(x:XML):XML { // for (e in this.elements) this.removeChild(e); // return this.append(x); // } - inline function new(xml:XML) this = xml; + inline function new(xml:XML) + this = xml; - public function toError() : Null { - return (this.name != "error") ? null : xmpp.Stanza.Error.fromXML(this); - } + public function toError():Null { + return (this.name != "error") ? null : xmpp.Stanza.Error.fromXML(this); + } - public static inline function create(xmlns:String, name='query'):Payload + public static inline function create(xmlns:String, name = 'query'):Payload return new Payload(XML.create(name).set('xmlns', xmlns)); @:from public static inline function fromString(xmlns:String):Payload diff --git a/src/xmpp/Jid.hx b/src/xmpp/Jid.hx index 431febd4..ada560e9 100644 --- a/src/xmpp/Jid.hx +++ b/src/xmpp/Jid.hx @@ -23,9 +23,8 @@ using StringTools; - [XEP-0106: JID Escaping](https://xmpp.org/extensions/xep-0106.html) **/ @:nullSafety -@:forward(node,domain,resource) +@:forward(node, domain, resource) abstract Jid(CJid) from CJid { - public static inline var MAX_PARTSIZE = 1023; public static inline var MAX_SIZE = 3071; @@ -34,24 +33,22 @@ abstract Jid(CJid) from CJid { public inline function new(?node:Null, domain:String, ?resource:Null) this = new CJid(node, domain, resource); - public function getBare(): Null { + public function getBare():Null { return (this.node == null || this.domain == null) ? null : this.node + '@' + this.domain; } - @:to public function toString(): Null { + @:to public function toString():Null { var s = getBare(); - if (this.resource != null) s += '/' + this.resource; + if (this.resource != null) + s += '/' + this.resource; return s; } @:to public inline function toArray():Array> return [this.node, this.domain, this.resource]; - @:op(A==B) public function equals(jid:Jid):Bool { - return if(this.node != jid.node - || this.domain != jid.domain - || this.resource != jid.resource) - false else true; + @:op(A == B) public function equals(jid:Jid):Bool { + return if (this.node != jid.node || this.domain != jid.domain || this.resource != jid.resource) false else true; } @:arrayAccess function getPart(i:Int):String { @@ -65,9 +62,12 @@ abstract Jid(CJid) from CJid { @:arrayAccess function setPart(i:Int, str:String) { switch i { - case 0: this.node = str; - case 1: this.domain = str; - case 2: this.resource = str; + case 0: + this.node = str; + case 1: + this.domain = str; + case 2: + this.resource = str; default: } } @@ -81,10 +81,10 @@ abstract Jid(CJid) from CJid { /** Returns `true` if the given string is a valid jid. **/ - #if python - // TODO: HACK: python complains: Null safety: Cannot assign nullable value here. - @:nullSafety(Off) - #end + #if python + // TODO: HACK: python complains: Null safety: Cannot assign nullable value here. + @:nullSafety(Off) + #end public static function isValid(str:String):Bool { return (str == null || str.length > MAX_SIZE) ? false : EREG.match(str); } @@ -111,7 +111,7 @@ abstract Jid(CJid) from CJid { } public static function parseParts(str:String):Array { - final i = str.indexOf("@"); + final i = str.indexOf("@"); final j = str.indexOf("/"); final a = [str.substr(0, i)]; return a.concat((j == -1) ? [str.substring(i + 1)] : [str.substring(i + 1, j), str.substr(j + 1)]); @@ -124,20 +124,20 @@ abstract Jid(CJid) from CJid { Typically, escaping is performed only by a client that is processing information provided by a human user in unescaped form, or by a gateway to some external system (e.g., email or LDAP) that needs to generate a JID. - **/ - @xep(106) + **/ + @xep(106) public static function escapeNode(s:String):String { - //s.split("&").join("&") + // s.split("&").join("&") return s.replace("\\", "\\5c") - .replace(" ", "\\20") - .replace("\"", "\\22") - .replace("&", "\\26") - .replace("'", "\\27") - .replace("/", "\\2f") - .replace(":", "\\3a") - .replace("<", "\\3c") - .replace(">", "\\3e") - .replace("@", "\\40"); + .replace(" ", "\\20") + .replace("\"", "\\22") + .replace("&", "\\26") + .replace("'", "\\27") + .replace("/", "\\2f") + .replace(":", "\\3a") + .replace("<", "\\3c") + .replace(">", "\\3e") + .replace("@", "\\40"); } /** @@ -149,18 +149,18 @@ abstract Jid(CJid) from CJid { external system (e.g., email or LDAP) that needs to generate identifiers for foreign systems. **/ - @xep(106) + @xep(106) public static function unescapeNode(s:String):String { return s.replace("\\20", " ") - .replace("\\22", "\"") - .replace("\\26", "&") - .replace("\\27", "'") - .replace("\\2f", "/") - .replace("\\3a", ":") - .replace("\\3c", "<") - .replace("\\3e", ">") - .replace("\\40", "@") - .replace("\\5c", "\\"); + .replace("\\22", "\"") + .replace("\\26", "&") + .replace("\\27", "'") + .replace("\\2f", "/") + .replace("\\3a", ":") + .replace("\\3c", "<") + .replace("\\3e", ">") + .replace("\\40", "@") + .replace("\\5c", "\\"); } } @@ -168,6 +168,7 @@ abstract Jid(CJid) from CJid { public var node:Null; public var domain:String; public var resource:Null; + @:allow(xmpp.Jid) inline function new(?node:String, domain:String, ?resource:String) { this.node = node; diff --git a/src/xmpp/LastActivity.hx b/src/xmpp/LastActivity.hx index 75bc0b1f..b7763d14 100644 --- a/src/xmpp/LastActivity.hx +++ b/src/xmpp/LastActivity.hx @@ -1,15 +1,14 @@ package xmpp; /** - Communicate information about the last activity associated with an XMPP entity. + Communicate information about the last activity associated with an XMPP entity. - [XEP-0012: Last Activiy](https://xmpp.org/extensions/xep-0012.html) + [XEP-0012: Last Activiy](https://xmpp.org/extensions/xep-0012.html) **/ @xep(12) class LastActivity { - public static inline var XMLNS = "jabber:iq:last"; - public static inline function getLastActivity(stream: Stream, ?jid: String, handler: Response->Void) - stream.get(XMLNS, jid ?? stream.domain, handler); + public static inline function getLastActivity(stream:Stream, ?jid:String, handler:Response->Void) + stream.get(XMLNS, jid ?? stream.domain, handler); } diff --git a/src/xmpp/LastMessageCorrection.hx b/src/xmpp/LastMessageCorrection.hx index b18f9b9c..906c36b5 100644 --- a/src/xmpp/LastMessageCorrection.hx +++ b/src/xmpp/LastMessageCorrection.hx @@ -1,9 +1,9 @@ package xmpp; /** - Method for indicating that a message is a correction of the last sent message. + Indicate that a message is a correction of the last sent message. - [XEP-0308: Last Message Correction](https://xmpp.org/extensions/xep-0308.html) + @see [XEP-0308: Last Message Correction](https://xmpp.org/extensions/xep-0308.html) **/ @xep(308) class LastMessageCorrection { @@ -15,11 +15,11 @@ class LastMessageCorrection { **/ public static inline function correct(message : xmpp.Message, id: String) : xmpp.Message { message.properties.push(XML.create("replace").set('xmlns', XMLNS)); - return message; + return message;// } /** - Get the correction id of the given message stanze. + Get the correction id of the given message stanza. **/ public static inline function correction(message : xmpp.Message) : String { return message.properties.filter(e -> return e.is(XMLNS))[0].get('id'); diff --git a/src/xmpp/Message.hx b/src/xmpp/Message.hx index d65bafd5..d562c83b 100644 --- a/src/xmpp/Message.hx +++ b/src/xmpp/Message.hx @@ -1,29 +1,28 @@ package xmpp; enum abstract MessageType(String) to String { - /** - The message is a standalone message that is sent outside the context of a one-to-one conversation or groupchat, and to which it is expected that the recipient will reply. + The message is a standalone message that is sent outside the context of a one-to-one conversation or groupchat, and to which it is expected that the recipient will reply. **/ var normal; /** - The message is generated by an entity that experiences an error when processing a message received from another entity + The message is generated by an entity that experiences an error when processing a message received from another entity **/ var error; /** - The message is sent in the context of a one-to-one chat session. + The message is sent in the context of a one-to-one chat session. **/ var chat; /** - The message is sent in the context of a multi-user chat environment + The message is sent in the context of a multi-user chat environment **/ var groupchat; /** - The message provides an alert, a notification, or other transient information to which no reply is expected + The message provides an alert, a notification, or other transient information to which no reply is expected **/ var headline; @@ -45,7 +44,7 @@ enum abstract MessageType(String) to String { @see [RFC-3921 - Instant Messaging and Presence](https://xmpp.org/rfcs/rfc3921.html) **/ -@:forward(from,to,id,lang,error,type,body,subject,thread,properties) +@:forward(from, to, id, lang, error, type, body, subject, thread, properties) abstract Message(MessageStanza) to Stanza { public static inline var NAME = 'message'; @@ -66,26 +65,25 @@ abstract Message(MessageStanza) to Stanza { } private class MessageStanza extends Stanza { - /***/ public var type:MessageType; /** - Human-readable XML character data that specifies the textual contents. + Human-readable XML character data that specifies the textual contents. **/ public var body:String; /** - Human-readable XML character data that specifies the topic. + Human-readable XML character data that specifies the topic. **/ public var subject:String; /** - String to uniquely identify a conversation thread or chat session". - **/ + String to uniquely identify a conversation thread or chat session". + **/ public var thread:String; - public function new(?to:String, ?body:String, ?subject:String, ?type:MessageType=chat, ?thread:String, ?from:String) { + public function new(?to:String, ?body:String, ?subject:String, ?type:MessageType = chat, ?thread:String, ?from:String) { super(to, from); this.body = body; this.subject = subject; @@ -95,10 +93,14 @@ private class MessageStanza extends Stanza { public function toXML():XML { var xml = Stanza.createXML(this, Message.NAME); - if(type != null) xml.set("type", Std.string(type)); - if(body != null) xml.append(XML.create("body", body)); - if(thread != null) xml.append(XML.create("thread", thread)); - for(e in properties) xml.append(e); + if (type != null) + xml.set("type", Std.string(type)); + if (body != null) + xml.append(XML.create("body", body)); + if (thread != null) + xml.append(XML.create("thread", thread)); + for (e in properties) + xml.append(e); return xml; } @@ -107,11 +109,16 @@ private class MessageStanza extends Stanza { m.type = xml.get('type'); for (e in xml.elements) { switch e.name { - case 'body': m.body = e.text; - case 'subject': m.subject = e.text; - case 'thread': m.thread = e.text; - case 'error': m.error = e; - case _: m.properties.push(e); + case 'body': + m.body = e.text; + case 'subject': + m.subject = e.text; + case 'thread': + m.thread = e.text; + case 'error': + m.error = e; + case _: + m.properties.push(e); } } return m; diff --git a/src/xmpp/Muc.hx b/src/xmpp/Muc.hx index e57619b7..537a85c1 100644 --- a/src/xmpp/Muc.hx +++ b/src/xmpp/Muc.hx @@ -26,238 +26,244 @@ enum abstract Role(String) from String to String { } /** - [Multi-User Chat Status Codes](https://xmpp.org/registrar/mucstatus.html) + [Multi-User Chat Status Codes](https://xmpp.org/registrar/mucstatus.html) **/ enum abstract StatusCode(Int) from Int to Int { - var SEE_FULL_JID = 100; - var AFFILIATION_CHANGED_IN_ABSENCE = 101; - var SHOW_UNAVAILABLE_MEMBERS = 102; - var HIDE_UNAVAILABLE_MEMBERS = 103; - var CONFIGURATION_CHANGED = 104; - var PRESENCE_REFERS_TO_OCCUPANT = 110; - var LOGGING_ENABLED = 170; - var LOGGING_DISABLED = 171; - var NON_ANONYMOUS = 172; - var SEMI_ANONYMOUS = 173; - var FULL_ANONYMOUS = 174; - var NEW_ROOM = 201; - var ROOMNICK_MODIFIED = 210; - var BANNED = 301; - var NEW_ROOM_NICKNAME = 303; - var KICKED = 307; - var REMOVED_CAUSE_AFFILIATION_CHANGE = 321; - var MEMBERS_ONLY_NOW = 322; - var SYSTEM_SHUTDOWN = 332; + var SEE_FULL_JID = 100; + var AFFILIATION_CHANGED_IN_ABSENCE = 101; + var SHOW_UNAVAILABLE_MEMBERS = 102; + var HIDE_UNAVAILABLE_MEMBERS = 103; + var CONFIGURATION_CHANGED = 104; + var PRESENCE_REFERS_TO_OCCUPANT = 110; + var LOGGING_ENABLED = 170; + var LOGGING_DISABLED = 171; + var NON_ANONYMOUS = 172; + var SEMI_ANONYMOUS = 173; + var FULL_ANONYMOUS = 174; + var NEW_ROOM = 201; + var ROOMNICK_MODIFIED = 210; + var BANNED = 301; + var NEW_ROOM_NICKNAME = 303; + var KICKED = 307; + var REMOVED_CAUSE_AFFILIATION_CHANGE = 321; + var MEMBERS_ONLY_NOW = 322; + var SYSTEM_SHUTDOWN = 332; } typedef Item = { - var jid : String; - var affiliation : Affiliation; - var role : Role; - var ?nick : String; - //var actor : String; - //var reason : String; - //var continue_ : String; + var jid:String; + var affiliation:Affiliation; + var role:Role; + var ?nick:String; + // var actor : String; + // var reason : String; + // var continue_ : String; } /** - Extension for multi-user text chat, whereby multiple users can exchange messages in the context of a room or channel. + Extension for multi-user text chat, whereby multiple users can exchange messages in the context of a room or channel. - In addition to standard chatroom features such as room topics and invitations, the protocol defines a strong room control model, including the ability to kick and ban users, to name room moderators and administrators, to require membership or passwords in order to join the room, etc. + In addition to standard chatroom features such as room topics and invitations, the protocol defines a strong room control model, including the ability to kick and ban users, to name room moderators and administrators, to require membership or passwords in order to join the room, etc. - [XEP-0045: Multi-User Chat](https://xmpp.org/extensions/xep-0045.html) - [XEP-0249: Direct MUC Invitations](https://xmpp.org/extensions/xep-0249.html) + [XEP-0045: Multi-User Chat](https://xmpp.org/extensions/xep-0045.html) + [XEP-0249: Direct MUC Invitations](https://xmpp.org/extensions/xep-0249.html) **/ @xep(45) class Muc { + public static inline var XMLNS = "http://jabber.org/protocol/muc"; - public static inline var XMLNS = "http://jabber.org/protocol/muc"; + public dynamic function onMessage(message:xmpp.Message) {} - public dynamic function onMessage(message:xmpp.Message) {} public dynamic function onPresence(presence:xmpp.Presence) {} - public dynamic function onNick(nick:String) {} - public dynamic function onSubject(subject:String) {} - public final stream : Stream; + public dynamic function onNick(nick:String) {} - public var host(default,null) : String; - public var room(default,null) : String; - public var nick(default,null) : String; - public var role(default,null) : Role; - public var affiliation(default,null) : Affiliation; - public var subject(default,null) : String; - //public var joined(default,null) = false; + public dynamic function onSubject(subject:String) {} - public var jid(get,null) : Jid; - inline function get_jid() return new Jid(room, host, nick); + public final stream:Stream; - public function new(stream:Stream, host:String, room:String) { - this.stream = stream; + public var host(default, null):String; + public var room(default, null):String; + public var nick(default, null):String; + public var role(default, null):Role; + public var affiliation(default, null):Affiliation; + public var subject(default, null):String; + + // public var joined(default,null) = false; + public var jid(get, null):Jid; + + inline function get_jid() + return new Jid(room, host, nick); + + public function new(stream:Stream, host:String, room:String) { + this.stream = stream; this.host = host; this.room = room; - } + } - /** - Enter groupchat. - **/ - public function join(nick : String, ?password : String, handler:Response->Void) { - final p = new Presence(); - p.id = Stream.makeRandomId(); - p.to = '$room@$host/$nick'; - p.properties.push(XML.create('x').set('xmlns','$XMLNS')); - stream.query(p, xml -> { - final r = parseUserPresence(xml); - if(r.statusCodes.contains(PRESENCE_REFERS_TO_OCCUPANT)) { - final item = r.items[0]; - this.affiliation = item.affiliation; - this.role = item.role; - this.nick = item.nick; - //onLeave(); - //handler(Result(null)); - } - if(r.statusCodes.contains(PRESENCE_REFERS_TO_OCCUPANT)) { - handler(Result(null)); - } else { - trace("???"); - } - }); - } + /** + Enter groupchat. + **/ + public function join(nick:String, ?password:String, handler:Response->Void) { + final p = new Presence(); + p.id = Stream.makeRandomId(); + p.to = '$room@$host/$nick'; + p.properties.push(XML.create('x').set('xmlns', '$XMLNS')); + stream.query(p, xml -> { + final r = parseUserPresence(xml); + if (r.statusCodes.contains(PRESENCE_REFERS_TO_OCCUPANT)) { + final item = r.items[0]; + this.affiliation = item.affiliation; + this.role = item.role; + this.nick = item.nick; + // onLeave(); + // handler(Result(null)); + } + if (r.statusCodes.contains(PRESENCE_REFERS_TO_OCCUPANT)) { + handler(Result(null)); + } else { + trace("???"); + } + }); + } - /** - Leave groupchat. - **/ - public function leave(?message: String, ?handler: Void->Void) { - final p = new Presence(null, message); - p.type = unavailable; - p.id = Stream.makeRandomId(); - p.to = jid; - //p.properties.push(XML.create('x').set('xmlns','$XMLNS')); - stream.query(p, xml -> { - final r = parseUserPresence(xml); - if(r.statusCodes.contains(PRESENCE_REFERS_TO_OCCUPANT)) { - final item = r.items[0]; - this.affiliation = item.affiliation; - this.role = item.role; - this.nick = item.nick; - //onLeave(); - if(handler != null) handler(); - } - }); - } + /** + Leave groupchat. + **/ + public function leave(?message:String, ?handler:Void->Void) { + final p = new Presence(null, message); + p.type = unavailable; + p.id = Stream.makeRandomId(); + p.to = jid; + // p.properties.push(XML.create('x').set('xmlns','$XMLNS')); + stream.query(p, xml -> { + final r = parseUserPresence(xml); + if (r.statusCodes.contains(PRESENCE_REFERS_TO_OCCUPANT)) { + final item = r.items[0]; + this.affiliation = item.affiliation; + this.role = item.role; + this.nick = item.nick; + // onLeave(); + if (handler != null) + handler(); + } + }); + } - /** - Invite user to this room. - **/ - public function invite(jid: String, reason: String) : Message { - //TODO: https://xmpp.org/extensions/xep-0249.html - final m = new xmpp.Message(jid); - m.properties.push(XML.create("x").set("xmlns",'$XMLNS#user') - .append(XML.create("invite").set("to", '$room@$host') - .append(XML.create("reason", reason)))); - return stream.send(m); - } + /** + Invite user to this room. + **/ + public function invite(jid:String, reason:String):Message { + // TODO: https://xmpp.org/extensions/xep-0249.html + final m = new xmpp.Message(jid); + m.properties.push(XML.create("x") + .set("xmlns", '$XMLNS#user') + .append(XML.create("invite").set("to", '$room@$host').append(XML.create("reason", reason)))); + return stream.send(m); + } - /** - Send a message to all occupants in the room. - **/ - public function say(message: String) { - var m = new Message(jid, message, null, groupchat); - //m.id = Stream.makeRandomId(); - return stream.send(m); - } + /** + Send a message to all occupants in the room. + **/ + public function say(message:String) { + var m = new Message(jid, message, null, groupchat); + // m.id = Stream.makeRandomId(); + return stream.send(m); + } - /** - Send a private message to a member of the room. - **/ - public function sendPrivateMessage(nick: String, message: String) { - var m = new Message('$room@$host/$nick', message, null, groupchat); - m.properties.push(XML.create('x').set('xmlns','$XMLNS#user')); - //m.id = Stream.makeRandomId(); - return stream.send(m); - } + /** + Send a private message to a member of the room. + **/ + public function sendPrivateMessage(nick:String, message:String) { + var m = new Message('$room@$host/$nick', message, null, groupchat); + m.properties.push(XML.create('x').set('xmlns', '$XMLNS#user')); + // m.id = Stream.makeRandomId(); + return stream.send(m); + } - /** - Change your nick name. - **/ - public function changeNick(nick: String, handler: xmpp.Stanza.Error->Void) { - final p = new Presence(); - p.id = Stream.makeRandomId(); - p.to = '$room@$host/$nick'; - stream.query(p, xml -> { - final r = parseUserPresence(xml); - if(r.statusCodes.contains(PRESENCE_REFERS_TO_OCCUPANT)) { - handler(null); - } else { - trace("????"); - } - }); - } + /** + Change your nick name. + **/ + public function changeNick(nick:String, handler:xmpp.Stanza.Error->Void) { + final p = new Presence(); + p.id = Stream.makeRandomId(); + p.to = '$room@$host/$nick'; + stream.query(p, xml -> { + final r = parseUserPresence(xml); + if (r.statusCodes.contains(PRESENCE_REFERS_TO_OCCUPANT)) { + handler(null); + } else { + trace("????"); + } + }); + } - /** - Change the room subject. - **/ - public function changeSubject(subject :String) { - //todo id handling query - stream.send(new xmpp.Message('$room@$host', null, subject)); - } + /** + Change the room subject. + **/ + public function changeSubject(subject:String) { + // todo id handling query + stream.send(new xmpp.Message('$room@$host', null, subject)); + } - public function handlePresence(presence: Presence) { - final jid : Jid = presence.from; - if(jid.getBare() != this.jid.getBare()) { - trace('???????????????????????????'); - return; - } - //var nick = jid.resource; - var r = parseUserPresence(presence); - if(r.statusCodes.contains(PRESENCE_REFERS_TO_OCCUPANT)) { - if(r.statusCodes.contains(NEW_ROOM_NICKNAME)) { - onNick(r.items[0].nick); - } - } - } + public function handlePresence(presence:Presence) { + final jid:Jid = presence.from; + if (jid.getBare() != this.jid.getBare()) { + trace('???????????????????????????'); + return; + } + // var nick = jid.resource; + var r = parseUserPresence(presence); + if (r.statusCodes.contains(PRESENCE_REFERS_TO_OCCUPANT)) { + if (r.statusCodes.contains(NEW_ROOM_NICKNAME)) { + onNick(r.items[0].nick); + } + } + } - public function handleMessage(message: Message) { - final jid : Jid = message.from; - if(jid.getBare() != this.jid.getBare()) { - trace('???????????????????????????'); - return; - } - switch message.type { - case groupchat: - if(message.subject != null) { - onSubject(this.subject = message.subject); - } - if(message.body != null) { - onMessage(message); - } - case _: - trace("?????"); - } - } + public function handleMessage(message:Message) { + final jid:Jid = message.from; + if (jid.getBare() != this.jid.getBare()) { + trace('???????????????????????????'); + return; + } + switch message.type { + case groupchat: + if (message.subject != null) { + onSubject(this.subject = message.subject); + } + if (message.body != null) { + onMessage(message); + } + case _: + trace("?????"); + } + } - static function parseUserPresence(p:xmpp.Presence) : { statusCodes: Array, items: Array } { - final r = { statusCodes: new Array(), items: new Array()} - for(e in p.properties) { - //if(e != '$XMLNS#user') - if(!e.is('$XMLNS#user')) - continue; - for(e in e.elements) { - switch e.name { - case "status": - if(e.has("code")) { - final code : Null = try Std.parseInt(e["code"]) catch(e) null; - if(code != null) r.statusCodes.push(code); - } - case "item": - r.items.push({ - jid: e["jid"], - affiliation : e["affiliation"], - role : e["role"], - nick : e["nick"], - }); - } - } - } - return r; - } + static function parseUserPresence(p:xmpp.Presence):{statusCodes:Array, items:Array} { + final r = {statusCodes: new Array(), items: new Array()} + for (e in p.properties) { + // if(e != '$XMLNS#user') + if (!e.is('$XMLNS#user')) + continue; + for (e in e.elements) { + switch e.name { + case "status": + if (e.has("code")) { + final code:Null = try Std.parseInt(e["code"]) catch (e) null; + if (code != null) + r.statusCodes.push(code); + } + case "item": + r.items.push({ + jid: e["jid"], + affiliation: e["affiliation"], + role: e["role"], + nick: e["nick"], + }); + } + } + } + return r; + } } diff --git a/src/xmpp/Ping.hx b/src/xmpp/Ping.hx index 917dcbc8..11fa9818 100644 --- a/src/xmpp/Ping.hx +++ b/src/xmpp/Ping.hx @@ -3,15 +3,14 @@ package xmpp; import xmpp.IQ; /** - Application-level pings. + Application-level pings. - [XEP-0199](https://xmpp.org/extensions/xep-0199.html) + [XEP-0199](https://xmpp.org/extensions/xep-0199.html) **/ @xep(199) class Ping { - public static inline var XMLNS = "urn:xmpp:ping"; - public static inline function ping(stream: Stream, ?jid: String, ?handler: Response->Void): IQ - return stream.get(Payload.create(XMLNS, "ping"), jid, handler); + public static inline function ping(stream:Stream, ?jid:String, ?handler:Response->Void):IQ + return stream.get(Payload.create(XMLNS, "ping"), jid, handler); } diff --git a/src/xmpp/Presence.hx b/src/xmpp/Presence.hx index a682a0ed..202c32f0 100644 --- a/src/xmpp/Presence.hx +++ b/src/xmpp/Presence.hx @@ -3,7 +3,6 @@ package xmpp; using xmpp.Stanza; enum abstract Show(String) to String { - /** Especially socialable **/ var chat; @@ -13,21 +12,20 @@ enum abstract Show(String) to String { /** Extended Away **/ var xa; - /** Busy **/ + /** Busy (do not disturb) **/ var dnd; - @:from public static inline function fromString(s:String) - return switch s { - case 'chat': chat; - case 'away': away; - case 'xa': xa; - case 'dnd': dnd; - case _: null; - } + // @:from public static inline function fromString(s:String) + // return switch s { + // case 'chat': chat; + // case 'away': away; + // case 'xa': xa; + // case 'dnd': dnd; + // case _: null; + // } } enum abstract PresenceType(String) to String { - /** An error has occurred regarding processing or delivery of a previously-sent presence stanza. **/ var error; @@ -49,7 +47,7 @@ enum abstract PresenceType(String) to String { /** The subscription request has been denied or a previously-granted subscription has been cancelled. **/ var unsubscribed; - @:from public static inline function fromString(s:String) : PresenceType { + @:from public static inline function fromString(s:String):PresenceType { return switch s { case 'error': error; case 'probe': probe; @@ -59,12 +57,12 @@ enum abstract PresenceType(String) to String { case 'unsubscribed': unsubscribed; case null, _: null; } - } + } } abstract Status(String) from String to String { - - @:noCompletion public inline function new(s:String) this = s; + @:noCompletion public inline function new(s:String) + this = s; @:to public inline function toXML():XML return XML.create("status", this); @@ -74,7 +72,6 @@ abstract Status(String) from String to String { } abstract Priority(Int) from Int to Int { - public static inline var MIN = -128; public static inline var MAX = 127; @@ -98,7 +95,6 @@ abstract Priority(Int) from Int to Int { Presence subscription states. **/ enum abstract Subscription(String) from String to String { - /** The user and subscriber have no interest in each other's presence. */ var none; @@ -130,11 +126,10 @@ enum abstract Subscription(String) from String to String { The `` element represents a broadcast or *publish-subscribe* mechanism, whereby multiple entities receive information about an entity to which they have subscribed (in this case, network availability information). - @see https://xmpp.org/rfcs/rfc3921.html> + @see https://xmpp.org/rfcs/rfc3921.html> **/ -@:forward(from,to,id,lang,error,type,show,status,priority,properties) +@:forward(from, to, id, lang, error, type, show, status, priority, properties) abstract Presence(PresenceStanza) to Stanza { - public static inline var NAME = 'presence'; public inline function new(?show:Show, ?status:Status, ?priority:Priority, ?type:PresenceType) @@ -154,39 +149,38 @@ abstract Presence(PresenceStanza) to Stanza { } private class PresenceStanza extends Stanza { - /** - The absence of a `type` attribute signals that the relevant entity is available for communication. - A `type` attribute with a value of *unavailable* signals that the relevant entity is not available for communication. + The absence of a `type` attribute signals that the relevant entity is available for communication. + A `type` attribute with a value of *unavailable* signals that the relevant entity is not available for communication. - Note: - - There is no default value for the `type` attribute of the `` element. - - There is no value of *available* for the `type` attribute of the `` element. + Note: + - There is no default value for the `type` attribute of the `` element. + - There is no value of *available* for the `type` attribute of the `` element. - @see - **/ + @see + **/ public var type:PresenceType; - /** - Optional `` element specifying the particular availability sub-state of an entity or a specific resource thereof. + /** + Optional `` element specifying the particular availability sub-state of an entity or a specific resource thereof. - @see https://xmpp.org/rfcs/rfc6121.html#presence-syntax-children-show - **/ + @see https://xmpp.org/rfcs/rfc6121.html#presence-syntax-children-show + **/ public var show:Show; /** - Optional `` element containing human-readable XML character data specifying a natural-language description of an entity's availability. - It is normally used in conjunction with the show element to provide a detailed description of an availability state (e.g., "In a meeting") when the presence stanza has no `type` attribute. + Optional `` element containing human-readable XML character data specifying a natural-language description of an entity's availability. + It is normally used in conjunction with the show element to provide a detailed description of an availability state (e.g., "In a meeting") when the presence stanza has no `type` attribute. - @see https://xmpp.org/rfcs/rfc6121.html#presence-syntax-children-status - **/ + @see https://xmpp.org/rfcs/rfc6121.html#presence-syntax-children-status + **/ public var status:Status; /** Optional `` element containing non-human-readable XML character data that specifies the priority level of the resource. The value MUST be an integer between `-128` and `+127`. - @see - **/ + @see + **/ public var priority:Null; public function new(?show:Show, ?status:String, ?priority:Priority, ?type:PresenceType) { @@ -199,11 +193,16 @@ private class PresenceStanza extends Stanza { public function toXML():XML { final xml = Stanza.createXML(this, Presence.NAME); - if (type != null) xml.set("type", type); - if (show != null) xml.append(XML.create("show", show)); - if (status != null) xml.append(XML.create("status", status)); - if (priority != null) xml.append(priority); - for (p in properties) xml.append(p); + if (type != null) + xml.set("type", type); + if (show != null) + xml.append(XML.create("show", show)); + if (status != null) + xml.append(XML.create("status", status)); + if (priority != null) + xml.append(priority); + for (p in properties) + xml.append(p); return xml; } @@ -212,11 +211,16 @@ private class PresenceStanza extends Stanza { p.type = xml.get('type'); for (e in xml.elements) { switch e.name { - case 'show': p.show = cast e.text; - case 'status': p.status= Std.string(e.text); - case 'priority': p.priority = try Std.parseInt(e.text) catch(e) null; - case 'error': p.error = e; - default: p.properties.push(e); + case 'show': + p.show = cast e.text; + case 'status': + p.status = Std.string(e.text); + case 'priority': + p.priority = try Std.parseInt(e.text) catch (e) null; + case 'error': + p.error = e; + default: + p.properties.push(e); } } return p; diff --git a/src/xmpp/PrivateStorage.hx b/src/xmpp/PrivateStorage.hx index 25e4e858..87bb8601 100644 --- a/src/xmpp/PrivateStorage.hx +++ b/src/xmpp/PrivateStorage.hx @@ -3,24 +3,23 @@ package xmpp; import xmpp.IQ; /** - Store any arbitrary XML on the server. + Store any arbitrary XML on the server. - [XEP-0049: Private XML Storage](https://xmpp.org/extensions/xep-0049.html) + [XEP-0049: Private XML Storage](https://xmpp.org/extensions/xep-0049.html) **/ @xep(49) class PrivateStorage { - public static inline var XMLNS = "jabber:iq:private"; - /** - Retrieve private data stored on server. - **/ - public static inline function getPrivateStorage(stream: Stream, data: XML, handler: Response->Void): IQ - return stream.get(Payload.create(XMLNS).append(data), handler); + /** + Retrieve private data stored on server. + **/ + public static inline function getPrivateStorage(stream:Stream, data:XML, handler:Response->Void):IQ + return stream.get(Payload.create(XMLNS).append(data), handler); - /** - Store private data on server. - **/ - public static inline function setPrivateStorage(stream: Stream, data: XML, handler: Response->Void) : IQ - return stream.set(Payload.create(XMLNS).append(data), handler); + /** + Store private data on server. + **/ + public static inline function setPrivateStorage(stream:Stream, data:XML, handler:Response->Void):IQ + return stream.set(Payload.create(XMLNS).append(data), handler); } diff --git a/src/xmpp/ServiceDiscovery.hx b/src/xmpp/ServiceDiscovery.hx index 2cfdb805..d9d67c4c 100644 --- a/src/xmpp/ServiceDiscovery.hx +++ b/src/xmpp/ServiceDiscovery.hx @@ -2,32 +2,39 @@ package xmpp; import xmpp.IQ; +typedef Identity = { + category:String, + type:String, + name:String, + ?lang:String +} + /** - Discover information about other XMPP entities. + Discover information about other XMPP entities. - [XEP-0030: Service Discovery](https://xmpp.org/extensions/xep-0030.html) + [XEP-0030: Service Discovery](https://xmpp.org/extensions/xep-0030.html) **/ @xep(30) class ServiceDiscovery { + public static inline var XMLNS_INFO = "http://jabber.org/protocol/disco#info"; + public static inline var XMLNS_ITEMS = "http://jabber.org/protocol/disco#items"; - public static inline var XMLNS_INFO = "http://jabber.org/protocol/disco#info"; - public static inline var XMLNS_ITEMS = "http://jabber.org/protocol/disco#items"; - - /** - Discover the identity and capabilities of an entity, including the protocols and features it supports. - **/ - public static inline function discoInfo(stream: Stream, ?jid: String, ?node: String, handler: Response->Void) : IQ - return disco(stream, XMLNS_INFO, jid, node, handler); + /** + Discover the identity and capabilities of an entity, including the protocols and features it supports. + **/ + public static inline function discoInfo(stream:Stream, ?jid:String, ?node:String, handler:Response->Void):IQ + return disco(stream, XMLNS_INFO, jid, node, handler); - /** - Discover the items associated with an entity, such as the list of rooms hosted at a multi-user chat service. - **/ - public static inline function discoItems(stream: Stream, ?jid: String, ?node: String, handler: Response->Void): IQ - return disco(stream, XMLNS_ITEMS, jid, node, handler); + /** + Discover the items associated with an entity, such as the list of rooms hosted at a multi-user chat service. + **/ + public static inline function discoItems(stream:Stream, ?jid:String, ?node:String, handler:Response->Void):IQ + return disco(stream, XMLNS_ITEMS, jid, node, handler); - static function disco(stream: Stream, xmlns: String, ?jid: String, ?node: String, handler: Response->Void) : IQ { - final xml = Payload.create(xmlns); - if(node != null) xml.set("node", node); - return stream.get(xml, jid ?? stream.domain, handler); - } + static function disco(stream:Stream, xmlns:String, ?jid:String, ?node:String, handler:Response->Void):IQ { + final xml = Payload.create(xmlns); + if (node != null) + xml.set("node", node); + return stream.get(xml, jid ?? stream.domain, handler); + } } diff --git a/src/xmpp/Stanza.hx b/src/xmpp/Stanza.hx index 32925675..83989658 100644 --- a/src/xmpp/Stanza.hx +++ b/src/xmpp/Stanza.hx @@ -1,7 +1,6 @@ package xmpp; enum abstract ErrorType(String) from String to String { - /** Retry after providing credentials. **/ var auth; @@ -19,16 +18,15 @@ enum abstract ErrorType(String) from String to String { } /** - Application-specific stanza error information. + Application-specific stanza error information. **/ typedef ApplicationErrorCondition = { var condition:String; var xmlns:String; - var ?properties:Array; + var ?properties:Array; } enum abstract ErrorCondition(String) from String to String { - /** The sender has sent XML that is malformed or that cannot be processed (e.g., an IQ stanza that includes an unrecognized value of the `type` attribute); The associated error type SHOULD be `modify`. **/ @@ -50,12 +48,12 @@ enum abstract ErrorCondition(String) from String to String { var forbidden; /** - The recipient or server can no longer be contacted at this address, typically on a permanent basis (as opposed to the `` error condition, which is used for temporary addressing failures); the associated error type SHOULD be `cancel` and the error stanza SHOULD include a new address (if available) as the XML character data of the `` element. + The recipient or server can no longer be contacted at this address, typically on a permanent basis (as opposed to the `` error condition, which is used for temporary addressing failures); the associated error type SHOULD be `cancel` and the error stanza SHOULD include a new address (if available) as the XML character data of the `` element. **/ var gone; /** - The server has experienced a misconfiguration or other internal error that prevents it from processing the stanza; the associated error type SHOULD be `cancel`. + The server has experienced a misconfiguration or other internal error that prevents it from processing the stanza; the associated error type SHOULD be `cancel`. **/ var internal_server_error = "internal-server-error"; @@ -70,7 +68,7 @@ enum abstract ErrorCondition(String) from String to String { var jid_malformed = "jid-malformed"; /** - The recipient or server understands the request but is refusing to process it because it does not meet criteria defined by the recipient or server (e.g., a local policy regarding acceptable words in messages); The associated error type SHOULD be `modify`. + The recipient or server understands the request but is refusing to process it because it does not meet criteria defined by the recipient or server (e.g., a local policy regarding acceptable words in messages); The associated error type SHOULD be `modify`. **/ var not_acceptable = "not-acceptable"; @@ -91,7 +89,7 @@ enum abstract ErrorCondition(String) from String to String { /** The requesting entity is not authorized to access the requested service because payment is required; The associated error type SHOULD be `auth`. - **/ + **/ var recipient_unavailable = "recipient-unavailable"; /** @@ -142,17 +140,15 @@ enum abstract ErrorCondition(String) from String to String { @:structInit private class CError { - /** **/ public var type:ErrorType; - - /** **/ + + /** **/ public var condition:ErrorCondition; /** Error generator */ public var by:String; - /** Describes the error in more detail */ public var text:String; @@ -172,81 +168,81 @@ private class CError { @:forward abstract Error(CError) from CError { - - public static inline var XMLNS = "urn:ietf:params:xml:ns:xmpp-stanzas"; + public static inline var XMLNS = "urn:ietf:params:xml:ns:xmpp-stanzas"; public inline function new(type:ErrorType, condition:ErrorCondition, ?text:String, ?app:ApplicationErrorCondition) - this = new CError(type, condition, text, app); + this = new CError(type, condition, text, app); - @:to public inline function toBool() : Bool - return this != null; + @:to public inline function toBool():Bool + return this != null; @:to public function toXML():XML { - var xml = XML.create('error').set('type', this.type) - .append(XML.create(this.condition).set('xmlns', XMLNS)); - if(this.by != null) xml.set('by', this.by); - if(this.text != null) + var xml = XML.create('error').set('type', this.type).append(XML.create(this.condition).set('xmlns', XMLNS)); + if (this.by != null) + xml.set('by', this.by); + if (this.text != null) xml.append(XML.create('text', this.text).set('xmlns', XMLNS)); - if(this.app != null && (this.app.condition != null && this.app.xmlns != null)) { - var c = XML.create(this.app.condition).set('xmlns', this.app.xmlns); - if(this.app.properties != null) for(e in this.app.properties) c.append(e); + if (this.app != null && (this.app.condition != null && this.app.xmlns != null)) { + var c = XML.create(this.app.condition).set('xmlns', this.app.xmlns); + if (this.app.properties != null) + for (e in this.app.properties) + c.append(e); xml.append(c); - } + } return xml; } @:from public static function fromXML(xml:XML):Error { - var condition: Null = null; - var text: Null = null; - var app: Null = null; + var condition:Null = null; + var text:Null = null; + var app:Null = null; for (e in xml.elements) { - switch e.ns { - case Error.XMLNS: - switch e.name { - case 'text': text = e.text; - case _: condition = e.name; - } - case _: - app = { - condition: e.name, - xmlns: e.ns, - properties: [for(e in e.elements) e] - }; - } + switch e.ns { + case Error.XMLNS: + switch e.name { + case 'text': text = e.text; + case _: condition = e.name; + } + case _: + app = { + condition: e.name, + xmlns: e.ns, + properties: [for (e in e.elements) e] + }; + } } return new Error(xml.get('type'), condition, text, app); } } abstract class Stanza { - /** JID of the intended recipient. **/ - public var to: Null; + public var to:Null; /** JID of the sender. **/ - public var from: Null; + public var from:Null; /** Used by the originating entity to track any response or error stanza that it might receive in relation to the generated stanza from another entity. **/ - public var id: Null; + public var id:Null; /** Specifies the default language of any such human-readable XML character data. **/ - public var lang: Null; + public var lang:Null; + + /** + **/ + public var error:Null; /** **/ - public var error: Null; - - /** - **/ - public var properties: Array = []; + public var properties:Array = []; inline function new(?to:String, ?from:String, ?id:String, ?lang:String) { this.to = to; @@ -262,10 +258,14 @@ abstract class Stanza { static function createXML(s:Stanza, name:String):XML { final x = XML.create(name); - if (s.to != null) x.set('to', s.to); - if (s.from != null) x.set('from', s.from); - if (s.id != null) x.set('id', s.id); - if (s.lang != null) x.set('xml:lang', s.lang); + if (s.to != null) + x.set('to', s.to); + if (s.from != null) + x.set('from', s.from); + if (s.id != null) + x.set('id', s.id); + if (s.lang != null) + x.set('xml:lang', s.lang); return x; } diff --git a/src/xmpp/Stream.hx b/src/xmpp/Stream.hx index 47842e53..1b590292 100644 --- a/src/xmpp/Stream.hx +++ b/src/xmpp/Stream.hx @@ -7,46 +7,54 @@ import xmpp.Response; using StringTools; private typedef Header = { - from: String, - to: String, - id: String, - lang: String, - version: String + from:String, + to:String, + id:String, + lang:String, + version:String } -abstract class Stream { +private typedef Feature = { + xmlns:String, + handler:IQ->IQ +} +abstract class Stream { public static inline var XMLNS = 'http://etherx.jabber.org/streams'; - /** Handle message stanzas **/ + /** Handle message stanzas **/ public dynamic function onMessage(m:Message) {} - /** Handle presence stanzas **/ + /** Handle presence stanzas **/ public dynamic function onPresence(p:Presence) {} - /** Handle iq (get,set) stanzas **/ + /** Handle iq (get,set) stanzas **/ public dynamic function onIQ(iq:IQ, res:Null>->Void) {} - /** Handle raw xml **/ - public dynamic function onRaw(xml:XML):Bool return false; + /** + Handle non-stanza xml. + This is the last ressort of handling received xml; must return `true` in order for the stream to resume. + **/ + public dynamic function onNonza(xml:XML) + return false; - /** Stream end handler **/ + /** Stream end handler **/ public dynamic function onEnd() {} public final xmlns:String; public final domain:String; public final lang:String; - public var id(default,null):String; - public var version(default,null) = "1.0"; - public var ready(default,null) = false; + public var id(default, null):String; + public var version(default, null) = "1.0"; + public var ready(default, null) = false; + + public var features = new Map(Null>->Void)->Void>(); public var input:String->Void; public var output:String->Void; - - public var features = new Map(?Null>->Void)->Void>(); - public var queries(default,null):MapVoid>; + var queries:MapVoid>; var buf:StringBuf; function new(xmlns:String, domain:String, ?lang:String) { @@ -55,105 +63,114 @@ abstract class Stream { this.lang = lang; } - /** - Info `get` query - **/ - public function get(payload:IQ.Payload, ?jid:String, ?handler:(response:Response)->Void):IQ { + /** + Info `get` query + **/ + public function get(payload:IQ.Payload, ?jid:String, ?handler:(response:Response) -> Void):IQ { final iq = new IQ(payload, IQType.Get, makeRandomId(), jid); - if(iq.id == null) iq.id = makeRandomId(); - if(handler != null) { - queries.set(iq.id, xml -> { - final iq : IQ = xml; - switch iq.type { - case Result: handler(Result(cast iq.payload)); - case Error: handler(Error(iq.error)); - default: - } - - }); - } else { - trace("TODO"); - //query(iq, null); - } + if (iq.id == null) + iq.id = makeRandomId(); + if (handler != null) { + queries.set(iq.id, xml -> { + final iq:IQ = xml; + switch iq.type { + case Result: handler(Result(cast iq.payload)); + case Error: handler(Error(iq.error)); + default: + } + }); + } else { + trace("TODO"); + // query(iq, null); + } send(iq); return iq; - } + } - /** - Info `set` query - **/ - public function set(payload:IQ.Payload, ?jid:String, handler:(response:Response)->Void):IQ { + /** + Info `set` query + **/ + public function set(payload:IQ.Payload, ?jid:String, handler:(response:Response) -> Void):IQ { final iq = new IQ(payload, IQType.Set, makeRandomId(), jid); - if(handler != null) { - queries.set(iq.id, xml -> { - final iq : IQ = xml; - switch iq.type { - case Result: handler(Result(cast iq.payload)); - case Error: handler(Error(iq.error)); - default: - } - }); - } else { - trace("TODO"); - //query(iq, null); - } - send(iq); + if (handler != null) { + queries.set(iq.id, xml -> { + final iq:IQ = xml; + switch iq.type { + case Result: handler(Result(cast iq.payload)); + case Error: handler(Error(iq.error)); + default: + } + }); + } else { + trace("TODO"); + // query(iq, null); + } + send(iq); return iq; } - public function query(stanza:Stanza, handler:(response:XML)->Void) { + public function query(stanza:Stanza, handler:(response:XML) -> Void) { if (stanza.id == null) - stanza.id = makeRandomId(); - if(handler != null) - queries.set(stanza.id, handler); + stanza.id = makeRandomId(); + if (handler != null) + queries.set(stanza.id, handler); send(stanza.toXML()); } - /** - Send stanza - **/ - public function send(xml:XML) : XML { + /** + Send stanza + **/ + // public function send(xml:XML) : XML { + // output(xml); + // return xml; + // } + // public function send(stanza:Stanza) : Stanza { + // output(stanza.toString()); + // return stanza; + // } + + public function send(xml:XML):XML { output(xml); - return xml; + return xml; } - /* - public function sendStanza(stanza:T, ?handler:T->Void) : T { - if(handler != null) { - if(stanza.id == null) stanza.id = Stream.makeRandomId(); - //queries.set(stanza.id, handler); - } - // if(stanza.id != null) { - // queries.set(stanza.id, handler); - // } - return stanza; - } - */ - - /** - Process incoming data - **/ - public function recv(str:String) : Bool { - if(str == null || str.length == 0) + /* + public function sendStanza(stanza:T, ?handler:T->Void) : T { + if(handler != null) { + if(stanza.id == null) stanza.id = Stream.makeRandomId(); + //queries.set(stanza.id, handler); + } + // if(stanza.id != null) { + // queries.set(stanza.id, handler); + // } + return stanza; + } + */ + /** + Process incoming data + **/ + public function recv(str:String):Bool { + if (str == null || str.length == 0) return false; - if(buf == null) buf = new StringBuf(); + if (buf == null) + buf = new StringBuf(); buf.add(str); - if(!str.endsWith('>')) - return false; - var received = buf.toString(); - if(received.endsWith("")) { - reset(); - onEnd(); - } else { - buf = new StringBuf(); - input(received); - } - return true; + if (!str.endsWith('>')) + return false; + final received = buf.toString(); + if (received.endsWith("")) { + reset(); + onEnd(); + } else { + buf = new StringBuf(); + input(received); + } + return true; } - /** - End stream - **/ + /** + End stream + **/ public function end() { output(''); reset(); @@ -162,85 +179,74 @@ abstract class Stream { function handleString(str:String) { if (!ready) return; - final xml = try Xml.parse(str) catch(e) { + final xml = try Xml.parse(str) catch (e) { trace(e); - if(str.endsWith("")) { - onEnd(); - return; - } + if (str.endsWith("")) { + onEnd(); + return; + } null; - } - //HACK: - if(@:privateAccess cast(xml.elements(),haxe.iterators.ArrayIterator).array.length == 1) { - handleXML(xml.firstElement()); - } else { - for(e in xml.elements()) { - handleXML(e); - } - } + } + // HACK: + if (@:privateAccess cast(xml.elements(), haxe.iterators.ArrayIterator).array.length == 1) { + handleXML(xml.firstElement()); + } else { + for (e in xml.elements()) + handleXML(e); + } } function handleXML(xml:XML) { - if(xml.has(xmlns) && xml.get('xmlns') != xmlns) { + if (xml.has(xmlns) && xml.get('xmlns') != xmlns) { trace("invalid stream namespace"); return; - } - if(xml.has("id")) { - final id = xml.get("id"); - if(queries.exists(id)) { - final h = queries.get(id); - queries.remove(id); - h(xml); - return; - } - } + } + if (xml.has("id")) { + final id = xml.get("id"); + if (queries.exists(id)) { + final h = queries.get(id); + queries.remove(id); + h(xml); + return; + } + } switch xml.name { - case 'message': onMessage(xml); - case 'presence': onPresence(xml); - case 'iq': - final iq:IQ = xml; - switch iq.type { - case Result, Error: - if (queries.exists(iq.id)) { - final h = queries.get(iq.id); - queries.remove(iq.id); - h(iq); - } else { - #if debug - trace('unhandled iq response'); - #end - } - case Get, Set: - if(iq.payload != null) { - final ns = iq.payload.xmlns; - if(features != null && features.exists(ns)) { - features.get(ns)(iq, (?res)->{ - send((res==null) - ? iq.createError({ type:cancel, condition:feature_not_implemented }) - : switch res { - case Result(r): iq.createResult(r); - case Error(e): iq.createError(e); - } - ); - }); - } else { - onIQ(iq, res -> { - send((res==null) - ? iq.createError({ type:cancel, condition:feature_not_implemented }) - : switch res { - case Result(r): iq.createResult(r); - case Error(e): iq.createError(e); - } - ); - }); - } - } - } - default: - //TODO async handler - if(!onRaw(xml)) { - end(); - } + case 'message': + onMessage(xml); + case 'presence': + onPresence(xml); + case 'iq': + final iq:IQ = xml; + switch iq.type { + case Result, Error: + if (queries.exists(iq.id)) { + final h = queries.get(iq.id); + queries.remove(iq.id); + h(iq); + } else { + #if debug + trace('unhandled iq response'); + #end + } + case Get, Set: + if (iq.payload != null) { + final ns = iq.payload.xmlns; + function handleRes(?res) { + send((res == null) ? iq.createError({type: cancel, condition: feature_not_implemented}) : switch res { + case Result(r): iq.createResult(r); + case Error(e): iq.createError(e); + }); + } + if (features != null && features.exists(ns)) { + features.get(ns)(iq, handleRes); + } else { + onIQ(iq, handleRes); + } + } + } + default: + if (!onNonza(xml)) + end(); } } @@ -249,17 +255,19 @@ abstract class Stream { queries = new Map(); buf = new StringBuf(); } - - public static function makeRandomId(seed:String='', length = 8) : String - return Std.string(Md5.encode(seed + Date.now().getTime() + (Math.random()*1))).substr(0, length); + + public static function makeRandomId(seed:String = '', length = 8):String + return Std.string(Md5.encode(seed + Date.now().getTime() + (Math.random() * 1))).substr(0, length); static function createHeader(xmlns:String, to:String, ?version:String, ?lang:String):String { final xml = XML.create('stream:stream') .set('xmlns', xmlns) .set('xmlns:stream', xmpp.Stream.XMLNS) .set('to', to); - if (version != null) xml.set('version', version); - if (lang != null) xml.set('xml:lang', lang); + if (version != null) + xml.set('version', version); + if (lang != null) + xml.set('xml:lang', lang); var str = xml.toString(); str = str.substr(0, str.lastIndexOf('/')) + '>'; return str; @@ -267,10 +275,11 @@ abstract class Stream { static function readHeader(str:String):Header { final r = ~/^(<\?xml) (.)+\?>/; - if (r.match(str)) str = r.matchedRight(); + if (r.match(str)) + str = r.matchedRight(); // TODO handle stream:error // var i = str.lastIndexOf( "/>" ); - if(!str.endsWith('/>')) { + if (!str.endsWith('/>')) { final i = str.indexOf(">"); if (i == -1) throw 'invalid stream header'; // TODO: @@ -290,7 +299,7 @@ abstract class Stream { var i = str.indexOf(""); if (i != -1) str = str.substr(0, i); diff --git a/src/xmpp/Uuid.hx b/src/xmpp/Uuid.hx index 74348e2d..cb14adeb 100644 --- a/src/xmpp/Uuid.hx +++ b/src/xmpp/Uuid.hx @@ -1,46 +1,51 @@ package xmpp; /** - Universally unique identifier. + Universally unique identifier. - 128-bit label used for information. + 128-bit label used for information. **/ class Uuid { - /** Returns a string value representing a UUID value. **/ - public static function make() : String { + public static function make():String { var s = []; - for(i in 0...8) s[i] = randomChar(); + for (i in 0...8) + s[i] = rchar(); s[8] = '-'; - for(i in 9...13) s[i] = randomChar(); + for (i in 9...13) + s[i] = rchar(); s[13] = '-'; s[14] = '4'; - for(i in 15...18) s[i] = randomChar(); + for (i in 15...18) + s[i] = rchar(); s[18] = '-'; - s[19] = "89AB".charAt(randomInt(0x3)); - for(i in 20...23) s[i] = randomChar(); + s[19] = "89AB".charAt(rint(0x3)); + for (i in 20...23) + s[i] = rchar(); s[23] = '-'; - for(i in 24...36) s[i] = randomChar(); + for (i in 24...36) + s[i] = rchar(); return s.join(''); } - public static inline function randomInt(max:Int) : Int + public static inline function rint(max:Int):Int return Math.floor(Math.random() * max); - public static inline function randomChar() : String - return "0123456789abcdef".charAt(randomInt(0x10)); + public static inline function rchar():String + return "0123456789abcdef".charAt(rint(0x10)); - public static function randomString(length: Int) : String { - var s = new StringBuf(); - for(_ in 0...length) s.add(randomChar()); - return s.toString(); - } + public static function rstring(length:Int):String { + var s = new StringBuf(); + for (_ in 0...length) + s.add(rchar()); + return s.toString(); + } /** Returns `true` if the passed `uuid` conforms to the UUID v.4 format. **/ - // public static function isValidUuid(str : String) : Bool { - // return ~/^[0123456789abcdef]{8}-[0123456789abcdef]{4}-4[0123456789abcdef]{3}-[89ab][0123456789abcdef]{3}-[0123456789abcdef]{12}$/i.match(str); + // public static function isValid( uuid : String ) : Bool { + // return ~/^[0123456789abcdef]{8}-[0123456789abcdef]{4}-4[0123456789abcdef]{3}-[89ab][0123456789abcdef]{3}-[0123456789abcdef]{12}$/i.match(uuid); // } } diff --git a/src/xmpp/VCard.hx b/src/xmpp/VCard.hx index 1f58b04b..60e38839 100644 --- a/src/xmpp/VCard.hx +++ b/src/xmpp/VCard.hx @@ -3,18 +3,17 @@ package xmpp; import xmpp.IQ; /** - vcard-temp + vcard-temp - [XEP-0054: vcard-temp](https://xmpp.org/extensions/xep-0054.html) + [XEP-0054: vcard-temp](https://xmpp.org/extensions/xep-0054.html) **/ @xep(54) class VCard { + public static inline var XMLNS = "vcard-temp"; - public static inline var XMLNS = "vcard-temp"; + public static inline function getVCard(stream:Stream, ?jid:String, handler:Response->Void):IQ + return stream.get(Payload.create(XMLNS, "vCard"), jid, handler); - public static inline function getVCard(stream: Stream, ?jid: String, handler: Response->Void) : IQ - return stream.get(Payload.create(XMLNS, "vCard"), jid, handler); - - public static inline function setVCard(stream: Stream, vcard: XML, handler: Response->Void) : IQ - return stream.set(Payload.create(XMLNS, "vCard").append(vcard), handler); + public static inline function setVCard(stream:Stream, vcard:XML, handler:Response->Void):IQ + return stream.set(Payload.create(XMLNS, "vCard").append(vcard), handler); } diff --git a/src/xmpp/XML.hx b/src/xmpp/XML.hx index f9a5e769..6ad17cc9 100644 --- a/src/xmpp/XML.hx +++ b/src/xmpp/XML.hx @@ -3,44 +3,51 @@ package xmpp; import Xml; @:access(Xml) -@:forward(attributes,children,addChild,removeChild) +@:forward(attributes, children, addChild, removeChild) abstract XML(Xml) from Xml to Xml { + public var parent(get, never):XML; - public var parent(get,never):XML; - inline function get_parent() : XML - return this.parent; + inline function get_parent():XML + return this.parent; /** Node type **/ - public var type(get,never):XmlType; + public var type(get, never):XmlType; + inline function get_type():XmlType return this.nodeType; /** Node name **/ - public var name(get,never):String; + public var name(get, never):String; + inline function get_name():String return this.nodeName; - public var firstElement(get,never):XML; + public var firstElement(get, never):XML; + inline function get_firstElement():XML return this.firstElement(); /** Element namespace **/ - public var ns(get,set):String; + public var ns(get, set):String; + inline function get_ns():String return this.get('xmlns'); + inline function set_ns(ns:String):String { this.set('xmlns', ns); return ns; } - public var text(get,set):Text; + public var text(get, set):Text; + function get_text():Text return switch type { case XmlType.Element: - final c = this.firstChild(); + final c = this.firstChild(); if (c == null) null else c.nodeValue; default: null; } + function set_text(v:Text):Text { switch type { case XmlType.Element: @@ -54,51 +61,53 @@ abstract XML(Xml) from Xml to Xml { return v; } - public var elements(get,never):xmpp.XML.NodeIterator; + public var elements(get, never):xmpp.XML.NodeIterator; + inline function get_elements():xmpp.XML.NodeIterator { return cast this.elements(); - //return this.firstChild().elements(); - } + // return this.firstChild().elements(); + } + // inline function set_elements(elements:xmpp.XML.NodeIterator):xmpp.XML.NodeIterator { // this.children = [for (e in elements) e]; // // //for (e in elements) this.addChild(e); // return get_elements(); // } - - //public var first(get,never) : XML; - //inline function get_first() : - + // public var first(get,never) : XML; + // inline function get_first() : // public var element(get,never) : ElementAccess; // function get_element() : ElementAccess return new ElementAccess( this ); - @:noDoc inline function new(x:Xml) this = x; + @:noDoc inline function new(x:Xml) + this = x; - //TODO: really? + // TODO: really? // public inline function iterator() : NodeIterator - // return this.elements(); + // return this.elements(); @:arrayAccess public inline function get(att:String):String return this.get(att); @:arrayAccess public function set(att:String, ?value:String):XML { - if(value==null) return unset(att); + if (value == null) + return unset(att); this.set(att, value); return this; } - public inline function has(att: String):Bool + public inline function has(att:String):Bool return this.exists(att); - - //@:op(A==B) - public inline function is(xmlns: String) : Bool - return this.get('xmlns') == xmlns; - public inline function unset(att: String): XML { - this.remove(att); - return this; - } + // @:op(A==B) + public inline function is(xmlns:String):Bool + return this.get('xmlns') == xmlns; - public inline function append(e:XML):XML { + public inline function unset(att:String):XML { + this.remove(att); + return this; + } + + public inline function append(e:XML):XML { this.addChild(e); return this; } @@ -113,8 +122,6 @@ abstract XML(Xml) from Xml to Xml { // // public inline function removeChild(x:XML):Bool // return this.removeChild(x); - - // @:arrayAccess public inline function getChildAt(i:Int):XML // return this.children[i]; @@ -123,58 +130,60 @@ abstract XML(Xml) from Xml to Xml { public static function create(name:String, ?attributes:Map, ?text:String):XML { var x:XML = Xml.createElement(name); - if(attributes != null) for(k => v in attributes) x.set(k, v); - if(text != null) x.append(Xml.createPCData(text)); + if (attributes != null) + for (k => v in attributes) + x.set(k, v); + if (text != null) + x.append(Xml.createPCData(text)); return x; } - /* - @:from public static inline function fromXml(x:Xml):XML { - return switch x.nodeType { - //case Document: x.firstElement(); - case Document: x.firstElement(); - // if(@:privateAccess cast(x.elements(), haxe.iterators.ArrayIterator).array.length == 1) { - // return x; - // } - return x.firstElement(); - case _: x; + /* + @:from public static inline function fromXml(x:Xml):XML { + return switch x.nodeType { + //case Document: x.firstElement(); + case Document: x.firstElement(); + // if(@:privateAccess cast(x.elements(), haxe.iterators.ArrayIterator).array.length == 1) { + // return x; + // } + return x.firstElement(); + case _: x; + } } - } - */ + */ @:from public static inline function fromString(str:String):XML { - var xml = Xml.parse(str); - //trace(@:privateAccess cast(xml.elements(), haxe.iterators.ArrayIterator).array.length); - return new XML(xml.firstElement()); - } - - //@:from - public static inline function parse(str:String):XML { - //return fromXml(Xml.parse(s).firstElement()); - //trace(@:privateAccess cast(x.elements(), haxe.iterators.ArrayIterator).array.length); - var x : Xml = Xml.parse(str); - return new XML(x); - } - - /* - macro public static function markup(mu):ExprOf { - return switch mu.expr { - case EMeta({name: ":markup"}, {expr: EConst(CString(s))}): - macro XML.parse($v{s}); - case _: - throw new haxe.macro.Expr.Error("not an xml literal", mu.pos); - } + var xml = Xml.parse(str); + // trace(@:privateAccess cast(xml.elements(), haxe.iterators.ArrayIterator).array.length); + return new XML(xml.firstElement()); + } + + // @:from + public static inline function parse(str:String):XML { + // return fromXml(Xml.parse(s).firstElement()); + // trace(@:privateAccess cast(x.elements(), haxe.iterators.ArrayIterator).array.length); + var x:Xml = Xml.parse(str); + return new XML(x); } - */ + /* + macro public static function markup(mu):ExprOf { + return switch mu.expr { + case EMeta({name: ":markup"}, {expr: EConst(CString(s))}): + macro XML.parse($v{s}); + case _: + throw new haxe.macro.Expr.Error("not an xml literal", mu.pos); + } + } + */ } @:noDoc -@:forward(next,hasNext) -//private abstract NodeIterator(Iterator) from Iterator to Iterator { +@:forward(next, hasNext) +// private abstract NodeIterator(Iterator) from Iterator to Iterator { private abstract NodeIterator(haxe.iterators.ArrayIterator) from haxe.iterators.ArrayIterator to haxe.iterators.ArrayIterator { + public var length(get, never):Int; - public var length(get,never) : Int; - inline function get_length() : Int - return @:privateAccess cast(this, haxe.iterators.ArrayIterator).array.length; + inline function get_length():Int + return @:privateAccess cast(this, haxe.iterators.ArrayIterator).array.length; // public inline function next():XML // return this.next(); @@ -183,18 +192,18 @@ private abstract NodeIterator(haxe.iterators.ArrayIterator) from haxe.itera Get nth(i) element. **/ @:arrayAccess public function index(i:Int):XML { - return @:privateAccess cast(this, haxe.iterators.ArrayIterator).array[i]; - /* - var j = 0; - while (j <= i) { - if (!this.hasNext()) - return null; - var n = next(); - if (j++ == i) - return n; - } - return null; - */ + return @:privateAccess cast(this, haxe.iterators.ArrayIterator).array[i]; + /* + var j = 0; + while (j <= i) { + if (!this.hasNext()) + return null; + var n = next(); + if (j++ == i) + return n; + } + return null; + */ } /** @@ -202,7 +211,7 @@ private abstract NodeIterator(haxe.iterators.ArrayIterator) from haxe.itera **/ @:arrayAccess public function named(name:String):Array { final e = new Array(); - while(this.hasNext()) { + while (this.hasNext()) { var c = this.next(); if (c.type == Element && c.name == name) e.push(c); @@ -210,35 +219,37 @@ private abstract NodeIterator(haxe.iterators.ArrayIterator) from haxe.itera return e; } - public function first(?f:XML->Bool) : XML { - if(f == null) - @:privateAccess cast(this, haxe.iterators.ArrayIterator).array[0]; - for(e in this) if(f(e)) return e; - return null; - } - - public function findElement(name: String) : XML { - var c : XML = null; - while(this.hasNext()) - if((c = this.next()).name == name) - return c; - //while((c = this.next()) != null) if(c.name == name) return c; - return null; - } + public function first(?f:XML->Bool):XML { + if (f == null) + @:privateAccess cast(this, haxe.iterators.ArrayIterator).array[0]; + for (e in this) + if (f(e)) + return e; + return null; + } + + public function findElement(name:String):XML { + var c:XML = null; + while (this.hasNext()) + if ((c = this.next()).name == name) + return c; + // while((c = this.next()) != null) if(c.name == name) return c; + return null; + } public function count():Int { - //trace('COUNT'); + // trace('COUNT'); var i = 0; - while(this.next() != null) i++; + while (this.next() != null) + i++; return i; - //return @:privateAccess cast(this, haxe.iterators.ArrayIterator).array.length; + // return @:privateAccess cast(this, haxe.iterators.ArrayIterator).array.length; } - // public function hasChild(f: XML->Bool) : Bool { - // for( e in this ) if( f(e) ) return true; - // return false; - // } - + // public function hasChild(f: XML->Bool) : Bool { + // for( e in this ) if( f(e) ) return true; + // return false; + // } /* function doFilter( f : XML->Bool ) : NodeIterator { return [for(e in this) if(f(e))e].iterator(); @@ -265,18 +276,17 @@ private abstract NodeIterator(haxe.iterators.ArrayIterator) from haxe.itera @:noDoc private abstract Text(String) from String to String { - @:to public inline function toFloat():Float return Std.parseFloat(this); @:to public inline function toInt():Int return Std.parseInt(this); - @:from public static inline function fromInt(i: Int) : Text - return Std.string(i); + @:from public static inline function fromInt(i:Int):Text + return Std.string(i); - @:from public static inline function fromFloat(f: Float) : Text - return Std.string(f); + @:from public static inline function fromFloat(f:Float):Text + return Std.string(f); // @:to function toBool():Null // return switch this { diff --git a/src/xmpp/client/Authentication.hx b/src/xmpp/client/Authentication.hx index a5c587ba..9a3af416 100644 --- a/src/xmpp/client/Authentication.hx +++ b/src/xmpp/client/Authentication.hx @@ -10,24 +10,28 @@ import xmpp.Response; - [RFC3920-SASL](https://xmpp.org/rfcs/rfc3920.html#sasl) - [RFC3920-BIND](https://xmpp.org/rfcs/rfc3920.html#bind) - - [RFC2222](https://datatracker.ietf.org/doc/html/rfc2222) + - [RFC2222](https://datatracker.ietf.org/doc/html/rfc2222) **/ class Authentication { - public static inline var XMLNS = 'urn:ietf:params:xml:ns:xmpp-sasl'; public static inline var XMLNS_BIND = 'urn:ietf:params:xml:ns:xmpp-bind'; public static inline var XMLNS_SESSION = 'urn:ietf:params:xml:ns:xmpp-session'; - /** - 1. Authenticate a client account using SASL - 2. Binds the resource to the connection - 3. Establishs a session with the server - **/ - public static function authenticate(stream:Stream, node:String, resource:String, ?password:String, mechanism:Mechanism, callback:(?error:xmpp.Stanza.Error) -> Void, ?streamStart:(XML->Void)->Void) { + /** + 1. Authenticate a client account using SASL + 2. Binds the resource to the connection + 3. Establishs a session with the server + **/ + public static function authenticate(stream:Stream, node:String, resource:String, ?password:String, mechanism:Mechanism, + callback:(?error:xmpp.Stanza.Error) -> Void, ?streamStart:(XML->Void)->Void) { saslAuthentication(stream, node, resource, password, mechanism, (?e, features) -> { - if (e != null) callback(e) else { - bindResource(stream, resource, (?e,r) -> { - if (e != null) callback(e) else { + if (e != null) + callback(e) + else { + bindResource(stream, resource, (?e, r) -> { + if (e != null) + callback(e) + else { initSesssion(stream, callback); } }); @@ -35,42 +39,43 @@ class Authentication { }, streamStart); } - /** - Authenticate a client account. - **/ - public static function saslAuthentication(stream:Stream, node:String, resource:String, ?password:String, mechanism:Mechanism, callback:(?error:xmpp.Stanza.Error, features:XML) -> Void, ?streamStart:(XML->Void)->Void) { + /** + Authenticate a client account. + **/ + public static function saslAuthentication(stream:Stream, node:String, resource:String, ?password:String, mechanism:Mechanism, + callback:(?error:xmpp.Stanza.Error, features:XML) -> Void, ?streamStart:(XML->Void)->Void) { var _input = stream.input; function _callback(?e, features) { stream.input = _input; callback(e, features); } stream.input = str -> { - // var xml = try XML.parse(str) catch(e:Dynamic) { - // callback(new xmpp.Stanza.Error(null, condition, text)); - // return; - // } + // var xml = try XML.parse(str) catch(e:Dynamic) { + // callback(new xmpp.Stanza.Error(null, condition, text)); + // return; + // } final xml = XML.parse(str).firstElement; switch xml.name { - case 'challenge': - var res = mechanism.createChallengeResponse(xml.text); - res = Base64.encode(Bytes.ofString(res)); - stream.send(XML.create('response', res).set('xmlns', XMLNS)); - case 'success': - if (streamStart == null) - streamStart = stream.start; - streamStart(features -> { - _callback(null, features); - }); - case 'failure': - var text : String = null; - var condition : xmpp.Stanza.ErrorCondition = null; - for(e in xml.elements) { - switch e.name { - case "text": text = e.text; - default: condition = e.name; - } - } - _callback(new xmpp.Stanza.Error(null, condition, text), null); + case 'challenge': + var res = mechanism.createChallengeResponse(xml.text); + res = Base64.encode(Bytes.ofString(res)); + stream.send(XML.create('response', res).set('xmlns', XMLNS)); + case 'success': + if (streamStart == null) + streamStart = stream.start; + streamStart(features -> { + _callback(null, features); + }); + case 'failure': + var text:String = null; + var condition:xmpp.Stanza.ErrorCondition = null; + for (e in xml.elements) { + switch e.name { + case "text": text = e.text; + default: condition = e.name; + } + } + _callback(new xmpp.Stanza.Error(null, condition, text), null); } } final text = mechanism.createAuthenticationText(node, stream.domain, password); @@ -80,29 +85,29 @@ class Authentication { stream.send(auth); } - /** - Bind the resource to the connection. - **/ + /** + Bind the resource to the connection. + **/ public static function bindResource(stream:Stream, resource:String, callback:(?error:xmpp.Stanza.Error, resource:String) -> Void) { - final xml = XML.create('bind').set('xmlns', XMLNS_BIND).append(XML.create('resource', resource)); + final xml = XML.create('bind').set('xmlns', XMLNS_BIND).append(XML.create('resource', resource)); stream.set(xml, (res:Response) -> { switch res { - case Error(e): callback(e, null); - case Result(payload): - switch payload.firstElement.name { - case 'jid': - callback(null, Jid.parseResource(payload.firstElement.text)); - case 'resource': - callback(null, payload.firstElement.text); - default: - } + case Error(e): callback(e, null); + case Result(payload): + switch payload.firstElement.name { + case 'jid': + callback(null, Jid.parseResource(payload.firstElement.text)); + case 'resource': + callback(null, payload.firstElement.text); + default: + } } }); } - /** - Establish a session with the server. - **/ + /** + Establish a session with the server. + **/ public static function initSesssion(stream:Stream, callback:(?error:xmpp.Stanza.Error) -> Void) { stream.set(XML.create('session').set('xmlns', XMLNS_SESSION), res -> { callback(switch res { diff --git a/src/xmpp/client/CSI.hx b/src/xmpp/client/CSI.hx index 59563fca..1c8a4dd2 100644 --- a/src/xmpp/client/CSI.hx +++ b/src/xmpp/client/CSI.hx @@ -1,21 +1,20 @@ package xmpp.client; /** - Indicate active/inactive state. + Indicate active/inactive state. - It is common for IM clients to be logged in and 'online' even while the user is not interacting with the application. - This protocol allows the client to indicate to the server when the user is not actively using the client, allowing the server to optimise traffic to the client accordingly. This can save bandwidth and resources on both the client and server. + It is common for IM clients to be logged in and 'online' even while the user is not interacting with the application. + This protocol allows the client to indicate to the server when the user is not actively using the client, allowing the server to optimise traffic to the client accordingly. This can save bandwidth and resources on both the client and server. - [XEP-0352: Client State Indication](https://xmpp.org/extensions/xep-0352.html) + [XEP-0352: Client State Indication](https://xmpp.org/extensions/xep-0352.html) **/ @xep(352) class CSI { + public static inline var XMLNS = "urn:xmpp:csi:0"; - public static inline var XMLNS = "urn:xmpp:csi:0"; - - /** - Indicate to the server when the user is (not) actively using the client. - **/ - public static inline function csi(stream:xmpp.client.Stream, active=true) : XML - return stream.send(XML.create(active?"active":"inactive").set("xmlns",XMLNS)); + /** + Indicate to the server when the user is (not) actively using the client. + **/ + public static inline function csi(stream:xmpp.client.Stream, active = true):XML + return stream.send(XML.create(active ? "active" : "inactive").set("xmlns", XMLNS)); } diff --git a/src/xmpp/client/Roster.hx b/src/xmpp/client/Roster.hx index 6b96c822..1229ddbd 100644 --- a/src/xmpp/client/Roster.hx +++ b/src/xmpp/client/Roster.hx @@ -4,56 +4,53 @@ import xmpp.IQ; import xmpp.Presence; enum abstract AskType(String) from String to String { - /** - Denotes that a request to subscribe to a entities presence has been made. - **/ + Denotes that a request to subscribe to a entities presence has been made. + **/ var subscribe; /** - Denotes that a request to unscubscribe from a users presence has been made. - **/ + Denotes that a request to unscubscribe from a users presence has been made. + **/ var unsubscribe; } /** - A user's roster is stored by the user's server on the user's behalf so that the user can access roster information from any device. + A user's roster is stored by the user's server on the user's behalf so that the user can access roster information from any device. - @see https://xmpp.org/rfcs/rfc6121.html#roster + @see https://xmpp.org/rfcs/rfc6121.html#roster **/ class Roster { - - public static inline var XMLNS = "jabber:iq:roster"; - - public static inline function getRoster(stream: Stream, handler : Response->Void) : IQ - return stream.get(XMLNS, null, handler); - - public static function addRosterItem(stream: Stream, jid:String, name: String, ?groups:Array, handler: Response->Void) : IQ { - final item = XML.create("item").set("jid", jid).set("name", name); - if(groups != null) for(g in groups) item.append(XML.create('group',g)); - return stream.set(Payload.create(XMLNS).append(item), handler); - } - - public static function removeRosterItem(stream: Stream, jid:String, handler: Response->Void) : IQ { - return stream.set(Payload.create(XMLNS) - .append(XML.create("item") - .set("jid", jid) - .set("subscription", "remove")), handler); - } - - public static function subscribePresence(stream: Stream, jid: String) { - var p = new Presence(); - p.to = jid; - p.type = subscribe; - p.id = xmpp.Stream.makeRandomId(); - stream.send(p); - } - - public static function unsubscribePresence(stream: Stream, jid: String) { - var p = new Presence(); - p.to = jid; - p.type = unsubscribed; - p.id = xmpp.Stream.makeRandomId(); - stream.send(p); - } + public static inline var XMLNS = "jabber:iq:roster"; + + public static inline function loadRoster(stream:Stream, handler:Response->Void):IQ + return stream.get(XMLNS, null, handler); + + public static function addRosterItem(stream:Stream, jid:String, name:String, ?groups:Array, handler:Response->Void):IQ { + final item = XML.create("item").set("jid", jid).set("name", name); + if (groups != null) + for (g in groups) + item.append(XML.create('group', g)); + return stream.set(Payload.create(XMLNS).append(item), handler); + } + + public static function removeRosterItem(stream:Stream, jid:String, handler:Response->Void):IQ { + return stream.set(Payload.create(XMLNS).append(XML.create("item").set("jid", jid).set("subscription", "remove")), handler); + } + + public static function subscribePresence(stream:Stream, jid:String) { + var p = new Presence(); + p.to = jid; + p.type = subscribe; + p.id = xmpp.Stream.makeRandomId(); + stream.send(p); + } + + public static function unsubscribePresence(stream:Stream, jid:String) { + var p = new Presence(); + p.to = jid; + p.type = unsubscribed; + p.id = xmpp.Stream.makeRandomId(); + stream.send(p); + } } diff --git a/src/xmpp/client/Stream.hx b/src/xmpp/client/Stream.hx index 4c1d3c2d..640dd709 100644 --- a/src/xmpp/client/Stream.hx +++ b/src/xmpp/client/Stream.hx @@ -6,7 +6,6 @@ package xmpp.client; https://xmpp.org/rfcs/rfc6120.html#examples-c2s **/ class Stream extends xmpp.Stream { - /** IANA registered `xmpp-client` port (5222) **/ public static inline var PORT = 5222; @@ -20,11 +19,11 @@ class Stream extends xmpp.Stream { public function start(callback:(features:XML) -> Void):Stream { reset(); input = (str:String) -> { - if(ready) { + if (ready) { handleString(str); } else { - if(id == null) { - final header = xmpp.Stream.readHeader(str); + if (id == null) { + final header = xmpp.Stream.readHeader(str); id = header.id; version = header.version; var features = xmpp.Stream.readFeatures(str); @@ -40,8 +39,8 @@ class Stream extends xmpp.Stream { callback(features); } } - } - }; + } + }; output(xmpp.Stream.createHeader(XMLNS, domain, version, lang)); return this; } diff --git a/src/xmpp/component/Stream.hx b/src/xmpp/component/Stream.hx index 3c08325a..afc7af38 100644 --- a/src/xmpp/component/Stream.hx +++ b/src/xmpp/component/Stream.hx @@ -6,48 +6,50 @@ import haxe.crypto.Sha1; [XEP-0114: Jabber Component Protocol](https://xmpp.org/extensions/xep-0114.html) **/ class Stream extends xmpp.Stream { - public static inline var PORT = 5275; + // public dynamic function onDiscoInfo(iq:IQ) + // public dynamic function onDiscoItems(iq:IQ) + /** - Server (accept) component namespace. - **/ + Server (accept) component namespace. + **/ public static inline var XMLNS = 'jabber:component:accept'; /** - Component name. + Component name. **/ - public var name(default,null) : String; + public var name(default, null):String; - public function new(name: String, domain: String, ?xmlns:String, ?lang: String) { + public function new(name:String, domain:String, ?xmlns:String, ?lang:String) { super(xmlns ?? XMLNS, domain, lang); this.name = name; } - public function start(secret:String, callback:(?error:xmpp.Stanza.Error)->Void) { + public function start(secret:String, handler:(?error:xmpp.Stanza.Error) -> Void) { reset(); input = (str:String) -> { - final header = xmpp.Stream.readHeader( str ); + final header = xmpp.Stream.readHeader(str); id = header.id; - send(XML.create('handshake', Sha1.encode(id+secret))); + send(XML.create('handshake', Sha1.encode(id + secret))); input = (str) -> { final xml = Xml.parse(str); - final nodeName = xml.firstChild().nodeName; + final nodeName = xml.firstChild().nodeName; switch nodeName { - case 'handshake': - ready = true; - input = handleString; - callback(); - default: - switch nodeName { - case 'stream:error': - ready = false; - //TODO - trace(xml); - var error = xmpp.Stanza.Error.fromXML(xml); - //trace(error.condition); - callback(error); - } + case 'handshake': + ready = true; + input = handleString; + handler(); + default: + switch nodeName { + case 'stream:error': + ready = false; + // TODO + trace(xml); + var error = xmpp.Stanza.Error.fromXML(xml); + // trace(error.condition); + handler(error); + } } } } diff --git a/src/xmpp/net/BOSH.hx b/src/xmpp/net/BOSH.hx index bc245aa8..eccbdfa1 100644 --- a/src/xmpp/net/BOSH.hx +++ b/src/xmpp/net/BOSH.hx @@ -1,6 +1,11 @@ package xmpp.net; import haxe.Timer; +#if (cpp || hl || neko) +import sys.thread.Thread; +#elseif nodejs +import js.node.Http; +#end using haxe.io.Path; @@ -9,9 +14,8 @@ using haxe.io.Path; Bidirectional-streams Over Synchronous HTTP (BOSH): http://xmpp.org/extensions/xep-0124.html XMPP Over BOSH: http://xmpp.org/extensions/xep-0206.html -*/ + */ class BOSH { - public static inline var BOSH_VERSION = "1.6"; public static inline var XMLNS = "http://jabber.org/protocol/httpbind"; public static inline var XBOSH = "urn:xmpp:xbosh"; @@ -19,64 +23,66 @@ class BOSH { static inline var INTERVAL = 0; static inline var MAX_CHILD_ELEMENTS = 10; - public dynamic function onConnect( features : XML ) {} - public dynamic function onDisconnect( ?error : String ) {} - public dynamic function onData( data : String ) {} - //public dynamic function onStanza( s ) {} + public dynamic function onConnect(features:XML) {} + + public dynamic function onDisconnect(?error:String) {} + + public dynamic function output(data:String) {} + + // public dynamic function onStanza( s ) {} /***/ - public var host(default,null) : String; + public var host(default, null):String; /***/ - public var port(default,null) : Null; + public var port(default, null):Null; /** Server HTTP path */ - public var path(default,null) : String; + public var path(default, null):String; /** Maximum number of requests the connection manager is allowed to keep waiting at any one time during the session. */ - public var hold(default,null) : Int; + public var hold(default, null):Int; /** Longest time (in seconds) that the connection manager is allowed to wait before responding to any request during the session. */ - public var wait(default,null) : Int; + public var wait(default, null):Int; /** Session id */ - public var sid(default,null) : String; + public var sid(default, null):String; /** Request ID */ - public var rid(default,null) : Int; + public var rid(default, null):Int; /** */ - public var secure(default,null) : Bool; + public var secure(default, null):Bool; /** */ - public var maxConcurrentRequests(default,null) : Int; + public var maxConcurrentRequests(default, null):Int; /** */ - //public var timeoutOffset(default,null) : Int; - + // public var timeoutOffset(default,null) : Int; var initialized = false; + var ready = false; var attached = false; var pollingEnabled = true; - var requestCount : Int; - var requestQueue : Array; - var responseQueue : Array; - var responseTimer : Timer; + var requestCount:Int; + var requestQueue:Array; + var responseQueue:Array; + var responseTimer:Timer; - var pauseTimer : Timer; - var maxPause : Int; + var pauseTimer:Timer; + var maxPause:Int; var pauseEnabled = false; - //var inactivity : Int; + // var inactivity : Int; + var timeoutTimer:Timer; + var timeoutOffset:Int; - var timeoutTimer : Timer; - var timeoutOffset : Int; + var stream:xmpp.client.Stream; + var startCallback:XML->Void; - var stream : xmpp.client.Stream; - var startCallback : XML->Void; - - public function new( host : String, ?port : Int, path : String, hold = 1, wait = 30, secure = false, maxConcurrentRequests = 2, timeoutOffset = 25 ) { + public function new(host:String, ?port:Int, path:String, hold = 1, wait = 30, secure = false, maxConcurrentRequests = 2, timeoutOffset = 25) { this.host = host; this.port = port; this.path = path; @@ -87,259 +93,311 @@ class BOSH { this.timeoutOffset = timeoutOffset; } - public function start( stream : xmpp.client.Stream, callback : (features:XML)->Void ) { - + public function start(stream:xmpp.client.Stream, callback:(features:XML) -> Void) { this.stream = stream; this.startCallback = callback; - rid = Std.int( Math.random() * 10000000 ); + rid = Std.int(Math.random() * 10000000); requestCount = 0; requestQueue = []; responseQueue = []; - responseTimer = new Timer( INTERVAL ); + responseTimer = new Timer(INTERVAL); initialized = true; - sendRequests( XML.create( "body" ) - .set( 'xmlns', XMLNS ) - .set( 'xml:lang', 'en' ) - .set( 'xmlns:xmpp', XBOSH ) - .set( 'xmpp:version', '1.0' ) - .set( 'ver', BOSH_VERSION ) - .set( 'hold', Std.string( hold ) ) - .set( 'rid', Std.string( rid ) ) - .set( 'wait', Std.string( wait ) ) - .set( 'to', host ) - .set( 'secure', Std.string( secure ) ) - ); + sendRequests(XML.create("body") + .set('xmlns', XMLNS) + .set('xml:lang', 'en') + .set('xmlns:xmpp', XBOSH) + .set('xmpp:version', '1.0') + .set('ver', BOSH_VERSION) + .set('hold', Std.string(hold)) + .set('rid', Std.string(rid)) + .set('wait', Std.string(wait)) + .set('to', host) + .set('secure', Std.string(secure))); } - public function send( str : String ) : Bool { - return sendQueuedRequests( str ); + public function send(str:String):Bool { + return sendQueuedRequests(str); } public function poll() { - if( !ready || !pollingEnabled || requestCount > 0 || sendQueuedRequests() ) + if (!ready || !pollingEnabled || requestCount > 0 || sendQueuedRequests()) return; - sendRequests( null, true ); + sendRequests(null, true); } - public function restart( callback : XML->Void ) { - //var _processor = @:privateAccess stream.processor; - @:privateAccess stream.input = function(e){ + public function restart(callback:XML->Void) { + trace("restart"); + // var _processor = @:privateAccess stream.processor; + @:privateAccess stream.input = function(e) { // Simulate stream restart - //stream.processor = _processor; + // stream.processor = _processor; stream.input = stream.handleString; stream.reset(); stream.ready = true; - callback( e ); + callback(e); } - sendRequests( createRequest() - .set( 'xmlns', XMLNS ) - .set( "xmpp:restart", "true" ) - .set( "xmlns:xmpp", XBOSH ) - //.set( "xml:lang", "en" ) - .set( "to", host ) - ); + sendRequests(createRequest().set('xmlns', XMLNS) + .set("xmpp:restart", "true") + .set("xmlns:xmpp", XBOSH) // .set( "xml:lang", "en" ) + .set("to", host)); } - function sendQueuedRequests( ?str : String ) : Bool { - if( str != null ) - requestQueue.push( str ); - else if( requestQueue.length == 0 ) + function sendQueuedRequests(?str:String):Bool { + if (str != null) + requestQueue.push(str); + else if (requestQueue.length == 0) return false; - return sendRequests( null ); + return sendRequests(null); } - function sendRequests( ?xml : XML, poll = false ) : Bool { - - if( requestCount >= maxConcurrentRequests ) { - trace( 'max concurrent http request limit ($requestCount,$maxConcurrentRequests)' ); + function sendRequests(?xml:XML, poll = false):Bool { + if (requestCount >= maxConcurrentRequests) { + trace('max concurrent http request limit ($requestCount,$maxConcurrentRequests)'); return false; } requestCount++; - if( xml == null ) { - if( poll ) xml = createRequest() else { + if (xml == null) { + if (poll) + xml = createRequest() + else { var i = 0; - var e = [while( i++ < MAX_CHILD_ELEMENTS && requestQueue.length > 0 ) requestQueue.shift() ]; - xml = createRequest( e ); + var e = [while (i++ < MAX_CHILD_ELEMENTS && requestQueue.length > 0) requestQueue.shift()]; + xml = createRequest(e); } } - createHTTPRequest( xml ); + createHTTPRequest(xml); - if( timeoutTimer != null ) timeoutTimer.stop(); - timeoutTimer = new Timer( wait * 1000 + timeoutOffset * 1000 ); + if (timeoutTimer != null) + timeoutTimer.stop(); + timeoutTimer = new Timer(wait * 1000 + timeoutOffset * 1000); timeoutTimer.run = handleTimeout; return true; } - function createRequest( ?children : Array ) : XML { - var xml = XML.create( "body" ) - .set( "xmlns", XMLNS ) - .set( "rid", Std.string( ++rid ) ) - .set( "sid", sid ); - if( children != null ) for( e in children ) xml.append( e ); + function createRequest(?children:Array):XML { + var xml = XML.create("body") + .set("xmlns", XMLNS) + .set("rid", Std.string(++rid)) + .set("sid", sid); + if (children != null) + for (e in children) + xml.append(e); return xml; } - function createHTTPRequest( body : String ) { - + function createHTTPRequest(body:String) { var httpPath = 'http'; - if( secure ) httpPath += 's'; + if (secure) + httpPath += 's'; httpPath += '://$host'; - if( port != null ) httpPath += ':$port'; + if (port != null) + httpPath += ':$port'; httpPath += '/$path'; #if nodejs - - var options : HttpRequestOptions = { - host : host, - port : port, - path : httpPath, - method : js.node.http.Method.Post, - //requestCert: false, - //rejectUnauthorized: false, - //'Content-Type' : 'text/xml' + var options:HttpRequestOptions = { + host: host, + port: port, + path: httpPath, + method: js.node.http.Method.Post, + // requestCert: false, + // rejectUnauthorized: false, + // 'Content-Type' : 'text/xml' headers: { - 'Content-Type' : 'text/xml', - 'Content-Length' : Std.string( body.length ) + 'Content-Type': 'text/xml', + 'Content-Length': Std.string(body.length) } }; - var req = js.node.Http.request( options, res -> { - res.setEncoding( 'utf8' ); - res.on( 'data', buf -> handleData( buf.toString() ) ); - res.on( 'end', () -> { + var req = js.node.Http.request(options, res -> { + res.setEncoding('utf8'); + res.on('data', buf -> handleData(buf.toString())); + res.on('end', () -> { trace('No more data in response.'); }); }); - req.on( 'error', e -> { + req.on('error', e -> { trace(e); }); - //req.write( body ); - req.end( body ); - + // req.write( body ); + req.end(body); #elseif js - var xhr = new js.html.XMLHttpRequest(); - xhr.open( "POST", httpPath, true ); - xhr.onreadystatechange = function(e){ - if( xhr.readyState != 4 ) + xhr.open("POST", httpPath, true); + xhr.onreadystatechange = function(e) { + if (xhr.readyState != 4) return; var s = xhr.status; - if( s != null && s >= 200 && s < 400 ) - handleData( xhr.responseText ); + if (s != null && s >= 200 && s < 400) + handleData(xhr.responseText); else - handleError( "Http Error #"+xhr.status ); + handleError("Http Error #" + xhr.status); } - xhr.send( body ); - + xhr.send(body); #elseif sys - - //TODO + // TODO /* var req = new xmpp.net.BOSHRequest(); - req.send( host, port, path, body, - function(res) { - handleData(res); - }, - function(e) { - trace(e); - handleError(e); - } - ); - */ + req.send( host, port, path, body, + function(res) { + handleData(res); + }, + function(e) { + trace(e); + handleError(e); + } + ); + */ #end } - function handleData( str : String ) { - var xml : XML = str; - if( xml.get( 'xmlns' ) != XMLNS ) { - trace( 'Invalid BOSH body ($xml)' ); + function handleData(str:String) { + var xml:XML = str; + if (xml.get('xmlns') != XMLNS) { + trace('Invalid BOSH body ($xml)'); return; } requestCount--; - if( timeoutTimer != null ) timeoutTimer.stop(); - if( ready ) { + if (timeoutTimer != null) + timeoutTimer.stop(); + if (ready) { /* - switch xml.get( "type" ) { - case 'terminate': - case 'error': - case 'terminate': - } - */ + switch xml.get( "type" ) { + case 'terminate': + case 'error': + case 'terminate': + } + */ var child = xml.firstElement; - if( child == null ) { - if( requestCount == 0 ) poll() else sendQueuedRequests(); + if (child == null) { + if (requestCount == 0) + poll() + else + sendQueuedRequests(); return; } - - for( e in xml.elements ) { - responseQueue.push( e ); + + for (e in xml.elements) { + responseQueue.push(e); } resetResponseProcessor(); - if( requestCount == 0 && !sendQueuedRequests() ) { - if( responseQueue.length > 0 ) Timer.delay( poll, 0 ) else poll(); + if (requestCount == 0 && !sendQueuedRequests()) { + if (responseQueue.length > 0) + Timer.delay(poll, 0) + else + poll(); } } else { - if( !initialized ) + if (!initialized) return; sid = xml["sid"]; - if( sid == null ) { - //TODO - //cleanup(); - //onDisconnect( "invalid sid" ); - trace( "invalid sid" ); + if (sid == null) { + // TODO + // cleanup(); + // onDisconnect( "invalid sid" ); + trace("invalid sid"); return; } - wait = Std.parseInt( xml["wait"] ); - if( xml.has( 'maxpause' ) ) { - maxPause = Std.parseInt( xml.get( 'maxpause' ) ) * 1000; + wait = Std.parseInt(xml["wait"]); + if (xml.has('maxpause')) { + maxPause = Std.parseInt(xml.get('maxpause')) * 1000; pauseEnabled = true; } - if( xml.has( 'requests' ) ) - maxConcurrentRequests = Std.parseInt( xml.get( 'requests' ) ); - if( xml.has( 'inactivity' ) ) - maxConcurrentRequests = Std.parseInt( xml.get( 'inactivity' ) ); + if (xml.has('requests')) + maxConcurrentRequests = Std.parseInt(xml.get('requests')); + if (xml.has('inactivity')) + maxConcurrentRequests = Std.parseInt(xml.get('inactivity')); ready = true; - startCallback( xml.firstElement ); + startCallback(xml.firstElement); } } function resetResponseProcessor() { - if( responseQueue != null && responseQueue.length > 0 ) { - if( responseTimer != null ) responseTimer.stop(); - responseTimer = new Timer( INTERVAL ); + if (responseQueue != null && responseQueue.length > 0) { + if (responseTimer != null) + responseTimer.stop(); + responseTimer = new Timer(INTERVAL); responseTimer.run = processResponse; } } function processResponse() { responseTimer.stop(); - onData( responseQueue.shift().toString() ); + output(responseQueue.shift().toString()); resetResponseProcessor(); } - function handleError( e ) { + function handleError(e) { trace(e); } function handleTimeout() { trace("handleTimeout"); - //cleanup(); - //onDisconnect( "timeout" ); + // cleanup(); + // onDisconnect( "timeout" ); } - function cleanup() { - if( timeoutTimer != null ) timeoutTimer.stop(); - if( responseTimer != null ) responseTimer.stop(); - ready = initialized = false; - sid = null; - requestQueue = null; - responseQueue = null; - requestCount = 0; - } + function cleanup() { + if (timeoutTimer != null) + timeoutTimer.stop(); + if (responseTimer != null) + responseTimer.stop(); + ready = initialized = false; + sid = null; + requestQueue = null; + responseQueue = null; + requestCount = 0; + } +} + +#if (cpp || hl || neko) +class BOSHRequest { + public function new() {} + + public function send(host:String, port:Int, path:String, data:String, onData:String->Void, onError:String->Void) { + var t = sys.thread.Thread.create(_send); + t.sendMessage(Thread.current()); + t.sendMessage(host); + t.sendMessage(port); + t.sendMessage(path); + t.sendMessage(data); + // t.sendMessage( onData ); + // t.sendMessage( onError ); + var m = Thread.readMessage(true); + onData(m); + // pending--; + } + static function _send() { + var main:Thread = Thread.readMessage(true); + var host:String = Thread.readMessage(true); + var port:Int = Thread.readMessage(true); + var path:String = Thread.readMessage(true); + var data:String = Thread.readMessage(true); + // var _onData : String->Void = Thread.readMessage( true ); + // var _onError : String->Void = Thread.readMessage( true ); + + var url = 'http://$host:$port/$path'; + // trace(">>>",path,data); + + var http = new haxe.Http(url); + http.setPostData(data); + http.onData = function(d) { + trace(d); + // _onData( d ); + // main.sendMessage( d ); + } + http.onError = function(d) { + trace(d); + // _onError( d );( d ); + // main.sendMessage( d ); + } + http.request(true); + } } +#end diff --git a/src/xmpp/xml/Printer.hx b/src/xmpp/xml/Printer.hx index 1c9ed425..841a90dd 100644 --- a/src/xmpp/xml/Printer.hx +++ b/src/xmpp/xml/Printer.hx @@ -8,8 +8,8 @@ private enum EStringOrXml { } private abstract StringOrXml(EStringOrXml) { - - inline function new(sox:EStringOrXml) this = sox; + inline function new(sox:EStringOrXml) + this = sox; public inline function isXml():Bool return switch this { @@ -37,17 +37,17 @@ private abstract StringOrXml(EStringOrXml) { } class Printer { - public var pretty:Bool; - public function new(pretty=true) this.pretty = pretty; - - public inline function it(sox:StringOrXml):String + public function new(pretty = true) + this.pretty = pretty; + + public inline function it(sox:StringOrXml):String return sox.isXml() ? printXml(sox) : printString(sox); public function printString(str:String):String { if (pretty) { - var xml:Xml = try Xml.parse(str) catch(e) { + var xml:Xml = try Xml.parse(str) catch (e) { return str; } return printXml(xml); @@ -58,6 +58,6 @@ class Printer { public inline function printXml(xml:Xml):String return haxe.xml.Printer.print(xml, pretty); - public static inline function print(sox:StringOrXml, ?pretty: Bool):String - return new Printer(pretty).it(sox); + public static inline function print(sox:StringOrXml, ?pretty:Bool):String + return new Printer(pretty).it(sox); } diff --git a/src/xmpp/xml/Schema.hx b/src/xmpp/xml/Schema.hx index b169379c..cc5d9e0a 100644 --- a/src/xmpp/xml/Schema.hx +++ b/src/xmpp/xml/Schema.hx @@ -1,183 +1,182 @@ package xmpp.xml; enum AnnotationContent { - appinfo( v : Appinfo ); - documentation( v : Documentation ); + appinfo(v:Appinfo); + documentation(v:Documentation); } typedef Annotation = { - //@:optional var id : String; - var content : AnnotationContent; + // @:optional var id : String; + var content:AnnotationContent; } typedef Any = { - //@:optional var id : String; - @:optional var namespace : String; - @:optional var minOccurs : String; - @:optional var maxOccurs : String; - @:optional var processContents : String; // strict, lax or skip + // @:optional var id : String; + @:optional var namespace:String; + @:optional var minOccurs:String; + @:optional var maxOccurs:String; + @:optional var processContents:String; // strict, lax or skip } typedef Appinfo = { - var source : String; - var content : String; + var source:String; + var content:String; } typedef Attribute = { - //var default_ : String; - @:optional var fixed : String; - @:optional var form : String; - @:optional var id : String; - @:optional var name : String; - @:optional var ref : String; - @:optional var type : String; - @:optional var use : String; - //var scope : String; - //var valueConstraint : ValueConstraint; - @:optional var annotation : Annotation; - @:optional var simpleType : SimpleType; - @:optional var default_ : String; - //var required : Bool; + // var default_ : String; + @:optional var fixed:String; + @:optional var form:String; + @:optional var id:String; + @:optional var name:String; + @:optional var ref:String; + @:optional var type:String; + @:optional var use:String; + // var scope : String; + // var valueConstraint : ValueConstraint; + @:optional var annotation:Annotation; + @:optional var simpleType:SimpleType; + @:optional var default_:String; + // var required : Bool; } typedef Choice = { - @:optional var maxOccurs : String; - @:optional var minOccurs : String; - var elements : Array; + @:optional var maxOccurs:String; + @:optional var minOccurs:String; + var elements:Array; } typedef ComplexContent = { - @:optional var id : String; - @:optional var mixed : String; - //var extension : Extension; + @:optional var id:String; + @:optional var mixed:String; + // var extension : Extension; } typedef ComplexType = { - @:optional var name : String; - //... - //@:optional var abstract : Bool; - //@:optional var contentType : Bool; - //.. - @:optional var annotations : Array; - @:optional var simpleContent : SimpleContent; - @:optional var complexContent : ComplexContent; - @:optional var sequence : Sequence; - @:optional var attribute : Array; - @:optional var choice : Choice; + @:optional var name:String; + // ... + // @:optional var abstract : Bool; + // @:optional var contentType : Bool; + // .. + @:optional var annotations:Array; + @:optional var simpleContent:SimpleContent; + @:optional var complexContent:ComplexContent; + @:optional var sequence:Sequence; + @:optional var attribute:Array; + @:optional var choice:Choice; } typedef Documentation = { - var source : String; - var lang : String; - var content : String; + var source:String; + var lang:String; + var content:String; } typedef Element = { - @:optional var id : String; - var name : String; - var type : String; - @:optional var ref : String; - @:optional var substitutionGroup : String; - @:optional var default_ : String; - @:optional var fixed : String; - @:optional var form : String; - @:optional var maxOccurs : String; //TODO Int - @:optional var minOccurs : String; //TODO Int - @:optional var nillable : String; - @:optional var abstract_ : String; - @:optional var block : String; - @:optional var final_ : String; - // @:optional var use : String; - @:optional var simpleType : SimpleType; - @:optional var complexType : ComplexType; - //var scope : String; + @:optional var id:String; + var name:String; + var type:String; + @:optional var ref:String; + @:optional var substitutionGroup:String; + @:optional var default_:String; + @:optional var fixed:String; + @:optional var form:String; + @:optional var maxOccurs:String; // TODO Int + @:optional var minOccurs:String; // TODO Int + @:optional var nillable:String; + @:optional var abstract_:String; + @:optional var block:String; + @:optional var final_:String; + // @:optional var use : String; + @:optional var simpleType:SimpleType; + @:optional var complexType:ComplexType; + // var scope : String; } typedef Enumeration = { - var value : String; + var value:String; } typedef Example = { - var count : Int; - var size : Int; - //var content : (all | any*) + var count:Int; + var size:Int; + // var content : (all | any*) } typedef Extension = { - var base : String; - var attribute : Array; + var base:String; + var attribute:Array; } typedef MaxLength = { - //TODO @:optional var id : ; - @:optional var fixed : Bool; - @:optional var value : Int; + // TODO @:optional var id : ; + @:optional var fixed:Bool; + @:optional var value:Int; } typedef MinLength = MaxLength; typedef Notation = { - // @:optional var id : String; - var name : String; - var public_ : String; - @:optional var system : String; + // @:optional var id : String; + var name:String; + var public_:String; + @:optional var system:String; } typedef Restriction = { - //@:optional var id : String; - var base : String; - var attribute : Array; - var enumeration : Array; - @:optional var minLength : MinLength; //Int; - @:optional var maxLength : MaxLength; //Int; + // @:optional var id : String; + var base:String; + var attribute:Array; + var enumeration:Array; + @:optional var minLength:MinLength; // Int; + @:optional var maxLength:MaxLength; // Int; } typedef Sequence = { - //@:optional var id : String; - @:optional var maxOccurs : String; - @:optional var minOccurs : String; - var elements : Array; - var any : Array; - var choice : Array; + // @:optional var id : String; + @:optional var maxOccurs:String; + @:optional var minOccurs:String; + var elements:Array; + var any:Array; + var choice:Array; } typedef SimpleContent = { - //@:optional var id : String; - //var annotation : Annotation; - @:optional var restriction : Restriction; - @:optional var extension : Extension; + // @:optional var id : String; + // var annotation : Annotation; + @:optional var restriction:Restriction; + @:optional var extension:Extension; } typedef SimpleType = { - //@:optional var id : String; - var name : String; - var restriction : Array; - //list - //union + // @:optional var id : String; + var name:String; + var restriction:Array; + // list + // union } typedef Unique = { - @:optional var id : String; - var name : String; - var content : { - ?annotation : Annotation - }; + @:optional var id:String; + var name:String; + var content:{ + ?annotation:Annotation + }; } class Schema { + public var targetNamespace:String; + public var xmlns:String; + public var elementFormDefault:String; - public var targetNamespace : String; - public var xmlns : String; - public var elementFormDefault : String; + public var xmlns_xml:String; - public var xmlns_xml : String; + public var annotation:Annotation; + public var elements:Array; + public var simpleType:Array; + public var complexType:Array; - public var annotation : Annotation; - public var elements : Array; - public var simpleType : Array; - public var complexType : Array; - - public function new( targetNamespace : String, xmlns : String, ?elementFormDefault : String ) { + public function new(targetNamespace:String, xmlns:String, ?elementFormDefault:String) { this.targetNamespace = targetNamespace; this.xmlns = xmlns; this.elementFormDefault = elementFormDefault; @@ -185,240 +184,265 @@ class Schema { simpleType = []; complexType = []; } - - public function toXML() : XML { - var xml = XML.create( 'xs:schema' ); - for( e in elements ) { - var c = XML.create( 'xs:element' ); - c.set( 'name', e.name ); - if( e.complexType != null ) { - } - xml.append( c ); + public function toXML():XML { + var xml = XML.create('xs:schema'); + for (e in elements) { + var c = XML.create('xs:element'); + c.set('name', e.name); + if (e.complexType != null) {} + xml.append(c); } - //TODO + // TODO return xml; } - public static function parse( xml : XML ) : Schema { - var schema = new Schema( xml['targetNamespace'], xml['xmlns'], xml['elementFormDefault'] ); - schema.xmlns_xml = xml['xmlns:xml']; //TODO - for( e in xml.elements ) { - switch e.name { - case 'xs:annotation': - for( e in e.elements ) { - switch e.name { - case 'xs:documentation': schema.annotation = parseAnnotation(e); - } - } - case 'xs:complexType': schema.complexType.push( parseComplexType(e) ); - case 'xs:element': schema.elements.push( parseElement(e) ); - case 'xs:simpleType': schema.simpleType.push( parseSimpleType(e) ); - } - } - return schema; - } - - static function parseAnnotation( xml : XML ) : Annotation { - return { - //id: xml['id'], - content: switch xml.name{ - case 'xs:documentation': - documentation( parseDocumentation( xml ) ); - case 'xs:appinfo': - var e = xml; - appinfo({ - source: e.get('source'), - content: e.text - }); - default: null; - }, - }; - } - - static function parseAny( xml : XML ) : Any { - return { - //id: xml['id'], - namespace: xml['namespace'], - maxOccurs: xml['maxOccurs'], - minOccurs: xml['minOccurs'], - processContents: xml['processContents'] - }; - } - - static function parseAttribute( xml : XML ) : Attribute { - var attr : Attribute = { - name : xml['name'], - ref : xml['ref'], - type : xml['type'], - use : xml['use'], - default_ : xml['default'], - } - for( e in xml.elements ) switch e.name { - case 'xs:simpleType': attr.simpleType = parseSimpleType(e); - } - return attr; - } - - static function parseChoice( xml : XML ) : Choice { - var choice : Choice = { + public static function parse(xml:XML):Schema { + var schema = new Schema(xml['targetNamespace'], xml['xmlns'], xml['elementFormDefault']); + schema.xmlns_xml = xml['xmlns:xml']; // TODO + for (e in xml.elements) { + switch e.name { + case 'xs:annotation': + for (e in e.elements) { + switch e.name { + case 'xs:documentation': schema.annotation = parseAnnotation(e); + } + } + case 'xs:complexType': + schema.complexType.push(parseComplexType(e)); + case 'xs:element': + schema.elements.push(parseElement(e)); + case 'xs:simpleType': + schema.simpleType.push(parseSimpleType(e)); + } + } + return schema; + } + + static function parseAnnotation(xml:XML):Annotation { + return { + // id: xml['id'], + content: switch xml.name { + case 'xs:documentation': + documentation(parseDocumentation(xml)); + case 'xs:appinfo': + var e = xml; + appinfo({ + source: e.get('source'), + content: e.text + }); + default: null; + }, + }; + } + + static function parseAny(xml:XML):Any { + return { + // id: xml['id'], + namespace: xml['namespace'], maxOccurs: xml['maxOccurs'], - minOccurs: xml['minOccurs'], - elements: [] - }; - for( e in xml.elements ) switch e.name { - case 'xs:element': choice.elements.push( parseElement(e) ); - } - return choice; - } - - static function parseComplexContent( xml : XML ) : ComplexContent { - var content : ComplexContent = { - //id: xml['id'], - mixed: xml['mixed'], - }; + minOccurs: xml['minOccurs'], + processContents: xml['processContents'] + }; + } + + static function parseAttribute(xml:XML):Attribute { + var attr:Attribute = { + name: xml['name'], + ref: xml['ref'], + type: xml['type'], + use: xml['use'], + default_: xml['default'], + } + for (e in xml.elements) + switch e.name { + case 'xs:simpleType': + attr.simpleType = parseSimpleType(e); + } + return attr; + } + + static function parseChoice(xml:XML):Choice { + var choice:Choice = { + maxOccurs: xml['maxOccurs'], + minOccurs: xml['minOccurs'], + elements: [] + }; + for (e in xml.elements) + switch e.name { + case 'xs:element': + choice.elements.push(parseElement(e)); + } + return choice; + } + + static function parseComplexContent(xml:XML):ComplexContent { + var content:ComplexContent = { + // id: xml['id'], + mixed: xml['mixed'], + }; /* - for( e in xml.elements ) switch e.name { - //case 'xs:element': choice.elements.push( parseElement(e) ); - } - */ - return content; - } - - static function parseComplexType( xml : XML ) : ComplexType { - var complexType : ComplexType = { - attribute: [], - name: xml.get('name') - }; - for( e in xml.elements ) { - switch e.name { - case 'xs:annotations': complexType.annotations.push( parseAnnotation(e) ); - case 'xs:simpleContent': complexType.simpleContent = parseSimpleContent(e); - case 'xs:complexContent': complexType.complexContent = parseComplexContent(e); - case 'xs:sequence': complexType.sequence = parseSequence(e); - case 'xs:attribute': complexType.attribute.push( parseAttribute(e) ); - case 'xs:choice': complexType.choice = parseChoice(e); - } - } - return complexType; - } - - static function parseDocumentation( xml : XML ) : Documentation { - return { - source: xml['source'], - lang: xml['lang'], - content: xml.text - }; - } - - static function parseElement( xml : XML ) : Element { - var element : Element = { - minOccurs: xml['minOccurs'], - maxOccurs: xml['maxOccurs'], - name: xml['name'], - ref: xml['ref'], - type: xml['type'], - }; - for( e in xml.elements ) { - switch e.name { - case 'xs:simpleType': element.simpleType = parseSimpleType(e); - case 'xs:complexType': element.complexType = parseComplexType(e); - } - } - return element; - } - - static function parseEnumeration( xml : XML ) : Enumeration { - var enumeration = { - value: xml['value'], - }; - return enumeration; - } - - static function parseExtension( xml : XML ) : Extension { - var extension : Extension = { - attribute: [], - base: xml['base'] - }; - for( e in xml.elements ) switch e.name { - case 'xs:attribute': extension.attribute.push( parseAttribute(e) ); - } - return extension; - } - - static function parseMaxLength( xml : XML ) : MaxLength { - return { + for( e in xml.elements ) switch e.name { + //case 'xs:element': choice.elements.push( parseElement(e) ); + } + */ + return content; + } + + static function parseComplexType(xml:XML):ComplexType { + var complexType:ComplexType = { + attribute: [], + name: xml.get('name') + }; + for (e in xml.elements) { + switch e.name { + case 'xs:annotations': + complexType.annotations.push(parseAnnotation(e)); + case 'xs:simpleContent': + complexType.simpleContent = parseSimpleContent(e); + case 'xs:complexContent': + complexType.complexContent = parseComplexContent(e); + case 'xs:sequence': + complexType.sequence = parseSequence(e); + case 'xs:attribute': + complexType.attribute.push(parseAttribute(e)); + case 'xs:choice': + complexType.choice = parseChoice(e); + } + } + return complexType; + } + + static function parseDocumentation(xml:XML):Documentation { + return { + source: xml['source'], + lang: xml['lang'], + content: xml.text + }; + } + + static function parseElement(xml:XML):Element { + var element:Element = { + minOccurs: xml['minOccurs'], + maxOccurs: xml['maxOccurs'], + name: xml['name'], + ref: xml['ref'], + type: xml['type'], + }; + for (e in xml.elements) { + switch e.name { + case 'xs:simpleType': + element.simpleType = parseSimpleType(e); + case 'xs:complexType': + element.complexType = parseComplexType(e); + } + } + return element; + } + + static function parseEnumeration(xml:XML):Enumeration { + var enumeration = { + value: xml['value'], + }; + return enumeration; + } + + static function parseExtension(xml:XML):Extension { + var extension:Extension = { + attribute: [], + base: xml['base'] + }; + for (e in xml.elements) + switch e.name { + case 'xs:attribute': + extension.attribute.push(parseAttribute(e)); + } + return extension; + } + + static function parseMaxLength(xml:XML):MaxLength { + return { fixed: xml['fixed'] == 'true', - value: Std.parseInt( xml['value'] ) - }; - } - - static function parseMinLength( xml : XML ) : MinLength { - return parseMaxLength( xml ); - } - - static function parseRestriction( xml : XML ) : Restriction { - //trace(xml); - //trace(xml['xs:minLength'] ); - //trace(xml['minLength'], int( xml['minLength'] ) ); - var restriction : Restriction = { - //id: xml['id'], - base: xml['base'], - attribute: [], - enumeration: [], - }; - for( e in xml.elements ) { - switch e.name { - case 'xs:attribute': restriction.attribute.push( parseAttribute(e) ); - case 'xs:enumeration': restriction.enumeration.push( parseEnumeration(e) ); - case 'xs:minLength': restriction.minLength = parseMinLength(e); - case 'xs:maxLength': restriction.maxLength = parseMaxLength(e); - } - } - return restriction; - } - - static function parseSequence( xml : XML ) : Sequence { - var sequence : Sequence = { - minOccurs: xml['minOccurs'], - elements: [], - any: [], - choice: [] - }; - for( e in xml.elements ) switch e.name { - case 'xs:element': sequence.elements.push( parseElement(e) ); - case 'xs:any': sequence.any.push( parseAny(e) ); - case 'xs:choice': sequence.choice.push( parseChoice(e) ); - } - return sequence; - } - - static function parseSimpleContent( xml : XML ) : SimpleContent { - var obj : SimpleContent = {}; - for( e in xml.elements ) { - switch e.name { - case 'xs:extension': obj.extension = parseExtension(e); - case 'xs:restriction': obj.restriction = parseRestriction(e); - } - } - return obj; - } - - static function parseSimpleType( xml : XML ) : SimpleType { - var simpleType : SimpleType = { - name : xml['name'], + value: Std.parseInt(xml['value']) + }; + } + + static function parseMinLength(xml:XML):MinLength { + return parseMaxLength(xml); + } + + static function parseRestriction(xml:XML):Restriction { + // trace(xml); + // trace(xml['xs:minLength'] ); + // trace(xml['minLength'], int( xml['minLength'] ) ); + var restriction:Restriction = { + // id: xml['id'], + base: xml['base'], + attribute: [], + enumeration: [], + }; + for (e in xml.elements) { + switch e.name { + case 'xs:attribute': + restriction.attribute.push(parseAttribute(e)); + case 'xs:enumeration': + restriction.enumeration.push(parseEnumeration(e)); + case 'xs:minLength': + restriction.minLength = parseMinLength(e); + case 'xs:maxLength': + restriction.maxLength = parseMaxLength(e); + } + } + return restriction; + } + + static function parseSequence(xml:XML):Sequence { + var sequence:Sequence = { + minOccurs: xml['minOccurs'], + elements: [], + any: [], + choice: [] + }; + for (e in xml.elements) + switch e.name { + case 'xs:element': + sequence.elements.push(parseElement(e)); + case 'xs:any': + sequence.any.push(parseAny(e)); + case 'xs:choice': + sequence.choice.push(parseChoice(e)); + } + return sequence; + } + + static function parseSimpleContent(xml:XML):SimpleContent { + var obj:SimpleContent = {}; + for (e in xml.elements) { + switch e.name { + case 'xs:extension': + obj.extension = parseExtension(e); + case 'xs:restriction': + obj.restriction = parseRestriction(e); + } + } + return obj; + } + + static function parseSimpleType(xml:XML):SimpleType { + var simpleType:SimpleType = { + name: xml['name'], restriction: [] }; - for( e in xml.elements ) { - switch e.name { - case 'xs:restriction': simpleType.restriction.push( parseRestriction(e) ); - } - } - return simpleType; - } - - static inline function int(s : String ) { - return (s == null) ? null : Std.parseInt(s); + for (e in xml.elements) { + switch e.name { + case 'xs:restriction': + simpleType.restriction.push(parseRestriction(e)); + } + } + return simpleType; } + static inline function int(s:String) { + return (s == null) ? null : Std.parseInt(s); + } }