From 28045361f3f970ee5c6145e975dc600055c9b2f1 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 19 Feb 2017 20:46:44 +0100 Subject: [PATCH 0001/1020] initial commit --- Cargo.toml | 7 ++ src/error.rs | 32 ++++++ src/lib.rs | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/error.rs create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..765a12ddfcb7d4715cf4b7a8c2fd3155b78ff631 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "minidom" +version = "0.1.0" +authors = ["lumi "] + +[dependencies] +xml-rs = "*" diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..db184064d6a49210e4817f384f8b0afec85152ad --- /dev/null +++ b/src/error.rs @@ -0,0 +1,32 @@ +use std::io; + +use std::convert::From; + +use xml::writer::Error as WriterError; +use xml::reader::Error as ReaderError; + +#[derive(Debug)] +pub enum Error { + IoError(io::Error), + XmlWriterError(WriterError), + XmlReaderError(ReaderError), + EndOfDocument, +} + +impl From for Error { + fn from(err: io::Error) -> Error { + Error::IoError(err) + } +} + +impl From for Error { + fn from(err: WriterError) -> Error { + Error::XmlWriterError(err) + } +} + +impl From for Error { + fn from(err: ReaderError) -> Error { + Error::XmlReaderError(err) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..7509f40c273b2742776dc7512b37238ecf5eb12b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,281 @@ +extern crate xml; + +pub mod error; + +use std::io::prelude::*; + +use std::convert::From; + +use std::fmt; + +use xml::name::{OwnedName, Name}; +use xml::reader::{XmlEvent as ReaderEvent, EventReader}; +use xml::writer::{XmlEvent as WriterEvent, EventWriter}; +use xml::attribute::OwnedAttribute; + +use error::Error; + +#[derive(Clone, PartialEq, Eq)] +pub struct Element { + name: OwnedName, + attributes: Vec, + children: Vec, +} + +impl fmt::Debug for Element { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "<{}", self.name)?; + for attr in &self.attributes { + write!(fmt, " {}", attr)?; + } + write!(fmt, ">")?; + for child in &self.children { + match *child { + Fork::Element(ref e) => { + write!(fmt, "{:?}", e)?; + }, + Fork::Text(ref s) => { + write!(fmt, "{}", s)?; + }, + } + } + write!(fmt, "", self.name)?; + Ok(()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Fork { + Element(Element), + Text(String), +} + +impl Element { + pub fn new(name: OwnedName, attributes: Vec) -> Element { + Element { + name: name, + attributes: attributes, + children: Vec::new(), + } + } + + pub fn builder>(name: S) -> ElementBuilder { + ElementBuilder { + name: OwnedName::local(name), + attributes: Vec::new(), + } + } + + pub fn tag(&self) -> &str { + &self.name.local_name + } + + pub fn ns(&self) -> Option<&str> { + self.name.namespace.as_ref() + .map(String::as_ref) + } + + pub fn attr(&self, key: &str) -> Option<&str> { + for attr in &self.attributes { + if attr.name.local_name == key { + return Some(&attr.value); + } + } + None + } + + pub fn from_reader(reader: &mut EventReader) -> Result { + loop { + let e = reader.next()?; + match e { + ReaderEvent::StartElement { name, attributes, .. } => { + let mut root = Element::new(name, attributes); + root.from_reader_inner(reader); + return Ok(root); + }, + ReaderEvent::EndDocument => { + return Err(Error::EndOfDocument); + }, + _ => () // TODO: may need more errors + } + } + } + + fn from_reader_inner(&mut self, reader: &mut EventReader) -> Result<(), Error> { + loop { + let e = reader.next()?; + match e { + ReaderEvent::StartElement { name, attributes, .. } => { + let elem = Element::new(name, attributes); + let elem_ref = self.append_child(elem); + elem_ref.from_reader_inner(reader); + }, + ReaderEvent::EndElement { .. } => { + // TODO: may want to check whether we're closing the correct element + return Ok(()); + }, + ReaderEvent::Characters(s) => { + self.append_text_node(s); + }, + ReaderEvent::CData(s) => { + self.append_text_node(s); + }, + ReaderEvent::EndDocument => { + return Err(Error::EndOfDocument); + }, + _ => (), // TODO: may need to implement more + } + } + } + + pub fn write_to(&self, writer: &mut EventWriter) -> Result<(), Error> { + let mut start = WriterEvent::start_element(self.name.borrow()); + if let Some(ref ns) = self.name.namespace { + start = start.default_ns(ns.as_ref()); + } + for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently + start = start.attr(attr.name.borrow(), &attr.value); + } + writer.write(start)?; + for child in &self.children { + match *child { + Fork::Element(ref e) => { + e.write_to(writer)?; + }, + Fork::Text(ref s) => { + writer.write(WriterEvent::characters(s))?; + }, + } + } + writer.write(WriterEvent::end_element())?; + Ok(()) + } + + pub fn children<'a>(&'a self) -> Children<'a> { + unimplemented!(); + } + + pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { + unimplemented!(); + } + + pub fn append_child(&mut self, child: Element) -> &mut Element { + self.children.push(Fork::Element(child)); + if let Fork::Element(ref mut cld) = *self.children.last_mut().unwrap() { + cld + } + else { + unreachable!() + } + } + + pub fn append_text_node>(&mut self, child: S) { + self.children.push(Fork::Text(child.into())); + } + + pub fn text(&self) -> &str { + unimplemented!() + } + + pub fn get_child<'a, N: Into>>(&self, name: N) -> Option<&Element> { + unimplemented!() + } + + pub fn get_child_mut<'a, N: Into>>(&mut self, name: N) -> Option<&mut Element> { + unimplemented!() + } + + pub fn into_child<'a, N: Into>>(self, name: N) -> Option { + unimplemented!() + } +} + +pub struct Children<'a> { + elem: &'a Element, +} + +pub struct ChildrenMut<'a> { + elem: &'a mut Element, +} + +pub struct ElementBuilder { + name: OwnedName, + attributes: Vec, +} + +impl ElementBuilder { + pub fn ns>(mut self, namespace: S) -> ElementBuilder { + self.name.namespace = Some(namespace.into()); + self + } + + pub fn attr, V: Into>(mut self, name: S, value: V) -> ElementBuilder { + self.attributes.push(OwnedAttribute::new(OwnedName::local(name), value)); + self + } + + pub fn attr_ns, N: Into, V: Into>(mut self, name: S, namespace: N, value: V) -> ElementBuilder { + self.attributes.push(OwnedAttribute::new(OwnedName::qualified::<_, _, &'static str>(name, namespace, None), value)); + self + } + + pub fn build(self) -> Element { + Element::new(self.name, self.attributes) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_STRING: &'static str = r#"meownya"#; + + fn build_test_tree() -> Element { + let mut root = Element::builder("root") + .ns("root_ns") + .attr("a", "b") + .build(); + root.append_text_node("meow"); + let child = Element::builder("child") + .attr("c", "d") + .build(); + root.append_child(child); + let other_child = Element::builder("child") + .ns("child_ns") + .attr("d", "e") + .build(); + root.append_child(other_child); + root.append_text_node("nya"); + root + } + + #[test] + fn reader_works() { + use std::io::Cursor; + let mut reader = EventReader::new(Cursor::new(TEST_STRING)); + // TODO: fix a bunch of namespace stuff so this test passes + assert_eq!(Element::from_reader(&mut reader).unwrap(), build_test_tree()); + } + + #[test] + fn writer_works() { + let root = build_test_tree(); + let mut out = Vec::new(); + { + let mut writer = EventWriter::new(&mut out); + root.write_to(&mut writer); + } + assert_eq!(String::from_utf8(out).unwrap(), TEST_STRING); + } + + #[test] + fn builder_works() { + let elem = Element::builder("a") + .ns("b") + .attr("c", "d") + .build(); + assert_eq!(elem.tag(), "a"); + assert_eq!(elem.ns(), Some("b")); + assert_eq!(elem.attr("c"), Some("d")); + } +} From f0ebf7583aa9b4d70f99dd3c29593a2a44335712 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 19 Feb 2017 20:57:59 +0100 Subject: [PATCH 0002/1020] add .gitignore, add README.md, add license --- .gitignore | 2 + COPYING | 675 +++++++++++++++++++++++++++++++++++++++++++++++++ COPYING.LESSER | 166 ++++++++++++ README.md | 12 + 4 files changed, 855 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 COPYING.LESSER create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a9d37c560c6ab8d4afbf47eda643e8c42e857716 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..a737dcfed5db21fb99a8fcb812e995937d38401b --- /dev/null +++ b/COPYING @@ -0,0 +1,675 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/COPYING.LESSER b/COPYING.LESSER new file mode 100644 index 0000000000000000000000000000000000000000..5f5ff16a4a0f6104fadb6a5beef527573e46b425 --- /dev/null +++ b/COPYING.LESSER @@ -0,0 +1,166 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e477240b6b4307f5575cda9a12f037ed087f1d52 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +minidom-rs +========== + +What's this? +------------ + +A minimal DOM library on top of minidom-rs. + +What license is it under? +------------------------- + +LGPLv3. From 977dace5368452cf5562ed703dd53037de9d129d Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 19 Feb 2017 20:59:34 +0100 Subject: [PATCH 0003/1020] resolve a cyclic dependency in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e477240b6b4307f5575cda9a12f037ed087f1d52..d253eb0f7d6ce4cf0a96fe37cf6b2e3ddd890342 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ minidom-rs What's this? ------------ -A minimal DOM library on top of minidom-rs. +A minimal DOM library on top of xml-rs. What license is it under? ------------------------- From 75b494e02ceaed92f296abf6fc016228bb3549d8 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 19 Feb 2017 20:02:28 +0000 Subject: [PATCH 0004/1020] Add .gitlab-ci.yml --- .gitlab-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..cb292343052f44cf4efaa6f5bd686defc527ae8a --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,6 @@ +image: "scorpil/rust:stable" + +test:cargo: + script: + - rustc --version && cargo --version + - cargo test --verbose --jobs 1 --release \ No newline at end of file From fda38ff242620ef45561eef797de0cc591d9f2ba Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 19 Feb 2017 21:45:51 +0100 Subject: [PATCH 0005/1020] add child iterators and fix up some of the tests, which still do not pass (yet) --- src/lib.rs | 47 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7509f40c273b2742776dc7512b37238ecf5eb12b..7268c97079ea8fdb0d88f6fa00fd0fb7dc0bb746 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,10 @@ use std::io::prelude::*; use std::convert::From; +use std::iter::Iterator; + +use std::slice; + use std::fmt; use xml::name::{OwnedName, Name}; @@ -152,11 +156,15 @@ impl Element { } pub fn children<'a>(&'a self) -> Children<'a> { - unimplemented!(); + Children { + iter: self.children.iter(), + } } pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { - unimplemented!(); + ChildrenMut { + iter: self.children.iter_mut(), + } } pub fn append_child(&mut self, child: Element) -> &mut Element { @@ -191,11 +199,37 @@ impl Element { } pub struct Children<'a> { - elem: &'a Element, + iter: slice::Iter<'a, Fork>, +} + +impl<'a> Iterator for Children<'a> { + type Item = &'a Element; + + fn next(&mut self) -> Option<&'a Element> { + while let Some(item) = self.iter.next() { + if let Fork::Element(ref child) = *item { + return Some(child); + } + } + None + } } pub struct ChildrenMut<'a> { - elem: &'a mut Element, + iter: slice::IterMut<'a, Fork>, +} + +impl<'a> Iterator for ChildrenMut<'a> { + type Item = &'a mut Element; + + fn next(&mut self) -> Option<&'a mut Element> { + while let Some(item) = self.iter.next() { + if let Fork::Element(ref mut child) = *item { + return Some(child); + } + } + None + } } pub struct ElementBuilder { @@ -228,6 +262,9 @@ impl ElementBuilder { mod tests { use super::*; + use xml::reader::EventReader; + use xml::writer::EventWriter; + const TEST_STRING: &'static str = r#"meownya"#; fn build_test_tree() -> Element { @@ -263,7 +300,7 @@ mod tests { let mut out = Vec::new(); { let mut writer = EventWriter::new(&mut out); - root.write_to(&mut writer); + root.write_to(&mut writer).unwrap(); } assert_eq!(String::from_utf8(out).unwrap(), TEST_STRING); } From 8663a140403248b0c8358c0415d271d37c399887 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 19 Feb 2017 23:15:43 +0100 Subject: [PATCH 0006/1020] lots of fixes and simplifications of the API --- src/lib.rs | 125 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 35 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7268c97079ea8fdb0d88f6fa00fd0fb7dc0bb746..f52477229a6ff6bddad1de23ccb70991d401912e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ pub mod error; use std::io::prelude::*; -use std::convert::From; +use std::convert::{From, AsRef}; use std::iter::Iterator; @@ -12,23 +12,51 @@ use std::slice; use std::fmt; -use xml::name::{OwnedName, Name}; use xml::reader::{XmlEvent as ReaderEvent, EventReader}; use xml::writer::{XmlEvent as WriterEvent, EventWriter}; -use xml::attribute::OwnedAttribute; +use xml::name::Name; +use xml::escape::escape_str_attribute; +use xml::namespace::NS_NO_PREFIX; use error::Error; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Attribute { + pub name: String, + pub value: String, +} + +impl fmt::Display for Attribute { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}=\"{}\"", self.name, escape_str_attribute(&self.value)) + } +} + +impl Attribute { + pub fn new, V: Into>(name: N, value: V) -> Attribute { + Attribute { + name: name.into(), + value: value.into(), + } + } +} + #[derive(Clone, PartialEq, Eq)] pub struct Element { - name: OwnedName, - attributes: Vec, + name: String, + namespace: Option, + attributes: Vec, children: Vec, } impl fmt::Debug for Element { - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "<{}", self.name)?; + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + if let Some(ref ns) = self.namespace { + write!(fmt, "<{{{}}}{}", ns, self.name)?; + } + else { + write!(fmt, "<{}", self.name)?; + } for attr in &self.attributes { write!(fmt, " {}", attr)?; } @@ -55,9 +83,10 @@ pub enum Fork { } impl Element { - pub fn new(name: OwnedName, attributes: Vec) -> Element { + pub fn new(name: String, namespace: Option, attributes: Vec) -> Element { Element { name: name, + namespace: namespace, attributes: attributes, children: Vec::new(), } @@ -65,35 +94,44 @@ impl Element { pub fn builder>(name: S) -> ElementBuilder { ElementBuilder { - name: OwnedName::local(name), + name: name.into(), + namespace: None, attributes: Vec::new(), } } - pub fn tag(&self) -> &str { - &self.name.local_name + pub fn name(&self) -> &str { + &self.name } pub fn ns(&self) -> Option<&str> { - self.name.namespace.as_ref() - .map(String::as_ref) + self.namespace.as_ref() + .map(String::as_ref) } - pub fn attr(&self, key: &str) -> Option<&str> { + pub fn attr(&self, name: &str) -> Option<&str> { for attr in &self.attributes { - if attr.name.local_name == key { + if attr.name == name { return Some(&attr.value); } } None } + pub fn is, NS: AsRef>(&self, name: N, namespace: NS) -> bool { + let ns = self.namespace.as_ref().map(String::as_ref); + self.name == name.as_ref() && ns == Some(namespace.as_ref()) + } + pub fn from_reader(reader: &mut EventReader) -> Result { loop { let e = reader.next()?; match e { - ReaderEvent::StartElement { name, attributes, .. } => { - let mut root = Element::new(name, attributes); + ReaderEvent::StartElement { name, attributes, namespace } => { + let attributes = attributes.into_iter() + .map(|o| Attribute::new(o.name.local_name, o.value)) + .collect(); + let mut root = Element::new(name.local_name, namespace.get(NS_NO_PREFIX).map(|s| s.to_owned()), attributes); root.from_reader_inner(reader); return Ok(root); }, @@ -109,8 +147,11 @@ impl Element { loop { let e = reader.next()?; match e { - ReaderEvent::StartElement { name, attributes, .. } => { - let elem = Element::new(name, attributes); + ReaderEvent::StartElement { name, attributes, namespace } => { + let attributes = attributes.into_iter() + .map(|o| Attribute::new(o.name.local_name, o.value)) + .collect(); + let elem = Element::new(name.local_name, namespace.get(NS_NO_PREFIX).map(|s| s.to_owned()), attributes); let elem_ref = self.append_child(elem); elem_ref.from_reader_inner(reader); }, @@ -133,12 +174,18 @@ impl Element { } pub fn write_to(&self, writer: &mut EventWriter) -> Result<(), Error> { - let mut start = WriterEvent::start_element(self.name.borrow()); - if let Some(ref ns) = self.name.namespace { + let name = if let Some(ref ns) = self.namespace { + Name::qualified(&self.name, &ns, None) + } + else { + Name::local(&self.name) + }; + let mut start = WriterEvent::start_element(name); + if let Some(ref ns) = self.namespace { start = start.default_ns(ns.as_ref()); } for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently - start = start.attr(attr.name.borrow(), &attr.value); + start = start.attr(Name::local(&attr.name), &attr.value); } writer.write(start)?; for child in &self.children { @@ -167,7 +214,10 @@ impl Element { } } - pub fn append_child(&mut self, child: Element) -> &mut Element { + pub fn append_child(&mut self, mut child: Element) -> &mut Element { + if child.namespace.is_none() { + child.namespace = self.namespace.clone(); + } self.children.push(Fork::Element(child)); if let Fork::Element(ref mut cld) = *self.children.last_mut().unwrap() { cld @@ -233,28 +283,24 @@ impl<'a> Iterator for ChildrenMut<'a> { } pub struct ElementBuilder { - name: OwnedName, - attributes: Vec, + name: String, + namespace: Option, + attributes: Vec, } impl ElementBuilder { pub fn ns>(mut self, namespace: S) -> ElementBuilder { - self.name.namespace = Some(namespace.into()); + self.namespace = Some(namespace.into()); self } pub fn attr, V: Into>(mut self, name: S, value: V) -> ElementBuilder { - self.attributes.push(OwnedAttribute::new(OwnedName::local(name), value)); - self - } - - pub fn attr_ns, N: Into, V: Into>(mut self, name: S, namespace: N, value: V) -> ElementBuilder { - self.attributes.push(OwnedAttribute::new(OwnedName::qualified::<_, _, &'static str>(name, namespace, None), value)); + self.attributes.push(Attribute::new(name, value)); self } pub fn build(self) -> Element { - Element::new(self.name, self.attributes) + Element::new(self.name, self.namespace, self.attributes) } } @@ -290,7 +336,6 @@ mod tests { fn reader_works() { use std::io::Cursor; let mut reader = EventReader::new(Cursor::new(TEST_STRING)); - // TODO: fix a bunch of namespace stuff so this test passes assert_eq!(Element::from_reader(&mut reader).unwrap(), build_test_tree()); } @@ -311,8 +356,18 @@ mod tests { .ns("b") .attr("c", "d") .build(); - assert_eq!(elem.tag(), "a"); + assert_eq!(elem.name(), "a"); assert_eq!(elem.ns(), Some("b")); assert_eq!(elem.attr("c"), Some("d")); + assert_eq!(elem.is("a", "b"), true); + } + + #[test] + fn children_iter_works() { + let root = build_test_tree(); + let mut iter = root.children(); + assert!(iter.next().unwrap().is("child", "root_ns")); + assert!(iter.next().unwrap().is("child", "child_ns")); + assert_eq!(iter.next(), None); } } From 2d97e2d5d9afae6ea4dd71f9ad96cb447b38be8c Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 19 Feb 2017 23:29:19 +0100 Subject: [PATCH 0007/1020] put Attribute into its own module, take into account prefixes when determining namespaces --- src/attribute.rs | 24 ++++++++++++++++++++++++ src/lib.rs | 39 ++++++++++++++++----------------------- 2 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 src/attribute.rs diff --git a/src/attribute.rs b/src/attribute.rs new file mode 100644 index 0000000000000000000000000000000000000000..ac41f1288a583a23f5b4ecc4d138c296194cbfe2 --- /dev/null +++ b/src/attribute.rs @@ -0,0 +1,24 @@ +use xml::escape::escape_str_attribute; + +use std::fmt; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Attribute { + pub name: String, + pub value: String, +} + +impl fmt::Display for Attribute { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}=\"{}\"", self.name, escape_str_attribute(&self.value)) + } +} + +impl Attribute { + pub fn new, V: Into>(name: N, value: V) -> Attribute { + Attribute { + name: name.into(), + value: value.into(), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index f52477229a6ff6bddad1de23ccb70991d401912e..51a83732a65007f4eac5153f97baafa20c0d7c61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ extern crate xml; pub mod error; +pub mod attribute; use std::io::prelude::*; @@ -15,31 +16,11 @@ use std::fmt; use xml::reader::{XmlEvent as ReaderEvent, EventReader}; use xml::writer::{XmlEvent as WriterEvent, EventWriter}; use xml::name::Name; -use xml::escape::escape_str_attribute; use xml::namespace::NS_NO_PREFIX; use error::Error; -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Attribute { - pub name: String, - pub value: String, -} - -impl fmt::Display for Attribute { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}=\"{}\"", self.name, escape_str_attribute(&self.value)) - } -} - -impl Attribute { - pub fn new, V: Into>(name: N, value: V) -> Attribute { - Attribute { - name: name.into(), - value: value.into(), - } - } -} +use attribute::Attribute; #[derive(Clone, PartialEq, Eq)] pub struct Element { @@ -131,7 +112,13 @@ impl Element { let attributes = attributes.into_iter() .map(|o| Attribute::new(o.name.local_name, o.value)) .collect(); - let mut root = Element::new(name.local_name, namespace.get(NS_NO_PREFIX).map(|s| s.to_owned()), attributes); + let ns = if let Some(ref prefix) = name.prefix { + namespace.get(prefix) + } + else { + namespace.get(NS_NO_PREFIX) + }.map(|s| s.to_owned()); + let mut root = Element::new(name.local_name, ns, attributes); root.from_reader_inner(reader); return Ok(root); }, @@ -151,7 +138,13 @@ impl Element { let attributes = attributes.into_iter() .map(|o| Attribute::new(o.name.local_name, o.value)) .collect(); - let elem = Element::new(name.local_name, namespace.get(NS_NO_PREFIX).map(|s| s.to_owned()), attributes); + let ns = if let Some(ref prefix) = name.prefix { + namespace.get(prefix) + } + else { + namespace.get(NS_NO_PREFIX) + }.map(|s| s.to_owned()); + let elem = Element::new(name.local_name, ns, attributes); let elem_ref = self.append_child(elem); elem_ref.from_reader_inner(reader); }, From 663eba6754036d8b92272af83499e12f7360c43f Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 19 Feb 2017 23:42:31 +0100 Subject: [PATCH 0008/1020] implemented text, get_child and get_child_mut; added and updated some tests --- src/lib.rs | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 51a83732a65007f4eac5153f97baafa20c0d7c61..063d0b94ada63c19e58fbd7f4f83f3e392e29be7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -224,21 +224,38 @@ impl Element { self.children.push(Fork::Text(child.into())); } - pub fn text(&self) -> &str { - unimplemented!() + pub fn text(&self) -> String { + let mut ret = String::new(); + for fork in &self.children { + if let Fork::Text(ref s) = *fork { + ret += s; + } + } + ret } - pub fn get_child<'a, N: Into>>(&self, name: N) -> Option<&Element> { - unimplemented!() + pub fn get_child, NS: AsRef>(&self, name: N, namespace: NS) -> Option<&Element> { + for fork in &self.children { + if let Fork::Element(ref e) = *fork { + if e.is(name.as_ref(), namespace.as_ref()) { + return Some(e); + } + } + } + None } - pub fn get_child_mut<'a, N: Into>>(&mut self, name: N) -> Option<&mut Element> { - unimplemented!() + pub fn get_child_mut, NS: AsRef>(&mut self, name: N, namespace: NS) -> Option<&mut Element> { + for fork in &mut self.children { + if let Fork::Element(ref mut e) = *fork { + if e.is(name.as_ref(), namespace.as_ref()) { + return Some(e); + } + } + } + None } - pub fn into_child<'a, N: Into>>(self, name: N) -> Option { - unimplemented!() - } } pub struct Children<'a> { @@ -352,7 +369,8 @@ mod tests { assert_eq!(elem.name(), "a"); assert_eq!(elem.ns(), Some("b")); assert_eq!(elem.attr("c"), Some("d")); - assert_eq!(elem.is("a", "b"), true); + assert_eq!(elem.attr("x"), None); + assert!(elem.is("a", "b")); } #[test] @@ -363,4 +381,15 @@ mod tests { assert!(iter.next().unwrap().is("child", "child_ns")); assert_eq!(iter.next(), None); } + + #[test] + fn get_child_works() { + let root = build_test_tree(); + assert_eq!(root.get_child("child", "inexistent_ns"), None); + assert_eq!(root.get_child("not_a_child", "root_ns"), None); + assert!(root.get_child("child", "root_ns").unwrap().is("child", "root_ns")); + assert!(root.get_child("child", "child_ns").unwrap().is("child", "child_ns")); + assert_eq!(root.get_child("child", "root_ns").unwrap().attr("c"), Some("d")); + assert_eq!(root.get_child("child", "child_ns").unwrap().attr("d"), Some("e")); + } } From 41e1cf2654d34e78722a52f73ae6c42cad69f46b Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 20 Feb 2017 02:04:45 +0100 Subject: [PATCH 0009/1020] added has_child, fixed some warnings --- src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 063d0b94ada63c19e58fbd7f4f83f3e392e29be7..0292bbe99bdc88830dded31a426b0089cd331b41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ pub mod attribute; use std::io::prelude::*; -use std::convert::{From, AsRef}; +use std::convert::AsRef; use std::iter::Iterator; @@ -119,7 +119,7 @@ impl Element { namespace.get(NS_NO_PREFIX) }.map(|s| s.to_owned()); let mut root = Element::new(name.local_name, ns, attributes); - root.from_reader_inner(reader); + root.from_reader_inner(reader)?; return Ok(root); }, ReaderEvent::EndDocument => { @@ -146,7 +146,7 @@ impl Element { }.map(|s| s.to_owned()); let elem = Element::new(name.local_name, ns, attributes); let elem_ref = self.append_child(elem); - elem_ref.from_reader_inner(reader); + elem_ref.from_reader_inner(reader)?; }, ReaderEvent::EndElement { .. } => { // TODO: may want to check whether we're closing the correct element @@ -256,6 +256,9 @@ impl Element { None } + pub fn has_child, NS: AsRef>(&self, name: N, namespace: NS) -> bool { + self.get_child(name, namespace).is_some() + } } pub struct Children<'a> { From e0b35477cd2ae6ecb93d4b6f1e90f84aa034940c Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 20 Feb 2017 23:58:44 +0100 Subject: [PATCH 0010/1020] add ElementBuilder::text --- src/lib.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0292bbe99bdc88830dded31a426b0089cd331b41..ce8a26d8498db8df477ffbc724c0afe9f076fb5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,7 @@ impl Element { pub fn builder>(name: S) -> ElementBuilder { ElementBuilder { name: name.into(), + text: None, namespace: None, attributes: Vec::new(), } @@ -297,6 +298,7 @@ impl<'a> Iterator for ChildrenMut<'a> { pub struct ElementBuilder { name: String, + text: Option, namespace: Option, attributes: Vec, } @@ -312,8 +314,17 @@ impl ElementBuilder { self } + pub fn text>(mut self, text: S) -> ElementBuilder { + self.text = Some(text.into()); + self + } + pub fn build(self) -> Element { - Element::new(self.name, self.namespace, self.attributes) + let mut elem = Element::new(self.name, self.namespace, self.attributes); + if let Some(text) = self.text { + elem.append_text_node(text); + } + elem } } @@ -368,11 +379,13 @@ mod tests { let elem = Element::builder("a") .ns("b") .attr("c", "d") + .text("e") .build(); assert_eq!(elem.name(), "a"); assert_eq!(elem.ns(), Some("b")); assert_eq!(elem.attr("c"), Some("d")); assert_eq!(elem.attr("x"), None); + assert_eq!(elem.text(), "e"); assert!(elem.is("a", "b")); } From 0a45a6993e8c119fffd6e6a2ce8f55b34ba070af Mon Sep 17 00:00:00 2001 From: lumi Date: Tue, 21 Feb 2017 15:46:06 +0100 Subject: [PATCH 0011/1020] document all the things! --- examples/articles.rs | 45 +++++++ src/attribute.rs | 19 +++ src/error.rs | 1 + src/lib.rs | 274 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 334 insertions(+), 5 deletions(-) create mode 100644 examples/articles.rs diff --git a/examples/articles.rs b/examples/articles.rs new file mode 100644 index 0000000000000000000000000000000000000000..6db61d86d078032baf8ed6057770c2db0bab89ae --- /dev/null +++ b/examples/articles.rs @@ -0,0 +1,45 @@ +extern crate minidom; + +use minidom::Element; + +const DATA: &'static str = r#" +
+ 10 Terrible Bugs You Would NEVER Believe Happened + + Rust fixed them all. <3 + +
+
+ BREAKING NEWS: Physical Bug Jumps Out Of Programmer's Screen + + Just kidding! + +
+
"#; + +const ARTICLE_NS: &'static str = "article"; + +#[derive(Debug)] +pub struct Article { + title: String, + body: String, +} + +fn main() { + let root: Element = DATA.parse().unwrap(); + + let mut articles: Vec
= Vec::new(); + + for child in root.children() { + if child.is("article", ARTICLE_NS) { + let title = child.get_child("title", ARTICLE_NS).unwrap().text(); + let body = child.get_child("body", ARTICLE_NS).unwrap().text(); + articles.push(Article { + title: title, + body: body.trim().to_owned(), + }); + } + } + + println!("{:?}", articles); +} diff --git a/src/attribute.rs b/src/attribute.rs index ac41f1288a583a23f5b4ecc4d138c296194cbfe2..5d45f40fad0f5ec77be5705cc370a2d6630ea051 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -2,9 +2,16 @@ use xml::escape::escape_str_attribute; use std::fmt; +/// An attribute of a DOM element. +/// +/// This is of the form: `name`="`value`" +/// +/// This does not support prefixed/namespaced attributes yet. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Attribute { + /// The name of the attribute. pub name: String, + /// The value of the attribute. pub value: String, } @@ -15,6 +22,18 @@ impl fmt::Display for Attribute { } impl Attribute { + /// Construct a new attribute from the given `name` and `value`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Attribute; + /// + /// let attr = Attribute::new("name", "value"); + /// + /// assert_eq!(attr.name, "name"); + /// assert_eq!(attr.value, "value"); + /// ``` pub fn new, V: Into>(name: N, value: V) -> Attribute { Attribute { name: name.into(), diff --git a/src/error.rs b/src/error.rs index db184064d6a49210e4817f384f8b0afec85152ad..38f4069051b2de05338ac2ff08b8ff81394e54a2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,6 +5,7 @@ use std::convert::From; use xml::writer::Error as WriterError; use xml::reader::Error as ReaderError; +/// An enum representing the possible errors. #[derive(Debug)] pub enum Error { IoError(io::Error), diff --git a/src/lib.rs b/src/lib.rs index ce8a26d8498db8df477ffbc724c0afe9f076fb5d..5420df83efc3982d2038ad99c467d951b37ef889 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,76 @@ +//! A minimal DOM crate built on top of xml-rs. +//! +//! This library exports an `Element` struct which represents a DOM tree. +//! +//! # Example +//! +//! Run with `cargo run --example articles`. Located in `examples/articles.rs`. +//! +//! ```rust,ignore +//! extern crate minidom; +//! +//! use minidom::Element; +//! +//! const DATA: &'static str = r#" +//!
+//! 10 Terrible Bugs You Would NEVER Believe Happened +//! +//! Rust fixed them all. <3 +//! +//!
+//!
+//! BREAKING NEWS: Physical Bug Jumps Out Of Programmer's Screen +//! +//! Just kidding! +//! +//!
+//!
"#; +//! +//! const ARTICLE_NS: &'static str = "article"; +//! +//! #[derive(Debug)] +//! pub struct Article { +//! title: String, +//! body: String, +//! } +//! +//! fn main() { +//! let root: Element = DATA.parse().unwrap(); +//! +//! let mut articles: Vec
= Vec::new(); +//! +//! for child in root.children() { +//! if child.is("article", ARTICLE_NS) { +//! let title = child.get_child("title", ARTICLE_NS).unwrap().text(); +//! let body = child.get_child("body", ARTICLE_NS).unwrap().text(); +//! articles.push(Article { +//! title: title, +//! body: body.trim().to_owned(), +//! }); +//! } +//! } +//! +//! println!("{:?}", articles); +//! } +//! ``` +//! +//! # Usage +//! +//! To use `minidom`, add this to your `Cargo.toml`: +//! +//! ```toml,ignore +//! [dependencies.minidom] +//! git = "https://gitlab.com/lumi/minidom-rs.git" +//! ``` + extern crate xml; -pub mod error; -pub mod attribute; +mod error; + +mod attribute; use std::io::prelude::*; +use std::io::Cursor; use std::convert::AsRef; @@ -13,16 +80,19 @@ use std::slice; use std::fmt; +use std::str::FromStr; + use xml::reader::{XmlEvent as ReaderEvent, EventReader}; use xml::writer::{XmlEvent as WriterEvent, EventWriter}; use xml::name::Name; use xml::namespace::NS_NO_PREFIX; -use error::Error; +pub use error::Error; -use attribute::Attribute; +pub use attribute::Attribute; #[derive(Clone, PartialEq, Eq)] +/// A struct representing a DOM Element. pub struct Element { name: String, namespace: Option, @@ -57,13 +127,40 @@ impl fmt::Debug for Element { } } +impl FromStr for Element { + type Err = Error; + + fn from_str(s: &str) -> Result { + let mut reader = EventReader::new(Cursor::new(s)); + Element::from_reader(&mut reader) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] -pub enum Fork { +enum Fork { Element(Element), Text(String), } impl Element { + /// Constructs a new `Element` with the given `name`, `namespace` and `attributes`. + /// + /// You probably should be using `Element::builder` instead of this. + /// + /// # Examples + /// + /// ``` + /// use minidom::{Element, Attribute}; + /// + /// let elem = Element::new( "name".to_owned() + /// , Some("namespace".to_owned()) + /// , vec![ Attribute::new("name", "value") ] ); + /// + /// assert_eq!(elem.name(), "name"); + /// assert_eq!(elem.ns(), Some("namespace")); + /// assert_eq!(elem.attr("name"), Some("value")); + /// assert_eq!(elem.attr("inexistent"), None); + /// ``` pub fn new(name: String, namespace: Option, attributes: Vec) -> Element { Element { name: name, @@ -73,6 +170,25 @@ impl Element { } } + /// Return a builder for an `Element` with the given `name`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem = Element::builder("name") + /// .ns("namespace") + /// .attr("name", "value") + /// .text("inner") + /// .build(); + /// + /// assert_eq!(elem.name(), "name"); + /// assert_eq!(elem.ns(), Some("namespace")); + /// assert_eq!(elem.attr("name"), Some("value")); + /// assert_eq!(elem.attr("inexistent"), None); + /// assert_eq!(elem.text(), "inner"); + /// ``` pub fn builder>(name: S) -> ElementBuilder { ElementBuilder { name: name.into(), @@ -82,15 +198,41 @@ impl Element { } } + /// Returns a bare minimum `Element` with this name. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let bare = Element::bare("name"); + /// + /// assert_eq!(bare.name(), "name"); + /// assert_eq!(bare.ns(), None); + /// assert_eq!(bare.attr("name"), None); + /// assert_eq!(bare.text(), ""); + /// ``` + pub fn bare>(name: S) -> Element { + Element { + name: name.into(), + namespace: None, + attributes: Vec::new(), + children: Vec::new(), + } + } + + /// Returns a reference to the name of this element. pub fn name(&self) -> &str { &self.name } + /// Returns a reference to the namespace of this element, if it has one, else `None`. pub fn ns(&self) -> Option<&str> { self.namespace.as_ref() .map(String::as_ref) } + /// Returns a reference to the value of the given attribute, if it exists, else `None`. pub fn attr(&self, name: &str) -> Option<&str> { for attr in &self.attributes { if attr.name == name { @@ -100,6 +242,20 @@ impl Element { None } + /// Returns whether the element has the given name and namespace. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem = Element::builder("name").ns("namespace").build(); + /// + /// assert_eq!(elem.is("name", "namespace"), true); + /// assert_eq!(elem.is("name", "wrong"), false); + /// assert_eq!(elem.is("wrong", "namespace"), false); + /// assert_eq!(elem.is("wrong", "wrong"), false); + /// ``` pub fn is, NS: AsRef>(&self, name: N, namespace: NS) -> bool { let ns = self.namespace.as_ref().map(String::as_ref); self.name == name.as_ref() && ns == Some(namespace.as_ref()) @@ -196,18 +352,57 @@ impl Element { Ok(()) } + /// Returns an iterator over references to the children of this element. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem: Element = "".parse().unwrap(); + /// + /// let mut iter = elem.children(); + /// assert_eq!(iter.next().unwrap().name(), "child1"); + /// assert_eq!(iter.next().unwrap().name(), "child2"); + /// assert_eq!(iter.next().unwrap().name(), "child3"); + /// assert_eq!(iter.next(), None); + /// ``` pub fn children<'a>(&'a self) -> Children<'a> { Children { iter: self.children.iter(), } } + /// Returns an iterator over mutable references to the children of this element. pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { ChildrenMut { iter: self.children.iter_mut(), } } + /// Appends a child node to the `Element`, returning the appended node. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let mut elem = Element::bare("root"); + /// + /// assert_eq!(elem.children().count(), 0); + /// + /// elem.append_child(Element::bare("child")); + /// + /// { + /// let mut iter = elem.children(); + /// assert_eq!(iter.next().unwrap().name(), "child"); + /// assert_eq!(iter.next(), None); + /// } + /// + /// let child = elem.append_child(Element::bare("new")); + /// + /// assert_eq!(child.name(), "new"); + /// ``` pub fn append_child(&mut self, mut child: Element) -> &mut Element { if child.namespace.is_none() { child.namespace = self.namespace.clone(); @@ -221,10 +416,36 @@ impl Element { } } + /// Appends a text node to an `Element`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let mut elem = Element::bare("node"); + /// + /// assert_eq!(elem.text(), ""); + /// + /// elem.append_text_node("text"); + /// + /// assert_eq!(elem.text(), "text"); + /// ``` pub fn append_text_node>(&mut self, child: S) { self.children.push(Fork::Text(child.into())); } + /// Returns the concatenation of all text nodes in the `Element`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem: Element = "hello, world!".parse().unwrap(); + /// + /// assert_eq!(elem.text(), "hello, world!"); + /// ``` pub fn text(&self) -> String { let mut ret = String::new(); for fork in &self.children { @@ -235,6 +456,23 @@ impl Element { ret } + /// Returns a reference to the first child element with the specific name and namespace, if it + /// exists in the direct descendants of this `Element`, else returns `None`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem: Element = r#""#.parse().unwrap(); + /// + /// assert!(elem.get_child("a", "ns").unwrap().is("a", "ns")); + /// assert!(elem.get_child("a", "other_ns").unwrap().is("a", "other_ns")); + /// assert!(elem.get_child("b", "ns").unwrap().is("b", "ns")); + /// assert_eq!(elem.get_child("c", "ns"), None); + /// assert_eq!(elem.get_child("b", "other_ns"), None); + /// assert_eq!(elem.get_child("a", "inexistent_ns"), None); + /// ``` pub fn get_child, NS: AsRef>(&self, name: N, namespace: NS) -> Option<&Element> { for fork in &self.children { if let Fork::Element(ref e) = *fork { @@ -246,6 +484,8 @@ impl Element { None } + /// Returns a mutable reference to the first child element with the specific name and namespace, + /// if it exists in the direct descendants of this `Element`, else returns `None`. pub fn get_child_mut, NS: AsRef>(&mut self, name: N, namespace: NS) -> Option<&mut Element> { for fork in &mut self.children { if let Fork::Element(ref mut e) = *fork { @@ -257,11 +497,29 @@ impl Element { None } + /// Returns whether a specific child with this name and namespace exists in the direct + /// descendants of the `Element`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem: Element = r#""#.parse().unwrap(); + /// + /// assert_eq!(elem.has_child("a", "other_ns"), true); + /// assert_eq!(elem.has_child("a", "ns"), true); + /// assert_eq!(elem.has_child("a", "inexistent_ns"), false); + /// assert_eq!(elem.has_child("b", "ns"), true); + /// assert_eq!(elem.has_child("b", "other_ns"), false); + /// assert_eq!(elem.has_child("b", "inexistent_ns"), false); + /// ``` pub fn has_child, NS: AsRef>(&self, name: N, namespace: NS) -> bool { self.get_child(name, namespace).is_some() } } +/// An iterator over references to children of an `Element`. pub struct Children<'a> { iter: slice::Iter<'a, Fork>, } @@ -279,6 +537,7 @@ impl<'a> Iterator for Children<'a> { } } +/// An iterator over mutable references to children of an `Element`. pub struct ChildrenMut<'a> { iter: slice::IterMut<'a, Fork>, } @@ -296,6 +555,7 @@ impl<'a> Iterator for ChildrenMut<'a> { } } +/// A builder for `Element`s. pub struct ElementBuilder { name: String, text: Option, @@ -304,21 +564,25 @@ pub struct ElementBuilder { } impl ElementBuilder { + /// Sets the namespace. pub fn ns>(mut self, namespace: S) -> ElementBuilder { self.namespace = Some(namespace.into()); self } + /// Sets an attribute. pub fn attr, V: Into>(mut self, name: S, value: V) -> ElementBuilder { self.attributes.push(Attribute::new(name, value)); self } + /// Sets the inner text. pub fn text>(mut self, text: S) -> ElementBuilder { self.text = Some(text.into()); self } + /// Builds the `Element`. pub fn build(self) -> Element { let mut elem = Element::new(self.name, self.namespace, self.attributes); if let Some(text) = self.text { From 9a00c998ae153268509b0b3f5553d0b4d407e300 Mon Sep 17 00:00:00 2001 From: lumi Date: Sat, 25 Feb 2017 00:10:18 +0100 Subject: [PATCH 0012/1020] add a test for an issue with namespace propagation --- src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 5420df83efc3982d2038ad99c467d951b37ef889..c5f8f9abbaf2ff246a3e67f389a451d01df02d06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -672,4 +672,15 @@ mod tests { assert_eq!(root.get_child("child", "root_ns").unwrap().attr("c"), Some("d")); assert_eq!(root.get_child("child", "child_ns").unwrap().attr("d"), Some("e")); } + + #[test] + fn namespace_propagation_works() { + let mut root = Element::builder("root").ns("root_ns").build(); + let mut child = Element::bare("child"); + let grandchild = Element::bare("grandchild"); + child.append_child(grandchild); + root.append_child(child); + assert_eq!(root.get_child("child", "root_ns").unwrap().ns(), root.ns()); + assert_eq!(root.get_child("grandchild", "root_ns").unwrap().ns(), root.ns()); + } } From a047fdd8708614c31af781696cf2e60db9c3dd86 Mon Sep 17 00:00:00 2001 From: lumi Date: Sat, 25 Feb 2017 15:31:58 +0100 Subject: [PATCH 0013/1020] add data to Cargo.toml --- Cargo.toml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 765a12ddfcb7d4715cf4b7a8c2fd3155b78ff631..7b9131611d368c24e54becdc80db46c17ee42076 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,15 @@ name = "minidom" version = "0.1.0" authors = ["lumi "] +description = "A small, simple DOM implementation on top of xml-rs." +homepage = "https://gitlab.com/lumi/minidom-rs" +repository = "https://gitlab.com/lumi/minidom-rs" +readme = "README.md" +keywords = ["xml"] +license = "LGPL-3.0+" + +[badges] +gitlab = { repository = "lumi/minidom-rs" } [dependencies] -xml-rs = "*" +xml-rs = "0.3.6" From 112b3fa0c8b0363ec1536758ec401cbbbcd7a050 Mon Sep 17 00:00:00 2001 From: lumi Date: Sat, 25 Feb 2017 15:43:56 +0100 Subject: [PATCH 0014/1020] added documentation url and fixed a dumb mistake --- Cargo.toml | 3 ++- src/lib.rs | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7b9131611d368c24e54becdc80db46c17ee42076..a28773a998f9f0e29dc2467bd5a8c4267e7188b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,11 @@ [package] name = "minidom" -version = "0.1.0" +version = "0.1.1" authors = ["lumi "] description = "A small, simple DOM implementation on top of xml-rs." homepage = "https://gitlab.com/lumi/minidom-rs" repository = "https://gitlab.com/lumi/minidom-rs" +documentation = "https://docs.rs/minidom" readme = "README.md" keywords = ["xml"] license = "LGPL-3.0+" diff --git a/src/lib.rs b/src/lib.rs index c5f8f9abbaf2ff246a3e67f389a451d01df02d06..9dfed4489b2e9282bc47e1648424e21b0394dd35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,11 +56,10 @@ //! //! # Usage //! -//! To use `minidom`, add this to your `Cargo.toml`: +//! To use `minidom`, add this to your `Cargo.toml` under `dependencies`: //! //! ```toml,ignore -//! [dependencies.minidom] -//! git = "https://gitlab.com/lumi/minidom-rs.git" +//! minidom = "*" //! ``` extern crate xml; From 4725e5f17445a5f5b464b3011a8920177155aaae Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 27 Feb 2017 15:35:57 +0100 Subject: [PATCH 0015/1020] initial commit --- .gitignore | 2 + Cargo.toml | 6 ++ src/lib.rs | 311 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a9d37c560c6ab8d4afbf47eda643e8c42e857716 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..2197a7982491878d4b1642a993d9351b27e0292e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "jid" +version = "0.1.0" +authors = ["lumi "] + +[dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..8214cf1e10eba56007b08f8798b7d35f141faf71 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,311 @@ +//! Provides a type for Jabber IDs. + +use std::fmt; + +use std::convert::Into; + +use std::str::FromStr; + +/// An error that signifies that a `Jid` cannot be parsed from a string. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum JidParseError { + NoDomain, +} + +/// A struct representing a Jabber ID. +/// +/// A Jabber ID is composed of 3 components, of which 2 are optional: +/// +/// - A node/name, `node`, which is the optional part before the @. +/// - A domain, `domain`, which is the mandatory part after the @ but before the /. +/// - A resource, `resource`, which is the optional part after the /. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Jid { + /// The node part of the Jabber ID, if it exists, else None. + pub node: Option, + /// The domain of the Jabber ID. + pub domain: String, + /// The resource of the Jabber ID, if it exists, else None. + pub resource: Option, +} + +impl fmt::Display for Jid { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + // TODO: may need escaping + if let Some(ref node) = self.node { + write!(fmt, "{}@", node)?; + } + write!(fmt, "{}", self.domain)?; + if let Some(ref resource) = self.resource { + write!(fmt, "/{}", resource)?; + } + Ok(()) + } +} + +enum ParserState { + Node, + Domain, + Resource +} + +impl FromStr for Jid { + type Err = JidParseError; + + fn from_str(s: &str) -> Result { + // TODO: very naive, may need to do it differently + let iter = s.chars(); + let mut buf = String::new(); + let mut state = ParserState::Node; + let mut node = None; + let mut domain = None; + let mut resource = None; + for c in iter { + match state { + ParserState::Node => { + match c { + '@' => { + state = ParserState::Domain; + node = Some(buf.clone()); // TODO: performance tweaks, do not need to copy it + buf.clear(); + }, + '/' => { + state = ParserState::Resource; + domain = Some(buf.clone()); // TODO: performance tweaks + buf.clear(); + }, + c => { + buf.push(c); + }, + } + }, + ParserState::Domain => { + match c { + '/' => { + state = ParserState::Resource; + domain = Some(buf.clone()); // TODO: performance tweaks + buf.clear(); + }, + c => { + buf.push(c); + }, + } + }, + ParserState::Resource => { + buf.push(c); + }, + } + } + if !buf.is_empty() { + match state { + ParserState::Node => { + domain = Some(buf); + }, + ParserState::Domain => { + domain = Some(buf); + }, + ParserState::Resource => { + resource = Some(buf); + }, + } + } + Ok(Jid { + node: node, + domain: domain.ok_or(JidParseError::NoDomain)?, + resource: resource, + }) + } +} + +impl Jid { + /// Constructs a Jabber ID containing all three components. + /// + /// This is of the form `node`@`domain`/`resource`. + /// + /// # Examples + /// + /// ``` + /// use jid::Jid; + /// + /// let jid = Jid::full("node", "domain", "resource"); + /// + /// assert_eq!(jid.node, Some("node".to_owned())); + /// assert_eq!(jid.domain, "domain".to_owned()); + /// assert_eq!(jid.resource, Some("resource".to_owned())); + /// ``` + pub fn full(node: NS, domain: DS, resource: RS) -> Jid + where NS: Into + , DS: Into + , RS: Into { + Jid { + node: Some(node.into()), + domain: domain.into(), + resource: Some(resource.into()), + } + } + + /// Constructs a Jabber ID containing only the `node` and `domain` components. + /// + /// This is of the form `node`@`domain`. + /// + /// # Examples + /// + /// ``` + /// use jid::Jid; + /// + /// let jid = Jid::bare("node", "domain"); + /// + /// assert_eq!(jid.node, Some("node".to_owned())); + /// assert_eq!(jid.domain, "domain".to_owned()); + /// assert_eq!(jid.resource, None); + /// ``` + pub fn bare(node: NS, domain: DS) -> Jid + where NS: Into + , DS: Into { + Jid { + node: Some(node.into()), + domain: domain.into(), + resource: None, + } + } + + /// Constructs a Jabber ID containing only a `domain`. + /// + /// This is of the form `domain`. + /// + /// # Examples + /// + /// ``` + /// use jid::Jid; + /// + /// let jid = Jid::domain("domain"); + /// + /// assert_eq!(jid.node, None); + /// assert_eq!(jid.domain, "domain".to_owned()); + /// assert_eq!(jid.resource, None); + /// ``` + pub fn domain(domain: DS) -> Jid + where DS: Into { + Jid { + node: None, + domain: domain.into(), + resource: None, + } + } + + /// Constructs a Jabber ID containing the `domain` and `resource` components. + /// + /// This is of the form `domain`/`resource`. + /// + /// # Examples + /// + /// ``` + /// use jid::Jid; + /// + /// let jid = Jid::domain_with_resource("domain", "resource"); + /// + /// assert_eq!(jid.node, None); + /// assert_eq!(jid.domain, "domain".to_owned()); + /// assert_eq!(jid.resource, Some("resource".to_owned())); + /// ``` + pub fn domain_with_resource(domain: DS, resource: RS) -> Jid + where DS: Into + , RS: Into { + Jid { + node: None, + domain: domain.into(), + resource: Some(resource.into()), + } + } + + /// Constructs a new Jabber ID from an existing one, with the node swapped out with a new one. + /// + /// # Examples + /// + /// ``` + /// use jid::Jid; + /// + /// let jid = Jid::domain("domain"); + /// + /// assert_eq!(jid.node, None); + /// + /// let new_jid = jid.with_node("node"); + /// + /// assert_eq!(new_jid.node, Some("node".to_owned())); + /// ``` + pub fn with_node(&self, node: S) -> Jid + where S: Into { + Jid { + node: Some(node.into()), + domain: self.domain.clone(), + resource: self.resource.clone(), + } + } + + /// Constructs a new Jabber ID from an existing one, with the domain swapped out with a new one. + /// + /// # Examples + /// + /// ``` + /// use jid::Jid; + /// + /// let jid = Jid::domain("domain"); + /// + /// assert_eq!(jid.domain, "domain"); + /// + /// let new_jid = jid.with_domain("new_domain"); + /// + /// assert_eq!(new_jid.domain, "new_domain"); + /// ``` + pub fn with_domain(&self, domain: S) -> Jid + where S: Into { + Jid { + node: self.node.clone(), + domain: domain.into(), + resource: self.resource.clone(), + } + } + + /// Constructs a new Jabber ID from an existing one, with the resource swapped out with a new one. + /// + /// # Examples + /// + /// ``` + /// use jid::Jid; + /// + /// let jid = Jid::domain("domain"); + /// + /// assert_eq!(jid.resource, None); + /// + /// let new_jid = jid.with_resource("resource"); + /// + /// assert_eq!(new_jid.resource, Some("resource".to_owned())); + /// ``` + pub fn with_resource(&self, resource: S) -> Jid + where S: Into { + Jid { + node: self.node.clone(), + domain: self.domain.clone(), + resource: Some(resource.into()), + } + } + +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::str::FromStr; + + #[test] + fn can_parse_jids() { + assert_eq!(Jid::from_str("a@b.c/d"), Ok(Jid::full("a", "b.c", "d"))); + assert_eq!(Jid::from_str("a@b.c"), Ok(Jid::bare("a", "b.c"))); + assert_eq!(Jid::from_str("b.c"), Ok(Jid::domain("b.c"))); + + assert_eq!(Jid::from_str(""), Err(JidParseError::NoDomain)); + + assert_eq!(Jid::from_str("a/b@c"), Ok(Jid::domain_with_resource("a", "b@c"))); + } +} From 5308b6b1f1715c17f4df00d05fdc9f39dc15a8ff Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 27 Feb 2017 15:06:15 +0000 Subject: [PATCH 0016/1020] Add .gitlab-ci.yml --- .gitlab-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..cb292343052f44cf4efaa6f5bd686defc527ae8a --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,6 @@ +image: "scorpil/rust:stable" + +test:cargo: + script: + - rustc --version && cargo --version + - cargo test --verbose --jobs 1 --release \ No newline at end of file From 19efcf3560a5c4bdc14c300ed87b8ef463783831 Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 27 Feb 2017 16:42:09 +0100 Subject: [PATCH 0017/1020] add license, prepare for release --- COPYING | 675 +++++++++++++++++++++++++++++++++++++++++++++++++ COPYING.LESSER | 166 ++++++++++++ Cargo.toml | 10 + README.md | 31 +++ src/lib.rs | 2 + 5 files changed, 884 insertions(+) create mode 100644 COPYING create mode 100644 COPYING.LESSER create mode 100644 README.md diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..a737dcfed5db21fb99a8fcb812e995937d38401b --- /dev/null +++ b/COPYING @@ -0,0 +1,675 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/COPYING.LESSER b/COPYING.LESSER new file mode 100644 index 0000000000000000000000000000000000000000..5f5ff16a4a0f6104fadb6a5beef527573e46b425 --- /dev/null +++ b/COPYING.LESSER @@ -0,0 +1,166 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/Cargo.toml b/Cargo.toml index 2197a7982491878d4b1642a993d9351b27e0292e..95026c8e5e9144985becef8109c425b46e253d14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,15 @@ name = "jid" version = "0.1.0" authors = ["lumi "] +description = "A crate which provides a Jid struct for Jabber IDs." +homepage = "https://gitlab.com/lumi/jid-rs" +repository = "https://gitlab.com/lumi/jid-rs" +documentation = "https://docs.rs/jid" +readme = "README.md" +keywords = ["xmpp", "jid"] +license = "LGPL-3.0+" + +[badges] +gitlab = { repository = "lumi/jid-rs" } [dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8610e178b008ad6f6e42bc2d60b59b5e51b44511 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +jid-rs +====== + +What's this? +------------ + +A crate which provides a struct Jid for Jabber IDs. It's used in xmpp-rs but other XMPP libraries +can of course use this. + +What license is it under? +------------------------- + +LGPLv3 or later. See `COPYING` and `COPYING.LESSER`. + +License yadda yadda. +-------------------- + + Copyright 2017, jid-rs contributors. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . diff --git a/src/lib.rs b/src/lib.rs index 8214cf1e10eba56007b08f8798b7d35f141faf71..91a744bb2152804b6d69298c602e92a1f929a788 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ //! Provides a type for Jabber IDs. +//! +//! For usage, check the documentation on the `Jid` struct. use std::fmt; From 5da36ecdf44e8fc998426b371bc36ab16755450d Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 27 Feb 2017 16:59:47 +0100 Subject: [PATCH 0018/1020] license updates --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d253eb0f7d6ce4cf0a96fe37cf6b2e3ddd890342..ca2fae7a68b0167107a3f2e3e8e556e16dfc9c58 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,22 @@ A minimal DOM library on top of xml-rs. What license is it under? ------------------------- -LGPLv3. +LGPLv3 or later. See `COPYING` and `COPYING.LESSER`. + +License yadda yadda. +-------------------- + + Copyright 2017, minidom-rs contributors. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . From 5ae85aa8844a8ecc496f70e93652f291dbf0e7ec Mon Sep 17 00:00:00 2001 From: lumi Date: Tue, 28 Feb 2017 12:38:00 +0100 Subject: [PATCH 0019/1020] add #![deny(missing_docs)] and documentation --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 91a744bb2152804b6d69298c602e92a1f929a788..74f1d3cc1a768930cd956e14f451a1883c1bf923 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(missing_docs)] + //! Provides a type for Jabber IDs. //! //! For usage, check the documentation on the `Jid` struct. @@ -11,6 +13,7 @@ use std::str::FromStr; /// An error that signifies that a `Jid` cannot be parsed from a string. #[derive(Debug, Clone, PartialEq, Eq)] pub enum JidParseError { + /// Happens when there is no domain. (really, only happens when the string is empty) NoDomain, } From 7c04aff416135910a1226b17318cc9c2e5d5003b Mon Sep 17 00:00:00 2001 From: lumi Date: Wed, 8 Mar 2017 20:34:17 +0100 Subject: [PATCH 0020/1020] overhauling the library, made tests pass --- .gitlab-ci.yml | 4 +- src/attribute.rs | 2 + src/convert.rs | 101 ++++++++ src/element.rs | 558 ++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 6 + src/lib.rs | 624 +---------------------------------------------- src/tests.rs | 96 ++++++++ 7 files changed, 774 insertions(+), 617 deletions(-) create mode 100644 src/convert.rs create mode 100644 src/element.rs create mode 100644 src/tests.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cb292343052f44cf4efaa6f5bd686defc527ae8a..bd8cf4313765d2f38bea2ff4edc4a22ba61b9866 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,6 @@ -image: "scorpil/rust:stable" +image: "scorpil/rust:nightly" test:cargo: script: - rustc --version && cargo --version - - cargo test --verbose --jobs 1 --release \ No newline at end of file + - cargo test --verbose --jobs 1 --release diff --git a/src/attribute.rs b/src/attribute.rs index 5d45f40fad0f5ec77be5705cc370a2d6630ea051..57373a06dc7ee6b6a62b9b226edc0911dc1e22f4 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -1,3 +1,5 @@ +//! Provides an `Attribute` type which represents an attribute in an XML document. + use xml::escape::escape_str_attribute; use std::fmt; diff --git a/src/convert.rs b/src/convert.rs new file mode 100644 index 0000000000000000000000000000000000000000..83c187713fd98177a8a998a563e7ad928464a8ec --- /dev/null +++ b/src/convert.rs @@ -0,0 +1,101 @@ +//! A module which exports a few traits for converting types to elements and attributes. + +use element::{Element, ElementBuilder}; + +/// A struct which is used for emitting `Element`s and text nodes into an `Element` without +/// exposing more functionality than necessary. +pub struct ElementEmitter<'a>(&'a mut Element); + +impl<'a> ElementEmitter<'a> { + /// Creates a new `ElementEmitter`. + pub fn new(root: &'a mut Element) -> ElementEmitter<'a> { + ElementEmitter(root) + } + + /// Appends an `Element` to the target. + pub fn append_child(&mut self, element: Element) { + self.0.append_child(element); + } + + /// Appends a text node to the target. + pub fn append_text_node(&mut self, text: String) { + self.0.append_text_node(text); + } +} + +/// A trait for types which can be converted to one or multiple `Element`s. +pub trait IntoElements { + /// Emits this as a sequence of text nodes and `Element`s. + fn into_elements(self, emitter: &mut ElementEmitter); +} + +impl IntoElements for Vec { + fn into_elements(self, emitter: &mut ElementEmitter) { + for elem in self { + elem.into_elements(emitter); + } + } +} + +impl<'a, T: IntoElements + Clone> IntoElements for &'a [T] { + fn into_elements(self, emitter: &mut ElementEmitter) { + self.to_vec().into_elements(emitter); + } +} + +impl IntoElements for Option { + fn into_elements(self, emitter: &mut ElementEmitter) { + match self { + Some(e) => e.into_elements(emitter), + None => (), + } + } +} + +impl IntoElements for Element { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self); + } +} + +impl IntoElements for ElementBuilder { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.build()); + } +} + +impl IntoElements for String { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_text_node(self); + } +} + +impl<'a> IntoElements for &'a str { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_text_node(self.to_owned()); + } +} + +/// A trait for types which can be converted to an attribute value. +pub trait IntoAttributeValue { + /// Turns this into an attribute string, or None if it shouldn't be added. + fn into_attribute_value(self) -> Option; +} + +impl IntoAttributeValue for String { + fn into_attribute_value(self) -> Option { + Some(self.clone()) + } +} + +impl<'a> IntoAttributeValue for &'a str { + fn into_attribute_value(self) -> Option { + Some((*self).to_owned()) + } +} + +impl IntoAttributeValue for Option { + fn into_attribute_value(self) -> Option { + self.and_then(|t| t.into_attribute_value()) + } +} diff --git a/src/element.rs b/src/element.rs new file mode 100644 index 0000000000000000000000000000000000000000..cedb90e5d9ede927ef116369fb2c8aca0f4d7bd3 --- /dev/null +++ b/src/element.rs @@ -0,0 +1,558 @@ +//! Provides an `Element` type, which represents DOM nodes, and a builder to create them with. + +use std::io::prelude::*; +use std::io::Cursor; + +use std::fmt; + +use error::Error; + +use attribute::Attribute; + +use xml::reader::{XmlEvent as ReaderEvent, EventReader}; +use xml::writer::{XmlEvent as WriterEvent, EventWriter}; +use xml::name::Name; +use xml::namespace::NS_NO_PREFIX; + +use std::str::FromStr; + +use std::slice; + +use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; + +#[derive(Clone, PartialEq, Eq)] +/// A struct representing a DOM Element. +pub struct Element { + name: String, + namespace: Option, + attributes: Vec, + children: Vec, +} + +impl fmt::Debug for Element { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + if let Some(ref ns) = self.namespace { + write!(fmt, "<{{{}}}{}", ns, self.name)?; + } + else { + write!(fmt, "<{}", self.name)?; + } + for attr in &self.attributes { + write!(fmt, " {}", attr)?; + } + write!(fmt, ">")?; + for child in &self.children { + match *child { + Node::Element(ref e) => { + write!(fmt, "{:?}", e)?; + }, + Node::Text(ref s) => { + write!(fmt, "{}", s)?; + }, + } + } + write!(fmt, "", self.name)?; + Ok(()) + } +} + +impl FromStr for Element { + type Err = Error; + + fn from_str(s: &str) -> Result { + let mut reader = EventReader::new(Cursor::new(s)); + Element::from_reader(&mut reader) + } +} + +/// A node in an element tree. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Node { + /// An `Element`. + Element(Element), + /// A text node. + Text(String), +} + +impl Element { + fn new(name: String, namespace: Option, attributes: Vec, children: Vec) -> Element { + Element { + name: name, + namespace: namespace, + attributes: attributes, + children: children, + } + } + + /// Return a builder for an `Element` with the given `name`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem = Element::builder("name") + /// .ns("namespace") + /// .attr("name", "value") + /// .append("inner") + /// .build(); + /// + /// assert_eq!(elem.name(), "name"); + /// assert_eq!(elem.ns(), Some("namespace")); + /// assert_eq!(elem.attr("name"), Some("value")); + /// assert_eq!(elem.attr("inexistent"), None); + /// assert_eq!(elem.text(), "inner"); + /// ``` + pub fn builder>(name: S) -> ElementBuilder { + ElementBuilder { + root: Element::new(name.into(), None, Vec::new(), Vec::new()), + } + } + + /// Returns a bare minimum `Element` with this name. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let bare = Element::bare("name"); + /// + /// assert_eq!(bare.name(), "name"); + /// assert_eq!(bare.ns(), None); + /// assert_eq!(bare.attr("name"), None); + /// assert_eq!(bare.text(), ""); + /// ``` + pub fn bare>(name: S) -> Element { + Element { + name: name.into(), + namespace: None, + attributes: Vec::new(), + children: Vec::new(), + } + } + + /// Returns a reference to the name of this element. + pub fn name(&self) -> &str { + &self.name + } + + /// Returns a reference to the namespace of this element, if it has one, else `None`. + pub fn ns(&self) -> Option<&str> { + self.namespace.as_ref() + .map(String::as_ref) + } + + /// Returns a reference to the value of the given attribute, if it exists, else `None`. + pub fn attr(&self, name: &str) -> Option<&str> { + for attr in &self.attributes { + if attr.name == name { + return Some(&attr.value); + } + } + None + } + + /// Modifies the value of an attribute. + pub fn set_attr, V: IntoAttributeValue>(&mut self, name: S, val: V) { + let name = name.into(); + let val = val.into_attribute_value(); + for attr in &mut self.attributes { + if attr.name == name { + attr.value = val.expect("removing existing value via set_attr, this is not yet supported (TODO)"); // TODO + return; + } + } + if let Some(val) = val { + self.attributes.push(Attribute::new(name, val)); + } + } + + /// Returns whether the element has the given name and namespace. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem = Element::builder("name").ns("namespace").build(); + /// + /// assert_eq!(elem.is("name", "namespace"), true); + /// assert_eq!(elem.is("name", "wrong"), false); + /// assert_eq!(elem.is("wrong", "namespace"), false); + /// assert_eq!(elem.is("wrong", "wrong"), false); + /// ``` + pub fn is, NS: AsRef>(&self, name: N, namespace: NS) -> bool { + let ns = self.namespace.as_ref().map(String::as_ref); + self.name == name.as_ref() && ns == Some(namespace.as_ref()) + } + + /// Parse a document from an `EventReader`. + pub fn from_reader(reader: &mut EventReader) -> Result { + loop { + let e = reader.next()?; + match e { + ReaderEvent::StartElement { name, attributes, namespace } => { + let attributes = attributes.into_iter() + .map(|o| Attribute::new(o.name.local_name, o.value)) + .collect(); + let ns = if let Some(ref prefix) = name.prefix { + namespace.get(prefix) + } + else { + namespace.get(NS_NO_PREFIX) + }.map(|s| s.to_owned()); + let mut root = Element::new(name.local_name, ns, attributes, Vec::new()); + root.from_reader_inner(reader)?; + return Ok(root); + }, + ReaderEvent::EndDocument => { + return Err(Error::EndOfDocument); + }, + _ => () // TODO: may need more errors + } + } + } + + fn from_reader_inner(&mut self, reader: &mut EventReader) -> Result<(), Error> { + loop { + let e = reader.next()?; + match e { + ReaderEvent::StartElement { name, attributes, namespace } => { + let attributes = attributes.into_iter() + .map(|o| Attribute::new(o.name.local_name, o.value)) + .collect(); + let ns = if let Some(ref prefix) = name.prefix { + namespace.get(prefix) + } + else { + namespace.get(NS_NO_PREFIX) + }.map(|s| s.to_owned()); + let elem = Element::new(name.local_name, ns, attributes, Vec::with_capacity(1)); + let elem_ref = self.append_child(elem); + elem_ref.from_reader_inner(reader)?; + }, + ReaderEvent::EndElement { .. } => { + // TODO: may want to check whether we're closing the correct element + return Ok(()); + }, + ReaderEvent::Characters(s) => { + self.append_text_node(s); + }, + ReaderEvent::CData(s) => { + self.append_text_node(s); + }, + ReaderEvent::EndDocument => { + return Err(Error::EndOfDocument); + }, + _ => (), // TODO: may need to implement more + } + } + } + + /// Output a document to an `EventWriter`. + pub fn write_to(&self, writer: &mut EventWriter) -> Result<(), Error> { + let name = if let Some(ref ns) = self.namespace { + Name::qualified(&self.name, &ns, None) + } + else { + Name::local(&self.name) + }; + let mut start = WriterEvent::start_element(name); + if let Some(ref ns) = self.namespace { + start = start.default_ns(ns.as_ref()); + } + for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently + start = start.attr(Name::local(&attr.name), &attr.value); + } + writer.write(start)?; + for child in &self.children { + match *child { + Node::Element(ref e) => { + e.write_to(writer)?; + }, + Node::Text(ref s) => { + writer.write(WriterEvent::characters(s))?; + }, + } + } + writer.write(WriterEvent::end_element())?; + Ok(()) + } + + /// Returns an iterator over references to the children of this element. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem: Element = "".parse().unwrap(); + /// + /// let mut iter = elem.children(); + /// assert_eq!(iter.next().unwrap().name(), "child1"); + /// assert_eq!(iter.next().unwrap().name(), "child2"); + /// assert_eq!(iter.next().unwrap().name(), "child3"); + /// assert_eq!(iter.next(), None); + /// ``` + pub fn children<'a>(&'a self) -> Children<'a> { + Children { + iter: self.children.iter(), + } + } + + /// Returns an iterator over mutable references to the children of this element. + pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { + ChildrenMut { + iter: self.children.iter_mut(), + } + } + + fn propagate_namespaces(&mut self) { + let ns = self.namespace.clone(); + for child in self.children_mut() { + if child.namespace.is_none() { + child.namespace = ns.clone(); + child.propagate_namespaces(); + } + } + } + + /// Appends a child node to the `Element`, returning the appended node. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let mut elem = Element::bare("root"); + /// + /// assert_eq!(elem.children().count(), 0); + /// + /// elem.append_child(Element::bare("child")); + /// + /// { + /// let mut iter = elem.children(); + /// assert_eq!(iter.next().unwrap().name(), "child"); + /// assert_eq!(iter.next(), None); + /// } + /// + /// let child = elem.append_child(Element::bare("new")); + /// + /// assert_eq!(child.name(), "new"); + /// ``` + pub fn append_child(&mut self, mut child: Element) -> &mut Element { + if child.namespace.is_none() && self.namespace.is_some() { + child.namespace = self.namespace.clone(); + child.propagate_namespaces(); + } + self.children.push(Node::Element(child)); + if let Node::Element(ref mut cld) = *self.children.last_mut().unwrap() { + cld + } + else { + unreachable!() + } + } + + /// Appends a text node to an `Element`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let mut elem = Element::bare("node"); + /// + /// assert_eq!(elem.text(), ""); + /// + /// elem.append_text_node("text"); + /// + /// assert_eq!(elem.text(), "text"); + /// ``` + pub fn append_text_node>(&mut self, child: S) { + self.children.push(Node::Text(child.into())); + } + + /// Appends a node to an `Element`. + /// + /// # Examples + /// + /// ``` + /// use minidom::{Element, Node}; + /// + /// let mut elem = Element::bare("node"); + /// + /// elem.append_node(Node::Text("hello".to_owned())); + /// + /// assert_eq!(elem.text(), "hello"); + /// ``` + pub fn append_node(&mut self, node: Node) { + self.children.push(node); + } + + /// Returns the concatenation of all text nodes in the `Element`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem: Element = "hello, world!".parse().unwrap(); + /// + /// assert_eq!(elem.text(), "hello, world!"); + /// ``` + pub fn text(&self) -> String { + let mut ret = String::new(); + for fork in &self.children { + if let Node::Text(ref s) = *fork { + ret += s; + } + } + ret + } + + /// Returns a reference to the first child element with the specific name and namespace, if it + /// exists in the direct descendants of this `Element`, else returns `None`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem: Element = r#""#.parse().unwrap(); + /// + /// assert!(elem.get_child("a", "ns").unwrap().is("a", "ns")); + /// assert!(elem.get_child("a", "other_ns").unwrap().is("a", "other_ns")); + /// assert!(elem.get_child("b", "ns").unwrap().is("b", "ns")); + /// assert_eq!(elem.get_child("c", "ns"), None); + /// assert_eq!(elem.get_child("b", "other_ns"), None); + /// assert_eq!(elem.get_child("a", "inexistent_ns"), None); + /// ``` + pub fn get_child, NS: AsRef>(&self, name: N, namespace: NS) -> Option<&Element> { + for fork in &self.children { + if let Node::Element(ref e) = *fork { + if e.is(name.as_ref(), namespace.as_ref()) { + return Some(e); + } + } + } + None + } + + /// Returns a mutable reference to the first child element with the specific name and namespace, + /// if it exists in the direct descendants of this `Element`, else returns `None`. + pub fn get_child_mut, NS: AsRef>(&mut self, name: N, namespace: NS) -> Option<&mut Element> { + for fork in &mut self.children { + if let Node::Element(ref mut e) = *fork { + if e.is(name.as_ref(), namespace.as_ref()) { + return Some(e); + } + } + } + None + } + + /// Returns whether a specific child with this name and namespace exists in the direct + /// descendants of the `Element`. + /// + /// # Examples + /// + /// ``` + /// use minidom::Element; + /// + /// let elem: Element = r#""#.parse().unwrap(); + /// + /// assert_eq!(elem.has_child("a", "other_ns"), true); + /// assert_eq!(elem.has_child("a", "ns"), true); + /// assert_eq!(elem.has_child("a", "inexistent_ns"), false); + /// assert_eq!(elem.has_child("b", "ns"), true); + /// assert_eq!(elem.has_child("b", "other_ns"), false); + /// assert_eq!(elem.has_child("b", "inexistent_ns"), false); + /// ``` + pub fn has_child, NS: AsRef>(&self, name: N, namespace: NS) -> bool { + self.get_child(name, namespace).is_some() + } +} + +/// An iterator over references to children of an `Element`. +pub struct Children<'a> { + iter: slice::Iter<'a, Node>, +} + +impl<'a> Iterator for Children<'a> { + type Item = &'a Element; + + fn next(&mut self) -> Option<&'a Element> { + while let Some(item) = self.iter.next() { + if let Node::Element(ref child) = *item { + return Some(child); + } + } + None + } +} + +/// An iterator over mutable references to children of an `Element`. +pub struct ChildrenMut<'a> { + iter: slice::IterMut<'a, Node>, +} + +impl<'a> Iterator for ChildrenMut<'a> { + type Item = &'a mut Element; + + fn next(&mut self) -> Option<&'a mut Element> { + while let Some(item) = self.iter.next() { + if let Node::Element(ref mut child) = *item { + return Some(child); + } + } + None + } +} + +/// A builder for `Element`s. +pub struct ElementBuilder { + root: Element, +} + +impl ElementBuilder { + /// Sets the namespace. + pub fn ns>(mut self, namespace: S) -> ElementBuilder { + self.root.namespace = Some(namespace.into()); + self + } + + /// Sets an attribute. + pub fn attr, V: IntoAttributeValue>(mut self, name: S, value: V) -> ElementBuilder { + self.root.set_attr(name, value); + self + } + + /// Appends anything implementing `IntoElements` into the tree. + pub fn append(mut self, into: T) -> ElementBuilder { + { + let mut emitter = ElementEmitter::new(&mut self.root); + into.into_elements(&mut emitter); + } + self + } + + /// Builds the `Element`. + pub fn build(self) -> Element { + self.root + } +} + +#[test] +fn test_element_new() { + let elem = Element::new( "name".to_owned() + , Some("namespace".to_owned()) + , vec![ Attribute::new("name", "value") ] + , Vec::new() ); + + assert_eq!(elem.name(), "name"); + assert_eq!(elem.ns(), Some("namespace")); + assert_eq!(elem.attr("name"), Some("value")); + assert_eq!(elem.attr("inexistent"), None); +} diff --git a/src/error.rs b/src/error.rs index 38f4069051b2de05338ac2ff08b8ff81394e54a2..dc818e2f651e8452d548182f5957b57663147aa1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,5 @@ +//! Provides an error type for this crate. + use std::io; use std::convert::From; @@ -8,9 +10,13 @@ use xml::reader::Error as ReaderError; /// An enum representing the possible errors. #[derive(Debug)] pub enum Error { + /// An io::Error. IoError(io::Error), + /// An error in the xml-rs `EventWriter`. XmlWriterError(WriterError), + /// An error in the xml-rs `EventReader`. XmlReaderError(ReaderError), + /// The end of the document has been reached unexpectedly. EndOfDocument, } diff --git a/src/lib.rs b/src/lib.rs index 9dfed4489b2e9282bc47e1648424e21b0394dd35..4728608db797f5ee40e62273acd5bffa58e38f8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(missing_docs)] + //! A minimal DOM crate built on top of xml-rs. //! //! This library exports an `Element` struct which represents a DOM tree. @@ -64,622 +66,14 @@ extern crate xml; -mod error; - -mod attribute; - -use std::io::prelude::*; -use std::io::Cursor; - -use std::convert::AsRef; - -use std::iter::Iterator; - -use std::slice; - -use std::fmt; +pub mod error; +pub mod attribute; +pub mod element; +pub mod convert; -use std::str::FromStr; - -use xml::reader::{XmlEvent as ReaderEvent, EventReader}; -use xml::writer::{XmlEvent as WriterEvent, EventWriter}; -use xml::name::Name; -use xml::namespace::NS_NO_PREFIX; +#[cfg(test)] mod tests; pub use error::Error; - pub use attribute::Attribute; - -#[derive(Clone, PartialEq, Eq)] -/// A struct representing a DOM Element. -pub struct Element { - name: String, - namespace: Option, - attributes: Vec, - children: Vec, -} - -impl fmt::Debug for Element { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - if let Some(ref ns) = self.namespace { - write!(fmt, "<{{{}}}{}", ns, self.name)?; - } - else { - write!(fmt, "<{}", self.name)?; - } - for attr in &self.attributes { - write!(fmt, " {}", attr)?; - } - write!(fmt, ">")?; - for child in &self.children { - match *child { - Fork::Element(ref e) => { - write!(fmt, "{:?}", e)?; - }, - Fork::Text(ref s) => { - write!(fmt, "{}", s)?; - }, - } - } - write!(fmt, "", self.name)?; - Ok(()) - } -} - -impl FromStr for Element { - type Err = Error; - - fn from_str(s: &str) -> Result { - let mut reader = EventReader::new(Cursor::new(s)); - Element::from_reader(&mut reader) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -enum Fork { - Element(Element), - Text(String), -} - -impl Element { - /// Constructs a new `Element` with the given `name`, `namespace` and `attributes`. - /// - /// You probably should be using `Element::builder` instead of this. - /// - /// # Examples - /// - /// ``` - /// use minidom::{Element, Attribute}; - /// - /// let elem = Element::new( "name".to_owned() - /// , Some("namespace".to_owned()) - /// , vec![ Attribute::new("name", "value") ] ); - /// - /// assert_eq!(elem.name(), "name"); - /// assert_eq!(elem.ns(), Some("namespace")); - /// assert_eq!(elem.attr("name"), Some("value")); - /// assert_eq!(elem.attr("inexistent"), None); - /// ``` - pub fn new(name: String, namespace: Option, attributes: Vec) -> Element { - Element { - name: name, - namespace: namespace, - attributes: attributes, - children: Vec::new(), - } - } - - /// Return a builder for an `Element` with the given `name`. - /// - /// # Examples - /// - /// ``` - /// use minidom::Element; - /// - /// let elem = Element::builder("name") - /// .ns("namespace") - /// .attr("name", "value") - /// .text("inner") - /// .build(); - /// - /// assert_eq!(elem.name(), "name"); - /// assert_eq!(elem.ns(), Some("namespace")); - /// assert_eq!(elem.attr("name"), Some("value")); - /// assert_eq!(elem.attr("inexistent"), None); - /// assert_eq!(elem.text(), "inner"); - /// ``` - pub fn builder>(name: S) -> ElementBuilder { - ElementBuilder { - name: name.into(), - text: None, - namespace: None, - attributes: Vec::new(), - } - } - - /// Returns a bare minimum `Element` with this name. - /// - /// # Examples - /// - /// ``` - /// use minidom::Element; - /// - /// let bare = Element::bare("name"); - /// - /// assert_eq!(bare.name(), "name"); - /// assert_eq!(bare.ns(), None); - /// assert_eq!(bare.attr("name"), None); - /// assert_eq!(bare.text(), ""); - /// ``` - pub fn bare>(name: S) -> Element { - Element { - name: name.into(), - namespace: None, - attributes: Vec::new(), - children: Vec::new(), - } - } - - /// Returns a reference to the name of this element. - pub fn name(&self) -> &str { - &self.name - } - - /// Returns a reference to the namespace of this element, if it has one, else `None`. - pub fn ns(&self) -> Option<&str> { - self.namespace.as_ref() - .map(String::as_ref) - } - - /// Returns a reference to the value of the given attribute, if it exists, else `None`. - pub fn attr(&self, name: &str) -> Option<&str> { - for attr in &self.attributes { - if attr.name == name { - return Some(&attr.value); - } - } - None - } - - /// Returns whether the element has the given name and namespace. - /// - /// # Examples - /// - /// ``` - /// use minidom::Element; - /// - /// let elem = Element::builder("name").ns("namespace").build(); - /// - /// assert_eq!(elem.is("name", "namespace"), true); - /// assert_eq!(elem.is("name", "wrong"), false); - /// assert_eq!(elem.is("wrong", "namespace"), false); - /// assert_eq!(elem.is("wrong", "wrong"), false); - /// ``` - pub fn is, NS: AsRef>(&self, name: N, namespace: NS) -> bool { - let ns = self.namespace.as_ref().map(String::as_ref); - self.name == name.as_ref() && ns == Some(namespace.as_ref()) - } - - pub fn from_reader(reader: &mut EventReader) -> Result { - loop { - let e = reader.next()?; - match e { - ReaderEvent::StartElement { name, attributes, namespace } => { - let attributes = attributes.into_iter() - .map(|o| Attribute::new(o.name.local_name, o.value)) - .collect(); - let ns = if let Some(ref prefix) = name.prefix { - namespace.get(prefix) - } - else { - namespace.get(NS_NO_PREFIX) - }.map(|s| s.to_owned()); - let mut root = Element::new(name.local_name, ns, attributes); - root.from_reader_inner(reader)?; - return Ok(root); - }, - ReaderEvent::EndDocument => { - return Err(Error::EndOfDocument); - }, - _ => () // TODO: may need more errors - } - } - } - - fn from_reader_inner(&mut self, reader: &mut EventReader) -> Result<(), Error> { - loop { - let e = reader.next()?; - match e { - ReaderEvent::StartElement { name, attributes, namespace } => { - let attributes = attributes.into_iter() - .map(|o| Attribute::new(o.name.local_name, o.value)) - .collect(); - let ns = if let Some(ref prefix) = name.prefix { - namespace.get(prefix) - } - else { - namespace.get(NS_NO_PREFIX) - }.map(|s| s.to_owned()); - let elem = Element::new(name.local_name, ns, attributes); - let elem_ref = self.append_child(elem); - elem_ref.from_reader_inner(reader)?; - }, - ReaderEvent::EndElement { .. } => { - // TODO: may want to check whether we're closing the correct element - return Ok(()); - }, - ReaderEvent::Characters(s) => { - self.append_text_node(s); - }, - ReaderEvent::CData(s) => { - self.append_text_node(s); - }, - ReaderEvent::EndDocument => { - return Err(Error::EndOfDocument); - }, - _ => (), // TODO: may need to implement more - } - } - } - - pub fn write_to(&self, writer: &mut EventWriter) -> Result<(), Error> { - let name = if let Some(ref ns) = self.namespace { - Name::qualified(&self.name, &ns, None) - } - else { - Name::local(&self.name) - }; - let mut start = WriterEvent::start_element(name); - if let Some(ref ns) = self.namespace { - start = start.default_ns(ns.as_ref()); - } - for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently - start = start.attr(Name::local(&attr.name), &attr.value); - } - writer.write(start)?; - for child in &self.children { - match *child { - Fork::Element(ref e) => { - e.write_to(writer)?; - }, - Fork::Text(ref s) => { - writer.write(WriterEvent::characters(s))?; - }, - } - } - writer.write(WriterEvent::end_element())?; - Ok(()) - } - - /// Returns an iterator over references to the children of this element. - /// - /// # Examples - /// - /// ``` - /// use minidom::Element; - /// - /// let elem: Element = "".parse().unwrap(); - /// - /// let mut iter = elem.children(); - /// assert_eq!(iter.next().unwrap().name(), "child1"); - /// assert_eq!(iter.next().unwrap().name(), "child2"); - /// assert_eq!(iter.next().unwrap().name(), "child3"); - /// assert_eq!(iter.next(), None); - /// ``` - pub fn children<'a>(&'a self) -> Children<'a> { - Children { - iter: self.children.iter(), - } - } - - /// Returns an iterator over mutable references to the children of this element. - pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { - ChildrenMut { - iter: self.children.iter_mut(), - } - } - - /// Appends a child node to the `Element`, returning the appended node. - /// - /// # Examples - /// - /// ``` - /// use minidom::Element; - /// - /// let mut elem = Element::bare("root"); - /// - /// assert_eq!(elem.children().count(), 0); - /// - /// elem.append_child(Element::bare("child")); - /// - /// { - /// let mut iter = elem.children(); - /// assert_eq!(iter.next().unwrap().name(), "child"); - /// assert_eq!(iter.next(), None); - /// } - /// - /// let child = elem.append_child(Element::bare("new")); - /// - /// assert_eq!(child.name(), "new"); - /// ``` - pub fn append_child(&mut self, mut child: Element) -> &mut Element { - if child.namespace.is_none() { - child.namespace = self.namespace.clone(); - } - self.children.push(Fork::Element(child)); - if let Fork::Element(ref mut cld) = *self.children.last_mut().unwrap() { - cld - } - else { - unreachable!() - } - } - - /// Appends a text node to an `Element`. - /// - /// # Examples - /// - /// ``` - /// use minidom::Element; - /// - /// let mut elem = Element::bare("node"); - /// - /// assert_eq!(elem.text(), ""); - /// - /// elem.append_text_node("text"); - /// - /// assert_eq!(elem.text(), "text"); - /// ``` - pub fn append_text_node>(&mut self, child: S) { - self.children.push(Fork::Text(child.into())); - } - - /// Returns the concatenation of all text nodes in the `Element`. - /// - /// # Examples - /// - /// ``` - /// use minidom::Element; - /// - /// let elem: Element = "hello, world!".parse().unwrap(); - /// - /// assert_eq!(elem.text(), "hello, world!"); - /// ``` - pub fn text(&self) -> String { - let mut ret = String::new(); - for fork in &self.children { - if let Fork::Text(ref s) = *fork { - ret += s; - } - } - ret - } - - /// Returns a reference to the first child element with the specific name and namespace, if it - /// exists in the direct descendants of this `Element`, else returns `None`. - /// - /// # Examples - /// - /// ``` - /// use minidom::Element; - /// - /// let elem: Element = r#""#.parse().unwrap(); - /// - /// assert!(elem.get_child("a", "ns").unwrap().is("a", "ns")); - /// assert!(elem.get_child("a", "other_ns").unwrap().is("a", "other_ns")); - /// assert!(elem.get_child("b", "ns").unwrap().is("b", "ns")); - /// assert_eq!(elem.get_child("c", "ns"), None); - /// assert_eq!(elem.get_child("b", "other_ns"), None); - /// assert_eq!(elem.get_child("a", "inexistent_ns"), None); - /// ``` - pub fn get_child, NS: AsRef>(&self, name: N, namespace: NS) -> Option<&Element> { - for fork in &self.children { - if let Fork::Element(ref e) = *fork { - if e.is(name.as_ref(), namespace.as_ref()) { - return Some(e); - } - } - } - None - } - - /// Returns a mutable reference to the first child element with the specific name and namespace, - /// if it exists in the direct descendants of this `Element`, else returns `None`. - pub fn get_child_mut, NS: AsRef>(&mut self, name: N, namespace: NS) -> Option<&mut Element> { - for fork in &mut self.children { - if let Fork::Element(ref mut e) = *fork { - if e.is(name.as_ref(), namespace.as_ref()) { - return Some(e); - } - } - } - None - } - - /// Returns whether a specific child with this name and namespace exists in the direct - /// descendants of the `Element`. - /// - /// # Examples - /// - /// ``` - /// use minidom::Element; - /// - /// let elem: Element = r#""#.parse().unwrap(); - /// - /// assert_eq!(elem.has_child("a", "other_ns"), true); - /// assert_eq!(elem.has_child("a", "ns"), true); - /// assert_eq!(elem.has_child("a", "inexistent_ns"), false); - /// assert_eq!(elem.has_child("b", "ns"), true); - /// assert_eq!(elem.has_child("b", "other_ns"), false); - /// assert_eq!(elem.has_child("b", "inexistent_ns"), false); - /// ``` - pub fn has_child, NS: AsRef>(&self, name: N, namespace: NS) -> bool { - self.get_child(name, namespace).is_some() - } -} - -/// An iterator over references to children of an `Element`. -pub struct Children<'a> { - iter: slice::Iter<'a, Fork>, -} - -impl<'a> Iterator for Children<'a> { - type Item = &'a Element; - - fn next(&mut self) -> Option<&'a Element> { - while let Some(item) = self.iter.next() { - if let Fork::Element(ref child) = *item { - return Some(child); - } - } - None - } -} - -/// An iterator over mutable references to children of an `Element`. -pub struct ChildrenMut<'a> { - iter: slice::IterMut<'a, Fork>, -} - -impl<'a> Iterator for ChildrenMut<'a> { - type Item = &'a mut Element; - - fn next(&mut self) -> Option<&'a mut Element> { - while let Some(item) = self.iter.next() { - if let Fork::Element(ref mut child) = *item { - return Some(child); - } - } - None - } -} - -/// A builder for `Element`s. -pub struct ElementBuilder { - name: String, - text: Option, - namespace: Option, - attributes: Vec, -} - -impl ElementBuilder { - /// Sets the namespace. - pub fn ns>(mut self, namespace: S) -> ElementBuilder { - self.namespace = Some(namespace.into()); - self - } - - /// Sets an attribute. - pub fn attr, V: Into>(mut self, name: S, value: V) -> ElementBuilder { - self.attributes.push(Attribute::new(name, value)); - self - } - - /// Sets the inner text. - pub fn text>(mut self, text: S) -> ElementBuilder { - self.text = Some(text.into()); - self - } - - /// Builds the `Element`. - pub fn build(self) -> Element { - let mut elem = Element::new(self.name, self.namespace, self.attributes); - if let Some(text) = self.text { - elem.append_text_node(text); - } - elem - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use xml::reader::EventReader; - use xml::writer::EventWriter; - - const TEST_STRING: &'static str = r#"meownya"#; - - fn build_test_tree() -> Element { - let mut root = Element::builder("root") - .ns("root_ns") - .attr("a", "b") - .build(); - root.append_text_node("meow"); - let child = Element::builder("child") - .attr("c", "d") - .build(); - root.append_child(child); - let other_child = Element::builder("child") - .ns("child_ns") - .attr("d", "e") - .build(); - root.append_child(other_child); - root.append_text_node("nya"); - root - } - - #[test] - fn reader_works() { - use std::io::Cursor; - let mut reader = EventReader::new(Cursor::new(TEST_STRING)); - assert_eq!(Element::from_reader(&mut reader).unwrap(), build_test_tree()); - } - - #[test] - fn writer_works() { - let root = build_test_tree(); - let mut out = Vec::new(); - { - let mut writer = EventWriter::new(&mut out); - root.write_to(&mut writer).unwrap(); - } - assert_eq!(String::from_utf8(out).unwrap(), TEST_STRING); - } - - #[test] - fn builder_works() { - let elem = Element::builder("a") - .ns("b") - .attr("c", "d") - .text("e") - .build(); - assert_eq!(elem.name(), "a"); - assert_eq!(elem.ns(), Some("b")); - assert_eq!(elem.attr("c"), Some("d")); - assert_eq!(elem.attr("x"), None); - assert_eq!(elem.text(), "e"); - assert!(elem.is("a", "b")); - } - - #[test] - fn children_iter_works() { - let root = build_test_tree(); - let mut iter = root.children(); - assert!(iter.next().unwrap().is("child", "root_ns")); - assert!(iter.next().unwrap().is("child", "child_ns")); - assert_eq!(iter.next(), None); - } - - #[test] - fn get_child_works() { - let root = build_test_tree(); - assert_eq!(root.get_child("child", "inexistent_ns"), None); - assert_eq!(root.get_child("not_a_child", "root_ns"), None); - assert!(root.get_child("child", "root_ns").unwrap().is("child", "root_ns")); - assert!(root.get_child("child", "child_ns").unwrap().is("child", "child_ns")); - assert_eq!(root.get_child("child", "root_ns").unwrap().attr("c"), Some("d")); - assert_eq!(root.get_child("child", "child_ns").unwrap().attr("d"), Some("e")); - } - - #[test] - fn namespace_propagation_works() { - let mut root = Element::builder("root").ns("root_ns").build(); - let mut child = Element::bare("child"); - let grandchild = Element::bare("grandchild"); - child.append_child(grandchild); - root.append_child(child); - assert_eq!(root.get_child("child", "root_ns").unwrap().ns(), root.ns()); - assert_eq!(root.get_child("grandchild", "root_ns").unwrap().ns(), root.ns()); - } -} +pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder}; +pub use convert::{IntoElements, IntoAttributeValue}; diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..b9263446cd110826889fbd63e7d9d560d4188e6a --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,96 @@ +use std::io::Cursor; + +use std::iter::Iterator; + +use xml::reader::EventReader; +use xml::writer::EventWriter; + +use element::Element; + +const TEST_STRING: &'static str = r#"meownya"#; + +fn build_test_tree() -> Element { + let mut root = Element::builder("root") + .ns("root_ns") + .attr("a", "b") + .build(); + root.append_text_node("meow"); + let child = Element::builder("child") + .attr("c", "d") + .build(); + root.append_child(child); + let other_child = Element::builder("child") + .ns("child_ns") + .attr("d", "e") + .build(); + root.append_child(other_child); + root.append_text_node("nya"); + root +} + +#[test] +fn reader_works() { + let mut reader = EventReader::new(Cursor::new(TEST_STRING)); + assert_eq!(Element::from_reader(&mut reader).unwrap(), build_test_tree()); +} + +#[test] +fn writer_works() { + let root = build_test_tree(); + let mut out = Vec::new(); + { + let mut writer = EventWriter::new(&mut out); + root.write_to(&mut writer).unwrap(); + } + assert_eq!(String::from_utf8(out).unwrap(), TEST_STRING); +} + +#[test] +fn builder_works() { + let elem = Element::builder("a") + .ns("b") + .attr("c", "d") + .append(Element::builder("child")) + .append("e") + .build(); + assert_eq!(elem.name(), "a"); + assert_eq!(elem.ns(), Some("b")); + assert_eq!(elem.attr("c"), Some("d")); + assert_eq!(elem.attr("x"), None); + assert_eq!(elem.text(), "e"); + assert!(elem.has_child("child", "b")); + assert!(elem.is("a", "b")); +} + +#[test] +fn children_iter_works() { + let root = build_test_tree(); + let mut iter = root.children(); + assert!(iter.next().unwrap().is("child", "root_ns")); + assert!(iter.next().unwrap().is("child", "child_ns")); + assert_eq!(iter.next(), None); +} + +#[test] +fn get_child_works() { + let root = build_test_tree(); + assert_eq!(root.get_child("child", "inexistent_ns"), None); + assert_eq!(root.get_child("not_a_child", "root_ns"), None); + assert!(root.get_child("child", "root_ns").unwrap().is("child", "root_ns")); + assert!(root.get_child("child", "child_ns").unwrap().is("child", "child_ns")); + assert_eq!(root.get_child("child", "root_ns").unwrap().attr("c"), Some("d")); + assert_eq!(root.get_child("child", "child_ns").unwrap().attr("d"), Some("e")); +} + +#[test] +fn namespace_propagation_works() { + let mut root = Element::builder("root").ns("root_ns").build(); + let mut child = Element::bare("child"); + let grandchild = Element::bare("grandchild"); + child.append_child(grandchild); + root.append_child(child); + assert_eq!(root.get_child("child", "root_ns").unwrap().ns(), root.ns()); + assert_eq!(root.get_child("child", "root_ns").unwrap() + .get_child("grandchild", "root_ns").unwrap() + .ns(), root.ns()); +} From f301f2cb106f121a8d732f185f4f0274d2b78417 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 31 Mar 2017 16:59:20 +0100 Subject: [PATCH 0021/1020] make Debug on Element return valid XML --- src/element.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/element.rs b/src/element.rs index cedb90e5d9ede927ef116369fb2c8aca0f4d7bd3..50b63cd01359354040447926e0c4634245a1ba5b 100644 --- a/src/element.rs +++ b/src/element.rs @@ -31,11 +31,9 @@ pub struct Element { impl fmt::Debug for Element { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "<{}", self.name)?; if let Some(ref ns) = self.namespace { - write!(fmt, "<{{{}}}{}", ns, self.name)?; - } - else { - write!(fmt, "<{}", self.name)?; + write!(fmt, " xmlns=\"{}\"", ns)?; } for attr in &self.attributes { write!(fmt, " {}", attr)?; From 9af7d252d7041646a64685ce847b91a60cb3421e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 31 Mar 2017 17:01:48 +0100 Subject: [PATCH 0022/1020] auto-close elements with no child --- src/element.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/element.rs b/src/element.rs index 50b63cd01359354040447926e0c4634245a1ba5b..13c8baf1c4e02fd86d0d86767964a424626de476 100644 --- a/src/element.rs +++ b/src/element.rs @@ -38,18 +38,23 @@ impl fmt::Debug for Element { for attr in &self.attributes { write!(fmt, " {}", attr)?; } - write!(fmt, ">")?; - for child in &self.children { - match *child { - Node::Element(ref e) => { - write!(fmt, "{:?}", e)?; - }, - Node::Text(ref s) => { - write!(fmt, "{}", s)?; - }, + if self.children.is_empty() { + write!(fmt, "/>")?; + } + else { + write!(fmt, ">")?; + for child in &self.children { + match *child { + Node::Element(ref e) => { + write!(fmt, "{:?}", e)?; + }, + Node::Text(ref s) => { + write!(fmt, "{}", s)?; + }, + } } + write!(fmt, "", self.name)?; } - write!(fmt, "", self.name)?; Ok(()) } } From 9cf1521775d2eda1df49c63bfe1485777efde945 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Apr 2017 20:44:36 +0100 Subject: [PATCH 0023/1020] Hello world! --- .hgignore | 2 + Cargo.toml | 7 + src/data_forms.rs | 148 +++++++++++++++++++ src/disco.rs | 193 +++++++++++++++++++++++++ src/ecaps2.rs | 330 +++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 23 +++ src/lib.rs | 9 ++ src/media_element.rs | 177 +++++++++++++++++++++++ src/ns.rs | 3 + 9 files changed, 892 insertions(+) create mode 100644 .hgignore create mode 100644 Cargo.toml create mode 100644 src/data_forms.rs create mode 100644 src/disco.rs create mode 100644 src/ecaps2.rs create mode 100644 src/error.rs create mode 100644 src/lib.rs create mode 100644 src/media_element.rs create mode 100644 src/ns.rs diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000000000000000000000000000000000000..a9d37c560c6ab8d4afbf47eda643e8c42e857716 --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..10c55dd53824d3fe4df43a74c6b6b8975951ee11 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "xmpp-parsers" +version = "0.1.0" +authors = ["Emmanuel Gil Peyrot "] + +[dependencies] +minidom = "0.1.1" diff --git a/src/data_forms.rs b/src/data_forms.rs new file mode 100644 index 0000000000000000000000000000000000000000..f4eb70c834487de4db9b088974e2b782db1d0aa5 --- /dev/null +++ b/src/data_forms.rs @@ -0,0 +1,148 @@ +extern crate minidom; + +use std::str::FromStr; + +use minidom::Element; + +use error::Error; +use ns::{DATA_FORMS_NS, MEDIA_ELEMENT_NS}; + +use media_element::{MediaElement, parse_media_element}; + +#[derive(Debug)] +pub struct Field { + pub var: String, + pub type_: String, // TODO: use an enum here. + pub label: Option, + pub values: Vec, + pub media: Vec, +} + +#[derive(Debug, PartialEq)] +pub enum DataFormType { + Cancel, + Form, + Result_, + Submit, +} + +impl FromStr for DataFormType { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "cancel" { + Ok(DataFormType::Cancel) + } else if s == "form" { + Ok(DataFormType::Form) + } else if s == "result" { + Ok(DataFormType::Result_) + } else if s == "submit" { + Ok(DataFormType::Submit) + } else { + Err(Error::ParseError("Unknown data form type.")) + } + } +} + +#[derive(Debug)] +pub struct DataForm { + pub type_: DataFormType, + pub form_type: Option, + pub fields: Vec, +} + +pub fn parse_data_form(root: &Element) -> Result { + assert!(root.is("x", DATA_FORMS_NS)); + let type_: DataFormType = match root.attr("type") { + Some(type_) => type_.parse()?, + None => return Err(Error::ParseError("Type attribute on data form is mandatory.")), + }; + let mut fields = vec!(); + let mut form_type = None; + for field in root.children() { + if field.is("field", DATA_FORMS_NS) { + let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?; + let field_type = field.attr("type").unwrap_or("text-single"); + let label = field.attr("label").and_then(|label| label.parse().ok()); + let mut values = vec!(); + let mut media = vec!(); + for element in field.children() { + if element.is("value", DATA_FORMS_NS) { + values.push(element.text()); + } else if element.is("media", MEDIA_ELEMENT_NS) { + match parse_media_element(element) { + Ok(media_element) => media.push(media_element), + Err(_) => (), // TODO: is it really nice to swallow this error? + } + } else { + return Err(Error::ParseError("Field child isn’t a value or media element.")); + } + } + if var == "FORM_TYPE" && field_type == "hidden" { + if form_type != None { + return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); + } + if values.len() != 1 { + return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); + } + form_type = Some(values[0].clone()); + } + fields.push(Field { + var: var.to_owned(), + type_: field_type.to_owned(), + label: label, + values: values, + media: media, + }); + } else { + return Err(Error::ParseError("Unknown field type in data form.")); + } + } + Ok(DataForm { type_: type_, form_type: form_type, fields: fields }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use data_forms; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let form = data_forms::parse_data_form(&elem).unwrap(); + assert_eq!(form.type_, data_forms::DataFormType::Result_); + assert!(form.form_type.is_none()); + assert!(form.fields.is_empty()); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = data_forms::parse_data_form(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Type attribute on data form is mandatory."); + + let elem: Element = "".parse().unwrap(); + let error = data_forms::parse_data_form(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown data form type."); + } + + #[test] + fn test_wrong_child() { + let elem: Element = "".parse().unwrap(); + let error = data_forms::parse_data_form(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown field type in data form."); + } +} diff --git a/src/disco.rs b/src/disco.rs new file mode 100644 index 0000000000000000000000000000000000000000..7b2232ecd92c706b7bbdddc5abb564f8e9c6c387 --- /dev/null +++ b/src/disco.rs @@ -0,0 +1,193 @@ +extern crate minidom; + +use minidom::Element; + +use error::Error; +use ns::{DISCO_INFO_NS, DATA_FORMS_NS}; + +use data_forms::{DataForm, DataFormType, parse_data_form}; + +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct Feature { + pub var: String, +} + +#[derive(Debug)] +pub struct Identity { + pub category: String, // TODO: use an enum here. + pub type_: String, // TODO: use an enum here. + pub xml_lang: String, + pub name: Option, +} + +#[derive(Debug)] +pub struct Disco { + pub node: Option, + pub identities: Vec, + pub features: Vec, + pub extensions: Vec, +} + +pub fn parse_disco(root: &Element) -> Result { + assert!(root.is("query", DISCO_INFO_NS)); + let mut identities: Vec = vec!(); + let mut features: Vec = vec!(); + let mut extensions: Vec = vec!(); + + let node = root.attr("node") + .and_then(|node| node.parse().ok()); + + for child in root.children() { + if child.is("feature", DISCO_INFO_NS) { + let feature = child.attr("var") + .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?; + features.push(Feature { + var: feature.to_owned(), + }); + } else if child.is("identity", DISCO_INFO_NS) { + let category = child.attr("category") + .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?; + if category == "" { + return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) + } + + let type_ = child.attr("type") + .ok_or(Error::ParseError("Identity must have a 'type' attribute."))?; + if type_ == "" { + return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) + } + + // TODO: this must check for the namespace of the attribute, but minidom doesn’t support that yet, see issue #2. + let xml_lang = child.attr("lang").unwrap_or(""); + let name = child.attr("name") + .and_then(|node| node.parse().ok()); + identities.push(Identity { + category: category.to_owned(), + type_: type_.to_owned(), + xml_lang: xml_lang.to_owned(), + name: name, + }); + } else if child.is("x", DATA_FORMS_NS) { + let data_form = parse_data_form(child)?; + match data_form.type_ { + DataFormType::Result_ => (), + _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")), + } + match data_form.form_type { + Some(_) => extensions.push(data_form), + None => return Err(Error::ParseError("Data form found without a FORM_TYPE.")), + } + } else { + return Err(Error::ParseError("Unknown element in disco#info.")); + } + } + + if identities.is_empty() { + return Err(Error::ParseError("There must be at least one identity in disco#info.")); + } + if features.is_empty() { + return Err(Error::ParseError("There must be at least one feature in disco#info.")); + } + if !features.contains(&Feature { var: DISCO_INFO_NS.to_owned() }) { + return Err(Error::ParseError("disco#info feature not present in disco#info.")); + } + + return Ok(Disco { + node: node, + identities: identities, + features: features, + extensions: extensions + }); +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use disco; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let query = disco::parse_disco(&elem).unwrap(); + assert!(query.node.is_none()); + assert_eq!(query.identities.len(), 1); + assert_eq!(query.features.len(), 1); + assert!(query.extensions.is_empty()); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown element in disco#info."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "There must be at least one identity in disco#info."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Identity must have a 'category' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Identity must have a non-empty 'category' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Identity must have a 'type' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Identity must have a non-empty 'type' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Feature must have a 'var' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "There must be at least one feature in disco#info."); + + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "disco#info feature not present in disco#info."); + } +} diff --git a/src/ecaps2.rs b/src/ecaps2.rs new file mode 100644 index 0000000000000000000000000000000000000000..50b94128ee78fed1849251d620e41b9c1b9f47b4 --- /dev/null +++ b/src/ecaps2.rs @@ -0,0 +1,330 @@ +extern crate minidom; + +use minidom::Element; + +use error::Error; + +use disco::{Feature, Identity, Disco, parse_disco}; +use data_forms::DataForm; + +fn compute_item(field: String) -> Vec { + let mut bytes = field.as_bytes().to_vec(); + bytes.push(0x1f); + bytes +} + +fn compute_items Vec>(things: Vec, separator: u8, encode: F) -> Vec { + let mut string: Vec = vec!(); + let mut accumulator: Vec> = vec!(); + for thing in things { + let bytes = encode(thing); + accumulator.push(bytes); + } + // This works using the expected i;octet collation. + accumulator.sort(); + for mut bytes in accumulator { + string.append(&mut bytes); + } + string.push(separator); + string +} + +fn compute_features(features: Vec) -> Vec { + compute_items(features, 0x1c, |feature| compute_item(feature.var)) +} + +fn compute_identities(identities: Vec) -> Vec { + compute_items(identities, 0x1c, |identity| { + let mut bytes = compute_item(identity.category); + bytes.append(&mut compute_item(identity.type_)); + bytes.append(&mut compute_item(identity.xml_lang)); + bytes.append(&mut compute_item(identity.name.unwrap_or(String::new()))); + bytes.push(0x1e); + bytes + }) +} + +fn compute_extensions(extensions: Vec) -> Vec { + compute_items(extensions, 0x1c, |extension| { + compute_items(extension.fields, 0x1d, |field| { + let mut bytes = compute_item(field.var); + bytes.append(&mut compute_items(field.values, 0x1e, + |value| compute_item(value))); + bytes + }) + }) +} + +fn compute_disco(disco: Disco) -> Vec { + let features_string = compute_features(disco.features); + let identities_string = compute_identities(disco.identities); + let extensions_string = compute_extensions(disco.extensions); + + let mut final_string = vec!(); + final_string.extend(features_string); + final_string.extend(identities_string); + final_string.extend(extensions_string); + final_string +} + +pub fn convert_element(root: &Element) -> Result, Error> { + let disco = parse_disco(root)?; + let final_string = compute_disco(disco); + Ok(final_string) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use ecaps2; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + assert_eq!(ecaps2.len(), 54); + } + + #[test] + fn test_xep_ex1() { + let elem: Element = r#" + + + + + + + + + + + + + + + + + + + + +"#.parse().unwrap(); + let expected = vec![104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, + 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, + 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, + 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 104, + 97, 116, 115, 116, 97, 116, 101, 115, 31, 104, 116, 116, 112, 58, + 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, + 110, 102, 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, + 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, + 108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, + 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, + 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, + 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, + 114, 111, 115, 116, 101, 114, 120, 31, 104, 116, 116, 112, 58, 47, + 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112, + 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, + 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47, 112, 114, 111, + 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, 114, 97, 110, + 115, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, + 58, 108, 97, 115, 116, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, + 58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114, + 58, 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98, + 98, 101, 114, 58, 105, 113, 58, 116, 105, 109, 101, 31, 106, 97, + 98, 98, 101, 114, 58, 105, 113, 58, 118, 101, 114, 115, 105, 111, + 110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 111, 111, 98, 31, + 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, + 117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, + 112, 116, 115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, + 105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111, + 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, + 100, 31, 30, 28, 28]; + let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + assert_eq!(ecaps2.len(), 0x1d9); + assert_eq!(ecaps2, expected); + + /* + let sha_256 = hash(ecaps2, "sha-256"); + assert_eq!(sha_256, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); + let sha3_256 = hash(ecaps2, "sha3-256"); + assert_eq!(sha3_256, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); + */ + } + + #[test] + fn test_xep_ex2() { + let elem: Element = r#" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + urn:xmpp:dataforms:softwareinfo + + + Tkabber + + + 0.11.1-svn-20111216-mod (Tcl/Tk 8.6b2) + + + Windows + + + XP + + + +"#.parse().unwrap(); + let expected = vec![103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100, + 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, + 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 97, + 99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47, + 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111, 108, 47, 97, 99, 116, 105, 118, 105, 116, + 121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116, 116, 112, 58, + 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, + 101, 97, 109, 115, 31, 104, 116, 116, 112, 58,47, 47, 106, 97, 98, + 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, + 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, + 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111, + 109, 109, 97, 110, 100, 115, 31,104,116, 116, 112, 58, 47, 47, 106, + 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, + 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102, + 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, + 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, + 100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31, 104, 116, + 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, + 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108, + 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, + 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 102, + 101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31, 104, 116, 116, + 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, + 112, 114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, + 99, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, + 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,111, 108, 47, + 103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, + 114, 103,47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, + 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, + 46, 111, 114, 103, 47, 112, 114, 111,116, 111, 99, 111, 108, 47, + 105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, + 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,111, + 99, 111, 108, 47, 109, 111, 111, 100, 31, 104, 116, 116, 112, 58, + 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111,108, 47, 109, 111, 111, 100, 43, 110, 111, + 116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, + 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, + 99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116, + 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, + 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, + 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, + 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47, + 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, + 114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47, + 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, + 111, 116, 111, 99, 111, 108, 47, 116, 117, 110, 101, 31, 104, 116, + 116, 112, 58, 47, 47, 119, 119, 119, 46, 102, 97, 99, 101, 98, 111, + 111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109, 101, + 115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47, + 119, 119, 119, 46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101, + 120, 116, 101, 110, 115, 105, 111, 110, 115, 47, 120, 101, 112, 45, + 48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115, 45, 109, 101, + 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31, + 106, 97, 98, 98, 101, 114,58, 105,113, 58, 97, 118, 97, 116, 97, + 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 98, 114, 111, + 119, 115, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, + 100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, + 102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101, + 114, 58, 105, 113, 58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114, + 58, 105, 113, 58, 105, 110, 98, 97, 110, 100, 31, 106, 97, 98, 98, + 101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105, 110, 107, 31, + 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31, + 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106, + 97,98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99, + 121, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111, + 115, 116, 101, 114,31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, + 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, + 58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, + 114, 58, 120, 58, 100, 97, 116, 97, 31, 106, 97, 98, 98, 101, 114, + 58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98, 98, 101, + 114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, + 112, 112, 58, 97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97, + 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121,31, 117, 114, + 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, 117, 114, + 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116, + 115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, + 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 101, + 110, 31, 84, 107, 97, 98, 98, 101, 114,31, 30, 99, 108, 105, 101, + 110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208, + 176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79, + 82, 77, 95, 84, 89, 80, 69, 31, 117, 114, 110, 58, 120, 109, 112, + 112, 58, 100, 97, 116, 97, 102, 111, 114, 109, 115, 58, 115, 111, + 102, 116, 119, 97, 114, 101,105, 110, 102, 111, 31, 30, 111, 115, + 31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118, + 101, 114, 115, 105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102, + 116, 119, 97, 114, 101, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30, + 115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114, 115, 105, + 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, + 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, + 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; + let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + assert_eq!(ecaps2.len(), 0x543); + assert_eq!(ecaps2, expected); + + /* + let sha_256 = hash(ecaps2, "sha-256"); + assert_eq!(sha_256, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); + let sha3_256 = hash(ecaps2, "sha3-256"); + assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); + */ + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..bd7d2d9cb78de8139f056e49809df24309f4e63c --- /dev/null +++ b/src/error.rs @@ -0,0 +1,23 @@ +use std::convert::From; +use std::io; + +use minidom; + +#[derive(Debug)] +pub enum Error { + IoError(io::Error), + XMLError(minidom::Error), + ParseError(&'static str), +} + +impl From for Error { + fn from(err: io::Error) -> Error { + Error::IoError(err) + } +} + +impl From for Error { + fn from(err: minidom::Error) -> Error { + Error::XMLError(err) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..a6aa19012c75972a40a775a9399d47e266df8377 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,9 @@ +extern crate minidom; + +pub mod error; +pub mod ns; + +pub mod disco; +pub mod data_forms; +pub mod media_element; +pub mod ecaps2; diff --git a/src/media_element.rs b/src/media_element.rs new file mode 100644 index 0000000000000000000000000000000000000000..1fc84507deae634f804ae1c3880b743282a06159 --- /dev/null +++ b/src/media_element.rs @@ -0,0 +1,177 @@ +use minidom::Element; + +use error::Error; + +const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; + +#[derive(Debug)] +pub struct URI { + pub type_: String, + pub uri: String, +} + +#[derive(Debug)] +pub struct MediaElement { + pub width: Option, + pub height: Option, + pub uris: Vec, +} + +pub fn parse_media_element(root: &Element) -> Result { + assert!(root.is("media", MEDIA_ELEMENT_NS)); + let width = root.attr("width").and_then(|width| width.parse().ok()); + let height = root.attr("height").and_then(|height| height.parse().ok()); + let mut uris = vec!(); + for uri in root.children() { + if uri.is("uri", MEDIA_ELEMENT_NS) { + let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?; + let text = uri.text().trim().to_owned(); + if text == "" { + return Err(Error::ParseError("URI missing in uri.")); + } + uris.push(URI { type_: type_.to_owned(), uri: text }); + } else { + return Err(Error::ParseError("Unknown child in media element.")); + } + } + Ok(MediaElement { width: width, height: height, uris: uris }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use media_element; + use data_forms; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.width.is_none()); + assert!(media.height.is_none()); + assert!(media.uris.is_empty()); + } + + #[test] + fn test_width_height() { + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert_eq!(media.width.unwrap(), 32); + assert_eq!(media.height.unwrap(), 32); + } + + #[test] + fn test_uri() { + let elem: Element = "https://example.org/".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert_eq!(media.uris.len(), 1); + assert_eq!(media.uris[0].type_, "text/html"); + assert_eq!(media.uris[0].uri, "https://example.org/"); + } + + #[test] + fn test_invalid_width_height() { + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.width.is_none()); + + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.width.is_none()); + + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.height.is_none()); + + let elem: Element = "".parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.height.is_none()); + } + + #[test] + fn test_unknown_child() { + let elem: Element = "".parse().unwrap(); + let error = media_element::parse_media_element(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in media element."); + } + + #[test] + fn test_bad_uri() { + let elem: Element = "https://example.org/".parse().unwrap(); + let error = media_element::parse_media_element(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Attribute type on uri is mandatory."); + + let elem: Element = "".parse().unwrap(); + let error = media_element::parse_media_element(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "URI missing in uri."); + } + + #[test] + fn test_xep_ex1() { + let elem: Element = r#" + + + http://victim.example.com/challenges/speech.wav?F3A6292C + + + cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org + + + http://victim.example.com/challenges/speech.mp3?F3A6292C + +"#.parse().unwrap(); + let media = media_element::parse_media_element(&elem).unwrap(); + assert!(media.width.is_none()); + assert!(media.height.is_none()); + assert_eq!(media.uris.len(), 3); + assert_eq!(media.uris[0].type_, "audio/x-wav"); + assert_eq!(media.uris[0].uri, "http://victim.example.com/challenges/speech.wav?F3A6292C"); + assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex"); + assert_eq!(media.uris[1].uri, "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org"); + assert_eq!(media.uris[2].type_, "audio/mpeg"); + assert_eq!(media.uris[2].uri, "http://victim.example.com/challenges/speech.mp3?F3A6292C"); + } + + #[test] + fn test_xep_ex2() { + let elem: Element = r#" + + [ ... ] + + + + http://www.victim.com/challenges/ocr.jpeg?F3A6292C + + + cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org + + + + [ ... ] +"#.parse().unwrap(); + let form = data_forms::parse_data_form(&elem).unwrap(); + assert_eq!(form.fields.len(), 1); + assert_eq!(form.fields[0].var, "ocr"); + assert_eq!(form.fields[0].media[0].width, Some(290)); + assert_eq!(form.fields[0].media[0].height, Some(80)); + assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg"); + assert_eq!(form.fields[0].media[0].uris[0].uri, "http://www.victim.com/challenges/ocr.jpeg?F3A6292C"); + assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg"); + assert_eq!(form.fields[0].media[0].uris[1].uri, "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org"); + } +} diff --git a/src/ns.rs b/src/ns.rs new file mode 100644 index 0000000000000000000000000000000000000000..36bd127c0837df33ee0a1c8067d2914893567a86 --- /dev/null +++ b/src/ns.rs @@ -0,0 +1,3 @@ +pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info"; +pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; +pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; From 82fa22cd0637a9867e25ab10ddf5fc206a16f182 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 02:27:33 +0100 Subject: [PATCH 0024/1020] disco: Rename misnamed variable. --- src/disco.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/disco.rs b/src/disco.rs index 7b2232ecd92c706b7bbdddc5abb564f8e9c6c387..bc65e3a1115945cbeb54bbf0b33aa63e89372239 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -60,7 +60,7 @@ pub fn parse_disco(root: &Element) -> Result { // TODO: this must check for the namespace of the attribute, but minidom doesn’t support that yet, see issue #2. let xml_lang = child.attr("lang").unwrap_or(""); let name = child.attr("name") - .and_then(|node| node.parse().ok()); + .and_then(|name| name.parse().ok()); identities.push(Identity { category: category.to_owned(), type_: type_.to_owned(), From 9da488f9095a29e514ad1c6eea469beb57c85b90 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 02:27:42 +0100 Subject: [PATCH 0025/1020] Add a Jingle parser. --- src/jingle.rs | 491 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/ns.rs | 1 + 3 files changed, 493 insertions(+) create mode 100644 src/jingle.rs diff --git a/src/jingle.rs b/src/jingle.rs new file mode 100644 index 0000000000000000000000000000000000000000..e732c6b430b57a8331cae6c6863784da477f3ff6 --- /dev/null +++ b/src/jingle.rs @@ -0,0 +1,491 @@ +extern crate minidom; + +use std::str::FromStr; + +use minidom::Element; + +use error::Error; +use ns::{JINGLE_NS}; + +#[derive(Debug, PartialEq)] +pub enum Action { + ContentAccept, + ContentAdd, + ContentModify, + ContentReject, + ContentRemove, + DescriptionInfo, + SecurityInfo, + SessionAccept, + SessionInfo, + SessionInitiate, + SessionTerminate, + TransportAccept, + TransportInfo, + TransportReject, + TransportReplace, +} + +impl FromStr for Action { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "content-accept" { + Ok(Action::ContentAccept) + } else if s == "content-add" { + Ok(Action::ContentAdd) + } else if s == "content-modify" { + Ok(Action::ContentModify) + } else if s == "content-reject" { + Ok(Action::ContentReject) + } else if s == "content-remove" { + Ok(Action::ContentRemove) + } else if s == "description-info" { + Ok(Action::DescriptionInfo) + } else if s == "security-info" { + Ok(Action::SecurityInfo) + } else if s == "session-accept" { + Ok(Action::SessionAccept) + } else if s == "session-info" { + Ok(Action::SessionInfo) + } else if s == "session-initiate" { + Ok(Action::SessionInitiate) + } else if s == "session-terminate" { + Ok(Action::SessionTerminate) + } else if s == "transport-accept" { + Ok(Action::TransportAccept) + } else if s == "transport-info" { + Ok(Action::TransportInfo) + } else if s == "transport-reject" { + Ok(Action::TransportReject) + } else if s == "transport-replace" { + Ok(Action::TransportReplace) + } else { + Err(Error::ParseError("Unknown action.")) + } + } +} + +// TODO: use a real JID type. +type Jid = String; + +#[derive(Debug, PartialEq)] +pub enum Creator { + Initiator, + Responder, +} + +impl FromStr for Creator { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "initiator" { + Ok(Creator::Initiator) + } else if s == "responder" { + Ok(Creator::Responder) + } else { + Err(Error::ParseError("Unknown creator.")) + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Senders { + Both, + Initiator, + None_, + Responder, +} + +impl FromStr for Senders { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "both" { + Ok(Senders::Both) + } else if s == "initiator" { + Ok(Senders::Initiator) + } else if s == "none" { + Ok(Senders::None_) + } else if s == "responder" { + Ok(Senders::Responder) + } else { + Err(Error::ParseError("Unknown senders.")) + } + } +} + +#[derive(Debug)] +pub struct Content { + pub creator: Creator, + pub disposition: String, + pub name: String, + pub senders: Senders, + pub description: String, + pub transport: String, + pub security: Option, +} + +#[derive(Debug, PartialEq)] +pub enum Reason { + AlternativeSession, //(String), + Busy, + Cancel, + ConnectivityError, + Decline, + Expired, + FailedApplication, + FailedTransport, + GeneralError, + Gone, + IncompatibleParameters, + MediaError, + SecurityError, + Success, + Timeout, + UnsupportedApplications, + UnsupportedTransports, +} + +impl FromStr for Reason { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "alternative-session" { + Ok(Reason::AlternativeSession) + } else if s == "busy" { + Ok(Reason::Busy) + } else if s == "cancel" { + Ok(Reason::Cancel) + } else if s == "connectivity-error" { + Ok(Reason::ConnectivityError) + } else if s == "decline" { + Ok(Reason::Decline) + } else if s == "expired" { + Ok(Reason::Expired) + } else if s == "failed-application" { + Ok(Reason::FailedApplication) + } else if s == "failed-transport" { + Ok(Reason::FailedTransport) + } else if s == "general-error" { + Ok(Reason::GeneralError) + } else if s == "gone" { + Ok(Reason::Gone) + } else if s == "incompatible-parameters" { + Ok(Reason::IncompatibleParameters) + } else if s == "media-error" { + Ok(Reason::MediaError) + } else if s == "security-error" { + Ok(Reason::SecurityError) + } else if s == "success" { + Ok(Reason::Success) + } else if s == "timeout" { + Ok(Reason::Timeout) + } else if s == "unsupported-applications" { + Ok(Reason::UnsupportedApplications) + } else if s == "unsupported-transports" { + Ok(Reason::UnsupportedTransports) + } else { + Err(Error::ParseError("Unknown reason.")) + } + } +} + +#[derive(Debug)] +pub struct ReasonElement { + pub reason: Reason, + pub text: Option, +} + +#[derive(Debug)] +pub struct Jingle { + pub action: Action, + pub initiator: Option, + pub responder: Option, + pub sid: String, + pub contents: Vec, + pub reason: Option, + //pub other: Vec, +} + +pub fn parse_jingle(root: &Element) -> Result { + assert!(root.is("jingle", JINGLE_NS)); + let mut contents: Vec = vec!(); + + let action = root.attr("action") + .ok_or(Error::ParseError("Jingle must have an 'action' attribute."))? + .parse()?; + let initiator = root.attr("initiator") + .and_then(|initiator| initiator.parse().ok()); + let responder = root.attr("responder") + .and_then(|responder| responder.parse().ok()); + let sid = root.attr("sid") + .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; + let mut reason_element = None; + + for child in root.children() { + if child.is("content", JINGLE_NS) { + let creator = child.attr("creator") + .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? + .parse()?; + let disposition = child.attr("disposition") + .unwrap_or("session"); + let name = child.attr("name") + .ok_or(Error::ParseError("Content must have a 'name' attribute."))?; + let senders = child.attr("senders") + .unwrap_or("both") + .parse()?; + let mut description = None; + let mut transport = None; + let mut security = None; + for stuff in child.children() { + if stuff.name() == "description" { + if description.is_some() { + return Err(Error::ParseError("Content must not have more than one description.")); + } + description = Some(stuff.ns().ok_or(Error::ParseError("Description without a namespace."))?); + } else if stuff.name() == "transport" { + if transport.is_some() { + return Err(Error::ParseError("Content must not have more than one transport.")); + } + transport = Some(stuff.ns().ok_or(Error::ParseError("Transport without a namespace."))?); + } else if stuff.name() == "security" { + if security.is_some() { + return Err(Error::ParseError("Content must not have more than one security.")); + } + security = stuff.ns().and_then(|ns| ns.parse().ok()); + } + } + if description.is_none() { + return Err(Error::ParseError("Content must have one description.")); + } + if transport.is_none() { + return Err(Error::ParseError("Content must have one transport.")); + } + let description = description.unwrap().to_owned(); + let transport = transport.unwrap().to_owned(); + contents.push(Content { + creator: creator, + disposition: disposition.to_owned(), + name: name.to_owned(), + senders: senders, + description: description, + transport: transport, + security: security.to_owned(), + }); + } else if child.is("reason", JINGLE_NS) { + if reason_element.is_some() { + return Err(Error::ParseError("Jingle must not have more than one reason.")); + } + let mut reason = None; + let mut text = None; + for stuff in child.children() { + if stuff.ns() != Some(JINGLE_NS) { + return Err(Error::ParseError("Reason contains a foreign element.")); + } + let name = stuff.name(); + if name == "text" { + text = Some(stuff.text()); + } else { + reason = Some(name.parse()?); + } + } + if reason.is_none() { + return Err(Error::ParseError("Reason doesn’t contain a valid reason.")); + } + reason_element = Some(ReasonElement { + reason: reason.unwrap(), + text: text, + }); + } else { + return Err(Error::ParseError("Unknown element in jingle.")); + } + } + + return Ok(Jingle { + action: action, + initiator: initiator, + responder: responder, + sid: sid.to_owned(), + contents: contents, + reason: reason_element, + }); +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use jingle; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + assert_eq!(jingle.action, jingle::Action::SessionInitiate); + assert_eq!(jingle.sid, "coucou"); + } + + #[test] + fn test_invalid_jingle() { + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Jingle must have an 'action' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Jingle must have a 'sid' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown action."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown element in jingle."); + } + + #[test] + fn test_content() { + let elem: Element = "".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + assert_eq!(jingle.contents[0].creator, jingle::Creator::Initiator); + assert_eq!(jingle.contents[0].name, "coucou"); + assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); + assert_eq!(jingle.contents[0].disposition, "session"); + println!("{:#?}", jingle); + + let elem: Element = "".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); + + let elem: Element = "".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + assert_eq!(jingle.contents[0].disposition, "early-session"); + } + + #[test] + fn test_invalid_content() { + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Content must have a 'creator' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Content must have a 'name' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown creator."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown senders."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown senders."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Content must have one description."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Content must have one transport."); + } + + #[test] + fn test_reason() { + let elem: Element = "".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + let reason = jingle.reason.unwrap(); + assert_eq!(reason.reason, jingle::Reason::Success); + assert_eq!(reason.text, None); + + let elem: Element = "coucou".parse().unwrap(); + let jingle = jingle::parse_jingle(&elem).unwrap(); + let reason = jingle.reason.unwrap(); + assert_eq!(reason.reason, jingle::Reason::Success); + assert_eq!(reason.text, Some(String::from("coucou"))); + } + + #[test] + fn test_invalid_reason() { + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Reason doesn’t contain a valid reason."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown reason."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Reason contains a foreign element."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Jingle must not have more than one reason."); + } +} diff --git a/src/lib.rs b/src/lib.rs index a6aa19012c75972a40a775a9399d47e266df8377..56d17f4726097d4084e40af7bce81213834deab3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,3 +7,4 @@ pub mod disco; pub mod data_forms; pub mod media_element; pub mod ecaps2; +pub mod jingle; diff --git a/src/ns.rs b/src/ns.rs index 36bd127c0837df33ee0a1c8067d2914893567a86..55da2eb8b6011c9dc06f1162585140bdd9b18405 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,3 +1,4 @@ pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info"; pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; +pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; From 9d129701d09983898d059575312b8fc0b4e81a11 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 02:38:10 +0100 Subject: [PATCH 0026/1020] jingle: Add an error when there is more than one in a . --- src/jingle.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index e732c6b430b57a8331cae6c6863784da477f3ff6..37912a020c6a02edd9e54d14e85a5f4fdb722189 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use minidom::Element; use error::Error; -use ns::{JINGLE_NS}; +use ns::JINGLE_NS; #[derive(Debug, PartialEq)] pub enum Action { @@ -285,6 +285,9 @@ pub fn parse_jingle(root: &Element) -> Result { } let name = stuff.name(); if name == "text" { + if text.is_some() { + return Err(Error::ParseError("Reason must not have more than one text.")); + } text = Some(stuff.text()); } else { reason = Some(name.parse()?); @@ -487,5 +490,13 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Jingle must not have more than one reason."); + + let elem: Element = "".parse().unwrap(); + let error = jingle::parse_jingle(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Reason must not have more than one text."); } } From 87cd047a46d61d4cb0bf633dda6d41888806aa96 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 18:58:43 +0100 Subject: [PATCH 0027/1020] media_element: Import the namespace from ns. --- src/media_element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/media_element.rs b/src/media_element.rs index 1fc84507deae634f804ae1c3880b743282a06159..86f286eebce54140104076fa18200e8e757521c4 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -2,7 +2,7 @@ use minidom::Element; use error::Error; -const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; +use ns::MEDIA_ELEMENT_NS; #[derive(Debug)] pub struct URI { From fdc76eca3cc5ecc9079754dc933b3d15b293d9cc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 18:59:07 +0100 Subject: [PATCH 0028/1020] Add a ping parser. --- src/lib.rs | 1 + src/ns.rs | 1 + src/ping.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 src/ping.rs diff --git a/src/lib.rs b/src/lib.rs index 56d17f4726097d4084e40af7bce81213834deab3..d8a3f8cc56528b71a57ec9c7265a1eabf05b6b73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,3 +8,4 @@ pub mod data_forms; pub mod media_element; pub mod ecaps2; pub mod jingle; +pub mod ping; diff --git a/src/ns.rs b/src/ns.rs index 55da2eb8b6011c9dc06f1162585140bdd9b18405..7c0903aa2b3bcc06ce7b5b1d07775b301530f3d2 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -2,3 +2,4 @@ pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info"; pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; +pub const PING_NS: &'static str = "urn:xmpp:ping"; diff --git a/src/ping.rs b/src/ping.rs new file mode 100644 index 0000000000000000000000000000000000000000..7176fa42baffa14fdad058c7c94312f2db7f189b --- /dev/null +++ b/src/ping.rs @@ -0,0 +1,53 @@ +use minidom::Element; + +use error::Error; + +use ns::PING_NS; + +#[derive(Debug)] +pub struct Ping { +} + +pub fn parse_ping(root: &Element) -> Result { + assert!(root.is("ping", PING_NS)); + for _ in root.children() { + return Err(Error::ParseError("Unknown child in ping element.")); + } + Ok(Ping { }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use ping; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + ping::parse_ping(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = ping::parse_ping(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in ping element."); + } + + #[test] + #[ignore] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = ping::parse_ping(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in ping element."); + } +} From 69f1405b9c6c396f1c136c41f091ac99d7cdb0ae Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 19:15:57 +0100 Subject: [PATCH 0029/1020] Add a chatstate parser. --- src/chatstates.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/ns.rs | 1 + 3 files changed, 82 insertions(+) create mode 100644 src/chatstates.rs diff --git a/src/chatstates.rs b/src/chatstates.rs new file mode 100644 index 0000000000000000000000000000000000000000..6ec2e22df947ad3d050967c931f555a76c9c43bf --- /dev/null +++ b/src/chatstates.rs @@ -0,0 +1,80 @@ +use minidom::Element; + +use error::Error; + +use ns::CHATSTATES_NS; + +#[derive(Debug)] +pub enum ChatState { + Active, + Composing, + Gone, + Inactive, + Paused, +} + +pub fn parse_chatstate(root: &Element) -> Result { + for _ in root.children() { + return Err(Error::ParseError("Unknown child in chatstate element.")); + } + if root.is("active", CHATSTATES_NS) { + Ok(ChatState::Active) + } else if root.is("composing", CHATSTATES_NS) { + Ok(ChatState::Composing) + } else if root.is("gone", CHATSTATES_NS) { + Ok(ChatState::Gone) + } else if root.is("inactive", CHATSTATES_NS) { + Ok(ChatState::Inactive) + } else if root.is("paused", CHATSTATES_NS) { + Ok(ChatState::Paused) + } else { + Err(Error::ParseError("Unknown chatstate element.")) + } +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use chatstates; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + chatstates::parse_chatstate(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown chatstate element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in chatstate element."); + } + + #[test] + #[ignore] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in chatstate element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index d8a3f8cc56528b71a57ec9c7265a1eabf05b6b73..62996e9c0285fce8a2831c0c4cbab5bfbfd1aefb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,3 +9,4 @@ pub mod media_element; pub mod ecaps2; pub mod jingle; pub mod ping; +pub mod chatstates; diff --git a/src/ns.rs b/src/ns.rs index 7c0903aa2b3bcc06ce7b5b1d07775b301530f3d2..84ca86e90fcdc432a778f695042b3e12ef9dfb4e 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -3,3 +3,4 @@ pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; pub const PING_NS: &'static str = "urn:xmpp:ping"; +pub const CHATSTATES_NS: &'static str = "http://jabber.org/protocol/chatstates"; From 31a7d8be23741adeb916e1cf41d8112703167612 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 21:50:14 +0100 Subject: [PATCH 0030/1020] disco: Drop some unneeded derived traits from Feature. --- src/disco.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/disco.rs b/src/disco.rs index bc65e3a1115945cbeb54bbf0b33aa63e89372239..e436b2620e899bada9cb0b50a9123ba3e7da858c 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -7,7 +7,7 @@ use ns::{DISCO_INFO_NS, DATA_FORMS_NS}; use data_forms::{DataForm, DataFormType, parse_data_form}; -#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Debug, PartialEq)] pub struct Feature { pub var: String, } From 4f31727a1ad47bc461c009caf81b47c04d7b56c6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 21:52:14 +0100 Subject: [PATCH 0031/1020] Add an In-Band Bytestream parser. --- src/ibb.rs | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/ns.rs | 1 + 3 files changed, 124 insertions(+) create mode 100644 src/ibb.rs diff --git a/src/ibb.rs b/src/ibb.rs new file mode 100644 index 0000000000000000000000000000000000000000..1868772ee83e424b326780f5dd14fbb7dc44f9aa --- /dev/null +++ b/src/ibb.rs @@ -0,0 +1,122 @@ +use std::str::FromStr; + +use minidom::Element; + +use error::Error; + +use ns::IBB_NS; + +#[derive(Debug)] +pub enum Stanza { + Iq, + Message, +} + +impl Default for Stanza { + fn default() -> Stanza { + Stanza::Iq + } +} + +impl FromStr for Stanza { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "iq" { + Ok(Stanza::Iq) + } else if s == "message" { + Ok(Stanza::Message) + } else { + Err(Error::ParseError("Unknown 'stanza' attribute.")) + } + } +} + +#[derive(Debug)] +pub enum IBB { + Open { block_size: u16, sid: String, stanza: Stanza }, + Data(u16, String, Vec), + Close(String), +} + +fn optional_attr(root: &Element, attr: &str) -> Option { + root.attr(attr) + .and_then(|value| value.parse().ok()) +} + +fn required_attr(root: &Element, attr: &str, err: Error) -> Result { + optional_attr(root, attr).ok_or(err) +} + +pub fn parse_ibb(root: &Element) -> Result { + if root.is("open", IBB_NS) { + let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; + let stanza = root.attr("stanza") + .and_then(|value| value.parse().ok()) + .unwrap_or(Default::default()); + for _ in root.children() { + return Err(Error::ParseError("Unknown child in open element.")); + } + Ok(IBB::Open { + block_size: block_size, + sid: sid, + stanza: stanza + }) + } else { + Err(Error::ParseError("Unknown ibb element.")) + } +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use ibb; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + ibb::parse_ibb(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'block-size' missing in open element."); + + // TODO: maybe make a better error message here. + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'block-size' missing in open element."); + + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'sid' missing in open element."); + } + + #[test] + #[ignore] + fn test_invalid_stanza() { + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Wrong value for 'stanza' attribute in open."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 62996e9c0285fce8a2831c0c4cbab5bfbfd1aefb..f7c964b95a803c16468cd88f87ae9c258a9945be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,3 +10,4 @@ pub mod ecaps2; pub mod jingle; pub mod ping; pub mod chatstates; +pub mod ibb; diff --git a/src/ns.rs b/src/ns.rs index 84ca86e90fcdc432a778f695042b3e12ef9dfb4e..24b81133ab6b16346aad8104e74f0b920b2d06b8 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -4,3 +4,4 @@ pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; pub const PING_NS: &'static str = "urn:xmpp:ping"; pub const CHATSTATES_NS: &'static str = "http://jabber.org/protocol/chatstates"; +pub const IBB_NS: &'static str = "http://jabber.org/protocol/ibb"; From 948ecd7dd72147f1ae455a3d800cabc5cc0224dc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 23:21:23 +0100 Subject: [PATCH 0032/1020] Add a body plugin. --- src/body.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/ns.rs | 1 + 3 files changed, 73 insertions(+) create mode 100644 src/body.rs diff --git a/src/body.rs b/src/body.rs new file mode 100644 index 0000000000000000000000000000000000000000..0bf2f498dcae3490d8bc1723198c97a64dbbe11e --- /dev/null +++ b/src/body.rs @@ -0,0 +1,71 @@ +use minidom::Element; + +use error::Error; +use super::MessagePayload; + +// TODO: also support components and servers. +use ns::JABBER_CLIENT_NS; + +#[derive(Debug)] +pub struct Body { + body: String, +} + +impl MessagePayload for Body {} + +pub fn parse_body(root: &Element) -> Result { + if !root.is("body", JABBER_CLIENT_NS) { + return Err(Error::ParseError("Not a body element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in body element.")); + } + Ok(Body { body: root.text() }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use chatstates; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + chatstates::parse_chatstate(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown chatstate element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in chatstate element."); + } + + #[test] + #[ignore] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in chatstate element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index f7c964b95a803c16468cd88f87ae9c258a9945be..e0798c12882cb74615f84ffa422a90356b6ba465 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ extern crate minidom; pub mod error; pub mod ns; +pub mod body; pub mod disco; pub mod data_forms; pub mod media_element; diff --git a/src/ns.rs b/src/ns.rs index 24b81133ab6b16346aad8104e74f0b920b2d06b8..4f18ccc84a4d58aa14701895458bde650374c774 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,3 +1,4 @@ +pub const JABBER_CLIENT_NS: &'static str = "jabber:client"; pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info"; pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; From 6a0724d1335a3c80efd1f8f71a27cc908574d409 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 23:21:53 +0100 Subject: [PATCH 0033/1020] Add a MessagePayload trait, and implement it for Body and ChatState. --- src/chatstates.rs | 3 +++ src/lib.rs | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/chatstates.rs b/src/chatstates.rs index 6ec2e22df947ad3d050967c931f555a76c9c43bf..8941408dbdecdf19f46fea81172f660f5dfc4671 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -1,6 +1,7 @@ use minidom::Element; use error::Error; +use super::MessagePayload; use ns::CHATSTATES_NS; @@ -13,6 +14,8 @@ pub enum ChatState { Paused, } +impl MessagePayload for ChatState {} + pub fn parse_chatstate(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); diff --git a/src/lib.rs b/src/lib.rs index e0798c12882cb74615f84ffa422a90356b6ba465..8914a6c6eba0bded291f4200c76c06f3cd7c88bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,3 +12,18 @@ pub mod jingle; pub mod ping; pub mod chatstates; pub mod ibb; + +use std::fmt::Debug; +use minidom::Element; + +pub trait MessagePayload: Debug {} + +pub fn parse_message_payload(elem: &Element) -> Option> { + if let Ok(body) = body::parse_body(elem) { + Some(Box::new(body)) + } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { + Some(Box::new(chatstate)) + } else { + None + } +} From fc7a0517d31f44302a772fd06e7fa00a2f9d52dc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 23:41:54 +0100 Subject: [PATCH 0034/1020] Replace assert!()s with proper errors in parsers. --- src/body.rs | 2 +- src/chatstates.rs | 2 +- src/data_forms.rs | 5 ++++- src/disco.rs | 5 ++++- src/ibb.rs | 2 +- src/jingle.rs | 5 ++++- src/media_element.rs | 5 ++++- src/ping.rs | 5 ++++- 8 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/body.rs b/src/body.rs index 0bf2f498dcae3490d8bc1723198c97a64dbbe11e..43bd6914852cfab737d2018e8215a49683158c1b 100644 --- a/src/body.rs +++ b/src/body.rs @@ -15,7 +15,7 @@ impl MessagePayload for Body {} pub fn parse_body(root: &Element) -> Result { if !root.is("body", JABBER_CLIENT_NS) { - return Err(Error::ParseError("Not a body element.")); + return Err(Error::ParseError("This is not a body element.")); } for _ in root.children() { return Err(Error::ParseError("Unknown child in body element.")); diff --git a/src/chatstates.rs b/src/chatstates.rs index 8941408dbdecdf19f46fea81172f660f5dfc4671..d84880c6591faddc50ff759ea634fccd4d730fd0 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -31,7 +31,7 @@ pub fn parse_chatstate(root: &Element) -> Result { } else if root.is("paused", CHATSTATES_NS) { Ok(ChatState::Paused) } else { - Err(Error::ParseError("Unknown chatstate element.")) + Err(Error::ParseError("This is not a chatstate element.")) } } diff --git a/src/data_forms.rs b/src/data_forms.rs index f4eb70c834487de4db9b088974e2b782db1d0aa5..68e0e00d43e9163bfb62394f7c67851e63ada81f 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -52,7 +52,10 @@ pub struct DataForm { } pub fn parse_data_form(root: &Element) -> Result { - assert!(root.is("x", DATA_FORMS_NS)); + if !root.is("x", DATA_FORMS_NS) { + return Err(Error::ParseError("This is not a data form element.")), + } + let type_: DataFormType = match root.attr("type") { Some(type_) => type_.parse()?, None => return Err(Error::ParseError("Type attribute on data form is mandatory.")), diff --git a/src/disco.rs b/src/disco.rs index e436b2620e899bada9cb0b50a9123ba3e7da858c..d51636e2c09be6c75117ef15ee1739154411701a 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -29,7 +29,10 @@ pub struct Disco { } pub fn parse_disco(root: &Element) -> Result { - assert!(root.is("query", DISCO_INFO_NS)); + if !root.is("query", DISCO_INFO_NS) { + return Err(Error::ParseError("This is not a disco#info element.")), + } + let mut identities: Vec = vec!(); let mut features: Vec = vec!(); let mut extensions: Vec = vec!(); diff --git a/src/ibb.rs b/src/ibb.rs index 1868772ee83e424b326780f5dd14fbb7dc44f9aa..64363c358164af5236816567d9b41d66c36fcde0 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -64,7 +64,7 @@ pub fn parse_ibb(root: &Element) -> Result { stanza: stanza }) } else { - Err(Error::ParseError("Unknown ibb element.")) + Err(Error::ParseError("This is not an ibb element.")) } } diff --git a/src/jingle.rs b/src/jingle.rs index 37912a020c6a02edd9e54d14e85a5f4fdb722189..00b390e4409cf2574bd86863fd990409bf59474f 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -209,7 +209,10 @@ pub struct Jingle { } pub fn parse_jingle(root: &Element) -> Result { - assert!(root.is("jingle", JINGLE_NS)); + if !root.is("jingle", JINGLE_NS) { + return Err(Error::ParseError("This is not a Jingle element.")), + } + let mut contents: Vec = vec!(); let action = root.attr("action") diff --git a/src/media_element.rs b/src/media_element.rs index 86f286eebce54140104076fa18200e8e757521c4..d912f126f80200445d6f42135052b2b5a8cfb702 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -18,7 +18,10 @@ pub struct MediaElement { } pub fn parse_media_element(root: &Element) -> Result { - assert!(root.is("media", MEDIA_ELEMENT_NS)); + if !root.is("media", MEDIA_ELEMENT_NS) { + return Err(Error::ParseError("This is not a media element.")), + } + let width = root.attr("width").and_then(|width| width.parse().ok()); let height = root.attr("height").and_then(|height| height.parse().ok()); let mut uris = vec!(); diff --git a/src/ping.rs b/src/ping.rs index 7176fa42baffa14fdad058c7c94312f2db7f189b..39145e2253ee0f9255e733e656041e34b8a77271 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -9,7 +9,10 @@ pub struct Ping { } pub fn parse_ping(root: &Element) -> Result { - assert!(root.is("ping", PING_NS)); + if !root.is("ping", PING_NS) { + return Err(Error::ParseError("This is not a ping element.")), + } + for _ in root.children() { return Err(Error::ParseError("Unknown child in ping element.")); } From b8b0494c191d10dc89fdee7eb0c33b4763d9e49e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 00:14:29 +0100 Subject: [PATCH 0035/1020] Fix a stupid copy/paste syntax error. --- src/data_forms.rs | 2 +- src/disco.rs | 2 +- src/jingle.rs | 2 +- src/media_element.rs | 2 +- src/ping.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 68e0e00d43e9163bfb62394f7c67851e63ada81f..342c0c453d4b06e0fb2b899ede67731eef5139a5 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -53,7 +53,7 @@ pub struct DataForm { pub fn parse_data_form(root: &Element) -> Result { if !root.is("x", DATA_FORMS_NS) { - return Err(Error::ParseError("This is not a data form element.")), + return Err(Error::ParseError("This is not a data form element.")); } let type_: DataFormType = match root.attr("type") { diff --git a/src/disco.rs b/src/disco.rs index d51636e2c09be6c75117ef15ee1739154411701a..3100d4b5db27b45b02764b8c4d8f64fcfc564836 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -30,7 +30,7 @@ pub struct Disco { pub fn parse_disco(root: &Element) -> Result { if !root.is("query", DISCO_INFO_NS) { - return Err(Error::ParseError("This is not a disco#info element.")), + return Err(Error::ParseError("This is not a disco#info element.")); } let mut identities: Vec = vec!(); diff --git a/src/jingle.rs b/src/jingle.rs index 00b390e4409cf2574bd86863fd990409bf59474f..a2fc331d80f0fff86ff835d53f9e18fdf77849e6 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -210,7 +210,7 @@ pub struct Jingle { pub fn parse_jingle(root: &Element) -> Result { if !root.is("jingle", JINGLE_NS) { - return Err(Error::ParseError("This is not a Jingle element.")), + return Err(Error::ParseError("This is not a Jingle element.")); } let mut contents: Vec = vec!(); diff --git a/src/media_element.rs b/src/media_element.rs index d912f126f80200445d6f42135052b2b5a8cfb702..7d77400883b38866d911d99518309bee5ea3acb3 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -19,7 +19,7 @@ pub struct MediaElement { pub fn parse_media_element(root: &Element) -> Result { if !root.is("media", MEDIA_ELEMENT_NS) { - return Err(Error::ParseError("This is not a media element.")), + return Err(Error::ParseError("This is not a media element.")); } let width = root.attr("width").and_then(|width| width.parse().ok()); diff --git a/src/ping.rs b/src/ping.rs index 39145e2253ee0f9255e733e656041e34b8a77271..de7aeb7b5f96ce369f9a67bafbc64ae1e054bae2 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -10,7 +10,7 @@ pub struct Ping { pub fn parse_ping(root: &Element) -> Result { if !root.is("ping", PING_NS) { - return Err(Error::ParseError("This is not a ping element.")), + return Err(Error::ParseError("This is not a ping element.")); } for _ in root.children() { From a9993b128194ea16cc25ed88bbfb90c91f0fdbfb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 00:14:47 +0100 Subject: [PATCH 0036/1020] Remove the MessagePayload trait, it was a bad idea. --- src/body.rs | 3 --- src/chatstates.rs | 3 --- src/lib.rs | 13 ++++++++----- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/body.rs b/src/body.rs index 43bd6914852cfab737d2018e8215a49683158c1b..43c6c83387200a6ae5630bba6ccf9d954d051562 100644 --- a/src/body.rs +++ b/src/body.rs @@ -1,7 +1,6 @@ use minidom::Element; use error::Error; -use super::MessagePayload; // TODO: also support components and servers. use ns::JABBER_CLIENT_NS; @@ -11,8 +10,6 @@ pub struct Body { body: String, } -impl MessagePayload for Body {} - pub fn parse_body(root: &Element) -> Result { if !root.is("body", JABBER_CLIENT_NS) { return Err(Error::ParseError("This is not a body element.")); diff --git a/src/chatstates.rs b/src/chatstates.rs index d84880c6591faddc50ff759ea634fccd4d730fd0..77db1413459f600b36f9384402f1f60877affbdb 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -1,7 +1,6 @@ use minidom::Element; use error::Error; -use super::MessagePayload; use ns::CHATSTATES_NS; @@ -14,8 +13,6 @@ pub enum ChatState { Paused, } -impl MessagePayload for ChatState {} - pub fn parse_chatstate(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); diff --git a/src/lib.rs b/src/lib.rs index 8914a6c6eba0bded291f4200c76c06f3cd7c88bf..c6f93d93665ce445ceb32fdbfc48881d47e476e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,16 +13,19 @@ pub mod ping; pub mod chatstates; pub mod ibb; -use std::fmt::Debug; use minidom::Element; -pub trait MessagePayload: Debug {} +#[derive(Debug)] +pub enum MessagePayload { + Body(body::Body), + ChatState(chatstates::ChatState), +} -pub fn parse_message_payload(elem: &Element) -> Option> { +pub fn parse_message_payload(elem: &Element) -> Option { if let Ok(body) = body::parse_body(elem) { - Some(Box::new(body)) + Some(MessagePayload::Body(body)) } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { - Some(Box::new(chatstate)) + Some(MessagePayload::ChatState(chatstate)) } else { None } From 861c933c568f42f02cb6b0a0cb24f50d4d35aaeb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 00:43:17 +0100 Subject: [PATCH 0037/1020] body, chatstates: Fix tests. --- src/body.rs | 24 ++++++++++++------------ src/chatstates.rs | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/body.rs b/src/body.rs index 43c6c83387200a6ae5630bba6ccf9d954d051562..d02e508c858e120af4adc6c3275e449829c0f03d 100644 --- a/src/body.rs +++ b/src/body.rs @@ -24,45 +24,45 @@ pub fn parse_body(root: &Element) -> Result { mod tests { use minidom::Element; use error::Error; - use chatstates; + use body; #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); - chatstates::parse_chatstate(&elem).unwrap(); + let elem: Element = "".parse().unwrap(); + body::parse_body(&elem).unwrap(); } #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let elem: Element = "".parse().unwrap(); + let error = body::parse_body(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown chatstate element."); + assert_eq!(message, "This is not a body element."); } #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let elem: Element = "".parse().unwrap(); + let error = body::parse_body(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in chatstate element."); + assert_eq!(message, "Unknown child in body element."); } #[test] #[ignore] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let elem: Element = "".parse().unwrap(); + let error = body::parse_body(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown attribute in chatstate element."); + assert_eq!(message, "Unknown attribute in body element."); } } diff --git a/src/chatstates.rs b/src/chatstates.rs index 77db1413459f600b36f9384402f1f60877affbdb..1651e3a5bcfcee290a226e9b28ec197a6f22f392 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -52,7 +52,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown chatstate element."); + assert_eq!(message, "This is not a chatstate element."); } #[test] From f3c9a58862942fafdfdda3facd3753739173e048 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 00:43:33 +0100 Subject: [PATCH 0038/1020] Add a receipts parser. --- src/lib.rs | 4 ++++ src/ns.rs | 1 + src/receipts.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/receipts.rs diff --git a/src/lib.rs b/src/lib.rs index c6f93d93665ce445ceb32fdbfc48881d47e476e2..f7608cd746e45ad3280fd4a16a476e7a7ad0e70f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ pub mod jingle; pub mod ping; pub mod chatstates; pub mod ibb; +pub mod receipts; use minidom::Element; @@ -19,6 +20,7 @@ use minidom::Element; pub enum MessagePayload { Body(body::Body), ChatState(chatstates::ChatState), + Receipt(receipts::Receipt), } pub fn parse_message_payload(elem: &Element) -> Option { @@ -26,6 +28,8 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::Body(body)) } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { Some(MessagePayload::ChatState(chatstate)) + } else if let Ok(receipt) = receipts::parse_receipt(elem) { + Some(MessagePayload::Receipt(receipt)) } else { None } diff --git a/src/ns.rs b/src/ns.rs index 4f18ccc84a4d58aa14701895458bde650374c774..49293f4f1dbb0ebcece1d3868f3657fb881b53fd 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -6,3 +6,4 @@ pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; pub const PING_NS: &'static str = "urn:xmpp:ping"; pub const CHATSTATES_NS: &'static str = "http://jabber.org/protocol/chatstates"; pub const IBB_NS: &'static str = "http://jabber.org/protocol/ibb"; +pub const RECEIPTS_NS: &'static str = "urn:xmpp:receipts"; diff --git a/src/receipts.rs b/src/receipts.rs new file mode 100644 index 0000000000000000000000000000000000000000..a19dff1c982155638d6dc9c92596256d3f60158d --- /dev/null +++ b/src/receipts.rs @@ -0,0 +1,44 @@ +use minidom::Element; + +use error::Error; + +use ns::RECEIPTS_NS; + +#[derive(Debug)] +pub enum Receipt { + Request, + Received(String), +} + +pub fn parse_receipt(root: &Element) -> Result { + for _ in root.children() { + return Err(Error::ParseError("Unknown child in receipt element.")); + } + if root.is("request", RECEIPTS_NS) { + Ok(Receipt::Request) + } else if root.is("received", RECEIPTS_NS) { + let id = root.attr("id").unwrap_or("").to_owned(); + Ok(Receipt::Received(id)) + } else { + Err(Error::ParseError("This is not a receipt element.")) + } +} + +#[cfg(test)] +mod tests { + use minidom::Element; + //use error::Error; + use receipts; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + receipts::parse_receipt(&elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + receipts::parse_receipt(&elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + receipts::parse_receipt(&elem).unwrap(); + } +} From 55f5435f6e1dfe1e106ed38c591bbd8f7e616de0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 01:31:03 +0100 Subject: [PATCH 0039/1020] body: Make the string public. --- src/body.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/body.rs b/src/body.rs index d02e508c858e120af4adc6c3275e449829c0f03d..7c5fcae4aa14f3e7d927e804f629f7c32787c485 100644 --- a/src/body.rs +++ b/src/body.rs @@ -7,7 +7,7 @@ use ns::JABBER_CLIENT_NS; #[derive(Debug)] pub struct Body { - body: String, + pub body: String, } pub fn parse_body(root: &Element) -> Result { From e0e6119e55bb3db0da1c94457696824e252d245b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 21:02:51 +0100 Subject: [PATCH 0040/1020] disco: Relax the parsing rules for get disco#info. --- src/disco.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 3100d4b5db27b45b02764b8c4d8f64fcfc564836..afb1fa7f6125654573937a33ac99d2ec0b0c99f8 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -85,6 +85,8 @@ pub fn parse_disco(root: &Element) -> Result { } } + /* + // TODO: encode these restrictions only for result disco#info, not get ones. if identities.is_empty() { return Err(Error::ParseError("There must be at least one identity in disco#info.")); } @@ -94,6 +96,7 @@ pub fn parse_disco(root: &Element) -> Result { if !features.contains(&Feature { var: DISCO_INFO_NS.to_owned() }) { return Err(Error::ParseError("disco#info feature not present in disco#info.")); } + */ return Ok(Disco { node: node, @@ -128,15 +131,10 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Unknown element in disco#info."); + } - let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "There must be at least one identity in disco#info."); - + #[test] + fn test_invalid_identity() { let elem: Element = "".parse().unwrap(); let error = disco::parse_disco(&elem).unwrap_err(); let message = match error { @@ -168,7 +166,10 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Identity must have a non-empty 'type' attribute."); + } + #[test] + fn test_invalid_feature() { let elem: Element = "".parse().unwrap(); let error = disco::parse_disco(&elem).unwrap_err(); let message = match error { @@ -176,6 +177,18 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Feature must have a 'var' attribute."); + } + + #[test] + #[ignore] + fn test_invalid_result() { + let elem: Element = "".parse().unwrap(); + let error = disco::parse_disco(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "There must be at least one identity in disco#info."); let elem: Element = "".parse().unwrap(); let error = disco::parse_disco(&elem).unwrap_err(); From 1190dd9001a5c945f576064281b2cdcc6f82f5a1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 21:03:02 +0100 Subject: [PATCH 0041/1020] disco: Add a serializer. --- src/disco.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index afb1fa7f6125654573937a33ac99d2ec0b0c99f8..2a4ff64a6f8beb18f7bae6fa7cc72e83c3543a42 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -106,6 +106,34 @@ pub fn parse_disco(root: &Element) -> Result { }); } +pub fn serialise_disco(disco: Disco) -> Element { + let mut root = Element::builder("query") + .ns(DISCO_INFO_NS) + .attr("node", disco.node) + .build(); + for identity in disco.identities { + let identity_element = Element::builder("identity") + .ns(DISCO_INFO_NS) + .attr("category", identity.category) + .attr("type", identity.type_) + .attr("xml:lang", identity.xml_lang) + .attr("name", identity.name) + .build(); + root.append_child(identity_element); + } + for feature in disco.features { + let feature_element = Element::builder("feature") + .ns(DISCO_INFO_NS) + .attr("var", feature.var) + .build(); + root.append_child(feature_element); + } + for _ in disco.extensions { + panic!("Not yet implemented!"); + } + root +} + #[cfg(test)] mod tests { use minidom::Element; From 83cf57abd05da4bf4b3ea9f2edb0453ba91087c7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 21:53:46 +0100 Subject: [PATCH 0042/1020] disco: Use a reference everywhere, instead of taking ownership. --- src/disco.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 2a4ff64a6f8beb18f7bae6fa7cc72e83c3543a42..4b2d0334e48350dd8f1f454490176b53581ae9b9 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -106,29 +106,29 @@ pub fn parse_disco(root: &Element) -> Result { }); } -pub fn serialise_disco(disco: Disco) -> Element { +pub fn serialise_disco(disco: &Disco) -> Element { let mut root = Element::builder("query") .ns(DISCO_INFO_NS) - .attr("node", disco.node) + .attr("node", disco.node.clone()) .build(); - for identity in disco.identities { + for identity in &disco.identities { let identity_element = Element::builder("identity") .ns(DISCO_INFO_NS) - .attr("category", identity.category) - .attr("type", identity.type_) - .attr("xml:lang", identity.xml_lang) - .attr("name", identity.name) + .attr("category", identity.category.clone()) + .attr("type", identity.type_.clone()) + .attr("xml:lang", identity.xml_lang.clone()) + .attr("name", identity.name.clone()) .build(); root.append_child(identity_element); } - for feature in disco.features { + for feature in &disco.features { let feature_element = Element::builder("feature") .ns(DISCO_INFO_NS) - .attr("var", feature.var) + .attr("var", feature.var.clone()) .build(); root.append_child(feature_element); } - for _ in disco.extensions { + for _ in &disco.extensions { panic!("Not yet implemented!"); } root From 411e421732a18a5fb950f0a0b09c2c74ffa29ff8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 21:53:46 +0100 Subject: [PATCH 0043/1020] ecaps2: Use a reference everywhere, instead of taking ownership. --- src/ecaps2.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 50b94128ee78fed1849251d620e41b9c1b9f47b4..fddc712b336e9ccf98cc317f9a05549cda8bdee5 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -7,13 +7,13 @@ use error::Error; use disco::{Feature, Identity, Disco, parse_disco}; use data_forms::DataForm; -fn compute_item(field: String) -> Vec { +fn compute_item(field: &String) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); bytes } -fn compute_items Vec>(things: Vec, separator: u8, encode: F) -> Vec { +fn compute_items Vec>(things: &Vec, separator: u8, encode: F) -> Vec { let mut string: Vec = vec!(); let mut accumulator: Vec> = vec!(); for thing in things { @@ -29,36 +29,36 @@ fn compute_items Vec>(things: Vec, separator: u8, encode: string } -fn compute_features(features: Vec) -> Vec { - compute_items(features, 0x1c, |feature| compute_item(feature.var)) +fn compute_features(features: &Vec) -> Vec { + compute_items(features, 0x1c, |feature| compute_item(&feature.var)) } -fn compute_identities(identities: Vec) -> Vec { +fn compute_identities(identities: &Vec) -> Vec { compute_items(identities, 0x1c, |identity| { - let mut bytes = compute_item(identity.category); - bytes.append(&mut compute_item(identity.type_)); - bytes.append(&mut compute_item(identity.xml_lang)); - bytes.append(&mut compute_item(identity.name.unwrap_or(String::new()))); + let mut bytes = compute_item(&identity.category); + bytes.append(&mut compute_item(&identity.type_)); + bytes.append(&mut compute_item(&identity.xml_lang)); + bytes.append(&mut compute_item(&identity.name.clone().unwrap_or(String::new()))); bytes.push(0x1e); bytes }) } -fn compute_extensions(extensions: Vec) -> Vec { +fn compute_extensions(extensions: &Vec) -> Vec { compute_items(extensions, 0x1c, |extension| { - compute_items(extension.fields, 0x1d, |field| { - let mut bytes = compute_item(field.var); - bytes.append(&mut compute_items(field.values, 0x1e, + compute_items(&extension.fields, 0x1d, |field| { + let mut bytes = compute_item(&field.var); + bytes.append(&mut compute_items(&field.values, 0x1e, |value| compute_item(value))); bytes }) }) } -fn compute_disco(disco: Disco) -> Vec { - let features_string = compute_features(disco.features); - let identities_string = compute_identities(disco.identities); - let extensions_string = compute_extensions(disco.extensions); +pub fn compute_disco(disco: &Disco) -> Vec { + let features_string = compute_features(&disco.features); + let identities_string = compute_identities(&disco.identities); + let extensions_string = compute_extensions(&disco.extensions); let mut final_string = vec!(); final_string.extend(features_string); @@ -69,7 +69,7 @@ fn compute_disco(disco: Disco) -> Vec { pub fn convert_element(root: &Element) -> Result, Error> { let disco = parse_disco(root)?; - let final_string = compute_disco(disco); + let final_string = compute_disco(&disco); Ok(final_string) } From 7288c2c74fe77abd5d3e9dd751d5b25ee2c29323 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Apr 2017 23:16:12 +0100 Subject: [PATCH 0044/1020] Import ns itself, and remove the _NS suffix on all namespaces. --- src/body.rs | 6 +++--- src/chatstates.rs | 12 ++++++------ src/data_forms.rs | 10 +++++----- src/disco.rs | 18 +++++++++--------- src/ibb.rs | 4 ++-- src/jingle.rs | 10 +++++----- src/media_element.rs | 6 +++--- src/ns.rs | 18 +++++++++--------- src/ping.rs | 4 ++-- src/receipts.rs | 6 +++--- 10 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/body.rs b/src/body.rs index 7c5fcae4aa14f3e7d927e804f629f7c32787c485..64624695e2d079ff1124a730256d47fb99972f63 100644 --- a/src/body.rs +++ b/src/body.rs @@ -2,8 +2,7 @@ use minidom::Element; use error::Error; -// TODO: also support components and servers. -use ns::JABBER_CLIENT_NS; +use ns; #[derive(Debug)] pub struct Body { @@ -11,7 +10,8 @@ pub struct Body { } pub fn parse_body(root: &Element) -> Result { - if !root.is("body", JABBER_CLIENT_NS) { + // TODO: also support components and servers. + if !root.is("body", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a body element.")); } for _ in root.children() { diff --git a/src/chatstates.rs b/src/chatstates.rs index 1651e3a5bcfcee290a226e9b28ec197a6f22f392..5a9a4dc4d5fe8ecd68d031a81a9f45441c58d555 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -2,7 +2,7 @@ use minidom::Element; use error::Error; -use ns::CHATSTATES_NS; +use ns; #[derive(Debug)] pub enum ChatState { @@ -17,15 +17,15 @@ pub fn parse_chatstate(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); } - if root.is("active", CHATSTATES_NS) { + if root.is("active", ns::CHATSTATES) { Ok(ChatState::Active) - } else if root.is("composing", CHATSTATES_NS) { + } else if root.is("composing", ns::CHATSTATES) { Ok(ChatState::Composing) - } else if root.is("gone", CHATSTATES_NS) { + } else if root.is("gone", ns::CHATSTATES) { Ok(ChatState::Gone) - } else if root.is("inactive", CHATSTATES_NS) { + } else if root.is("inactive", ns::CHATSTATES) { Ok(ChatState::Inactive) - } else if root.is("paused", CHATSTATES_NS) { + } else if root.is("paused", ns::CHATSTATES) { Ok(ChatState::Paused) } else { Err(Error::ParseError("This is not a chatstate element.")) diff --git a/src/data_forms.rs b/src/data_forms.rs index 342c0c453d4b06e0fb2b899ede67731eef5139a5..4ea50c965458e9d3aa433cf7e2415893bb71dd34 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use minidom::Element; use error::Error; -use ns::{DATA_FORMS_NS, MEDIA_ELEMENT_NS}; +use ns; use media_element::{MediaElement, parse_media_element}; @@ -52,7 +52,7 @@ pub struct DataForm { } pub fn parse_data_form(root: &Element) -> Result { - if !root.is("x", DATA_FORMS_NS) { + if !root.is("x", ns::DATA_FORMS) { return Err(Error::ParseError("This is not a data form element.")); } @@ -63,16 +63,16 @@ pub fn parse_data_form(root: &Element) -> Result { let mut fields = vec!(); let mut form_type = None; for field in root.children() { - if field.is("field", DATA_FORMS_NS) { + if field.is("field", ns::DATA_FORMS) { let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?; let field_type = field.attr("type").unwrap_or("text-single"); let label = field.attr("label").and_then(|label| label.parse().ok()); let mut values = vec!(); let mut media = vec!(); for element in field.children() { - if element.is("value", DATA_FORMS_NS) { + if element.is("value", ns::DATA_FORMS) { values.push(element.text()); - } else if element.is("media", MEDIA_ELEMENT_NS) { + } else if element.is("media", ns::MEDIA_ELEMENT) { match parse_media_element(element) { Ok(media_element) => media.push(media_element), Err(_) => (), // TODO: is it really nice to swallow this error? diff --git a/src/disco.rs b/src/disco.rs index 4b2d0334e48350dd8f1f454490176b53581ae9b9..39befce5fde89bc3c9dcb48faf388d8777aca565 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -3,7 +3,7 @@ extern crate minidom; use minidom::Element; use error::Error; -use ns::{DISCO_INFO_NS, DATA_FORMS_NS}; +use ns; use data_forms::{DataForm, DataFormType, parse_data_form}; @@ -29,7 +29,7 @@ pub struct Disco { } pub fn parse_disco(root: &Element) -> Result { - if !root.is("query", DISCO_INFO_NS) { + if !root.is("query", ns::DISCO_INFO) { return Err(Error::ParseError("This is not a disco#info element.")); } @@ -41,13 +41,13 @@ pub fn parse_disco(root: &Element) -> Result { .and_then(|node| node.parse().ok()); for child in root.children() { - if child.is("feature", DISCO_INFO_NS) { + if child.is("feature", ns::DISCO_INFO) { let feature = child.attr("var") .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?; features.push(Feature { var: feature.to_owned(), }); - } else if child.is("identity", DISCO_INFO_NS) { + } else if child.is("identity", ns::DISCO_INFO) { let category = child.attr("category") .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?; if category == "" { @@ -70,7 +70,7 @@ pub fn parse_disco(root: &Element) -> Result { xml_lang: xml_lang.to_owned(), name: name, }); - } else if child.is("x", DATA_FORMS_NS) { + } else if child.is("x", ns::DATA_FORMS) { let data_form = parse_data_form(child)?; match data_form.type_ { DataFormType::Result_ => (), @@ -93,7 +93,7 @@ pub fn parse_disco(root: &Element) -> Result { if features.is_empty() { return Err(Error::ParseError("There must be at least one feature in disco#info.")); } - if !features.contains(&Feature { var: DISCO_INFO_NS.to_owned() }) { + if !features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { return Err(Error::ParseError("disco#info feature not present in disco#info.")); } */ @@ -108,12 +108,12 @@ pub fn parse_disco(root: &Element) -> Result { pub fn serialise_disco(disco: &Disco) -> Element { let mut root = Element::builder("query") - .ns(DISCO_INFO_NS) + .ns(ns::DISCO_INFO) .attr("node", disco.node.clone()) .build(); for identity in &disco.identities { let identity_element = Element::builder("identity") - .ns(DISCO_INFO_NS) + .ns(ns::DISCO_INFO) .attr("category", identity.category.clone()) .attr("type", identity.type_.clone()) .attr("xml:lang", identity.xml_lang.clone()) @@ -123,7 +123,7 @@ pub fn serialise_disco(disco: &Disco) -> Element { } for feature in &disco.features { let feature_element = Element::builder("feature") - .ns(DISCO_INFO_NS) + .ns(ns::DISCO_INFO) .attr("var", feature.var.clone()) .build(); root.append_child(feature_element); diff --git a/src/ibb.rs b/src/ibb.rs index 64363c358164af5236816567d9b41d66c36fcde0..fb368a566cbb75e3990bf92d9c9fa51cd42eccac 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,7 @@ use minidom::Element; use error::Error; -use ns::IBB_NS; +use ns; #[derive(Debug)] pub enum Stanza { @@ -49,7 +49,7 @@ fn required_attr(root: &Element, attr: &str, err: Error) -> Result Result { - if root.is("open", IBB_NS) { + if root.is("open", ns::IBB) { let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; let stanza = root.attr("stanza") diff --git a/src/jingle.rs b/src/jingle.rs index a2fc331d80f0fff86ff835d53f9e18fdf77849e6..40ba69da3a965e79c92fd6b1d38068ed11288b8c 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use minidom::Element; use error::Error; -use ns::JINGLE_NS; +use ns; #[derive(Debug, PartialEq)] pub enum Action { @@ -209,7 +209,7 @@ pub struct Jingle { } pub fn parse_jingle(root: &Element) -> Result { - if !root.is("jingle", JINGLE_NS) { + if !root.is("jingle", ns::JINGLE) { return Err(Error::ParseError("This is not a Jingle element.")); } @@ -227,7 +227,7 @@ pub fn parse_jingle(root: &Element) -> Result { let mut reason_element = None; for child in root.children() { - if child.is("content", JINGLE_NS) { + if child.is("content", ns::JINGLE) { let creator = child.attr("creator") .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? .parse()?; @@ -276,14 +276,14 @@ pub fn parse_jingle(root: &Element) -> Result { transport: transport, security: security.to_owned(), }); - } else if child.is("reason", JINGLE_NS) { + } else if child.is("reason", ns::JINGLE) { if reason_element.is_some() { return Err(Error::ParseError("Jingle must not have more than one reason.")); } let mut reason = None; let mut text = None; for stuff in child.children() { - if stuff.ns() != Some(JINGLE_NS) { + if stuff.ns() != Some(ns::JINGLE) { return Err(Error::ParseError("Reason contains a foreign element.")); } let name = stuff.name(); diff --git a/src/media_element.rs b/src/media_element.rs index 7d77400883b38866d911d99518309bee5ea3acb3..db40dfc6042d809128259cea73d86bdea25bfb23 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -2,7 +2,7 @@ use minidom::Element; use error::Error; -use ns::MEDIA_ELEMENT_NS; +use ns; #[derive(Debug)] pub struct URI { @@ -18,7 +18,7 @@ pub struct MediaElement { } pub fn parse_media_element(root: &Element) -> Result { - if !root.is("media", MEDIA_ELEMENT_NS) { + if !root.is("media", ns::MEDIA_ELEMENT) { return Err(Error::ParseError("This is not a media element.")); } @@ -26,7 +26,7 @@ pub fn parse_media_element(root: &Element) -> Result { let height = root.attr("height").and_then(|height| height.parse().ok()); let mut uris = vec!(); for uri in root.children() { - if uri.is("uri", MEDIA_ELEMENT_NS) { + if uri.is("uri", ns::MEDIA_ELEMENT) { let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?; let text = uri.text().trim().to_owned(); if text == "" { diff --git a/src/ns.rs b/src/ns.rs index 49293f4f1dbb0ebcece1d3868f3657fb881b53fd..a1848e52d870b287db41528d696fc098bad66066 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,9 +1,9 @@ -pub const JABBER_CLIENT_NS: &'static str = "jabber:client"; -pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info"; -pub const DATA_FORMS_NS: &'static str = "jabber:x:data"; -pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; -pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; -pub const PING_NS: &'static str = "urn:xmpp:ping"; -pub const CHATSTATES_NS: &'static str = "http://jabber.org/protocol/chatstates"; -pub const IBB_NS: &'static str = "http://jabber.org/protocol/ibb"; -pub const RECEIPTS_NS: &'static str = "urn:xmpp:receipts"; +pub const JABBER_CLIENT: &'static str = "jabber:client"; +pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; +pub const DATA_FORMS: &'static str = "jabber:x:data"; +pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; +pub const JINGLE: &'static str = "urn:xmpp:jingle:1"; +pub const PING: &'static str = "urn:xmpp:ping"; +pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; +pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; +pub const RECEIPTS: &'static str = "urn:xmpp:receipts"; diff --git a/src/ping.rs b/src/ping.rs index de7aeb7b5f96ce369f9a67bafbc64ae1e054bae2..adb0f45c27011832de057c9632ffe3e08af470da 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -2,14 +2,14 @@ use minidom::Element; use error::Error; -use ns::PING_NS; +use ns; #[derive(Debug)] pub struct Ping { } pub fn parse_ping(root: &Element) -> Result { - if !root.is("ping", PING_NS) { + if !root.is("ping", ns::PING) { return Err(Error::ParseError("This is not a ping element.")); } diff --git a/src/receipts.rs b/src/receipts.rs index a19dff1c982155638d6dc9c92596256d3f60158d..a45a54c106ec554e016792d09a36eb6f10c8883c 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -2,7 +2,7 @@ use minidom::Element; use error::Error; -use ns::RECEIPTS_NS; +use ns; #[derive(Debug)] pub enum Receipt { @@ -14,9 +14,9 @@ pub fn parse_receipt(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in receipt element.")); } - if root.is("request", RECEIPTS_NS) { + if root.is("request", ns::RECEIPTS) { Ok(Receipt::Request) - } else if root.is("received", RECEIPTS_NS) { + } else if root.is("received", ns::RECEIPTS) { let id = root.attr("id").unwrap_or("").to_owned(); Ok(Receipt::Received(id)) } else { From efedb215c4933cef80114c9e0f774ea7a43fec1b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 00:01:57 +0100 Subject: [PATCH 0045/1020] ecaps2: Add Rust-Crypto and base64 dependencies, and implement hashing. --- Cargo.toml | 4 ++++ src/ecaps2.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 10c55dd53824d3fe4df43a74c6b6b8975951ee11..7102bc64eb6d1972ee60a46a6e4b0947ac81ae56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,7 @@ authors = ["Emmanuel Gil Peyrot "] [dependencies] minidom = "0.1.1" +base64 = "0.4.1" +sha2 = "0.5.0" +sha3 = "0.5.0" +blake2 = "0.5.0" diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fddc712b336e9ccf98cc317f9a05549cda8bdee5..21efc9c372a58add7baae397dfad113119283ce9 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -1,4 +1,8 @@ extern crate minidom; +extern crate sha2; +extern crate sha3; +extern crate blake2; +extern crate base64; use minidom::Element; @@ -7,6 +11,10 @@ use error::Error; use disco::{Feature, Identity, Disco, parse_disco}; use data_forms::DataForm; +use self::sha2::{Sha256, Sha512, Digest}; +use self::sha3::{Sha3_256, Sha3_512}; +use self::blake2::{Blake2b}; + fn compute_item(field: &String) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); @@ -73,6 +81,52 @@ pub fn convert_element(root: &Element) -> Result, Error> { Ok(final_string) } +pub fn hash_ecaps2(data: &Vec, algo: String) -> String { + match algo.as_ref() { + "sha-256" => { + let mut hasher = Sha256::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + "sha-512" => { + let mut hasher = Sha512::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + "sha3-256" => { + let mut hasher = Sha3_256::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + "sha3-512" => { + let mut hasher = Sha3_512::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + /* + "blake2b-256" => { + // TODO: bit length is most likely wrong here! + let mut hasher = Blake2b::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + "blake2b-512" => { + // TODO: bit length is most likely wrong here! + let mut hasher = Blake2b::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash) + }, + */ + _ => panic!(), + } +} + #[cfg(test)] mod tests { use minidom::Element; @@ -147,12 +201,10 @@ mod tests { assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); - /* - let sha_256 = hash(ecaps2, "sha-256"); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha-256")); assert_eq!(sha_256, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); - let sha3_256 = hash(ecaps2, "sha3-256"); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha3-256")); assert_eq!(sha3_256, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); - */ } #[test] @@ -320,11 +372,9 @@ mod tests { assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); - /* - let sha_256 = hash(ecaps2, "sha-256"); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha-256")); assert_eq!(sha_256, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); - let sha3_256 = hash(ecaps2, "sha3-256"); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha3-256")); assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); - */ } } From 88ae9aca3f1115a0297c1ef7ed25ea7810d157ea Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 00:24:20 +0100 Subject: [PATCH 0046/1020] ecaps2: Remove unused brackets around Blake2b. --- src/ecaps2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 21efc9c372a58add7baae397dfad113119283ce9..da36f9e19e6b8e8fcfcd16aedd5e969c0b98d049 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -13,7 +13,7 @@ use data_forms::DataForm; use self::sha2::{Sha256, Sha512, Digest}; use self::sha3::{Sha3_256, Sha3_512}; -use self::blake2::{Blake2b}; +use self::blake2::Blake2b; fn compute_item(field: &String) -> Vec { let mut bytes = field.as_bytes().to_vec(); From 62d9385728b689f7399bfd35f1fcb4ecb4ffaa35 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 00:41:15 +0100 Subject: [PATCH 0047/1020] Make all parsed types implement Clone. --- src/body.rs | 2 +- src/chatstates.rs | 2 +- src/data_forms.rs | 6 +++--- src/disco.rs | 6 +++--- src/ibb.rs | 4 ++-- src/jingle.rs | 14 +++++++------- src/media_element.rs | 4 ++-- src/ping.rs | 2 +- src/receipts.rs | 2 +- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/body.rs b/src/body.rs index 64624695e2d079ff1124a730256d47fb99972f63..45406e6c7f76b851270293420c93f8a951be578f 100644 --- a/src/body.rs +++ b/src/body.rs @@ -4,7 +4,7 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Body { pub body: String, } diff --git a/src/chatstates.rs b/src/chatstates.rs index 5a9a4dc4d5fe8ecd68d031a81a9f45441c58d555..8732d5653dc1aa5648bdb44663f52511cb58ac9c 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,7 +4,7 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ChatState { Active, Composing, diff --git a/src/data_forms.rs b/src/data_forms.rs index 4ea50c965458e9d3aa433cf7e2415893bb71dd34..dd9b3e11be87072e6e8049a83c283e614f1e94de 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -9,7 +9,7 @@ use ns; use media_element::{MediaElement, parse_media_element}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Field { pub var: String, pub type_: String, // TODO: use an enum here. @@ -18,7 +18,7 @@ pub struct Field { pub media: Vec, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum DataFormType { Cancel, Form, @@ -44,7 +44,7 @@ impl FromStr for DataFormType { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DataForm { pub type_: DataFormType, pub form_type: Option, diff --git a/src/disco.rs b/src/disco.rs index 39befce5fde89bc3c9dcb48faf388d8777aca565..a9d8b37082923346a966679fa791518a9c20f03d 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -7,12 +7,12 @@ use ns; use data_forms::{DataForm, DataFormType, parse_data_form}; -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct Feature { pub var: String, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Identity { pub category: String, // TODO: use an enum here. pub type_: String, // TODO: use an enum here. @@ -20,7 +20,7 @@ pub struct Identity { pub name: Option, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Disco { pub node: Option, pub identities: Vec, diff --git a/src/ibb.rs b/src/ibb.rs index fb368a566cbb75e3990bf92d9c9fa51cd42eccac..00d30f3f0a313ea7d1342fce32f3d94c14ed71a2 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -6,7 +6,7 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Stanza { Iq, Message, @@ -32,7 +32,7 @@ impl FromStr for Stanza { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum IBB { Open { block_size: u16, sid: String, stanza: Stanza }, Data(u16, String, Vec), diff --git a/src/jingle.rs b/src/jingle.rs index 40ba69da3a965e79c92fd6b1d38068ed11288b8c..2b7f253f49ed02f726b219b2cae4877dacac6a94 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -7,7 +7,7 @@ use minidom::Element; use error::Error; use ns; -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Action { ContentAccept, ContentAdd, @@ -69,7 +69,7 @@ impl FromStr for Action { // TODO: use a real JID type. type Jid = String; -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Creator { Initiator, Responder, @@ -89,7 +89,7 @@ impl FromStr for Creator { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Senders { Both, Initiator, @@ -115,7 +115,7 @@ impl FromStr for Senders { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Content { pub creator: Creator, pub disposition: String, @@ -126,7 +126,7 @@ pub struct Content { pub security: Option, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Reason { AlternativeSession, //(String), Busy, @@ -191,13 +191,13 @@ impl FromStr for Reason { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ReasonElement { pub reason: Reason, pub text: Option, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Jingle { pub action: Action, pub initiator: Option, diff --git a/src/media_element.rs b/src/media_element.rs index db40dfc6042d809128259cea73d86bdea25bfb23..b48636a507ce0a80ac7e3ec252690b8eb60161a1 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,13 +4,13 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct URI { pub type_: String, pub uri: String, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MediaElement { pub width: Option, pub height: Option, diff --git a/src/ping.rs b/src/ping.rs index adb0f45c27011832de057c9632ffe3e08af470da..2fb3e5cc693bbac4a03855cebb3e4d45f91b5401 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -4,7 +4,7 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Ping { } diff --git a/src/receipts.rs b/src/receipts.rs index a45a54c106ec554e016792d09a36eb6f10c8883c..052982ca8ecf8e4b5deaa1f238cb7242e078c6bd 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,7 +4,7 @@ use error::Error; use ns; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Receipt { Request, Received(String), From 0da3d55e40f783c54e65052a783e49b02555d69d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:06:30 +0100 Subject: [PATCH 0048/1020] First attempt at a documentation. --- src/lib.rs | 45 ++++++++++++++++++++++++++++++++++++++------- src/ns.rs | 32 +++++++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f7608cd746e45ad3280fd4a16a476e7a7ad0e70f..dc8fcdb1a277e7283975f36c667ebdfa2ce0164d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,51 @@ +//! A crate parsing common XMPP elements into Rust structures. +//! +//! The main entrypoint is `parse_message_payload`, it takes a minidom +//! `Element` reference and optionally returns `Some(MessagePayload)` if the +//! parsing succeeded. +//! +//! Parsed structs can then be manipulated internally, and serialised back +//! before being sent over the wire. + extern crate minidom; +use minidom::Element; +/// Error type returned by every parser on failure. pub mod error; +/// XML namespace definitions used through XMPP. pub mod ns; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod body; -pub mod disco; + +/// XEP-0004: Data Forms pub mod data_forms; -pub mod media_element; -pub mod ecaps2; -pub mod jingle; -pub mod ping; -pub mod chatstates; + +/// XEP-0030: Service Discovery +pub mod disco; + +/// XEP-0047: In-Band Bytestreams pub mod ibb; + +/// XEP-0085: Chat State Notifications +pub mod chatstates; + +/// XEP-0166: Jingle +pub mod jingle; + +/// XEP-0184: Message Delivery Receipts pub mod receipts; -use minidom::Element; +/// XEP-0199: XMPP Ping +pub mod ping; + +/// XEP-0221: Data Forms Media Element +pub mod media_element; + +/// XEP-0390: Entity Capabilities 2.0 +pub mod ecaps2; +/// Lists every known payload of a ``. #[derive(Debug)] pub enum MessagePayload { Body(body::Body), @@ -23,6 +53,7 @@ pub enum MessagePayload { Receipt(receipts::Receipt), } +/// Parse one of the payloads of a `` element, and return `Some` of a `MessagePayload` if parsing it succeeded, `None` otherwise. pub fn parse_message_payload(elem: &Element) -> Option { if let Ok(body) = body::parse_body(elem) { Some(MessagePayload::Body(body)) diff --git a/src/ns.rs b/src/ns.rs index a1848e52d870b287db41528d696fc098bad66066..5bcf6d1e8eab104fd7f6837755ab6e1a4d1fe67e 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,9 +1,31 @@ +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const JABBER_CLIENT: &'static str = "jabber:client"; -pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; + +/// XEP-0004: Data Forms pub const DATA_FORMS: &'static str = "jabber:x:data"; -pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; -pub const JINGLE: &'static str = "urn:xmpp:jingle:1"; -pub const PING: &'static str = "urn:xmpp:ping"; -pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; + +/// XEP-0030: Service Discovery +pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; + +/// XEP-0047: In-Band Bytestreams pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; + +/// XEP-0085: Chat State Notifications +pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; + +/// XEP-0166: Jingle +pub const JINGLE: &'static str = "urn:xmpp:jingle:1"; + +/// XEP-0184: Message Delivery Receipts pub const RECEIPTS: &'static str = "urn:xmpp:receipts"; + +/// XEP-0199: XMPP Ping +pub const PING: &'static str = "urn:xmpp:ping"; + +/// XEP-0221: Data Forms Media Element +pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; + +/// XEP-0390: Entity Capabilities 2.0 +pub const ECAPS2: &'static str = "urn:xmpp:caps"; +/// XEP-0390: Entity Capabilities 2.0 +pub const ECAPS2_OPTIMIZE: &'static str = "urn:xmpp:caps:optimize"; From 3209b04a50947c23520f59730f4873e935dc0747 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:28:58 +0100 Subject: [PATCH 0049/1020] Fix all warnings issued by `cargo clippy`. --- src/disco.rs | 4 ++-- src/ecaps2.rs | 22 +++++++++++----------- src/ibb.rs | 2 +- src/jingle.rs | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index a9d8b37082923346a966679fa791518a9c20f03d..4ac7240e163f6f901a7b6b995ac27542f7681a50 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -98,12 +98,12 @@ pub fn parse_disco(root: &Element) -> Result { } */ - return Ok(Disco { + Ok(Disco { node: node, identities: identities, features: features, extensions: extensions - }); + }) } pub fn serialise_disco(disco: &Disco) -> Element { diff --git a/src/ecaps2.rs b/src/ecaps2.rs index da36f9e19e6b8e8fcfcd16aedd5e969c0b98d049..e2e2de186127dee2f1dd4713fdd30e79adbfc4d7 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -15,13 +15,13 @@ use self::sha2::{Sha256, Sha512, Digest}; use self::sha3::{Sha3_256, Sha3_512}; use self::blake2::Blake2b; -fn compute_item(field: &String) -> Vec { +fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); bytes } -fn compute_items Vec>(things: &Vec, separator: u8, encode: F) -> Vec { +fn compute_items Vec>(things: &[T], separator: u8, encode: F) -> Vec { let mut string: Vec = vec!(); let mut accumulator: Vec> = vec!(); for thing in things { @@ -37,22 +37,22 @@ fn compute_items Vec>(things: &Vec, separator: u8, encode string } -fn compute_features(features: &Vec) -> Vec { +fn compute_features(features: &[Feature]) -> Vec { compute_items(features, 0x1c, |feature| compute_item(&feature.var)) } -fn compute_identities(identities: &Vec) -> Vec { +fn compute_identities(identities: &[Identity]) -> Vec { compute_items(identities, 0x1c, |identity| { let mut bytes = compute_item(&identity.category); bytes.append(&mut compute_item(&identity.type_)); bytes.append(&mut compute_item(&identity.xml_lang)); - bytes.append(&mut compute_item(&identity.name.clone().unwrap_or(String::new()))); + bytes.append(&mut compute_item(&identity.name.clone().unwrap_or_default())); bytes.push(0x1e); bytes }) } -fn compute_extensions(extensions: &Vec) -> Vec { +fn compute_extensions(extensions: &[DataForm]) -> Vec { compute_items(extensions, 0x1c, |extension| { compute_items(&extension.fields, 0x1d, |field| { let mut bytes = compute_item(&field.var); @@ -81,7 +81,7 @@ pub fn convert_element(root: &Element) -> Result, Error> { Ok(final_string) } -pub fn hash_ecaps2(data: &Vec, algo: String) -> String { +pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { match algo.as_ref() { "sha-256" => { let mut hasher = Sha256::default(); @@ -201,9 +201,9 @@ mod tests { assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha-256")); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, "sha-256"); assert_eq!(sha_256, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha3-256")); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); assert_eq!(sha3_256, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); } @@ -372,9 +372,9 @@ mod tests { assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha-256")); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, "sha-256"); assert_eq!(sha_256, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, String::from("sha3-256")); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); } } diff --git a/src/ibb.rs b/src/ibb.rs index 00d30f3f0a313ea7d1342fce32f3d94c14ed71a2..2e99506babd348553b5a77aa9611b851498abe3d 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -54,7 +54,7 @@ pub fn parse_ibb(root: &Element) -> Result { let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; let stanza = root.attr("stanza") .and_then(|value| value.parse().ok()) - .unwrap_or(Default::default()); + .unwrap_or_default(); for _ in root.children() { return Err(Error::ParseError("Unknown child in open element.")); } diff --git a/src/jingle.rs b/src/jingle.rs index 2b7f253f49ed02f726b219b2cae4877dacac6a94..0caba248fa4ef614a8b9c2e261c5f3e9bbff7790 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -308,14 +308,14 @@ pub fn parse_jingle(root: &Element) -> Result { } } - return Ok(Jingle { + Ok(Jingle { action: action, initiator: initiator, responder: responder, sid: sid.to_owned(), contents: contents, reason: reason_element, - }); + }) } #[cfg(test)] From 79c691e1c0b69905074efc0a018240ae49c5b635 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:30:29 +0100 Subject: [PATCH 0050/1020] ecaps2: Add a TODO about changing algo into an enum. --- src/ecaps2.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index e2e2de186127dee2f1dd4713fdd30e79adbfc4d7..5b74beaa4695219c2c5b126e95d73b61916228fa 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -81,8 +81,9 @@ pub fn convert_element(root: &Element) -> Result, Error> { Ok(final_string) } +// TODO: make algo into an enum. pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { - match algo.as_ref() { + match algo { "sha-256" => { let mut hasher = Sha256::default(); hasher.input(data); From 66004180702eeca00b4c68db6ca1f025025718a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:39:32 +0100 Subject: [PATCH 0051/1020] ecaps2: Remove function convert_element, used only in the tests. --- src/ecaps2.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 5b74beaa4695219c2c5b126e95d73b61916228fa..68012d960f117c88effb9d998476e9e6d6b45d81 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -1,14 +1,9 @@ -extern crate minidom; extern crate sha2; extern crate sha3; extern crate blake2; extern crate base64; -use minidom::Element; - -use error::Error; - -use disco::{Feature, Identity, Disco, parse_disco}; +use disco::{Feature, Identity, Disco}; use data_forms::DataForm; use self::sha2::{Sha256, Sha512, Digest}; @@ -75,12 +70,6 @@ pub fn compute_disco(disco: &Disco) -> Vec { final_string } -pub fn convert_element(root: &Element) -> Result, Error> { - let disco = parse_disco(root)?; - let final_string = compute_disco(&disco); - Ok(final_string) -} - // TODO: make algo into an enum. pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { match algo { @@ -131,12 +120,14 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { #[cfg(test)] mod tests { use minidom::Element; + use disco; use ecaps2; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + let disco = disco::parse_disco(&elem).unwrap(); + let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 54); } @@ -198,7 +189,8 @@ mod tests { 105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, 100, 31, 30, 28, 28]; - let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + let disco = disco::parse_disco(&elem).unwrap(); + let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); @@ -369,7 +361,8 @@ mod tests { 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; - let ecaps2 = ecaps2::convert_element(&elem).unwrap(); + let disco = disco::parse_disco(&elem).unwrap(); + let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); From 458099cef07c4c32918ed69f9f15806cec5d1749 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:53:47 +0100 Subject: [PATCH 0052/1020] Add an attention parser. --- src/attention.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ 3 files changed, 50 insertions(+) create mode 100644 src/attention.rs diff --git a/src/attention.rs b/src/attention.rs new file mode 100644 index 0000000000000000000000000000000000000000..2438dc6bce92c33cf271a53bf964ed13b1973180 --- /dev/null +++ b/src/attention.rs @@ -0,0 +1,44 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub enum Attention { + Attention, +} + +pub fn parse_attention(root: &Element) -> Result { + if !root.is("attention", ns::ATTENTION) { + return Err(Error::ParseError("This is not an attention element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in attention element.")); + } + Ok(Attention::Attention) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use attention; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + attention::parse_attention(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = attention::parse_attention(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in attention element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index dc8fcdb1a277e7283975f36c667ebdfa2ce0164d..2ba49d65fede95ed24ad8b819ae267d98f8f5a70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,9 @@ pub mod ping; /// XEP-0221: Data Forms Media Element pub mod media_element; +/// XEP-0224: Attention +pub mod attention; + /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; diff --git a/src/ns.rs b/src/ns.rs index 5bcf6d1e8eab104fd7f6837755ab6e1a4d1fe67e..2b5ba3f1e12b39313450d62715aa15e0fdb1daf0 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -25,6 +25,9 @@ pub const PING: &'static str = "urn:xmpp:ping"; /// XEP-0221: Data Forms Media Element pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; +/// XEP-0224: Attention +pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; + /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2: &'static str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 From 9410b9682a74ff33330214a3e16e1f468441c37d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 01:56:18 +0100 Subject: [PATCH 0053/1020] lib: Export attention as a MessagePayload. --- src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2ba49d65fede95ed24ad8b819ae267d98f8f5a70..1e4b975f61ec3d72b7cf68804fa001c1b230b289 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,9 +54,11 @@ pub enum MessagePayload { Body(body::Body), ChatState(chatstates::ChatState), Receipt(receipts::Receipt), + Attention(attention::Attention), } -/// Parse one of the payloads of a `` element, and return `Some` of a `MessagePayload` if parsing it succeeded, `None` otherwise. +/// Parse one of the payloads of a `` element, and return `Some` of a +/// `MessagePayload` if parsing it succeeded, `None` otherwise. pub fn parse_message_payload(elem: &Element) -> Option { if let Ok(body) = body::parse_body(elem) { Some(MessagePayload::Body(body)) @@ -64,6 +66,8 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::ChatState(chatstate)) } else if let Ok(receipt) = receipts::parse_receipt(elem) { Some(MessagePayload::Receipt(receipt)) + } else if let Ok(attention) = attention::parse_attention(elem) { + Some(MessagePayload::Attention(attention)) } else { None } From 8fba5cf9669443bde351244c64f9dc6581bc035d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 02:07:44 +0100 Subject: [PATCH 0054/1020] Add a Last Message Correction parser. --- src/lib.rs | 6 ++++++ src/message_correct.rs | 45 ++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 3 +++ 3 files changed, 54 insertions(+) create mode 100644 src/message_correct.rs diff --git a/src/lib.rs b/src/lib.rs index 1e4b975f61ec3d72b7cf68804fa001c1b230b289..c9315f6e5092d62d02de55f631cfde6796b43757 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,9 @@ pub mod media_element; /// XEP-0224: Attention pub mod attention; +/// XEP-0308: Last Message Correction +pub mod message_correct; + /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; @@ -55,6 +58,7 @@ pub enum MessagePayload { ChatState(chatstates::ChatState), Receipt(receipts::Receipt), Attention(attention::Attention), + MessageCorrect(message_correct::MessageCorrect), } /// Parse one of the payloads of a `` element, and return `Some` of a @@ -68,6 +72,8 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::Receipt(receipt)) } else if let Ok(attention) = attention::parse_attention(elem) { Some(MessagePayload::Attention(attention)) + } else if let Ok(replace) = message_correct::parse_message_correct(elem) { + Some(MessagePayload::MessageCorrect(replace)) } else { None } diff --git a/src/message_correct.rs b/src/message_correct.rs new file mode 100644 index 0000000000000000000000000000000000000000..789febb96d3ae634834e0804c4201358753ea2f7 --- /dev/null +++ b/src/message_correct.rs @@ -0,0 +1,45 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub enum MessageCorrect { + Replace(String), +} + +pub fn parse_message_correct(root: &Element) -> Result { + if !root.is("replace", ns::MESSAGE_CORRECT) { + return Err(Error::ParseError("This is not a replace element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in replace element.")); + } + let id = root.attr("id").unwrap_or("").to_owned(); + Ok(MessageCorrect::Replace(id)) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use message_correct; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + message_correct::parse_message_correct(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = message_correct::parse_message_correct(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in replace element."); + } +} diff --git a/src/ns.rs b/src/ns.rs index 2b5ba3f1e12b39313450d62715aa15e0fdb1daf0..df708fbea2f0805876464ea8197e104ed5dad978 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -28,6 +28,9 @@ pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; /// XEP-0224: Attention pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; +/// XEP-0308: Last Message Correction +pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; + /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2: &'static str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 From 6ba22a43e5e11f4372569a4456c1f17bd418ebac Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 02:46:41 +0100 Subject: [PATCH 0055/1020] lib: Import base64 at the top-level, and not in a module. --- src/ecaps2.rs | 2 +- src/error.rs | 10 +++++++++- src/lib.rs | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 68012d960f117c88effb9d998476e9e6d6b45d81..fce5e6ea8ccabfc869e497d69f26bb9e13a74a48 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -1,7 +1,6 @@ extern crate sha2; extern crate sha3; extern crate blake2; -extern crate base64; use disco::{Feature, Identity, Disco}; use data_forms::DataForm; @@ -9,6 +8,7 @@ use data_forms::DataForm; use self::sha2::{Sha256, Sha512, Digest}; use self::sha3::{Sha3_256, Sha3_512}; use self::blake2::Blake2b; +use base64; fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); diff --git a/src/error.rs b/src/error.rs index bd7d2d9cb78de8139f056e49809df24309f4e63c..c55a52a1d153b045c0d7c31e92ff1ad72a29a462 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,13 +1,15 @@ use std::convert::From; use std::io; +use base64; use minidom; #[derive(Debug)] pub enum Error { + ParseError(&'static str), IoError(io::Error), XMLError(minidom::Error), - ParseError(&'static str), + Base64Error(base64::Base64Error), } impl From for Error { @@ -21,3 +23,9 @@ impl From for Error { Error::XMLError(err) } } + +impl From for Error { + fn from(err: base64::Base64Error) -> Error { + Error::Base64Error(err) + } +} diff --git a/src/lib.rs b/src/lib.rs index c9315f6e5092d62d02de55f631cfde6796b43757..50679604da619fc2f1a8a3c72aa58711e5f270da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ //! before being sent over the wire. extern crate minidom; +extern crate base64; use minidom::Element; /// Error type returned by every parser on failure. From fb373c2b662ea76d3bb4d080748b45e5746a936a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 03:07:21 +0100 Subject: [PATCH 0056/1020] ibb: Add a parser for the element. --- src/ibb.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index 2e99506babd348553b5a77aa9611b851498abe3d..6811e68c9418da58d487faba9d221f25a033723d 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use minidom::Element; +use base64; use error::Error; @@ -35,7 +36,7 @@ impl FromStr for Stanza { #[derive(Debug, Clone)] pub enum IBB { Open { block_size: u16, sid: String, stanza: Stanza }, - Data(u16, String, Vec), + Data { seq: u16, sid: String, data: Vec }, Close(String), } @@ -63,6 +64,18 @@ pub fn parse_ibb(root: &Element) -> Result { sid: sid, stanza: stanza }) + } else if root.is("data", ns::IBB) { + let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + let data = base64::decode(&root.text())?; + for _ in root.children() { + return Err(Error::ParseError("Unknown child in data element.")); + } + Ok(IBB::Data { + seq: seq, + sid: sid, + data: data + }) } else { Err(Error::ParseError("This is not an ibb element.")) } @@ -76,7 +89,10 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); + ibb::parse_ibb(&elem).unwrap(); + + let elem: Element = "AAAA".parse().unwrap(); ibb::parse_ibb(&elem).unwrap(); } From 189f17c5696cf26707efedaff86701fc3c3d3161 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 03:09:10 +0100 Subject: [PATCH 0057/1020] ibb: Add a parser for the element. --- src/ibb.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ibb.rs b/src/ibb.rs index 6811e68c9418da58d487faba9d221f25a033723d..f77541c8f98ee97775bf340f4af08e5273d045e6 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -37,7 +37,7 @@ impl FromStr for Stanza { pub enum IBB { Open { block_size: u16, sid: String, stanza: Stanza }, Data { seq: u16, sid: String, data: Vec }, - Close(String), + Close { sid: String }, } fn optional_attr(root: &Element, attr: &str) -> Option { @@ -76,6 +76,14 @@ pub fn parse_ibb(root: &Element) -> Result { sid: sid, data: data }) + } else if root.is("close", ns::IBB) { + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + for _ in root.children() { + return Err(Error::ParseError("Unknown child in close element.")); + } + Ok(IBB::Close { + sid: sid, + }) } else { Err(Error::ParseError("This is not an ibb element.")) } @@ -94,6 +102,9 @@ mod tests { let elem: Element = "AAAA".parse().unwrap(); ibb::parse_ibb(&elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + ibb::parse_ibb(&elem).unwrap(); } #[test] From 94380fdbd5a0a78c4b1abb8f43f18ebaf09c8b10 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 03:21:06 +0100 Subject: [PATCH 0058/1020] ibb: Add some more tests. --- src/ibb.rs | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index f77541c8f98ee97775bf340f4af08e5273d045e6..89aad820d808bf04861cafa85c2276094ac072e1 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -7,7 +7,7 @@ use error::Error; use ns; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Stanza { Iq, Message, @@ -35,9 +35,19 @@ impl FromStr for Stanza { #[derive(Debug, Clone)] pub enum IBB { - Open { block_size: u16, sid: String, stanza: Stanza }, - Data { seq: u16, sid: String, data: Vec }, - Close { sid: String }, + Open { + block_size: u16, + sid: String, + stanza: Stanza, + }, + Data { + seq: u16, + sid: String, + data: Vec, + }, + Close { + sid: String, + }, } fn optional_attr(root: &Element, attr: &str) -> Option { @@ -98,13 +108,35 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - ibb::parse_ibb(&elem).unwrap(); + let open = ibb::parse_ibb(&elem).unwrap(); + match open { + ibb::IBB::Open { block_size, sid, stanza } => { + assert_eq!(block_size, 3); + assert_eq!(sid, "coucou"); + assert_eq!(stanza, ibb::Stanza::Iq); + }, + _ => panic!(), + } let elem: Element = "AAAA".parse().unwrap(); - ibb::parse_ibb(&elem).unwrap(); + let data = ibb::parse_ibb(&elem).unwrap(); + match data { + ibb::IBB::Data { seq, sid, data } => { + assert_eq!(seq, 0); + assert_eq!(sid, "coucou"); + assert_eq!(data, vec!(0, 0, 0)); + }, + _ => panic!(), + } let elem: Element = "".parse().unwrap(); - ibb::parse_ibb(&elem).unwrap(); + let close = ibb::parse_ibb(&elem).unwrap(); + match close { + ibb::IBB::Close { sid } => { + assert_eq!(sid, "coucou"); + }, + _ => panic!(), + } } #[test] From a680ab194ced4f7fd044ed45f42062581e4f26ca Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 03:45:05 +0100 Subject: [PATCH 0059/1020] Add an Explicit Message Encryption parser. --- src/eme.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 +++++ src/ns.rs | 3 +++ 3 files changed, 77 insertions(+) create mode 100644 src/eme.rs diff --git a/src/eme.rs b/src/eme.rs new file mode 100644 index 0000000000000000000000000000000000000000..cf33de7fbbee83b7be32680b1f573494e5b1f8a8 --- /dev/null +++ b/src/eme.rs @@ -0,0 +1,68 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct ExplicitMessageEncryption { + pub namespace: String, + pub name: Option, +} + +pub fn parse_explicit_message_encryption(root: &Element) -> Result { + if !root.is("encryption", ns::EME) { + return Err(Error::ParseError("This is not an encryption element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in encryption element.")); + } + let namespace = root.attr("namespace").ok_or(Error::ParseError("Mandatory argument 'namespace' not present in encryption element."))?.to_owned(); + let name = root.attr("name").and_then(|value| value.parse().ok()); + Ok(ExplicitMessageEncryption { + namespace: namespace, + name: name, + }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use eme; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let encryption = eme::parse_explicit_message_encryption(&elem).unwrap(); + assert_eq!(encryption.namespace, "urn:xmpp:otr:0"); + assert_eq!(encryption.name, None); + + let elem: Element = "".parse().unwrap(); + let encryption = eme::parse_explicit_message_encryption(&elem).unwrap(); + assert_eq!(encryption.namespace, "some.unknown.mechanism"); + assert_eq!(encryption.name, Some(String::from("SuperMechanism"))); + } + + #[test] + fn test_unknown() { + let elem: Element = "".parse().unwrap(); + let error = eme::parse_explicit_message_encryption(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not an encryption element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = eme::parse_explicit_message_encryption(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in encryption element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 50679604da619fc2f1a8a3c72aa58711e5f270da..167f58ec3d55be4b74b85f54af49e219ecd54f71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,9 @@ pub mod attention; /// XEP-0308: Last Message Correction pub mod message_correct; +/// XEP-0380: Explicit Message Encryption +pub mod eme; + /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; @@ -60,6 +63,7 @@ pub enum MessagePayload { Receipt(receipts::Receipt), Attention(attention::Attention), MessageCorrect(message_correct::MessageCorrect), + ExplicitMessageEncryption(eme::ExplicitMessageEncryption), } /// Parse one of the payloads of a `` element, and return `Some` of a @@ -75,6 +79,8 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::Attention(attention)) } else if let Ok(replace) = message_correct::parse_message_correct(elem) { Some(MessagePayload::MessageCorrect(replace)) + } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { + Some(MessagePayload::ExplicitMessageEncryption(eme)) } else { None } diff --git a/src/ns.rs b/src/ns.rs index df708fbea2f0805876464ea8197e104ed5dad978..6aa1d3f7d682f832faf7a177aa7c186f4723ba52 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -31,6 +31,9 @@ pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; +/// XEP-0380: Explicit Message Encryption +pub const EME: &'static str = "urn:xmpp:eme:0"; + /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2: &'static str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 From cfadff3bc9f13c9e007a35088f70ff3cdf08f5b0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 03:57:34 +0100 Subject: [PATCH 0060/1020] Add a Delayed Delivery parser. --- src/delay.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 +++++ src/ns.rs | 3 +++ 3 files changed, 75 insertions(+) create mode 100644 src/delay.rs diff --git a/src/delay.rs b/src/delay.rs new file mode 100644 index 0000000000000000000000000000000000000000..f4f59356786f320128d24c996bf8dab3d0385646 --- /dev/null +++ b/src/delay.rs @@ -0,0 +1,66 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct Delay { + pub from: Option, + pub stamp: String, + pub data: Option, +} + +pub fn parse_delay(root: &Element) -> Result { + if !root.is("delay", ns::DELAY) { + return Err(Error::ParseError("This is not a delay element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in delay element.")); + } + let from = root.attr("from").and_then(|value| value.parse().ok()); + let stamp = root.attr("stamp").ok_or(Error::ParseError("Mandatory argument 'stamp' not present in delay element."))?.to_owned(); + Ok(Delay { + from: from, + stamp: stamp, + data: None, + }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use delay; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let delay = delay::parse_delay(&elem).unwrap(); + assert_eq!(delay.from, Some(String::from("capulet.com"))); + assert_eq!(delay.stamp, "2002-09-10T23:08:25Z"); + assert_eq!(delay.data, None); + } + + #[test] + fn test_unknown() { + let elem: Element = "".parse().unwrap(); + let error = delay::parse_delay(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not a delay element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = delay::parse_delay(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in delay element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 167f58ec3d55be4b74b85f54af49e219ecd54f71..e00ccb525ecb571b31ee408047dcab12a66d91ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,9 @@ pub mod receipts; /// XEP-0199: XMPP Ping pub mod ping; +/// XEP-0203: Delayed Delivery +pub mod delay; + /// XEP-0221: Data Forms Media Element pub mod media_element; @@ -61,6 +64,7 @@ pub enum MessagePayload { Body(body::Body), ChatState(chatstates::ChatState), Receipt(receipts::Receipt), + Delay(delay::Delay), Attention(attention::Attention), MessageCorrect(message_correct::MessageCorrect), ExplicitMessageEncryption(eme::ExplicitMessageEncryption), @@ -75,6 +79,8 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::ChatState(chatstate)) } else if let Ok(receipt) = receipts::parse_receipt(elem) { Some(MessagePayload::Receipt(receipt)) + } else if let Ok(delay) = delay::parse_delay(elem) { + Some(MessagePayload::Delay(delay)) } else if let Ok(attention) = attention::parse_attention(elem) { Some(MessagePayload::Attention(attention)) } else if let Ok(replace) = message_correct::parse_message_correct(elem) { diff --git a/src/ns.rs b/src/ns.rs index 6aa1d3f7d682f832faf7a177aa7c186f4723ba52..8aefa2eeea0cfa6d95a2c1df5adb5953173d4b18 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -22,6 +22,9 @@ pub const RECEIPTS: &'static str = "urn:xmpp:receipts"; /// XEP-0199: XMPP Ping pub const PING: &'static str = "urn:xmpp:ping"; +/// XEP-0203: Delayed Delivery +pub const DELAY: &'static str = "urn:xmpp:delay"; + /// XEP-0221: Data Forms Media Element pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; From d39d13b7cb936ec82170e52ba48cb88b7138fb43 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 04:01:14 +0100 Subject: [PATCH 0061/1020] delay: Correctly parse the content data. --- src/delay.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/delay.rs b/src/delay.rs index f4f59356786f320128d24c996bf8dab3d0385646..b54dd04796399dd0ed208dbdbb906e702d5942d5 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -20,10 +20,14 @@ pub fn parse_delay(root: &Element) -> Result { } let from = root.attr("from").and_then(|value| value.parse().ok()); let stamp = root.attr("stamp").ok_or(Error::ParseError("Mandatory argument 'stamp' not present in delay element."))?.to_owned(); + let data = match root.text().as_ref() { + "" => None, + text => Some(text.to_owned()), + }; Ok(Delay { from: from, stamp: stamp, - data: None, + data: data, }) } From ea2ff8a35db030f57c332be44065cfbffcae1adb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Apr 2017 04:21:16 +0100 Subject: [PATCH 0062/1020] Add a hash parser. --- src/hashes.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ 3 files changed, 72 insertions(+) create mode 100644 src/hashes.rs diff --git a/src/hashes.rs b/src/hashes.rs new file mode 100644 index 0000000000000000000000000000000000000000..21301633ce89458edb97635fcc1e017c522b9f66 --- /dev/null +++ b/src/hashes.rs @@ -0,0 +1,66 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct Hash { + pub algo: String, + pub hash: String, +} + +pub fn parse_hash(root: &Element) -> Result { + if !root.is("hash", ns::HASHES) { + return Err(Error::ParseError("This is not a hash element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in hash element.")); + } + let algo = root.attr("algo").ok_or(Error::ParseError("Mandatory argument 'algo' not present in hash element."))?.to_owned(); + let hash = match root.text().as_ref() { + "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), + text => text.to_owned(), + }; + Ok(Hash { + algo: algo, + hash: hash, + }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use hashes; + + #[test] + fn test_simple() { + let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); + let hash = hashes::parse_hash(&elem).unwrap(); + assert_eq!(hash.algo, "sha-256"); + assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); + } + + #[test] + fn test_unknown() { + let elem: Element = "".parse().unwrap(); + let error = hashes::parse_hash(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not a hash element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = hashes::parse_hash(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in hash element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index e00ccb525ecb571b31ee408047dcab12a66d91ea..d26a5c9a6052c1061493dd3c91d2d1ccaaff8289 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,9 @@ pub mod media_element; /// XEP-0224: Attention pub mod attention; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub mod hashes; + /// XEP-0308: Last Message Correction pub mod message_correct; diff --git a/src/ns.rs b/src/ns.rs index 8aefa2eeea0cfa6d95a2c1df5adb5953173d4b18..d3d21068d638fec5c0cc480eb9efb1c8402fe4c0 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -31,6 +31,9 @@ pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; /// XEP-0224: Attention pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASHES: &'static str = "urn:xmpp:hashes:2"; + /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; From e1070a8b98b31c57e72ea6748d644f030c7aadef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 16:51:10 +0100 Subject: [PATCH 0063/1020] ecaps2: Add a parser too. --- src/ecaps2.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fce5e6ea8ccabfc869e497d69f26bb9e13a74a48..fabd2e51f6878e50bd10f9891b82750ce9a9d23b 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,12 +4,40 @@ extern crate blake2; use disco::{Feature, Identity, Disco}; use data_forms::DataForm; +use hashes::{Hash, parse_hash}; + +use minidom::Element; +use error::Error; +use ns; use self::sha2::{Sha256, Sha512, Digest}; use self::sha3::{Sha3_256, Sha3_512}; use self::blake2::Blake2b; use base64; +#[derive(Debug, Clone)] +pub struct ECaps2 { + hashes: Vec, +} + +pub fn parse_ecaps2(root: &Element) -> Result { + if !root.is("c", ns::ECAPS2) { + return Err(Error::ParseError("This is not an ecaps2 element.")); + } + let mut hashes = vec!(); + for child in root.children() { + if child.is("hash", ns::HASHES) { + let hash = parse_hash(child)?; + hashes.push(hash); + } else { + return Err(Error::ParseError("Unknown child in ecaps2 element.")); + } + } + Ok(ECaps2 { + hashes: hashes, + }) +} + fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); @@ -120,9 +148,32 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { #[cfg(test)] mod tests { use minidom::Element; + use error::Error; use disco; use ecaps2; + #[test] + fn test_parse() { + let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); + let ecaps2 = ecaps2::parse_ecaps2(&elem).unwrap(); + assert_eq!(ecaps2.hashes.len(), 2); + assert_eq!(ecaps2.hashes[0].algo, "sha-256"); + assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); + assert_eq!(ecaps2.hashes[1].algo, "sha3-256"); + assert_eq!(ecaps2.hashes[1].hash, "+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8="); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); + let error = ecaps2::parse_ecaps2(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in ecaps2 element."); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); From 0778ceea7d42dd85e569c0863d3a729b93a9be6c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 17:38:19 +0100 Subject: [PATCH 0064/1020] jingle: Remove extraneous println!(). --- src/jingle.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index 0caba248fa4ef614a8b9c2e261c5f3e9bbff7790..80ad7b0617bcd4eca807d6c9e4ed10f1046ff993 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -375,7 +375,6 @@ mod tests { assert_eq!(jingle.contents[0].name, "coucou"); assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); assert_eq!(jingle.contents[0].disposition, "session"); - println!("{:#?}", jingle); let elem: Element = "".parse().unwrap(); let jingle = jingle::parse_jingle(&elem).unwrap(); From edc5961a7969b1510998608f1d927a3c7f425c94 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 17:38:36 +0100 Subject: [PATCH 0065/1020] hashes: Implement PartialEq on Hash. --- src/hashes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hashes.rs b/src/hashes.rs index 21301633ce89458edb97635fcc1e017c522b9f66..f9e1e846c1a4c4f8b1cc2f5edf8efdba427e38a1 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,7 +4,7 @@ use error::Error; use ns; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Hash { pub algo: String, pub hash: String, From 000e5a50c29d365d1defa5fc765fd1f97007a88a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 17:39:00 +0100 Subject: [PATCH 0066/1020] error: Add ParseIntError to the possible errors. --- src/error.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/error.rs b/src/error.rs index c55a52a1d153b045c0d7c31e92ff1ad72a29a462..e9506bae476d052f24a9c2e4a6b34e2a60b91cbf 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,6 @@ use std::convert::From; use std::io; +use std::num; use base64; use minidom; @@ -10,6 +11,7 @@ pub enum Error { IoError(io::Error), XMLError(minidom::Error), Base64Error(base64::Base64Error), + ParseIntError(num::ParseIntError), } impl From for Error { @@ -29,3 +31,9 @@ impl From for Error { Error::Base64Error(err) } } + +impl From for Error { + fn from(err: num::ParseIntError) -> Error { + Error::ParseIntError(err) + } +} From fbb609485b04ddc9e9738c0142a06c9ec190ff09 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 17:39:21 +0100 Subject: [PATCH 0067/1020] Add a JingleFT parser. --- src/jingle_ft.rs | 175 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 5 ++ 3 files changed, 183 insertions(+) create mode 100644 src/jingle_ft.rs diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs new file mode 100644 index 0000000000000000000000000000000000000000..6e3e2803ba27ba6bb345adda6124b7757d1df27f --- /dev/null +++ b/src/jingle_ft.rs @@ -0,0 +1,175 @@ +extern crate minidom; + +use hashes::{Hash, parse_hash}; + +use minidom::Element; + +use error::Error; +use ns; + +#[derive(Debug, Clone, PartialEq)] +pub struct Range { + pub offset: u64, + pub length: Option, + pub hashes: Vec, +} + +#[derive(Debug, Clone)] +pub struct File { + pub date: Option, + pub media_type: Option, + pub name: Option, + pub size: Option, + pub range: Option, + pub hashes: Vec, +} + +#[derive(Debug, Clone)] +pub struct Description { + pub file: File, +} + +#[derive(Debug, Clone)] +pub struct Creator { + pub creator: String, +} + +#[derive(Debug, Clone)] +pub struct Checksum { + pub name: String, + pub creator: Creator, + pub file: File, +} + +pub fn parse_jingle_ft(root: &Element) -> Result { + if !root.is("description", ns::JINGLE_FT) { + return Err(Error::ParseError("This is not a JingleFT description element.")); + } + if root.children().collect::>().len() != 1 { + return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + } + + let mut date = None; + let mut media_type = None; + let mut name = None; + let mut size = None; + let mut range = None; + let mut hashes = vec!(); + for description_payload in root.children() { + if !description_payload.is("file", ns::JINGLE_FT) { + return Err(Error::ParseError("Unknown element in JingleFT description.")); + } + for file_payload in description_payload.children() { + if file_payload.is("date", ns::JINGLE_FT) { + if date.is_some() { + return Err(Error::ParseError("File must not have more than one date.")); + } + date = Some(file_payload.text()); + } else if file_payload.is("media-type", ns::JINGLE_FT) { + if media_type.is_some() { + return Err(Error::ParseError("File must not have more than one media-type.")); + } + media_type = Some(file_payload.text()); + } else if file_payload.is("name", ns::JINGLE_FT) { + if name.is_some() { + return Err(Error::ParseError("File must not have more than one name.")); + } + name = Some(file_payload.text()); + } else if file_payload.is("size", ns::JINGLE_FT) { + if size.is_some() { + return Err(Error::ParseError("File must not have more than one size.")); + } + size = Some(file_payload.text()); + } else if file_payload.is("range", ns::JINGLE_FT) { + if range.is_some() { + return Err(Error::ParseError("File must not have more than one range.")); + } + let offset = file_payload.attr("offset").unwrap_or("0").parse()?; + let length = match file_payload.attr("length") { + Some(length) => Some(length.parse()?), + None => None, + }; + let mut range_hashes = vec!(); + for hash_element in file_payload.children() { + if !hash_element.is("hash", ns::HASHES) { + return Err(Error::ParseError("Unknown element in JingleFT range.")); + } + range_hashes.push(parse_hash(hash_element)?); + } + range = Some(Range { + offset: offset, + length: length, + hashes: range_hashes, + }); + } else if file_payload.is("hash", ns::HASHES) { + hashes.push(parse_hash(file_payload)?); + } else { + return Err(Error::ParseError("Unknown element in JingleFT file.")); + } + } + } + + Ok(Description { + file: File { + date: date, + media_type: media_type, + name: name, + size: size, + range: range, + hashes: hashes, + }, + }) +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use jingle_ft; + + #[test] + fn test_description() { + let elem: Element = r#" + + + text/plain + test.txt + 2015-07-26T21:46:00 + 6144 + w0mcJylzCn+AfvuGdqkty2+KP48= + + +"#.parse().unwrap(); + + let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); + assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); + assert_eq!(desc.file.name, Some(String::from("test.txt"))); + assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); + assert_eq!(desc.file.size, Some(String::from("6144"))); + assert_eq!(desc.file.range, None); + assert_eq!(desc.file.hashes[0].algo, "sha-1"); + assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); + } + + #[test] + fn test_request() { + let elem: Element = r#" + + + w0mcJylzCn+AfvuGdqkty2+KP48= + + +"#.parse().unwrap(); + + let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); + assert_eq!(desc.file.media_type, None); + assert_eq!(desc.file.name, None); + assert_eq!(desc.file.date, None); + assert_eq!(desc.file.size, None); + assert_eq!(desc.file.range, None); + assert_eq!(desc.file.hashes[0].algo, "sha-1"); + assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); + } +} diff --git a/src/lib.rs b/src/lib.rs index d26a5c9a6052c1061493dd3c91d2d1ccaaff8289..614737dd937c195c8f03ff9b3aedb40a384402b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,9 @@ pub mod media_element; /// XEP-0224: Attention pub mod attention; +/// XEP-0234: Jingle File Transfer +pub mod jingle_ft; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub mod hashes; diff --git a/src/ns.rs b/src/ns.rs index d3d21068d638fec5c0cc480eb9efb1c8402fe4c0..5dfb3f5d18e77eddbf9dc99e824aedd35d6b1ff5 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -31,6 +31,11 @@ pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; /// XEP-0224: Attention pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; +/// XEP-0234: Jingle File Transfer +pub const JINGLE_FT: &'static str = "urn:xmpp:jingle:apps:file-transfer:5"; +/// XEP-0234: Jingle File Transfer +pub const JINGLE_FT_ERROR: &'static str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub const HASHES: &'static str = "urn:xmpp:hashes:2"; From 22ec4b19137233cc48be376b9b6800a30d1dcd9f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 19:15:29 +0100 Subject: [PATCH 0068/1020] ibb: Improve handling of optional stanza attribute. --- src/ibb.rs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index 89aad820d808bf04861cafa85c2276094ac072e1..a8705cd11d9046ce07474bc31a2b929e8d760a02 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -28,7 +28,7 @@ impl FromStr for Stanza { } else if s == "message" { Ok(Stanza::Message) } else { - Err(Error::ParseError("Unknown 'stanza' attribute.")) + Err(Error::ParseError("Invalid 'stanza' attribute.")) } } } @@ -50,37 +50,35 @@ pub enum IBB { }, } -fn optional_attr(root: &Element, attr: &str) -> Option { +fn required_attr(root: &Element, attr: &str, err: Error) -> Result { root.attr(attr) .and_then(|value| value.parse().ok()) -} - -fn required_attr(root: &Element, attr: &str, err: Error) -> Result { - optional_attr(root, attr).ok_or(err) + .ok_or(err) } pub fn parse_ibb(root: &Element) -> Result { if root.is("open", ns::IBB) { - let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; - let stanza = root.attr("stanza") - .and_then(|value| value.parse().ok()) - .unwrap_or_default(); for _ in root.children() { return Err(Error::ParseError("Unknown child in open element.")); } + let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; + let stanza = match root.attr("stanza") { + Some(stanza) => stanza.parse()?, + None => Default::default(), + }; Ok(IBB::Open { block_size: block_size, sid: sid, stanza: stanza }) } else if root.is("data", ns::IBB) { - let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; - let data = base64::decode(&root.text())?; for _ in root.children() { return Err(Error::ParseError("Unknown child in data element.")); } + let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + let data = base64::decode(&root.text())?; Ok(IBB::Data { seq: seq, sid: sid, @@ -168,7 +166,6 @@ mod tests { } #[test] - #[ignore] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); let error = ibb::parse_ibb(&elem).unwrap_err(); @@ -176,6 +173,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Wrong value for 'stanza' attribute in open."); + assert_eq!(message, "Invalid 'stanza' attribute."); } } From ff5be32a0e5f6675e8abdbf481d140f7c59cf929 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 19:15:48 +0100 Subject: [PATCH 0069/1020] Add a JingleIBB parser. --- src/jingle_ibb.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ 3 files changed, 107 insertions(+) create mode 100644 src/jingle_ibb.rs diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs new file mode 100644 index 0000000000000000000000000000000000000000..4ab73e684bdeee9243e501f697e3fbd4b7d90348 --- /dev/null +++ b/src/jingle_ibb.rs @@ -0,0 +1,101 @@ +use std::str::FromStr; + +use minidom::Element; + +use error::Error; + +use ns; + +use ibb::Stanza; + +#[derive(Debug, Clone)] +pub struct Transport { + block_size: u16, + sid: String, + stanza: Stanza, +} + +fn optional_attr(root: &Element, attr: &str) -> Option { + root.attr(attr) + .and_then(|value| value.parse().ok()) +} + +fn required_attr(root: &Element, attr: &str, err: Error) -> Result { + optional_attr(root, attr).ok_or(err) +} + +pub fn parse_jingle_ibb(root: &Element) -> Result { + if root.is("transport", ns::JINGLE_IBB) { + for _ in root.children() { + return Err(Error::ParseError("Unknown child in JingleIBB element.")); + } + let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in JingleIBB element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in JingleIBB element."))?; + let stanza = root.attr("stanza") + .unwrap_or("iq") + .parse()?; + Ok(Transport { + block_size: block_size, + sid: sid, + stanza: stanza + }) + } else { + Err(Error::ParseError("This is not an JingleIBB element.")) + } +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use ibb; + use jingle_ibb; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let transport = jingle_ibb::parse_jingle_ibb(&elem).unwrap(); + assert_eq!(transport.block_size, 3); + assert_eq!(transport.sid, "coucou"); + assert_eq!(transport.stanza, ibb::Stanza::Iq); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element."); + + // TODO: maybe make a better error message here. + let elem: Element = "".parse().unwrap(); + let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element."); + + let elem: Element = "".parse().unwrap(); + let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'sid' missing in JingleIBB element."); + } + + #[test] + fn test_invalid_stanza() { + let elem: Element = "".parse().unwrap(); + let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Invalid 'stanza' attribute."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 614737dd937c195c8f03ff9b3aedb40a384402b0..b311682b8c77afba18c7f4602ce1e7ecc30b3aa2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,9 @@ pub mod attention; /// XEP-0234: Jingle File Transfer pub mod jingle_ft; +/// XEP-0261: Jingle In-Band Bytestreams Transport Method +pub mod jingle_ibb; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub mod hashes; diff --git a/src/ns.rs b/src/ns.rs index 5dfb3f5d18e77eddbf9dc99e824aedd35d6b1ff5..de18053a4a57268a8e81693b6a534ecc68c53204 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -36,6 +36,9 @@ pub const JINGLE_FT: &'static str = "urn:xmpp:jingle:apps:file-transfer:5"; /// XEP-0234: Jingle File Transfer pub const JINGLE_FT_ERROR: &'static str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; +/// XEP-0261: Jingle In-Band Bytestreams Transport Method +pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub const HASHES: &'static str = "urn:xmpp:hashes:2"; From 24d563ff18a7be28ba3cecdd317dfba9c6fa9d7f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 19:30:17 +0100 Subject: [PATCH 0070/1020] jingle: Carry the minidom Element for description, transport and security. --- src/jingle.rs | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 80ad7b0617bcd4eca807d6c9e4ed10f1046ff993..fbda090cead4a18c6eea13a0c7439dd9c8c0ef4f 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -121,9 +121,9 @@ pub struct Content { pub disposition: String, pub name: String, pub senders: Senders, - pub description: String, - pub transport: String, - pub security: Option, + pub description: (String, Element), + pub transport: (String, Element), + pub security: Option<(String, Element)>, } #[derive(Debug, Clone, PartialEq)] @@ -246,17 +246,38 @@ pub fn parse_jingle(root: &Element) -> Result { if description.is_some() { return Err(Error::ParseError("Content must not have more than one description.")); } - description = Some(stuff.ns().ok_or(Error::ParseError("Description without a namespace."))?); + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on description element."))?; + description = Some(( + namespace, + stuff.clone(), + )); } else if stuff.name() == "transport" { if transport.is_some() { return Err(Error::ParseError("Content must not have more than one transport.")); } - transport = Some(stuff.ns().ok_or(Error::ParseError("Transport without a namespace."))?); + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on transport element."))?; + transport = Some(( + namespace, + stuff.clone(), + )); } else if stuff.name() == "security" { if security.is_some() { return Err(Error::ParseError("Content must not have more than one security.")); } - security = stuff.ns().and_then(|ns| ns.parse().ok()); + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on security element."))?; + security = Some(( + namespace, + stuff.clone(), + )); } } if description.is_none() { @@ -274,7 +295,7 @@ pub fn parse_jingle(root: &Element) -> Result { senders: senders, description: description, transport: transport, - security: security.to_owned(), + security: security, }); } else if child.is("reason", ns::JINGLE) { if reason_element.is_some() { From 6d6ac8a380043f3361fc2879da7eba6b813d2c16 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Apr 2017 20:49:17 +0100 Subject: [PATCH 0071/1020] body: Simplify the type of Body to an alias of String. --- src/body.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/body.rs b/src/body.rs index 45406e6c7f76b851270293420c93f8a951be578f..dcc4389d748cfd7db182fac77ea8ab2ac03b8b0c 100644 --- a/src/body.rs +++ b/src/body.rs @@ -4,10 +4,7 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct Body { - pub body: String, -} +pub type Body = String; pub fn parse_body(root: &Element) -> Result { // TODO: also support components and servers. @@ -17,7 +14,7 @@ pub fn parse_body(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in body element.")); } - Ok(Body { body: root.text() }) + Ok(root.text()) } #[cfg(test)] From ca6e65ad761746813b3087c7b7d8613a9c1d94ea Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 02:24:13 +0100 Subject: [PATCH 0072/1020] body: Add a serialise function. --- src/body.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/body.rs b/src/body.rs index dcc4389d748cfd7db182fac77ea8ab2ac03b8b0c..f170cd389b3d8717a3f2d0e01713af284d9e7927 100644 --- a/src/body.rs +++ b/src/body.rs @@ -17,6 +17,13 @@ pub fn parse_body(root: &Element) -> Result { Ok(root.text()) } +pub fn serialise(body: &Body) -> Element { + Element::builder("body") + .ns(ns::JABBER_CLIENT) + .append(body.to_owned()) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; From 54f404025125e18e8c008feae0a8648d108d6c23 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:20:52 +0100 Subject: [PATCH 0073/1020] attention: Simplify the payload type, and add a serialise function. --- src/attention.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 2438dc6bce92c33cf271a53bf964ed13b1973180..f64dbf5bddcda2f09edb94747509103a0e6778f0 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -5,9 +5,7 @@ use error::Error; use ns; #[derive(Debug, Clone)] -pub enum Attention { - Attention, -} +pub struct Attention; pub fn parse_attention(root: &Element) -> Result { if !root.is("attention", ns::ATTENTION) { @@ -16,7 +14,13 @@ pub fn parse_attention(root: &Element) -> Result { for _ in root.children() { return Err(Error::ParseError("Unknown child in attention element.")); } - Ok(Attention::Attention) + Ok(Attention) +} + +pub fn serialise(_: &Attention) -> Element { + Element::builder("attention") + .ns(ns::ATTENTION) + .build() } #[cfg(test)] @@ -41,4 +45,12 @@ mod tests { }; assert_eq!(message, "Unknown child in attention element."); } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let attention = attention::Attention; + let elem2 = attention::serialise(&attention); + assert_eq!(elem, elem2); + } } From ab841dc37563ec4ec7f386dc26dd074feb03984f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:21:21 +0100 Subject: [PATCH 0074/1020] receipts: Add a serialise function. --- src/receipts.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/receipts.rs b/src/receipts.rs index 052982ca8ecf8e4b5deaa1f238cb7242e078c6bd..8eb07ff991a5d6f1fefa1c08e5d3ca38fb77691e 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -24,11 +24,24 @@ pub fn parse_receipt(root: &Element) -> Result { } } +pub fn serialise(receipt: &Receipt) -> Element { + match *receipt { + Receipt::Request => Element::builder("request") + .ns(ns::RECEIPTS) + .build(), + Receipt::Received(ref id) => Element::builder("received") + .ns(ns::RECEIPTS) + .attr("id", id.clone()) + .build(), + } +} + #[cfg(test)] mod tests { use minidom::Element; //use error::Error; use receipts; + use ns; #[test] fn test_simple() { @@ -41,4 +54,16 @@ mod tests { let elem: Element = "".parse().unwrap(); receipts::parse_receipt(&elem).unwrap(); } + + #[test] + fn test_serialise() { + let receipt = receipts::Receipt::Request; + let elem = receipts::serialise(&receipt); + assert!(elem.is("request", ns::RECEIPTS)); + + let receipt = receipts::Receipt::Received("coucou".to_owned()); + let elem = receipts::serialise(&receipt); + assert!(elem.is("received", ns::RECEIPTS)); + assert_eq!(elem.attr("id"), Some("coucou")); + } } From c6036194b1f2fbcc24bc7212533d18adb0b064ab Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:21:53 +0100 Subject: [PATCH 0075/1020] delay: Add a serialise function. --- src/delay.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/delay.rs b/src/delay.rs index b54dd04796399dd0ed208dbdbb906e702d5942d5..54fa7e87be4229ab8e7a6bc079de387d9618028f 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -31,6 +31,15 @@ pub fn parse_delay(root: &Element) -> Result { }) } +pub fn serialise(delay: &Delay) -> Element { + Element::builder("delay") + .ns(ns::DELAY) + .attr("from", delay.from.clone()) + .attr("stamp", delay.stamp.clone()) + .append(delay.data.clone()) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; @@ -67,4 +76,28 @@ mod tests { }; assert_eq!(message, "Unknown child in delay element."); } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let delay = delay::Delay { + from: None, + stamp: "2002-09-10T23:08:25Z".to_owned(), + data: None, + }; + let elem2 = delay::serialise(&delay); + assert_eq!(elem, elem2); + } + + #[test] + fn test_serialise_data() { + let elem: Element = "Reason".parse().unwrap(); + let delay = delay::Delay { + from: Some(String::from("juliet@example.org")), + stamp: "2002-09-10T23:08:25Z".to_owned(), + data: Some(String::from("Reason")), + }; + let elem2 = delay::serialise(&delay); + assert_eq!(elem, elem2); + } } From cebccb363fad035204e82a2b5e9b7f08c1fe1bda Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:22:02 +0100 Subject: [PATCH 0076/1020] eme: Add a serialise function. --- src/eme.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/eme.rs b/src/eme.rs index cf33de7fbbee83b7be32680b1f573494e5b1f8a8..79d8c89b58c60b90f198053ced988e9ea3aaaf24 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -25,6 +25,14 @@ pub fn parse_explicit_message_encryption(root: &Element) -> Result Element { + Element::builder("encryption") + .ns(ns::EME) + .attr("namespace", eme.namespace.clone()) + .attr("name", eme.name.clone()) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; @@ -65,4 +73,12 @@ mod tests { }; assert_eq!(message, "Unknown child in encryption element."); } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let eme = eme::ExplicitMessageEncryption { namespace: String::from("coucou"), name: None }; + let elem2 = eme::serialise(&eme); + assert_eq!(elem, elem2); + } } From 7002578bc0a651e29eea5bd07a6472e8507f0149 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:22:25 +0100 Subject: [PATCH 0077/1020] body: Test the serialise function. --- src/body.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/body.rs b/src/body.rs index f170cd389b3d8717a3f2d0e01713af284d9e7927..7703d655c48a444a753f254c6447b083911128a4 100644 --- a/src/body.rs +++ b/src/body.rs @@ -29,6 +29,7 @@ mod tests { use minidom::Element; use error::Error; use body; + use ns; #[test] fn test_simple() { @@ -69,4 +70,11 @@ mod tests { }; assert_eq!(message, "Unknown attribute in body element."); } + + #[test] + fn test_serialise() { + let body = body::Body::from("Hello world!"); + let elem = body::serialise(&body); + assert!(elem.is("body", ns::JABBER_CLIENT)); + } } From 4128c4b9edec6483189dd8d73729b4bb9a39f6f9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:22:42 +0100 Subject: [PATCH 0078/1020] chatstates: Add a serialise function. --- src/chatstates.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/chatstates.rs b/src/chatstates.rs index 8732d5653dc1aa5648bdb44663f52511cb58ac9c..7632086e40f2d052af84ef2159a73de1485b1eb0 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -32,11 +32,23 @@ pub fn parse_chatstate(root: &Element) -> Result { } } +pub fn serialise(chatstate: &ChatState) -> Element { + Element::builder(match *chatstate { + ChatState::Active => "active", + ChatState::Composing => "composing", + ChatState::Gone => "gone", + ChatState::Inactive => "inactive", + ChatState::Paused => "paused", + }).ns(ns::CHATSTATES) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; use error::Error; use chatstates; + use ns; #[test] fn test_simple() { @@ -77,4 +89,11 @@ mod tests { }; assert_eq!(message, "Unknown attribute in chatstate element."); } + + #[test] + fn test_serialise() { + let chatstate = chatstates::ChatState::Active; + let elem = chatstates::serialise(&chatstate); + assert!(elem.is("active", ns::CHATSTATES)); + } } From 7e964b167a5af6e496de927aceba0aacccc04cfc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:23:13 +0100 Subject: [PATCH 0079/1020] message_correct: Add a serialise function, and simplify the representation. --- src/lib.rs | 4 ++-- src/message_correct.rs | 45 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b311682b8c77afba18c7f4602ce1e7ecc30b3aa2..8efa52a30d0b50b21d5c7f08b10763a0a2c224e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,7 +75,7 @@ pub enum MessagePayload { Receipt(receipts::Receipt), Delay(delay::Delay), Attention(attention::Attention), - MessageCorrect(message_correct::MessageCorrect), + MessageCorrect(message_correct::Replace), ExplicitMessageEncryption(eme::ExplicitMessageEncryption), } @@ -92,7 +92,7 @@ pub fn parse_message_payload(elem: &Element) -> Option { Some(MessagePayload::Delay(delay)) } else if let Ok(attention) = attention::parse_attention(elem) { Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = message_correct::parse_message_correct(elem) { + } else if let Ok(replace) = message_correct::parse_replace(elem) { Some(MessagePayload::MessageCorrect(replace)) } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { Some(MessagePayload::ExplicitMessageEncryption(eme)) diff --git a/src/message_correct.rs b/src/message_correct.rs index 789febb96d3ae634834e0804c4201358753ea2f7..09596ec078b7b0d4f7b4542cd4192056ca8a71f3 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -5,19 +5,29 @@ use error::Error; use ns; #[derive(Debug, Clone)] -pub enum MessageCorrect { - Replace(String), +pub struct Replace { + pub id: String, } -pub fn parse_message_correct(root: &Element) -> Result { +pub fn parse_replace(root: &Element) -> Result { if !root.is("replace", ns::MESSAGE_CORRECT) { return Err(Error::ParseError("This is not a replace element.")); } for _ in root.children() { return Err(Error::ParseError("Unknown child in replace element.")); } - let id = root.attr("id").unwrap_or("").to_owned(); - Ok(MessageCorrect::Replace(id)) + let id = match root.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in replace.")), + }; + Ok(Replace { id: id }) +} + +pub fn serialise(replace: &Replace) -> Element { + Element::builder("replace") + .ns(ns::MESSAGE_CORRECT) + .attr("id", replace.id.clone()) + .build() } #[cfg(test)] @@ -28,18 +38,37 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); - message_correct::parse_message_correct(&elem).unwrap(); + let elem: Element = "".parse().unwrap(); + message_correct::parse_replace(&elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = message_correct::parse_message_correct(&elem).unwrap_err(); + let error = message_correct::parse_replace(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in replace element."); } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = message_correct::parse_replace(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No 'id' attribute present in replace."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let replace = message_correct::Replace { id: String::from("coucou") }; + let elem2 = message_correct::serialise(&replace); + assert_eq!(elem, elem2); + } } From 90db24eed88342f87b66e3918de291c0858ac422 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:41:26 +0100 Subject: [PATCH 0080/1020] jingle: Simplify the FromStr using match. --- src/jingle.rs | 142 +++++++++++++++++++------------------------------- 1 file changed, 54 insertions(+), 88 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index fbda090cead4a18c6eea13a0c7439dd9c8c0ef4f..9cea6cf821d07072fca39916f5fcb95bf0941ba3 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -30,39 +30,25 @@ impl FromStr for Action { type Err = Error; fn from_str(s: &str) -> Result { - if s == "content-accept" { - Ok(Action::ContentAccept) - } else if s == "content-add" { - Ok(Action::ContentAdd) - } else if s == "content-modify" { - Ok(Action::ContentModify) - } else if s == "content-reject" { - Ok(Action::ContentReject) - } else if s == "content-remove" { - Ok(Action::ContentRemove) - } else if s == "description-info" { - Ok(Action::DescriptionInfo) - } else if s == "security-info" { - Ok(Action::SecurityInfo) - } else if s == "session-accept" { - Ok(Action::SessionAccept) - } else if s == "session-info" { - Ok(Action::SessionInfo) - } else if s == "session-initiate" { - Ok(Action::SessionInitiate) - } else if s == "session-terminate" { - Ok(Action::SessionTerminate) - } else if s == "transport-accept" { - Ok(Action::TransportAccept) - } else if s == "transport-info" { - Ok(Action::TransportInfo) - } else if s == "transport-reject" { - Ok(Action::TransportReject) - } else if s == "transport-replace" { - Ok(Action::TransportReplace) - } else { - Err(Error::ParseError("Unknown action.")) - } + Ok(match s { + "content-accept" => Action::ContentAccept, + "content-add" => Action::ContentAdd, + "content-modify" => Action::ContentModify, + "content-reject" => Action::ContentReject, + "content-remove" => Action::ContentRemove, + "description-info" => Action::DescriptionInfo, + "security-info" => Action::SecurityInfo, + "session-accept" => Action::SessionAccept, + "session-info" => Action::SessionInfo, + "session-initiate" => Action::SessionInitiate, + "session-terminate" => Action::SessionTerminate, + "transport-accept" => Action::TransportAccept, + "transport-info" => Action::TransportInfo, + "transport-reject" => Action::TransportReject, + "transport-replace" => Action::TransportReplace, + + _ => return Err(Error::ParseError("Unknown action.")), + }) } } @@ -79,13 +65,12 @@ impl FromStr for Creator { type Err = Error; fn from_str(s: &str) -> Result { - if s == "initiator" { - Ok(Creator::Initiator) - } else if s == "responder" { - Ok(Creator::Responder) - } else { - Err(Error::ParseError("Unknown creator.")) - } + Ok(match s { + "initiator" => Creator::Initiator, + "responder" => Creator::Responder, + + _ => return Err(Error::ParseError("Unknown creator.")), + }) } } @@ -101,17 +86,14 @@ impl FromStr for Senders { type Err = Error; fn from_str(s: &str) -> Result { - if s == "both" { - Ok(Senders::Both) - } else if s == "initiator" { - Ok(Senders::Initiator) - } else if s == "none" { - Ok(Senders::None_) - } else if s == "responder" { - Ok(Senders::Responder) - } else { - Err(Error::ParseError("Unknown senders.")) - } + Ok(match s { + "both" => Senders::Both, + "initiator" => Senders::Initiator, + "none" => Senders::None_, + "responder" => Senders::Responder, + + _ => return Err(Error::ParseError("Unknown senders.")), + }) } } @@ -151,43 +133,27 @@ impl FromStr for Reason { type Err = Error; fn from_str(s: &str) -> Result { - if s == "alternative-session" { - Ok(Reason::AlternativeSession) - } else if s == "busy" { - Ok(Reason::Busy) - } else if s == "cancel" { - Ok(Reason::Cancel) - } else if s == "connectivity-error" { - Ok(Reason::ConnectivityError) - } else if s == "decline" { - Ok(Reason::Decline) - } else if s == "expired" { - Ok(Reason::Expired) - } else if s == "failed-application" { - Ok(Reason::FailedApplication) - } else if s == "failed-transport" { - Ok(Reason::FailedTransport) - } else if s == "general-error" { - Ok(Reason::GeneralError) - } else if s == "gone" { - Ok(Reason::Gone) - } else if s == "incompatible-parameters" { - Ok(Reason::IncompatibleParameters) - } else if s == "media-error" { - Ok(Reason::MediaError) - } else if s == "security-error" { - Ok(Reason::SecurityError) - } else if s == "success" { - Ok(Reason::Success) - } else if s == "timeout" { - Ok(Reason::Timeout) - } else if s == "unsupported-applications" { - Ok(Reason::UnsupportedApplications) - } else if s == "unsupported-transports" { - Ok(Reason::UnsupportedTransports) - } else { - Err(Error::ParseError("Unknown reason.")) - } + Ok(match s { + "alternative-session" => Reason::AlternativeSession, + "busy" => Reason::Busy, + "cancel" => Reason::Cancel, + "connectivity-error" => Reason::ConnectivityError, + "decline" => Reason::Decline, + "expired" => Reason::Expired, + "failed-application" => Reason::FailedApplication, + "failed-transport" => Reason::FailedTransport, + "general-error" => Reason::GeneralError, + "gone" => Reason::Gone, + "incompatible-parameters" => Reason::IncompatibleParameters, + "media-error" => Reason::MediaError, + "security-error" => Reason::SecurityError, + "success" => Reason::Success, + "timeout" => Reason::Timeout, + "unsupported-applications" => Reason::UnsupportedApplications, + "unsupported-transports" => Reason::UnsupportedTransports, + + _ => return Err(Error::ParseError("Unknown reason.")), + }) } } From 659eaee14ecf688814cf8bff4294dc28bb85ac2a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:42:50 +0100 Subject: [PATCH 0081/1020] data_forms: Simplify the FromStr using match. --- src/data_forms.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index dd9b3e11be87072e6e8049a83c283e614f1e94de..f25c88188d1ab3d46456172a53301ada6f02712d 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -30,17 +30,14 @@ impl FromStr for DataFormType { type Err = Error; fn from_str(s: &str) -> Result { - if s == "cancel" { - Ok(DataFormType::Cancel) - } else if s == "form" { - Ok(DataFormType::Form) - } else if s == "result" { - Ok(DataFormType::Result_) - } else if s == "submit" { - Ok(DataFormType::Submit) - } else { - Err(Error::ParseError("Unknown data form type.")) - } + Ok(match s { + "cancel" => DataFormType::Cancel, + "form" => DataFormType::Form, + "result" => DataFormType::Result_, + "submit" => DataFormType::Submit, + + _ => return Err(Error::ParseError("Unknown data form type.")), + }) } } From 16a6ebd75148310121e54434453e48ab9c463cdc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:44:58 +0100 Subject: [PATCH 0082/1020] ibb: Simplify the FromStr using match. --- src/ibb.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index a8705cd11d9046ce07474bc31a2b929e8d760a02..507e3aa4392eecce48043473b7b20d1bd97c73e9 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -23,13 +23,12 @@ impl FromStr for Stanza { type Err = Error; fn from_str(s: &str) -> Result { - if s == "iq" { - Ok(Stanza::Iq) - } else if s == "message" { - Ok(Stanza::Message) - } else { - Err(Error::ParseError("Invalid 'stanza' attribute.")) - } + Ok(match s { + "iq" => Stanza::Iq, + "message" => Stanza::Message, + + _ => return Err(Error::ParseError("Invalid 'stanza' attribute.")), + }) } } From 15634b6500f33e47eddfe46a09f9c3537f4cc4b0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 03:45:14 +0100 Subject: [PATCH 0083/1020] ping: Remove extraneous brackets. --- src/ping.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ping.rs b/src/ping.rs index 2fb3e5cc693bbac4a03855cebb3e4d45f91b5401..a8c94c500f5fe5526c1dddde55fe3111e44a7af0 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,8 +5,7 @@ use error::Error; use ns; #[derive(Debug, Clone)] -pub struct Ping { -} +pub struct Ping; pub fn parse_ping(root: &Element) -> Result { if !root.is("ping", ns::PING) { From 0d2fda806475b5a02fcda016078350635b9fd70f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 14:49:00 +0100 Subject: [PATCH 0084/1020] implement From on String --- src/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 74f1d3cc1a768930cd956e14f451a1883c1bf923..fead7e730ad0b595c7ad28dad9c48fae5d1c7f77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,22 @@ pub struct Jid { pub resource: Option, } +impl From for String { + fn from(jid: Jid) -> String { + let mut string = String::new(); + if let Some(ref node) = jid.node { + string.push_str(node); + string.push('@'); + } + string.push_str(&jid.domain); + if let Some(ref resource) = jid.resource { + string.push('/'); + string.push_str(resource); + } + string + } +} + impl fmt::Display for Jid { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { // TODO: may need escaping @@ -313,4 +329,9 @@ mod tests { assert_eq!(Jid::from_str("a/b@c"), Ok(Jid::domain_with_resource("a", "b@c"))); } + + #[test] + fn serialise() { + assert_eq!(String::from(Jid::full("a", "b", "c")), String::from("a@b/c")); + } } From 5e7ad720c32630c2b42dbf8a9afaf31e8a35fe83 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 15:13:03 +0100 Subject: [PATCH 0085/1020] Add a message parser, along with a dependency on jid. --- Cargo.toml | 1 + src/lib.rs | 43 ++------- src/message.rs | 234 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 241 insertions(+), 37 deletions(-) create mode 100644 src/message.rs diff --git a/Cargo.toml b/Cargo.toml index 7102bc64eb6d1972ee60a46a6e4b0947ac81ae56..f513c3b4da9e4b15d7430d0fe46040505bfd7b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Emmanuel Gil Peyrot "] [dependencies] minidom = "0.1.1" +jid = "0.1.0" base64 = "0.4.1" sha2 = "0.5.0" sha3 = "0.5.0" diff --git a/src/lib.rs b/src/lib.rs index 8efa52a30d0b50b21d5c7f08b10763a0a2c224e8..bb9dae7ad0a53b277148a6780ec8e75360f72b96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,14 @@ //! A crate parsing common XMPP elements into Rust structures. //! -//! The main entrypoint is `parse_message_payload`, it takes a minidom -//! `Element` reference and optionally returns `Some(MessagePayload)` if the -//! parsing succeeded. +//! Each module implements a `parse` function, which takes a minidom +//! `Element` reference and returns `Some(MessagePayload)` if the parsing +//! succeeded, None otherwise. //! //! Parsed structs can then be manipulated internally, and serialised back //! before being sent over the wire. extern crate minidom; +extern crate jid; extern crate base64; use minidom::Element; @@ -16,6 +17,8 @@ pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod body; @@ -66,37 +69,3 @@ pub mod eme; /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; - -/// Lists every known payload of a ``. -#[derive(Debug)] -pub enum MessagePayload { - Body(body::Body), - ChatState(chatstates::ChatState), - Receipt(receipts::Receipt), - Delay(delay::Delay), - Attention(attention::Attention), - MessageCorrect(message_correct::Replace), - ExplicitMessageEncryption(eme::ExplicitMessageEncryption), -} - -/// Parse one of the payloads of a `` element, and return `Some` of a -/// `MessagePayload` if parsing it succeeded, `None` otherwise. -pub fn parse_message_payload(elem: &Element) -> Option { - if let Ok(body) = body::parse_body(elem) { - Some(MessagePayload::Body(body)) - } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { - Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = receipts::parse_receipt(elem) { - Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = delay::parse_delay(elem) { - Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = attention::parse_attention(elem) { - Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = message_correct::parse_replace(elem) { - Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { - Some(MessagePayload::ExplicitMessageEncryption(eme)) - } else { - None - } -} diff --git a/src/message.rs b/src/message.rs new file mode 100644 index 0000000000000000000000000000000000000000..985b3eb601ed784bab6b1a293635f357dca3735a --- /dev/null +++ b/src/message.rs @@ -0,0 +1,234 @@ +use std::str::FromStr; + +use minidom::Element; +use minidom::IntoAttributeValue; + +use jid::Jid; + +use error::Error; + +use ns; + +use body; +use chatstates; +use receipts; +use delay; +use attention; +use message_correct; +use eme; + +/// Lists every known payload of a ``. +#[derive(Debug, Clone)] +pub enum MessagePayload { + Body(body::Body), + ChatState(chatstates::ChatState), + Receipt(receipts::Receipt), + Delay(delay::Delay), + Attention(attention::Attention), + MessageCorrect(message_correct::Replace), + ExplicitMessageEncryption(eme::ExplicitMessageEncryption), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum MessageType { + Chat, + Error, + Groupchat, + Headline, + Normal, +} + +impl Default for MessageType { + fn default() -> MessageType { + MessageType::Normal + } +} + +impl FromStr for MessageType { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "chat" => MessageType::Chat, + "error" => MessageType::Error, + "groupchat" => MessageType::Groupchat, + "headline" => MessageType::Headline, + "normal" => MessageType::Normal, + + _ => return Err(Error::ParseError("Invalid 'type' attribute on message element.")), + }) + } +} + +impl IntoAttributeValue for MessageType { + fn into_attribute_value(self) -> Option { + Some(match self { + MessageType::Chat => "chat", + MessageType::Error => "error", + MessageType::Groupchat => "groupchat", + MessageType::Headline => "headline", + MessageType::Normal => "normal", + }.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub enum MessagePayloadType { + XML(Element), + Parsed(MessagePayload), +} + +#[derive(Debug, Clone)] +pub struct Message { + pub from: Option, + pub to: Option, + pub id: Option, + pub type_: MessageType, + pub payloads: Vec, +} + +pub fn parse_message(root: &Element) -> Result { + if !root.is("message", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a message element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_.parse()?, + None => Default::default(), + }; + let mut payloads = vec!(); + for elem in root.children() { + let payload = if let Ok(body) = body::parse_body(elem) { + Some(MessagePayload::Body(body)) + } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { + Some(MessagePayload::ChatState(chatstate)) + } else if let Ok(receipt) = receipts::parse_receipt(elem) { + Some(MessagePayload::Receipt(receipt)) + } else if let Ok(delay) = delay::parse_delay(elem) { + Some(MessagePayload::Delay(delay)) + } else if let Ok(attention) = attention::parse_attention(elem) { + Some(MessagePayload::Attention(attention)) + } else if let Ok(replace) = message_correct::parse_replace(elem) { + Some(MessagePayload::MessageCorrect(replace)) + } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { + Some(MessagePayload::ExplicitMessageEncryption(eme)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => MessagePayloadType::Parsed(payload), + None => MessagePayloadType::XML(elem.clone()), + }); + } + Ok(Message { + from: from, + to: to, + id: id, + type_: type_, + payloads: payloads, + }) +} + +pub fn serialise_payload(payload: &MessagePayload) -> Element { + match *payload { + MessagePayload::Body(ref body) => body::serialise(body), + MessagePayload::Attention(ref attention) => attention::serialise(attention), + MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), + MessagePayload::Receipt(ref receipt) => receipts::serialise(receipt), + MessagePayload::Delay(ref delay) => delay::serialise(delay), + MessagePayload::MessageCorrect(ref replace) => message_correct::serialise(replace), + MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme), + } +} + +pub fn serialise(message: &Message) -> Element { + let mut stanza = Element::builder("message") + .ns(ns::JABBER_CLIENT) + .attr("from", message.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", message.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", message.id.clone()) + .attr("type", message.type_.clone()) + .build(); + for child in message.payloads.clone() { + let elem = match child { + MessagePayloadType::XML(elem) => elem, + MessagePayloadType::Parsed(payload) => serialise_payload(&payload), + }; + stanza.append_child(elem); + } + stanza +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use minidom::Element; + use error::Error; + use jid::Jid; + use message; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let message = message::parse_message(&elem).unwrap(); + assert_eq!(message.from, None); + assert_eq!(message.to, None); + assert_eq!(message.id, None); + assert_eq!(message.type_, message::MessageType::Normal); + assert!(message.payloads.is_empty()); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let message = message::parse_message(&elem).unwrap(); + let message2 = message::Message { + from: None, + to: None, + id: None, + type_: message::MessageType::Normal, + payloads: vec!(), + }; + let elem2 = message::serialise(&message2); + assert_eq!(elem, elem2); + println!("{:#?}", message); + } + + #[test] + fn test_body() { + let elem: Element = "Hello world!".parse().unwrap(); + let message = message::parse_message(&elem).unwrap(); + println!("{:#?}", message); + } + + #[test] + fn test_serialise_body() { + let elem: Element = "Hello world!".parse().unwrap(); + let message = message::parse_message(&elem).unwrap(); + let message2 = message::Message { + from: None, + to: Some(Jid::from_str("coucou@example.org").unwrap()), + id: None, + type_: message::MessageType::Chat, + payloads: vec!( + message::MessagePayloadType::Parsed(message::MessagePayload::Body("Hello world!".to_owned())), + ), + }; + let elem2 = message::serialise(&message2); + assert_eq!(elem, elem2); + println!("{:#?}", message); + } + + #[test] + fn test_attention() { + let elem: Element = "".parse().unwrap(); + let message = message::parse_message(&elem).unwrap(); + let elem2 = message::serialise(&message); + assert_eq!(elem, elem2); + } +} From c81509744f0606b1bfb667ae4a0a22ee3ab11d22 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 15:32:38 +0100 Subject: [PATCH 0086/1020] lib, message, jingle_ft: Remove unused imports. --- src/jingle_ft.rs | 1 - src/lib.rs | 1 - src/message.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 6e3e2803ba27ba6bb345adda6124b7757d1df27f..82941b3606eedddb5971e07b221711c971be200a 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -124,7 +124,6 @@ pub fn parse_jingle_ft(root: &Element) -> Result { #[cfg(test)] mod tests { use minidom::Element; - use error::Error; use jingle_ft; #[test] diff --git a/src/lib.rs b/src/lib.rs index bb9dae7ad0a53b277148a6780ec8e75360f72b96..7be94346a209b544ab8ea9aee069958f13fd98bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,6 @@ extern crate minidom; extern crate jid; extern crate base64; -use minidom::Element; /// Error type returned by every parser on failure. pub mod error; diff --git a/src/message.rs b/src/message.rs index 985b3eb601ed784bab6b1a293635f357dca3735a..609aa77afe591f9020d91016ba05eff39bfa0d75 100644 --- a/src/message.rs +++ b/src/message.rs @@ -168,7 +168,6 @@ pub fn serialise(message: &Message) -> Element { mod tests { use std::str::FromStr; use minidom::Element; - use error::Error; use jid::Jid; use message; From bbde01160ac224007dacf36964aad28d6090da14 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 23 Apr 2017 17:20:50 +0200 Subject: [PATCH 0087/1020] Add a note about not supporting RFC7622 yet. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 8610e178b008ad6f6e42bc2d60b59b5e51b44511..e4b4fd370f7172f0fc2bed9773a095d2178fef54 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,11 @@ What license is it under? LGPLv3 or later. See `COPYING` and `COPYING.LESSER`. +Notes +----- + +This library does not yet implement RFC7622. + License yadda yadda. -------------------- From 90c4aec54f3789a2c81992789db63bffbaec30c8 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 23 Apr 2017 17:23:09 +0200 Subject: [PATCH 0088/1020] Bump the version number up to 0.2.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 95026c8e5e9144985becef8109c425b46e253d14..8ac64e9c1d78967e206d4e44778be5521d4c19a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.1.0" +version = "0.2.0" authors = ["lumi "] description = "A crate which provides a Jid struct for Jabber IDs." homepage = "https://gitlab.com/lumi/jid-rs" From 5e802b457efb52a78bef37ff0bbf5fbd0db2007c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 16:30:41 +0100 Subject: [PATCH 0089/1020] Cargo.toml: Update the jid crate to 0.2.0, which implements From on String. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f513c3b4da9e4b15d7430d0fe46040505bfd7b2c..262fe7e765ff686688645e69a9f0daf1b9945a29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Emmanuel Gil Peyrot "] [dependencies] minidom = "0.1.1" -jid = "0.1.0" +jid = "0.2.0" base64 = "0.4.1" sha2 = "0.5.0" sha3 = "0.5.0" From 6a569184842e337ef0425bd1ec341c2855ace56f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 17:30:07 +0100 Subject: [PATCH 0090/1020] ns: Add the namespaces of hashes functions we implement. --- src/ns.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ns.rs b/src/ns.rs index de18053a4a57268a8e81693b6a534ecc68c53204..130f1a19abf229b4c2265101c16a8719537183ca 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -41,6 +41,18 @@ pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub const HASHES: &'static str = "urn:xmpp:hashes:2"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_SHA_256: &'static str = "urn:xmpp:hash-function-text-name:sha-256"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_SHA_512: &'static str = "urn:xmpp:hash-function-text-name:sha-512"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_SHA3_256: &'static str = "urn:xmpp:hash-function-text-name:sha3-256"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_SHA3_512: &'static str = "urn:xmpp:hash-function-text-name:sha3-512"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_BLAKE2B_256: &'static str = "urn:xmpp:hash-function-text-name:id-blake2b256"; +/// XEP-0300: Use of Cryptographic Hash Functions in XMPP +pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-name:id-blake2b512"; /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; From 78e8a06ec2a75159774535d182a90ad7b25b3889 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 17:30:23 +0100 Subject: [PATCH 0091/1020] Add a presence parser. --- src/lib.rs | 2 + src/presence.rs | 179 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 src/presence.rs diff --git a/src/lib.rs b/src/lib.rs index 7be94346a209b544ab8ea9aee069958f13fd98bf..79b7d1daeb6e603bb7b01e8c17061e78b3465980 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,8 @@ pub mod ns; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod presence; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod body; /// XEP-0004: Data Forms diff --git a/src/presence.rs b/src/presence.rs new file mode 100644 index 0000000000000000000000000000000000000000..51e5854ea40959e5426877483037893aab64f317 --- /dev/null +++ b/src/presence.rs @@ -0,0 +1,179 @@ +use std::str::FromStr; + +use minidom::Element; +use minidom::IntoAttributeValue; + +use jid::Jid; + +use error::Error; + +use ns; + +use delay; + +/// Lists every known payload of a ``. +#[derive(Debug, Clone)] +pub enum PresencePayload { + Delay(delay::Delay), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum PresenceType { + /// This value is not an acceptable 'type' attribute, it is only used + /// internally to signal the absence of 'type'. + Available, + Error, + Probe, + Subscribe, + Subscribed, + Unavailable, + Unsubscribe, + Unsubscribed, +} + +impl Default for PresenceType { + fn default() -> PresenceType { + PresenceType::Available + } +} + +impl FromStr for PresenceType { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "error" => PresenceType::Error, + "probe" => PresenceType::Probe, + "subscribe" => PresenceType::Subscribe, + "subscribed" => PresenceType::Subscribed, + "unavailable" => PresenceType::Unavailable, + "unsubscribe" => PresenceType::Unsubscribe, + "unsubscribed" => PresenceType::Unsubscribed, + + _ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")), + }) + } +} + +impl IntoAttributeValue for PresenceType { + fn into_attribute_value(self) -> Option { + Some(match self { + PresenceType::Available => return None, + + PresenceType::Error => "error", + PresenceType::Probe => "probe", + PresenceType::Subscribe => "subscribe", + PresenceType::Subscribed => "subscribed", + PresenceType::Unavailable => "unavailable", + PresenceType::Unsubscribe => "unsubscribe", + PresenceType::Unsubscribed => "unsubscribed", + }.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub enum PresencePayloadType { + XML(Element), + Parsed(PresencePayload), +} + +#[derive(Debug, Clone)] +pub struct Presence { + pub from: Option, + pub to: Option, + pub id: Option, + pub type_: PresenceType, + pub payloads: Vec, +} + +pub fn parse_presence(root: &Element) -> Result { + if !root.is("presence", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a presence element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_.parse()?, + None => Default::default(), + }; + let mut payloads = vec!(); + for elem in root.children() { + let payload = if let Ok(delay) = delay::parse_delay(elem) { + Some(PresencePayload::Delay(delay)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => PresencePayloadType::Parsed(payload), + None => PresencePayloadType::XML(elem.clone()), + }); + } + Ok(Presence { + from: from, + to: to, + id: id, + type_: type_, + payloads: payloads, + }) +} + +pub fn serialise_payload(payload: &PresencePayload) -> Element { + match *payload { + PresencePayload::Delay(ref delay) => delay::serialise(delay), + } +} + +pub fn serialise(presence: &Presence) -> Element { + let mut stanza = Element::builder("presence") + .ns(ns::JABBER_CLIENT) + .attr("from", presence.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", presence.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", presence.id.clone()) + .attr("type", presence.type_.clone()) + .build(); + for child in presence.payloads.clone() { + let elem = match child { + PresencePayloadType::XML(elem) => elem, + PresencePayloadType::Parsed(payload) => serialise_payload(&payload), + }; + stanza.append_child(elem); + } + stanza +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use presence; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.from, None); + assert_eq!(presence.to, None); + assert_eq!(presence.id, None); + assert_eq!(presence.type_, presence::PresenceType::Available); + assert!(presence.payloads.is_empty()); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + let presence2 = presence::Presence { + from: None, + to: None, + id: None, + type_: presence::PresenceType::Unavailable, + payloads: vec!(), + }; + let elem2 = presence::serialise(&presence2); + assert_eq!(elem, elem2); + println!("{:#?}", presence); + } +} From 20949c283290d8ce2ed98982a7b418761f07408d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 18:36:12 +0100 Subject: [PATCH 0092/1020] hashes: Implement serialise. --- src/hashes.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/hashes.rs b/src/hashes.rs index f9e1e846c1a4c4f8b1cc2f5edf8efdba427e38a1..febf8b3f7ec65ecf1c08750f9b704c50cfe17bd9 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -28,6 +28,14 @@ pub fn parse_hash(root: &Element) -> Result { }) } +pub fn serialise(hash: &Hash) -> Element { + Element::builder("hash") + .ns(ns::HASHES) + .attr("algo", hash.algo.clone()) + .append(hash.hash.clone()) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; From 429abb996d84ac3a1c1d6e1bd2a02a7b24bc8fef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 18:36:37 +0100 Subject: [PATCH 0093/1020] ecaps2: Implement serialise. --- src/ecaps2.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fabd2e51f6878e50bd10f9891b82750ce9a9d23b..780c62e1e62fdc4128a0d470bcc85836e0f387fe 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,6 +4,7 @@ extern crate blake2; use disco::{Feature, Identity, Disco}; use data_forms::DataForm; +use hashes; use hashes::{Hash, parse_hash}; use minidom::Element; @@ -38,6 +39,17 @@ pub fn parse_ecaps2(root: &Element) -> Result { }) } +pub fn serialise(ecaps2: &ECaps2) -> Element { + let mut c = Element::builder("c") + .ns(ns::ECAPS2) + .build(); + for hash in ecaps2.hashes.clone() { + let hash_elem = hashes::serialise(&hash); + c.append_child(hash_elem); + } + c +} + fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); From 2c95fd678643d5d7bac0d1237de636c5b159a867 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 18:38:53 +0100 Subject: [PATCH 0094/1020] presence: Add ecaps2 as a possible payload. --- src/presence.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index 51e5854ea40959e5426877483037893aab64f317..bb3334ef0dd08197f8b488806d1f2965ab526e0a 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -10,11 +10,13 @@ use error::Error; use ns; use delay; +use ecaps2; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { Delay(delay::Delay), + ECaps2(ecaps2::ECaps2), } #[derive(Debug, Clone, PartialEq)] @@ -104,6 +106,8 @@ pub fn parse_presence(root: &Element) -> Result { for elem in root.children() { let payload = if let Ok(delay) = delay::parse_delay(elem) { Some(PresencePayload::Delay(delay)) + } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { + Some(PresencePayload::ECaps2(ecaps2)) } else { None }; @@ -124,6 +128,7 @@ pub fn parse_presence(root: &Element) -> Result { pub fn serialise_payload(payload: &PresencePayload) -> Element { match *payload { PresencePayload::Delay(ref delay) => delay::serialise(delay), + PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } } From 948b54d07363cf35b8a62519f4b8c8a1d81d2aa3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 19:28:03 +0100 Subject: [PATCH 0095/1020] ecaps2: Implement blake2b. --- Cargo.toml | 1 + src/ecaps2.rs | 25 +++++++++++++++++++------ src/lib.rs | 1 + 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 262fe7e765ff686688645e69a9f0daf1b9945a29..b46e00d702daffb61db3b4dcf9e906de10f17316 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ authors = ["Emmanuel Gil Peyrot "] minidom = "0.1.1" jid = "0.2.0" base64 = "0.4.1" +digest = "0.5.0" sha2 = "0.5.0" sha3 = "0.5.0" blake2 = "0.5.0" diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 780c62e1e62fdc4128a0d470bcc85836e0f387fe..d9e9a7dedd23d84052dd621ee5119a65ae3ecfd9 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -14,6 +14,7 @@ use ns; use self::sha2::{Sha256, Sha512, Digest}; use self::sha3::{Sha3_256, Sha3_512}; use self::blake2::Blake2b; +use digest::VariableOutput; use base64; #[derive(Debug, Clone)] @@ -137,22 +138,20 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { let hash = hasher.result(); base64::encode(&hash) }, - /* "blake2b-256" => { - // TODO: bit length is most likely wrong here! let mut hasher = Blake2b::default(); hasher.input(data); - let hash = hasher.result(); + let mut buf: [u8; 32] = [0; 32]; + let hash = hasher.variable_result(&mut buf).unwrap(); base64::encode(&hash) }, "blake2b-512" => { - // TODO: bit length is most likely wrong here! let mut hasher = Blake2b::default(); hasher.input(data); - let hash = hasher.result(); + let mut buf: [u8; 64] = [0; 64]; + let hash = hasher.variable_result(&mut buf).unwrap(); base64::encode(&hash) }, - */ _ => panic!(), } } @@ -163,6 +162,7 @@ mod tests { use error::Error; use disco; use ecaps2; + use base64; #[test] fn test_parse() { @@ -434,4 +434,17 @@ mod tests { let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); } + + #[test] + fn test_blake2b_512() { + let hash = ecaps2::hash_ecaps2("abc".as_bytes(), "blake2b-512"); + let known_hash: [u8; 64] = [ + 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, + 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, + 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, + 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, + ]; + let known_hash = base64::encode(&known_hash); + assert_eq!(hash, known_hash); + } } diff --git a/src/lib.rs b/src/lib.rs index 79b7d1daeb6e603bb7b01e8c17061e78b3465980..ba1f6501ba9304442b96c5cb0d95e714ad2d75b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ extern crate minidom; extern crate jid; extern crate base64; +extern crate digest; /// Error type returned by every parser on failure. pub mod error; From 93343f3a000ef30b0ac568146f04c906499967ed Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 19:28:25 +0100 Subject: [PATCH 0096/1020] Add a status parser. --- src/lib.rs | 5 +++- src/status.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/status.rs diff --git a/src/lib.rs b/src/lib.rs index ba1f6501ba9304442b96c5cb0d95e714ad2d75b7..2536aaa4b7eade372db77ed8ba5b048c253376f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,8 +21,11 @@ pub mod ns; pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod presence; -/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core + +/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod body; +/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence +pub mod status; /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/status.rs b/src/status.rs new file mode 100644 index 0000000000000000000000000000000000000000..6412ffc05a558ce53782866f6345c7a8a1e7afd8 --- /dev/null +++ b/src/status.rs @@ -0,0 +1,80 @@ +use minidom::Element; + +use error::Error; + +use ns; + +pub type Status = String; + +pub fn parse_status(root: &Element) -> Result { + // TODO: also support components and servers. + if !root.is("status", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a status element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in status element.")); + } + Ok(root.text()) +} + +pub fn serialise(status: &Status) -> Element { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(status.to_owned()) + .build() +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use status; + use ns; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + status::parse_status(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = status::parse_status(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not a status element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = status::parse_status(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in status element."); + } + + #[test] + #[ignore] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = status::parse_status(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in status element."); + } + + #[test] + fn test_serialise() { + let status = status::Status::from("Hello world!"); + let elem = status::serialise(&status); + assert!(elem.is("status", ns::JABBER_CLIENT)); + } +} From 944cb3964e88d07a140f65f360635ec39adb6494 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 19:32:48 +0100 Subject: [PATCH 0097/1020] presence: Add status as a possible payload. --- src/presence.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/presence.rs b/src/presence.rs index bb3334ef0dd08197f8b488806d1f2965ab526e0a..ea2da4a3fcdb1c0932febdc5b2ebafa628001ae2 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -9,12 +9,14 @@ use error::Error; use ns; +use status; use delay; use ecaps2; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { + Status(status::Status), Delay(delay::Delay), ECaps2(ecaps2::ECaps2), } @@ -104,7 +106,9 @@ pub fn parse_presence(root: &Element) -> Result { }; let mut payloads = vec!(); for elem in root.children() { - let payload = if let Ok(delay) = delay::parse_delay(elem) { + let payload = if let Ok(status) = status::parse_status(elem) { + Some(PresencePayload::Status(status)) + } else if let Ok(delay) = delay::parse_delay(elem) { Some(PresencePayload::Delay(delay)) } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { Some(PresencePayload::ECaps2(ecaps2)) @@ -127,6 +131,7 @@ pub fn parse_presence(root: &Element) -> Result { pub fn serialise_payload(payload: &PresencePayload) -> Element { match *payload { + PresencePayload::Status(ref status) => status::serialise(status), PresencePayload::Delay(ref delay) => delay::serialise(delay), PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } From 04af2f3d264d13b729dd34c0632debae79c75972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 23 Apr 2017 20:38:13 +0100 Subject: [PATCH 0098/1020] Implement Iq stanzas parsing --- src/iq.rs | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 2 files changed, 259 insertions(+) create mode 100644 src/iq.rs diff --git a/src/iq.rs b/src/iq.rs new file mode 100644 index 0000000000000000000000000000000000000000..afdb463f91062388701caed13350226e5fa6252e --- /dev/null +++ b/src/iq.rs @@ -0,0 +1,257 @@ +use minidom::Element; +use minidom::IntoAttributeValue; + +use jid::Jid; + +use error::Error; + +use ns; + +/// Lists every known payload of a ``. +#[derive(Debug, Clone)] +pub enum IqPayload { +} + +#[derive(Debug, Clone)] +pub enum IqPayloadType { + XML(Element), + Parsed(IqPayload), +} + +#[derive(Debug, Clone)] +pub enum IqType { + Get(IqPayloadType), + Set(IqPayloadType), + Result(Option), + Error(IqPayloadType), +} + +impl IntoAttributeValue for IqType { + fn into_attribute_value(self) -> Option { + Some(match self { + IqType::Get(_) => "get", + IqType::Set(_) => "set", + IqType::Result(_) => "result", + IqType::Error(_) => "error", + }.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub struct Iq { + pub from: Option, + pub to: Option, + pub id: Option, + pub payload: IqType, +} + +pub fn parse_iq(root: &Element) -> Result { + if !root.is("iq", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not an iq element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_, + None => return Err(Error::ParseError("Iq element requires a 'type' attribute.")), + }; + + let mut payload = None; + for elem in root.children() { + if payload.is_some() { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + if type_ == "error" { + if elem.is("error", ns::JABBER_CLIENT) { + payload = Some(elem); + } else if root.children().collect::>().len() != 2 { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else { + payload = Some(elem); + } + } + + let type_ = if type_ == "get" { + if let Some(payload) = payload.clone() { + IqType::Get(IqPayloadType::XML(payload.clone())) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else if type_ == "set" { + if let Some(payload) = payload.clone() { + IqType::Set(IqPayloadType::XML(payload.clone())) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else if type_ == "result" { + if let Some(payload) = payload.clone() { + IqType::Result(Some(IqPayloadType::XML(payload.clone()))) + } else { + IqType::Result(None) + } + } else if type_ == "error" { + if let Some(payload) = payload.clone() { + IqType::Error(IqPayloadType::XML(payload.clone())) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else { + panic!() + }; + + Ok(Iq { + from: from, + to: to, + id: id, + payload: type_, + }) +} + +pub fn serialise(iq: &Iq) -> Element { + let mut stanza = Element::builder("iq") + .ns(ns::JABBER_CLIENT) + .attr("from", iq.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", iq.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", iq.id.clone()) + .attr("type", iq.payload.clone()) + .build(); + let elem = match iq.payload.clone() { + IqType::Get(IqPayloadType::XML(elem)) => elem, + IqType::Set(IqPayloadType::XML(elem)) => elem, + IqType::Result(None) => return stanza, + IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Error(IqPayloadType::XML(elem)) => elem, + _ => panic!(), + }; + stanza.append_child(elem); + stanza +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use iq; + + #[test] + fn test_require_type() { + let elem: Element = "".parse().unwrap(); + let error = iq::parse_iq(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Iq element requires a 'type' attribute."); + } + + #[test] + fn test_get() { + let elem: Element = " + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let query: Element = "".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Get(iq::IqPayloadType::XML(element)) => element == query, + _ => false + }); + } + + #[test] + fn test_set() { + let elem: Element = " + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let vcard: Element = "".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Set(iq::IqPayloadType::XML(element)) => element == vcard, + _ => false + }); + } + + #[test] + fn test_result_empty() { + let elem: Element = "".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Result(None) => true, + _ => false, + }); + } + + #[test] + fn test_result() { + let elem: Element = " + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let query: Element = "".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Result(Some(iq::IqPayloadType::XML(element))) => element == query, + _ => false, + }); + } + + #[test] + fn test_error() { + let elem: Element = " + + + + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let error: Element = " + + ".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Error(iq::IqPayloadType::XML(element)) => element == error, + _ => false, + }); + } + + #[test] + fn test_children_invalid() { + let elem: Element = "".parse().unwrap(); + let error = iq::parse_iq(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Wrong number of children in iq element."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let iq2 = iq::Iq { + from: None, + to: None, + id: None, + payload: iq::IqType::Result(None), + }; + let elem2 = iq::serialise(&iq2); + assert_eq!(elem, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index 2536aaa4b7eade372db77ed8ba5b048c253376f9..a21168fe5a93f0c0067b2b4bde00638ae02ab686 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,8 @@ pub mod ns; pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod presence; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod iq; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod body; From b259ab9ef254743c07fe770670c196b0f1937eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 23 Apr 2017 21:12:27 +0100 Subject: [PATCH 0099/1020] iq: Link disco parser to Iq. --- src/iq.rs | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index afdb463f91062388701caed13350226e5fa6252e..87477c3ee5d4ad0473c12cf84fe14387bdba6664 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -7,9 +7,12 @@ use error::Error; use ns; +use disco; + /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum IqPayload { + Disco(disco::Disco), } #[derive(Debug, Clone)] @@ -67,36 +70,45 @@ pub fn parse_iq(root: &Element) -> Result { } if type_ == "error" { if elem.is("error", ns::JABBER_CLIENT) { - payload = Some(elem); + payload = Some(IqPayloadType::XML(elem.clone())); } else if root.children().collect::>().len() != 2 { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - payload = Some(elem); + let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { + Some(IqPayload::Disco(disco)) + } else { + None + }; + + payload = match parsed_payload { + Some(payload) => Some(IqPayloadType::Parsed(payload)), + None => Some(IqPayloadType::XML(elem.clone())), + }; } } let type_ = if type_ == "get" { if let Some(payload) = payload.clone() { - IqType::Get(IqPayloadType::XML(payload.clone())) + IqType::Get(payload.clone()) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else if type_ == "set" { if let Some(payload) = payload.clone() { - IqType::Set(IqPayloadType::XML(payload.clone())) + IqType::Set(payload.clone()) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else if type_ == "result" { if let Some(payload) = payload.clone() { - IqType::Result(Some(IqPayloadType::XML(payload.clone()))) + IqType::Result(Some(payload.clone())) } else { IqType::Result(None) } } else if type_ == "error" { if let Some(payload) = payload.clone() { - IqType::Error(IqPayloadType::XML(payload.clone())) + IqType::Error(payload.clone()) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } @@ -112,6 +124,12 @@ pub fn parse_iq(root: &Element) -> Result { }) } +pub fn serialise_payload(payload: &IqPayload) -> Element { + match *payload { + IqPayload::Disco(ref disco) => disco::serialise_disco(disco), + } +} + pub fn serialise(iq: &Iq) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) @@ -122,9 +140,12 @@ pub fn serialise(iq: &Iq) -> Element { .build(); let elem = match iq.payload.clone() { IqType::Get(IqPayloadType::XML(elem)) => elem, + IqType::Get(IqPayloadType::Parsed(payload)) => serialise_payload(&payload), IqType::Set(IqPayloadType::XML(elem)) => elem, + IqType::Set(IqPayloadType::Parsed(payload)) => serialise_payload(&payload), IqType::Result(None) => return stanza, IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), IqType::Error(IqPayloadType::XML(elem)) => elem, _ => panic!(), }; @@ -137,6 +158,7 @@ mod tests { use minidom::Element; use error::Error; use iq; + use disco; #[test] fn test_require_type() { @@ -152,10 +174,10 @@ mod tests { #[test] fn test_get() { let elem: Element = " - + ".parse().unwrap(); let iq = iq::parse_iq(&elem).unwrap(); - let query: Element = "".parse().unwrap(); + let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); @@ -254,4 +276,14 @@ mod tests { let elem2 = iq::serialise(&iq2); assert_eq!(elem, elem2); } + + #[test] + fn test_disco() { + let elem: Element = "".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + assert!(match iq.payload { + iq::IqType::Get(iq::IqPayloadType::Parsed(iq::IqPayload::Disco(disco::Disco { .. }))) => true, + _ => false, + }); + } } From c462d230f19bba7c44d32229408d6c7fd9b778a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 23 Apr 2017 21:17:26 +0100 Subject: [PATCH 0100/1020] ping, iq: Implement ping::serialise_ping and link to Iq. --- src/iq.rs | 5 +++++ src/ping.rs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 87477c3ee5d4ad0473c12cf84fe14387bdba6664..eda650ac9bcfc74f00a4747d16e430a41b7a6e47 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -8,11 +8,13 @@ use error::Error; use ns; use disco; +use ping; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum IqPayload { Disco(disco::Disco), + Ping(ping::Ping), } #[derive(Debug, Clone)] @@ -77,6 +79,8 @@ pub fn parse_iq(root: &Element) -> Result { } else { let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { Some(IqPayload::Disco(disco)) + } else if let Ok(ping) = ping::parse_ping(elem) { + Some(IqPayload::Ping(ping)) } else { None }; @@ -127,6 +131,7 @@ pub fn parse_iq(root: &Element) -> Result { pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), + IqPayload::Ping(_) => ping::serialise_ping(), } } diff --git a/src/ping.rs b/src/ping.rs index a8c94c500f5fe5526c1dddde55fe3111e44a7af0..1d5c89ac52ef77a78de5a305e6b3f674beac1f00 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -18,6 +18,10 @@ pub fn parse_ping(root: &Element) -> Result { Ok(Ping { }) } +pub fn serialise_ping() -> Element { + Element::builder("ping").ns(ns::PING).build() +} + #[cfg(test)] mod tests { use minidom::Element; From 6f0e88b25beab91b921090d1bbd4879105b1140b Mon Sep 17 00:00:00 2001 From: Eijebong Date: Mon, 24 Apr 2017 16:56:29 +0200 Subject: [PATCH 0101/1020] Correctly add namespaced attributes to elements Instead of adding the local_name of an attribute, if a prefix exists, add prefix:local_name to allow users to retrieve it via the namespaced key name. For example, with this XML: ``` ``` `root.attr("xml:lang").unwrap()` will now correctly return "en". `root.attr("lang")` will not retrieve "xml:lang" value anymore. This is a breaking change. Fixes #2 --- src/element.rs | 21 +++++++++++++++++++-- src/tests.rs | 12 +++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/element.rs b/src/element.rs index 13c8baf1c4e02fd86d0d86767964a424626de476..0db7ba81fd9aa816fcc650a8670e200eb345b825 100644 --- a/src/element.rs +++ b/src/element.rs @@ -197,7 +197,15 @@ impl Element { match e { ReaderEvent::StartElement { name, attributes, namespace } => { let attributes = attributes.into_iter() - .map(|o| Attribute::new(o.name.local_name, o.value)) + .map(|o| { + Attribute::new( + match o.name.prefix { + Some(prefix) => format!("{}:{}", prefix, o.name.local_name), + None => o.name.local_name + }, + o.value + ) + }) .collect(); let ns = if let Some(ref prefix) = name.prefix { namespace.get(prefix) @@ -205,6 +213,7 @@ impl Element { else { namespace.get(NS_NO_PREFIX) }.map(|s| s.to_owned()); + let mut root = Element::new(name.local_name, ns, attributes, Vec::new()); root.from_reader_inner(reader)?; return Ok(root); @@ -223,7 +232,15 @@ impl Element { match e { ReaderEvent::StartElement { name, attributes, namespace } => { let attributes = attributes.into_iter() - .map(|o| Attribute::new(o.name.local_name, o.value)) + .map(|o| { + Attribute::new( + match o.name.prefix { + Some(prefix) => format!("{}:{}", prefix, o.name.local_name), + None => o.name.local_name + }, + o.value + ) + }) .collect(); let ns = if let Some(ref prefix) = name.prefix { namespace.get(prefix) diff --git a/src/tests.rs b/src/tests.rs index b9263446cd110826889fbd63e7d9d560d4188e6a..03d3ab9ab60b81542f832d66867879fffe92c2a8 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -7,11 +7,12 @@ use xml::writer::EventWriter; use element::Element; -const TEST_STRING: &'static str = r#"meownya"#; +const TEST_STRING: &'static str = r#"meownya"#; fn build_test_tree() -> Element { let mut root = Element::builder("root") .ns("root_ns") + .attr("xml:lang", "en") .attr("a", "b") .build(); root.append_text_node("meow"); @@ -22,6 +23,7 @@ fn build_test_tree() -> Element { let other_child = Element::builder("child") .ns("child_ns") .attr("d", "e") + .attr("xml:lang", "fr") .build(); root.append_child(other_child); root.append_text_node("nya"); @@ -94,3 +96,11 @@ fn namespace_propagation_works() { .get_child("grandchild", "root_ns").unwrap() .ns(), root.ns()); } + +#[test] +fn namespace_attributes_works() { + let mut reader = EventReader::new(Cursor::new(TEST_STRING)); + let root = Element::from_reader(&mut reader).unwrap(); + assert_eq!("en", root.attr("xml:lang").unwrap()); + assert_eq!("fr", root.get_child("child", "child_ns").unwrap().attr("xml:lang").unwrap()); +} From 1b4c0c45895bd163a123cedd82974486a582b3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 24 Apr 2017 19:20:35 +0100 Subject: [PATCH 0102/1020] iq: Clippify --- src/iq.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index eda650ac9bcfc74f00a4747d16e430a41b7a6e47..9d7fd79ffadfe13761730fa2150da89536d0d3a2 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -144,14 +144,14 @@ pub fn serialise(iq: &Iq) -> Element { .attr("type", iq.payload.clone()) .build(); let elem = match iq.payload.clone() { - IqType::Get(IqPayloadType::XML(elem)) => elem, - IqType::Get(IqPayloadType::Parsed(payload)) => serialise_payload(&payload), - IqType::Set(IqPayloadType::XML(elem)) => elem, - IqType::Set(IqPayloadType::Parsed(payload)) => serialise_payload(&payload), + IqType::Get(IqPayloadType::XML(elem)) + | IqType::Set(IqPayloadType::XML(elem)) + | IqType::Result(Some(IqPayloadType::XML(elem))) + | IqType::Error(IqPayloadType::XML(elem)) => elem, + IqType::Get(IqPayloadType::Parsed(payload)) + | IqType::Set(IqPayloadType::Parsed(payload)) + | IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), IqType::Result(None) => return stanza, - IqType::Result(Some(IqPayloadType::XML(elem))) => elem, - IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), - IqType::Error(IqPayloadType::XML(elem)) => elem, _ => panic!(), }; stanza.append_child(elem); From c71b32ae244d60323fc95322da85855adfaa2e4c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 0103/1020] ibb: Implement IntoAttributeValue for Stanza. --- src/ibb.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ibb.rs b/src/ibb.rs index 507e3aa4392eecce48043473b7b20d1bd97c73e9..ba8fcafb77d4a31ca205a674480dd348f92f9a21 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use base64; use error::Error; @@ -32,6 +32,15 @@ impl FromStr for Stanza { } } +impl IntoAttributeValue for Stanza { + fn into_attribute_value(self) -> Option { + match self { + Stanza::Iq => None, + Stanza::Message => Some(String::from("message")), + } + } +} + #[derive(Debug, Clone)] pub enum IBB { Open { From 2b19a415d42751f5d2d985659ad553a51f7b3d02 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 0104/1020] jingle_ibb: Implement serialise. --- src/jingle_ibb.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 4ab73e684bdeee9243e501f697e3fbd4b7d90348..29590cba83cebbf08f131c70532b82fb8ab49df7 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -44,6 +44,15 @@ pub fn parse_jingle_ibb(root: &Element) -> Result { } } +pub fn serialise(transport: &Transport) -> Element { + Element::builder("transport") + .ns(ns::JINGLE_IBB) + .attr("block-size", format!("{}", transport.block_size)) + .attr("sid", transport.sid.clone()) + .attr("stanza", transport.stanza.clone()) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; From 90f1792ebcc851de3abf2e8d6aaeea63c4c0653d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 0105/1020] jingle_ibb: Make the fields in Transport public. --- src/jingle_ibb.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 29590cba83cebbf08f131c70532b82fb8ab49df7..d6f009a9d6041c586d68863390df9b340f50e0ef 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -10,9 +10,9 @@ use ibb::Stanza; #[derive(Debug, Clone)] pub struct Transport { - block_size: u16, - sid: String, - stanza: Stanza, + pub block_size: u16, + pub sid: String, + pub stanza: Stanza, } fn optional_attr(root: &Element, attr: &str) -> Option { From fa10ab4ebc340ed0adcf173f9e851e035952cd09 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 0106/1020] jingle: Implement From for String on all special attributes. --- src/jingle.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/jingle.rs b/src/jingle.rs index 9cea6cf821d07072fca39916f5fcb95bf0941ba3..c8a2dfc0506e33acaca91fa14493469e5890d6c6 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -52,6 +52,28 @@ impl FromStr for Action { } } +impl From for String { + fn from(action: Action) -> String { + String::from(match action { + Action::ContentAccept => "content-accept", + Action::ContentAdd => "content-add", + Action::ContentModify => "content-modify", + Action::ContentReject => "content-reject", + Action::ContentRemove => "content-remove", + Action::DescriptionInfo => "description-info", + Action::SecurityInfo => "security-info", + Action::SessionAccept => "session-accept", + Action::SessionInfo => "session-info", + Action::SessionInitiate => "session-initiate", + Action::SessionTerminate => "session-terminate", + Action::TransportAccept => "transport-accept", + Action::TransportInfo => "transport-info", + Action::TransportReject => "transport-reject", + Action::TransportReplace => "transport-replace", + }) + } +} + // TODO: use a real JID type. type Jid = String; @@ -74,6 +96,15 @@ impl FromStr for Creator { } } +impl From for String { + fn from(creator: Creator) -> String { + String::from(match creator { + Creator::Initiator => "initiator", + Creator::Responder => "responder", + }) + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Senders { Both, @@ -97,6 +128,17 @@ impl FromStr for Senders { } } +impl From for String { + fn from(senders: Senders) -> String { + String::from(match senders { + Senders::Both => "both", + Senders::Initiator => "initiator", + Senders::None_ => "none", + Senders::Responder => "responder", + }) + } +} + #[derive(Debug, Clone)] pub struct Content { pub creator: Creator, From db1a87e2eec9812a6f69389b1b37c128eb79b251 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 0107/1020] jingle: Implement serialise. --- src/jingle.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/jingle.rs b/src/jingle.rs index c8a2dfc0506e33acaca91fa14493469e5890d6c6..9e2549cf7a455d8867eb45459288b2a1c02df50a 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -347,6 +347,38 @@ pub fn parse_jingle(root: &Element) -> Result { }) } +pub fn serialise_content(content: &Content) -> Element { + let mut root = Element::builder("content") + .ns(ns::JINGLE) + .attr("creator", String::from(content.creator.clone())) + .attr("disposition", content.disposition.clone()) + .attr("name", content.name.clone()) + .attr("senders", String::from(content.senders.clone())) + .build(); + root.append_child(content.description.1.clone()); + root.append_child(content.transport.1.clone()); + if let Some(security) = content.security.clone() { + root.append_child(security.1.clone()); + } + root +} + +pub fn serialise(jingle: &Jingle) -> Element { + let mut root = Element::builder("jingle") + .ns(ns::JINGLE) + .attr("action", String::from(jingle.action.clone())) + .attr("initiator", jingle.initiator.clone()) + .attr("responder", jingle.responder.clone()) + .attr("sid", jingle.sid.clone()) + .build(); + for content in jingle.contents.clone() { + println!("{:?}", content); + let content_elem = serialise_content(&content); + root.append_child(content_elem); + } + root +} + #[cfg(test)] mod tests { use minidom::Element; From 5abf820fadb54e83a6cb178ed536a332ae05feb0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:25:00 +0100 Subject: [PATCH 0108/1020] jingle_ft: Implement serialise. --- src/jingle_ft.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 82941b3606eedddb5971e07b221711c971be200a..05ea3b149dd07012604cee49a5c6170b78bafa41 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -121,6 +121,54 @@ pub fn parse_jingle_ft(root: &Element) -> Result { }) } +pub fn serialise_file(file: &File) -> Element { + let mut root = Element::builder("file") + .ns(ns::JINGLE_FT) + .build(); + println!("{:#?}", file); + if let Some(ref date) = file.date { + root.append_child(Element::builder("date") + .ns(ns::JINGLE_FT) + .append(date.clone()) + .build()); + } + if let Some(ref media_type) = file.media_type { + root.append_child(Element::builder("media-type") + .ns(ns::JINGLE_FT) + .append(media_type.clone()) + .build()); + } + if let Some(ref name) = file.name { + root.append_child(Element::builder("name") + .ns(ns::JINGLE_FT) + .append(name.clone()) + .build()); + } + if let Some(ref size) = file.size { + root.append_child(Element::builder("size") + .ns(ns::JINGLE_FT) + .append(format!("{}", size)) + .build()); + } + if let Some(ref range) = file.range { + root.append_child(Element::builder("range") + .ns(ns::JINGLE_FT) + .append(range.clone()) + .build()); + } + for hash in file.hashes.clone() { + root.append_child(hashes::serialise(&hash)); + } + root +} + +pub fn serialise(desc: &Description) -> Element { + Element::builder("description") + .ns(ns::JINGLE_FT) + .append(serialise_file(&desc.file)) + .build() +} + #[cfg(test)] mod tests { use minidom::Element; From 049ef2359598ae6922cfbace63d069e8ba1556b6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 19:52:41 +0100 Subject: [PATCH 0109/1020] jingle_ft: Implement IntoElements on Range, and change size to be an u64. --- src/jingle_ft.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 05ea3b149dd07012604cee49a5c6170b78bafa41..596c1409476517829b01800f528c16084a6190f7 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -1,8 +1,10 @@ extern crate minidom; +use hashes; use hashes::{Hash, parse_hash}; -use minidom::Element; +use minidom::{Element, IntoElements}; +use minidom::convert::ElementEmitter; use error::Error; use ns; @@ -14,12 +16,29 @@ pub struct Range { pub hashes: Vec, } +impl IntoElements for Range { + fn into_elements(self, emitter: &mut ElementEmitter) { + let mut elem = Element::builder("range") + .ns(ns::JINGLE_FT) + .attr("offset", format!("{}", self.offset)) + .attr("length", match self.length { + Some(length) => Some(format!("{}", length)), + None => None + }) + .build(); + for hash in self.hashes { + elem.append_child(hashes::serialise(&hash)); + } + emitter.append_child(elem); + } +} + #[derive(Debug, Clone)] pub struct File { pub date: Option, pub media_type: Option, pub name: Option, - pub size: Option, + pub size: Option, pub range: Option, pub hashes: Vec, } @@ -79,7 +98,7 @@ pub fn parse_jingle_ft(root: &Element) -> Result { if size.is_some() { return Err(Error::ParseError("File must not have more than one size.")); } - size = Some(file_payload.text()); + size = Some(file_payload.text().parse()?); } else if file_payload.is("range", ns::JINGLE_FT) { if range.is_some() { return Err(Error::ParseError("File must not have more than one range.")); @@ -193,7 +212,7 @@ mod tests { assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); - assert_eq!(desc.file.size, Some(String::from("6144"))); + assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, "sha-1"); assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); From 7fc40dbb191b48f342501f998e19e3cbf405dfa5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Apr 2017 20:03:05 +0100 Subject: [PATCH 0110/1020] ecaps2: Remove extraneous borrowing on base64::encode. --- src/ecaps2.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index d9e9a7dedd23d84052dd621ee5119a65ae3ecfd9..fc9327a3d2913e8aae2e767537e12345e2c3a6ec 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -143,14 +143,14 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { hasher.input(data); let mut buf: [u8; 32] = [0; 32]; let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(&hash) + base64::encode(hash) }, "blake2b-512" => { let mut hasher = Blake2b::default(); hasher.input(data); let mut buf: [u8; 64] = [0; 64]; let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(&hash) + base64::encode(hash) }, _ => panic!(), } From 0d0c4b76eb674f02aa4cdccd3f3a9a8d466926d0 Mon Sep 17 00:00:00 2001 From: Eijebong Date: Mon, 24 Apr 2017 20:06:21 +0200 Subject: [PATCH 0111/1020] Fix the PartialEq implementation for Element The order of attributes in an `Element` doesn't matter anymore. `` and `` are now correctly considered equal. For that I had to derive `PartialOrd` and `Ord` for `Attribute`. This allows us to sort cloned vectors of `Attribute` in the `PartialEq` implementation and compare them instead of the struct `attributes`. Fixes #3 --- src/attribute.rs | 2 +- src/element.rs | 16 +++++++++++++++- src/tests.rs | 11 +++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/attribute.rs b/src/attribute.rs index 57373a06dc7ee6b6a62b9b226edc0911dc1e22f4..10e0e3d2a466196e406485e17d9b466a1b8e360e 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -9,7 +9,7 @@ use std::fmt; /// This is of the form: `name`="`value`" /// /// This does not support prefixed/namespaced attributes yet. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)] pub struct Attribute { /// The name of the attribute. pub name: String, diff --git a/src/element.rs b/src/element.rs index 13c8baf1c4e02fd86d0d86767964a424626de476..197d9ed0ebb0153353a2b8bbe52a4e085ea0c4d1 100644 --- a/src/element.rs +++ b/src/element.rs @@ -20,7 +20,7 @@ use std::slice; use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, Eq)] /// A struct representing a DOM Element. pub struct Element { name: String, @@ -29,6 +29,20 @@ pub struct Element { children: Vec, } +impl PartialEq for Element { + fn eq(&self, other: &Element) -> bool { + let mut my_attr = self.attributes.clone(); + my_attr.sort(); + let mut other_attr = other.attributes.clone(); + other_attr.sort(); + + self.name == other.name && + self.namespace == other.namespace && + my_attr == other_attr && + self.children == other.children + } +} + impl fmt::Debug for Element { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "<{}", self.name)?; diff --git a/src/tests.rs b/src/tests.rs index b9263446cd110826889fbd63e7d9d560d4188e6a..44fef37b89381a075a1c7c975989b51ad28b1468 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -94,3 +94,14 @@ fn namespace_propagation_works() { .get_child("grandchild", "root_ns").unwrap() .ns(), root.ns()); } + +#[test] +fn two_elements_with_same_arguments_different_order_are_equal() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert_eq!(elem1, elem2); + + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert_ne!(elem1, elem2); +} From d54b002243c5a8490016a5fddd8195a48ce81e3d Mon Sep 17 00:00:00 2001 From: lumi Date: Wed, 26 Apr 2017 01:14:16 +0200 Subject: [PATCH 0112/1020] bump to 2.0.0, switch license to MIT --- COPYING | 675 ------------------------------------------------- COPYING.LESSER | 166 ------------ Cargo.toml | 6 +- LICENSE | 7 + README.md | 17 +- 5 files changed, 15 insertions(+), 856 deletions(-) delete mode 100644 COPYING delete mode 100644 COPYING.LESSER create mode 100644 LICENSE diff --git a/COPYING b/COPYING deleted file mode 100644 index a737dcfed5db21fb99a8fcb812e995937d38401b..0000000000000000000000000000000000000000 --- a/COPYING +++ /dev/null @@ -1,675 +0,0 @@ - - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/COPYING.LESSER b/COPYING.LESSER deleted file mode 100644 index 5f5ff16a4a0f6104fadb6a5beef527573e46b425..0000000000000000000000000000000000000000 --- a/COPYING.LESSER +++ /dev/null @@ -1,166 +0,0 @@ - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/Cargo.toml b/Cargo.toml index a28773a998f9f0e29dc2467bd5a8c4267e7188b4..94dac84b005af32ffbdbc81bca8438f4b84774bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "minidom" -version = "0.1.1" -authors = ["lumi "] +version = "0.2.0" +authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] description = "A small, simple DOM implementation on top of xml-rs." homepage = "https://gitlab.com/lumi/minidom-rs" repository = "https://gitlab.com/lumi/minidom-rs" documentation = "https://docs.rs/minidom" readme = "README.md" keywords = ["xml"] -license = "LGPL-3.0+" +license = "MIT" [badges] gitlab = { repository = "lumi/minidom-rs" } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c13d199536618beae551d74c02dcb94caf466b15 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2017 minidom-rs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index ca2fae7a68b0167107a3f2e3e8e556e16dfc9c58..c9a9d31f72f391e5d6502c09db5435270092fb80 100644 --- a/README.md +++ b/README.md @@ -9,22 +9,15 @@ A minimal DOM library on top of xml-rs. What license is it under? ------------------------- -LGPLv3 or later. See `COPYING` and `COPYING.LESSER`. +MIT or later. See `LICENSE`. License yadda yadda. -------------------- - Copyright 2017, minidom-rs contributors. +Copyright 2017 minidom-rs contributors - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 8116c6e657235182fdea86a24b2d66f291e807bd Mon Sep 17 00:00:00 2001 From: lumi Date: Wed, 26 Apr 2017 01:15:56 +0200 Subject: [PATCH 0113/1020] Well, that's embarrassing. Hope nobody will ever look at this commit. (if you see this, please don't look) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9a9d31f72f391e5d6502c09db5435270092fb80..4cac5469b3f1226c13b2848b4fd70a202cfb8a81 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A minimal DOM library on top of xml-rs. What license is it under? ------------------------- -MIT or later. See `LICENSE`. +MIT. See `LICENSE`. License yadda yadda. -------------------- From a0eab6c1afff3cd4ae954121f95ec4817dadcfcd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 26 Apr 2017 00:20:50 +0100 Subject: [PATCH 0114/1020] ecaps2: Import Digest from digest. --- src/ecaps2.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fc9327a3d2913e8aae2e767537e12345e2c3a6ec..bfd208cbaedf792e188ee5017eb7918d48fbb2b9 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -11,10 +11,10 @@ use minidom::Element; use error::Error; use ns; -use self::sha2::{Sha256, Sha512, Digest}; +use self::sha2::{Sha256, Sha512}; use self::sha3::{Sha3_256, Sha3_512}; use self::blake2::Blake2b; -use digest::VariableOutput; +use digest::{Digest, VariableOutput}; use base64; #[derive(Debug, Clone)] From 5d51dff9e66d03cd651144d831d0bb5d18464f7e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 26 Apr 2017 00:22:40 +0100 Subject: [PATCH 0115/1020] disco: Bump minidom dependency to 0.2.0, which fixes xml:lang parsing. --- Cargo.toml | 2 +- src/disco.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b46e00d702daffb61db3b4dcf9e906de10f17316..c049bd6157236e831e0a5f7631e687bb941bc400 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emmanuel Gil Peyrot "] [dependencies] -minidom = "0.1.1" +minidom = "0.2.0" jid = "0.2.0" base64 = "0.4.1" digest = "0.5.0" diff --git a/src/disco.rs b/src/disco.rs index 4ac7240e163f6f901a7b6b995ac27542f7681a50..5961518c12de4bb80cd6f4b170e4219a927bcc61 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -60,8 +60,7 @@ pub fn parse_disco(root: &Element) -> Result { return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) } - // TODO: this must check for the namespace of the attribute, but minidom doesn’t support that yet, see issue #2. - let xml_lang = child.attr("lang").unwrap_or(""); + let xml_lang = child.attr("xml:lang").unwrap_or(""); let name = child.attr("name") .and_then(|name| name.parse().ok()); identities.push(Identity { From 2aef4aacb851cd40170201de575a753a46979a4e Mon Sep 17 00:00:00 2001 From: lumi Date: Wed, 26 Apr 2017 01:26:29 +0200 Subject: [PATCH 0116/1020] linewrap license --- LICENSE | 16 ++++++++++++---- README.md | 19 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/LICENSE b/LICENSE index c13d199536618beae551d74c02dcb94caf466b15..e3f453dd6acacbda82e62c5535f8055520dcad5e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,15 @@ -Copyright 2017 minidom-rs contributors -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 4cac5469b3f1226c13b2848b4fd70a202cfb8a81..c0426ba8e5a48d5f3e957b9769672152d8bc9587 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,17 @@ License yadda yadda. Copyright 2017 minidom-rs contributors -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From f87e2442d4350d187ce5403bb87919437cdfab21 Mon Sep 17 00:00:00 2001 From: Bastien Orivel Date: Wed, 26 Apr 2017 23:44:58 +0200 Subject: [PATCH 0117/1020] Use a BTreeMap instead of a Vec to store attributes This way we don't need to reimplement PartialEq for Element. It's also way easier to get an attribute by name as we don't need to iterate over every attribute to see if it exists. The only side effect is that now, in the Debug output, attributes are automatically sorted by names instead of being sorted by insertion order. Fixes #4 --- src/attribute.rs | 45 ----------------------------- src/element.rs | 75 ++++++++++++++++++------------------------------ src/lib.rs | 2 -- src/tests.rs | 2 +- 4 files changed, 29 insertions(+), 95 deletions(-) delete mode 100644 src/attribute.rs diff --git a/src/attribute.rs b/src/attribute.rs deleted file mode 100644 index 10e0e3d2a466196e406485e17d9b466a1b8e360e..0000000000000000000000000000000000000000 --- a/src/attribute.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Provides an `Attribute` type which represents an attribute in an XML document. - -use xml::escape::escape_str_attribute; - -use std::fmt; - -/// An attribute of a DOM element. -/// -/// This is of the form: `name`="`value`" -/// -/// This does not support prefixed/namespaced attributes yet. -#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)] -pub struct Attribute { - /// The name of the attribute. - pub name: String, - /// The value of the attribute. - pub value: String, -} - -impl fmt::Display for Attribute { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}=\"{}\"", self.name, escape_str_attribute(&self.value)) - } -} - -impl Attribute { - /// Construct a new attribute from the given `name` and `value`. - /// - /// # Examples - /// - /// ``` - /// use minidom::Attribute; - /// - /// let attr = Attribute::new("name", "value"); - /// - /// assert_eq!(attr.name, "name"); - /// assert_eq!(attr.value, "value"); - /// ``` - pub fn new, V: Into>(name: N, value: V) -> Attribute { - Attribute { - name: name.into(), - value: value.into(), - } - } -} diff --git a/src/element.rs b/src/element.rs index 58acc309198fc9a2313740b71dbd0fe411f090e6..4d3d59e1f10a1c9e949e6885eff166c78366c39b 100644 --- a/src/element.rs +++ b/src/element.rs @@ -2,13 +2,13 @@ use std::io::prelude::*; use std::io::Cursor; +use std::collections::BTreeMap; +use std::iter::FromIterator; use std::fmt; use error::Error; -use attribute::Attribute; - use xml::reader::{XmlEvent as ReaderEvent, EventReader}; use xml::writer::{XmlEvent as WriterEvent, EventWriter}; use xml::name::Name; @@ -20,28 +20,15 @@ use std::slice; use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; -#[derive(Clone, Eq)] +#[derive(Clone, PartialEq, Eq)] /// A struct representing a DOM Element. pub struct Element { name: String, namespace: Option, - attributes: Vec, + attributes: BTreeMap, children: Vec, } -impl PartialEq for Element { - fn eq(&self, other: &Element) -> bool { - let mut my_attr = self.attributes.clone(); - my_attr.sort(); - let mut other_attr = other.attributes.clone(); - other_attr.sort(); - - self.name == other.name && - self.namespace == other.namespace && - my_attr == other_attr && - self.children == other.children - } -} impl fmt::Debug for Element { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { @@ -50,7 +37,7 @@ impl fmt::Debug for Element { write!(fmt, " xmlns=\"{}\"", ns)?; } for attr in &self.attributes { - write!(fmt, " {}", attr)?; + write!(fmt, " {}=\"{}\"", attr.0, attr.1)?; } if self.children.is_empty() { write!(fmt, "/>")?; @@ -92,7 +79,7 @@ pub enum Node { } impl Element { - fn new(name: String, namespace: Option, attributes: Vec, children: Vec) -> Element { + fn new(name: String, namespace: Option, attributes: BTreeMap, children: Vec) -> Element { Element { name: name, namespace: namespace, @@ -122,7 +109,7 @@ impl Element { /// ``` pub fn builder>(name: S) -> ElementBuilder { ElementBuilder { - root: Element::new(name.into(), None, Vec::new(), Vec::new()), + root: Element::new(name.into(), None, BTreeMap::new(), Vec::new()), } } @@ -144,7 +131,7 @@ impl Element { Element { name: name.into(), namespace: None, - attributes: Vec::new(), + attributes: BTreeMap::new(), children: Vec::new(), } } @@ -162,10 +149,8 @@ impl Element { /// Returns a reference to the value of the given attribute, if it exists, else `None`. pub fn attr(&self, name: &str) -> Option<&str> { - for attr in &self.attributes { - if attr.name == name { - return Some(&attr.value); - } + if let Some(value) = self.attributes.get(name) { + return Some(&value) } None } @@ -174,14 +159,14 @@ impl Element { pub fn set_attr, V: IntoAttributeValue>(&mut self, name: S, val: V) { let name = name.into(); let val = val.into_attribute_value(); - for attr in &mut self.attributes { - if attr.name == name { - attr.value = val.expect("removing existing value via set_attr, this is not yet supported (TODO)"); // TODO - return; - } + + if let Some(value) = self.attributes.get_mut(&name) { + *value = val.expect("removing existing value via set_attr, this is not yet supported (TODO)"); // TODO + return; } + if let Some(val) = val { - self.attributes.push(Attribute::new(name, val)); + self.attributes.insert(name, val); } } @@ -212,13 +197,11 @@ impl Element { ReaderEvent::StartElement { name, attributes, namespace } => { let attributes = attributes.into_iter() .map(|o| { - Attribute::new( - match o.name.prefix { - Some(prefix) => format!("{}:{}", prefix, o.name.local_name), - None => o.name.local_name - }, - o.value - ) + (match o.name.prefix { + Some(prefix) => format!("{}:{}", prefix, o.name.local_name), + None => o.name.local_name + }, + o.value) }) .collect(); let ns = if let Some(ref prefix) = name.prefix { @@ -247,13 +230,11 @@ impl Element { ReaderEvent::StartElement { name, attributes, namespace } => { let attributes = attributes.into_iter() .map(|o| { - Attribute::new( - match o.name.prefix { - Some(prefix) => format!("{}:{}", prefix, o.name.local_name), - None => o.name.local_name - }, - o.value - ) + (match o.name.prefix { + Some(prefix) => format!("{}:{}", prefix, o.name.local_name), + None => o.name.local_name + }, + o.value) }) .collect(); let ns = if let Some(ref prefix) = name.prefix { @@ -297,7 +278,7 @@ impl Element { start = start.default_ns(ns.as_ref()); } for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently - start = start.attr(Name::local(&attr.name), &attr.value); + start = start.attr(Name::local(&attr.0), &attr.1); } writer.write(start)?; for child in &self.children { @@ -582,7 +563,7 @@ impl ElementBuilder { fn test_element_new() { let elem = Element::new( "name".to_owned() , Some("namespace".to_owned()) - , vec![ Attribute::new("name", "value") ] + , BTreeMap::from_iter(vec![ ("name".to_string(), "value".to_string()) ].into_iter() ) , Vec::new() ); assert_eq!(elem.name(), "name"); diff --git a/src/lib.rs b/src/lib.rs index 4728608db797f5ee40e62273acd5bffa58e38f8e..23e234c93f4205249f305a26e331a7dc9b65bd9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,13 +67,11 @@ extern crate xml; pub mod error; -pub mod attribute; pub mod element; pub mod convert; #[cfg(test)] mod tests; pub use error::Error; -pub use attribute::Attribute; pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder}; pub use convert::{IntoElements, IntoAttributeValue}; diff --git a/src/tests.rs b/src/tests.rs index 64a32f920d49db61214b3522f7976d2b1eaf4546..f58bb6ebb1804f095bb928ece8fb459da3dc6029 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -7,7 +7,7 @@ use xml::writer::EventWriter; use element::Element; -const TEST_STRING: &'static str = r#"meownya"#; +const TEST_STRING: &'static str = r#"meownya"#; fn build_test_tree() -> Element { let mut root = Element::builder("root") From 154afda327d8383c1ba3dcbbd7fbf46a50b3b321 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 27 Apr 2017 17:42:00 +0100 Subject: [PATCH 0118/1020] Bump xml-rs dependency to 0.4.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 94dac84b005af32ffbdbc81bca8438f4b84774bf..00705bec73f645b9ed3ccf7af645aee0b1aee9fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ license = "MIT" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -xml-rs = "0.3.6" +xml-rs = "0.4.1" From 6a48a6bf0065608f47ca3d40c890206544b1643d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 27 Apr 2017 18:33:02 +0100 Subject: [PATCH 0119/1020] ecaps2, error: Update to base64 0.5. --- Cargo.toml | 2 +- src/ecaps2.rs | 12 ++++++------ src/error.rs | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c049bd6157236e831e0a5f7631e687bb941bc400..e236251d960c3c6b32654e879805a25c03c29f99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Emmanuel Gil Peyrot "] [dependencies] minidom = "0.2.0" jid = "0.2.0" -base64 = "0.4.1" +base64 = "0.5.0" digest = "0.5.0" sha2 = "0.5.0" sha3 = "0.5.0" diff --git a/src/ecaps2.rs b/src/ecaps2.rs index bfd208cbaedf792e188ee5017eb7918d48fbb2b9..d5a13dd39f748b753bb4cf38b2b64954f8fce703 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -118,25 +118,25 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { let mut hasher = Sha256::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash) + base64::encode(&hash.as_slice()) }, "sha-512" => { let mut hasher = Sha512::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash) + base64::encode(&hash.as_slice()) }, "sha3-256" => { let mut hasher = Sha3_256::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash) + base64::encode(&hash.as_slice()) }, "sha3-512" => { let mut hasher = Sha3_512::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash) + base64::encode(&hash.as_slice()) }, "blake2b-256" => { let mut hasher = Blake2b::default(); @@ -438,12 +438,12 @@ mod tests { #[test] fn test_blake2b_512() { let hash = ecaps2::hash_ecaps2("abc".as_bytes(), "blake2b-512"); - let known_hash: [u8; 64] = [ + let known_hash: Vec = vec!( 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, - ]; + ); let known_hash = base64::encode(&known_hash); assert_eq!(hash, known_hash); } diff --git a/src/error.rs b/src/error.rs index e9506bae476d052f24a9c2e4a6b34e2a60b91cbf..07b01c199a792537bfdc4c5cc89b09715b8cc0d8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,7 +10,7 @@ pub enum Error { ParseError(&'static str), IoError(io::Error), XMLError(minidom::Error), - Base64Error(base64::Base64Error), + Base64Error(base64::DecodeError), ParseIntError(num::ParseIntError), } @@ -26,8 +26,8 @@ impl From for Error { } } -impl From for Error { - fn from(err: base64::Base64Error) -> Error { +impl From for Error { + fn from(err: base64::DecodeError) -> Error { Error::Base64Error(err) } } From 30a596cb262faab13f42126b9581184ce91d1b52 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 27 Apr 2017 19:05:51 +0100 Subject: [PATCH 0120/1020] ibb: Implement serialise. --- src/ibb.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ibb.rs b/src/ibb.rs index ba8fcafb77d4a31ca205a674480dd348f92f9a21..6281d140f1bee2f0bd25a45d67b3ff5846168a86 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -105,6 +105,33 @@ pub fn parse_ibb(root: &Element) -> Result { } } +pub fn serialise(ibb: &IBB) -> Element { + match *ibb { + IBB::Open { ref block_size, ref sid, ref stanza } => { + Element::builder("open") + .ns(ns::IBB) + .attr("block-size", format!("{}", block_size)) + .attr("sid", sid.to_owned()) + .attr("stanza", stanza.to_owned()) + .build() + }, + IBB::Data { ref seq, ref sid, ref data } => { + Element::builder("data") + .ns(ns::IBB) + .attr("seq", format!("{}", seq)) + .attr("sid", sid.to_owned()) + .attr("data", base64::encode(&data)) + .build() + }, + IBB::Close { ref sid } => { + Element::builder("close") + .ns(ns::IBB) + .attr("sid", sid.to_owned()) + .build() + }, + } +} + #[cfg(test)] mod tests { use minidom::Element; From fe3300b4b00a5e368642a6f20d9064c747b8dadd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:42:27 +0100 Subject: [PATCH 0121/1020] jingle_ft: Add the forgotten desc element. --- src/jingle_ft.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 596c1409476517829b01800f528c16084a6190f7..ee5bb954f4d65d5cc5da7a9ec063859123b61e39 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -38,6 +38,7 @@ pub struct File { pub date: Option, pub media_type: Option, pub name: Option, + pub desc: Option, pub size: Option, pub range: Option, pub hashes: Vec, @@ -71,6 +72,7 @@ pub fn parse_jingle_ft(root: &Element) -> Result { let mut date = None; let mut media_type = None; let mut name = None; + let mut desc = None; let mut size = None; let mut range = None; let mut hashes = vec!(); @@ -94,6 +96,11 @@ pub fn parse_jingle_ft(root: &Element) -> Result { return Err(Error::ParseError("File must not have more than one name.")); } name = Some(file_payload.text()); + } else if file_payload.is("desc", ns::JINGLE_FT) { + if desc.is_some() { + return Err(Error::ParseError("File must not have more than one desc.")); + } + desc = Some(file_payload.text()); } else if file_payload.is("size", ns::JINGLE_FT) { if size.is_some() { return Err(Error::ParseError("File must not have more than one size.")); @@ -133,6 +140,7 @@ pub fn parse_jingle_ft(root: &Element) -> Result { date: date, media_type: media_type, name: name, + desc: desc, size: size, range: range, hashes: hashes, @@ -163,6 +171,12 @@ pub fn serialise_file(file: &File) -> Element { .append(name.clone()) .build()); } + if let Some(ref desc) = file.desc { + root.append_child(Element::builder("desc") + .ns(ns::JINGLE_FT) + .append(desc.clone()) + .build()); + } if let Some(ref size) = file.size { root.append_child(Element::builder("size") .ns(ns::JINGLE_FT) @@ -211,6 +225,7 @@ mod tests { let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); + assert_eq!(desc.file.desc, None); assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); @@ -232,6 +247,7 @@ mod tests { let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); assert_eq!(desc.file.media_type, None); assert_eq!(desc.file.name, None); + assert_eq!(desc.file.desc, None); assert_eq!(desc.file.date, None); assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); From 9f6eea06ce97ca6119395f8370cae730ce573811 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:42:50 +0100 Subject: [PATCH 0122/1020] ns: Fix the hashes text-names namespaces. --- src/ns.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ns.rs b/src/ns.rs index 130f1a19abf229b4c2265101c16a8719537183ca..415858c9e92801eb820e5c4b6e601243a7c84a4f 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -42,17 +42,17 @@ pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub const HASHES: &'static str = "urn:xmpp:hashes:2"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA_256: &'static str = "urn:xmpp:hash-function-text-name:sha-256"; +pub const HASH_ALGO_SHA_256: &'static str = "urn:xmpp:hash-function-text-names:sha-256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA_512: &'static str = "urn:xmpp:hash-function-text-name:sha-512"; +pub const HASH_ALGO_SHA_512: &'static str = "urn:xmpp:hash-function-text-names:sha-512"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA3_256: &'static str = "urn:xmpp:hash-function-text-name:sha3-256"; +pub const HASH_ALGO_SHA3_256: &'static str = "urn:xmpp:hash-function-text-names:sha3-256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA3_512: &'static str = "urn:xmpp:hash-function-text-name:sha3-512"; +pub const HASH_ALGO_SHA3_512: &'static str = "urn:xmpp:hash-function-text-names:sha3-512"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_BLAKE2B_256: &'static str = "urn:xmpp:hash-function-text-name:id-blake2b256"; +pub const HASH_ALGO_BLAKE2B_256: &'static str = "urn:xmpp:hash-function-text-names:id-blake2b256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-name:id-blake2b512"; +pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-names:id-blake2b512"; /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; From eeb9b36620dafd91061f4fa5269209ec8d613ac3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:43:24 +0100 Subject: [PATCH 0123/1020] jingle_ft: Make Creator into an enum, instead of a String. --- src/jingle_ft.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index ee5bb954f4d65d5cc5da7a9ec063859123b61e39..4cd73b1dd9349a494ddc9c8738d31233172c9cdd 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -50,8 +50,9 @@ pub struct Description { } #[derive(Debug, Clone)] -pub struct Creator { - pub creator: String, +pub enum Creator { + Initiator, + Responder, } #[derive(Debug, Clone)] From de93c32cb57bcf1560ad9c5d56beb92e655cfed2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:43:47 +0100 Subject: [PATCH 0124/1020] jingle_ft: Add a received parser. --- src/jingle_ft.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 4cd73b1dd9349a494ddc9c8738d31233172c9cdd..4d5a86a687a4bce490df12bbb415ca6851497512 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -62,6 +62,26 @@ pub struct Checksum { pub file: File, } +#[derive(Debug, Clone)] +pub struct Received { + pub name: String, + pub creator: Creator, +} + +impl IntoElements for Received { + fn into_elements(self, emitter: &mut ElementEmitter) { + let elem = Element::builder("received") + .ns(ns::JINGLE_FT) + .attr("name", self.name) + .attr("creator", match self.creator { + Creator::Initiator => "initiator", + Creator::Responder => "responder", + }) + .build(); + emitter.append_child(elem); + } +} + pub fn parse_jingle_ft(root: &Element) -> Result { if !root.is("description", ns::JINGLE_FT) { return Err(Error::ParseError("This is not a JingleFT description element.")); From c20d37a842f4824a5bb5a41760a0011b289f945e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:45:02 +0100 Subject: [PATCH 0125/1020] jingle: Add serialisation for . --- src/jingle.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index 9e2549cf7a455d8867eb45459288b2a1c02df50a..800cb7aee03daa01b4236fe6232f7169ed8364a5 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -2,7 +2,8 @@ extern crate minidom; use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoElements}; +use minidom::convert::ElementEmitter; use error::Error; use ns; @@ -199,6 +200,31 @@ impl FromStr for Reason { } } +impl IntoElements for Reason { + fn into_elements(self, emitter: &mut ElementEmitter) { + let elem = Element::builder(match self { + Reason::AlternativeSession => "alternative-session", + Reason::Busy => "busy", + Reason::Cancel => "cancel", + Reason::ConnectivityError => "connectivity-error", + Reason::Decline => "decline", + Reason::Expired => "expired", + Reason::FailedApplication => "failed-application", + Reason::FailedTransport => "failed-transport", + Reason::GeneralError => "general-error", + Reason::Gone => "gone", + Reason::IncompatibleParameters => "incompatible-parameters", + Reason::MediaError => "media-error", + Reason::SecurityError => "security-error", + Reason::Success => "success", + Reason::Timeout => "timeout", + Reason::UnsupportedApplications => "unsupported-applications", + Reason::UnsupportedTransports => "unsupported-transports", + }).build(); + emitter.append_child(elem); + } +} + #[derive(Debug, Clone)] pub struct ReasonElement { pub reason: Reason, @@ -376,6 +402,13 @@ pub fn serialise(jingle: &Jingle) -> Element { let content_elem = serialise_content(&content); root.append_child(content_elem); } + if let Some(ref reason) = jingle.reason { + let reason_elem = Element::builder("reason") + .append(reason.reason.clone()) + .append(reason.text.clone()) + .build(); + root.append_child(reason_elem); + } root } From 10a336f8749817db0e507a42711500bd7c79e3b0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:45:27 +0100 Subject: [PATCH 0126/1020] jingle: Accept any unknown child, as per the specification. --- src/jingle.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 800cb7aee03daa01b4236fe6232f7169ed8364a5..1437207034ba2d8794a1d66e7d1da61e6b59f931 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -239,7 +239,7 @@ pub struct Jingle { pub sid: String, pub contents: Vec, pub reason: Option, - //pub other: Vec, + pub other: Vec, } pub fn parse_jingle(root: &Element) -> Result { @@ -259,6 +259,7 @@ pub fn parse_jingle(root: &Element) -> Result { let sid = root.attr("sid") .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; let mut reason_element = None; + let mut other = vec!(); for child in root.children() { if child.is("content", ns::JINGLE) { @@ -359,7 +360,7 @@ pub fn parse_jingle(root: &Element) -> Result { text: text, }); } else { - return Err(Error::ParseError("Unknown element in jingle.")); + other.push(child.clone()); } } @@ -370,6 +371,7 @@ pub fn parse_jingle(root: &Element) -> Result { sid: sid.to_owned(), contents: contents, reason: reason_element, + other: other, }) } @@ -451,14 +453,6 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Unknown action."); - - let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown element in jingle."); } #[test] From 4fb4727357fc006de836233d9ca21a95a9685f08 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:45:43 +0100 Subject: [PATCH 0127/1020] jingle: Remove extraneous println!. --- src/jingle.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index 1437207034ba2d8794a1d66e7d1da61e6b59f931..4c7a17c9f162ad5dfd3174ff1ed9d2ad13b23a49 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -400,7 +400,6 @@ pub fn serialise(jingle: &Jingle) -> Element { .attr("sid", jingle.sid.clone()) .build(); for content in jingle.contents.clone() { - println!("{:?}", content); let content_elem = serialise_content(&content); root.append_child(content_elem); } From 5e4ba2701bab08b148eccb9a0873ae8c062c43bc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:46:06 +0100 Subject: [PATCH 0128/1020] iq: Wire up the Jingle parser and serialiser. --- src/iq.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 9d7fd79ffadfe13761730fa2150da89536d0d3a2..2edf6c8640ea50f85d1a749dbec13f499de47a59 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -8,12 +8,14 @@ use error::Error; use ns; use disco; +use jingle; use ping; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum IqPayload { Disco(disco::Disco), + Jingle(jingle::Jingle), Ping(ping::Ping), } @@ -79,6 +81,8 @@ pub fn parse_iq(root: &Element) -> Result { } else { let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { Some(IqPayload::Disco(disco)) + } else if let Ok(jingle) = jingle::parse_jingle(elem) { + Some(IqPayload::Jingle(jingle)) } else if let Ok(ping) = ping::parse_ping(elem) { Some(IqPayload::Ping(ping)) } else { @@ -131,6 +135,7 @@ pub fn parse_iq(root: &Element) -> Result { pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), + IqPayload::Jingle(ref jingle) => jingle::serialise(jingle), IqPayload::Ping(_) => ping::serialise_ping(), } } From 846148d618082bf3d20d7616dfeeffdd37de3e1d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 28 Apr 2017 23:46:32 +0100 Subject: [PATCH 0129/1020] ibb: Fix serialisation of data. --- src/ibb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ibb.rs b/src/ibb.rs index 6281d140f1bee2f0bd25a45d67b3ff5846168a86..7152d722ce4f7058b16d5c6940d23a811b5fcd47 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -120,7 +120,7 @@ pub fn serialise(ibb: &IBB) -> Element { .ns(ns::IBB) .attr("seq", format!("{}", seq)) .attr("sid", sid.to_owned()) - .attr("data", base64::encode(&data)) + .append(base64::encode(&data)) .build() }, IBB::Close { ref sid } => { From 45b38bcef94e18d85c25be24a4571c8c903ba21b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 01:06:12 +0100 Subject: [PATCH 0130/1020] iq: Wire up the IBB parser and serialiser. --- src/iq.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 2edf6c8640ea50f85d1a749dbec13f499de47a59..e2ac6b356717c5f4d51ac92605f83b52417c124e 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -8,6 +8,7 @@ use error::Error; use ns; use disco; +use ibb; use jingle; use ping; @@ -15,6 +16,7 @@ use ping; #[derive(Debug, Clone)] pub enum IqPayload { Disco(disco::Disco), + IBB(ibb::IBB), Jingle(jingle::Jingle), Ping(ping::Ping), } @@ -81,6 +83,8 @@ pub fn parse_iq(root: &Element) -> Result { } else { let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { Some(IqPayload::Disco(disco)) + } else if let Ok(ibb) = ibb::parse_ibb(elem) { + Some(IqPayload::IBB(ibb)) } else if let Ok(jingle) = jingle::parse_jingle(elem) { Some(IqPayload::Jingle(jingle)) } else if let Ok(ping) = ping::parse_ping(elem) { @@ -135,6 +139,7 @@ pub fn parse_iq(root: &Element) -> Result { pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), + IqPayload::IBB(ref ibb) => ibb::serialise(ibb), IqPayload::Jingle(ref jingle) => jingle::serialise(jingle), IqPayload::Ping(_) => ping::serialise_ping(), } From b09c57ec727069fd6399eda919d7f59ece21e6b4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 01:31:39 +0100 Subject: [PATCH 0131/1020] ecaps2: Move hashing crates to the main library. --- src/ecaps2.rs | 10 +++------- src/lib.rs | 3 +++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index d5a13dd39f748b753bb4cf38b2b64954f8fce703..fade1f97f16d06edc1c9b2154ac2ed29a28221f3 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -1,7 +1,3 @@ -extern crate sha2; -extern crate sha3; -extern crate blake2; - use disco::{Feature, Identity, Disco}; use data_forms::DataForm; use hashes; @@ -11,9 +7,9 @@ use minidom::Element; use error::Error; use ns; -use self::sha2::{Sha256, Sha512}; -use self::sha3::{Sha3_256, Sha3_512}; -use self::blake2::Blake2b; +use sha2::{Sha256, Sha512}; +use sha3::{Sha3_256, Sha3_512}; +use blake2::Blake2b; use digest::{Digest, VariableOutput}; use base64; diff --git a/src/lib.rs b/src/lib.rs index a21168fe5a93f0c0067b2b4bde00638ae02ab686..19689510e84cd0cbda497508887ea1ac99d32697 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,9 @@ extern crate minidom; extern crate jid; extern crate base64; extern crate digest; +extern crate sha2; +extern crate sha3; +extern crate blake2; /// Error type returned by every parser on failure. pub mod error; From 8b964df645a856d25a07d808b24ddf78938b8bc8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 03:23:50 +0100 Subject: [PATCH 0132/1020] Add a stanza-id parser and serialiser. --- src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/stanza_id.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 src/stanza_id.rs diff --git a/src/lib.rs b/src/lib.rs index 19689510e84cd0cbda497508887ea1ac99d32697..e2a656b39d08761257466933e5d50597fb2df4dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,6 +74,9 @@ pub mod hashes; /// XEP-0308: Last Message Correction pub mod message_correct; +/// XEP-0359: Unique and Stable Stanza IDs +pub mod stanza_id; + /// XEP-0380: Explicit Message Encryption pub mod eme; diff --git a/src/ns.rs b/src/ns.rs index 415858c9e92801eb820e5c4b6e601243a7c84a4f..a34846370734d070eff6eabef48acf5c8f37fdfb 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -57,6 +57,9 @@ pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-nam /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; +/// XEP-0359: Unique and Stable Stanza IDs +pub const SID: &'static str = "urn:xmpp:sid:0"; + /// XEP-0380: Explicit Message Encryption pub const EME: &'static str = "urn:xmpp:eme:0"; diff --git a/src/stanza_id.rs b/src/stanza_id.rs new file mode 100644 index 0000000000000000000000000000000000000000..14d842b7c296baecf39714e6aa10c411ee0c9b9e --- /dev/null +++ b/src/stanza_id.rs @@ -0,0 +1,129 @@ +use minidom::Element; +use jid::Jid; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub enum StanzaId { + StanzaId { + id: String, + by: Jid, + }, + OriginId { + id: String, + }, +} + +pub fn parse_stanza_id(root: &Element) -> Result { + let is_stanza_id = root.is("stanza-id", ns::SID); + if !is_stanza_id && !root.is("origin-id", ns::SID) { + return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); + } + let id = match root.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in stanza-id or origin-id.")), + }; + Ok(if is_stanza_id { + let by = match root.attr("by") { + Some(by) => by.parse().unwrap(), + None => return Err(Error::ParseError("No 'by' attribute present in stanza-id.")), + }; + StanzaId::StanzaId { id, by } + } else { + StanzaId::OriginId { id } + }) +} + +pub fn serialise(stanza_id: &StanzaId) -> Element { + match *stanza_id { + StanzaId::StanzaId { ref id, ref by } => { + Element::builder("stanza-id") + .ns(ns::SID) + .attr("id", id.clone()) + .attr("by", String::from(by.clone())) + .build() + }, + StanzaId::OriginId { ref id } => { + Element::builder("origin-id") + .ns(ns::SID) + .attr("id", id.clone()) + .build() + }, + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use minidom::Element; + use jid::Jid; + use error::Error; + use stanza_id; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let stanza_id = stanza_id::parse_stanza_id(&elem).unwrap(); + if let stanza_id::StanzaId::StanzaId { id, by } = stanza_id { + assert_eq!(id, String::from("coucou")); + assert_eq!(by, Jid::from_str("coucou@coucou").unwrap()); + } else { + panic!(); + } + + let elem: Element = "".parse().unwrap(); + let stanza_id = stanza_id::parse_stanza_id(&elem).unwrap(); + if let stanza_id::StanzaId::OriginId { id } = stanza_id { + assert_eq!(id, String::from("coucou")); + } else { + panic!(); + } + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in stanza-id or origin-id element."); + } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No 'id' attribute present in stanza-id or origin-id."); + } + + #[test] + fn test_invalid_by() { + let elem: Element = "".parse().unwrap(); + let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No 'by' attribute present in stanza-id."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let stanza_id = stanza_id::StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; + let elem2 = stanza_id::serialise(&stanza_id); + assert_eq!(elem, elem2); + } +} From 4dc585f1c984e7af4027b5666f8f439430f4c46b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 03:50:02 +0100 Subject: [PATCH 0133/1020] delay: Implement IntoElements. --- src/delay.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/delay.rs b/src/delay.rs index 54fa7e87be4229ab8e7a6bc079de387d9618028f..6d0634a2a84ecb73e17133dd194be61c9833a81e 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -1,4 +1,5 @@ -use minidom::Element; +use minidom::{Element, IntoElements}; +use minidom::convert::ElementEmitter; use error::Error; @@ -40,6 +41,13 @@ pub fn serialise(delay: &Delay) -> Element { .build() } +impl IntoElements for Delay { + fn into_elements(self, emitter: &mut ElementEmitter) { + let elem = serialise(&self); + emitter.append_child(elem) + } +} + #[cfg(test)] mod tests { use minidom::Element; From d824a161b65c2064e9eddfd22de1ba6e057fd9a0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 03:50:20 +0100 Subject: [PATCH 0134/1020] message: Implement IntoElements. --- src/message.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/message.rs b/src/message.rs index 609aa77afe591f9020d91016ba05eff39bfa0d75..34dc49b5b61eb132af607a9c4b94d26ba53d2b03 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,7 +1,7 @@ use std::str::FromStr; -use minidom::Element; -use minidom::IntoAttributeValue; +use minidom::{Element, IntoElements, IntoAttributeValue}; +use minidom::convert::ElementEmitter; use jid::Jid; @@ -164,6 +164,13 @@ pub fn serialise(message: &Message) -> Element { stanza } +impl IntoElements for Message { + fn into_elements(self, emitter: &mut ElementEmitter) { + let elem = serialise(&self); + emitter.append_child(elem); + } +} + #[cfg(test)] mod tests { use std::str::FromStr; From 0f92a11894c0b5f86b4781c6b147bd7608c7b575 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 03:50:49 +0100 Subject: [PATCH 0135/1020] Add a forwarding parser and serialiser. --- src/forwarding.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ 3 files changed, 84 insertions(+) create mode 100644 src/forwarding.rs diff --git a/src/forwarding.rs b/src/forwarding.rs new file mode 100644 index 0000000000000000000000000000000000000000..d7925d7a9fbc212b78b1ae054a27e7295c584902 --- /dev/null +++ b/src/forwarding.rs @@ -0,0 +1,78 @@ +use minidom::{Element, IntoElements}; +use minidom::convert::ElementEmitter; + +use error::Error; + +use delay; +use message; + +use ns; + +#[derive(Debug, Clone)] +pub struct Forwarded { + pub delay: Option, + // XXX: really? Option? + pub stanza: Option, +} + +pub fn parse_forwarded(root: &Element) -> Result { + if !root.is("forwarded", ns::FORWARD) { + return Err(Error::ParseError("This is not a forwarded element.")); + } + let mut delay = None; + let mut stanza = None; + for child in root.children() { + if child.is("delay", ns::DELAY) { + delay = Some(delay::parse_delay(child)?); + } else if child.is("message", ns::JABBER_CLIENT) { + stanza = Some(message::parse_message(child)?); + // TODO: also handle the five other possibilities. + } else { + return Err(Error::ParseError("Unknown child in forwarded element.")); + } + } + Ok(Forwarded { + delay: delay, + stanza: stanza, + }) +} + +pub fn serialise(forwarded: &Forwarded) -> Element { + Element::builder("forwarded") + .ns(ns::FORWARD) + .append(forwarded.delay.clone()) + .append(forwarded.stanza.clone()) + .build() +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use forwarding; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + forwarding::parse_forwarded(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = forwarding::parse_forwarded(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in forwarded element."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let forwarded = forwarding::Forwarded { delay: None, stanza: None }; + let elem2 = forwarding::serialise(&forwarded); + assert_eq!(elem, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index e2a656b39d08761257466933e5d50597fb2df4dd..0dde87afdd43fb72e7e844b82736f58a6271d83d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,6 +68,9 @@ pub mod jingle_ft; /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub mod jingle_ibb; +/// XEP-0297: Stanza Forwarding +pub mod forwarding; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub mod hashes; diff --git a/src/ns.rs b/src/ns.rs index a34846370734d070eff6eabef48acf5c8f37fdfb..16f73394b92a143289840d80aa8bcbe6b5e59dbe 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -39,6 +39,9 @@ pub const JINGLE_FT_ERROR: &'static str = "urn:xmpp:jingle:apps:file-transfer:er /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; +/// XEP-0297: Stanza Forwarding +pub const FORWARD: &'static str = "urn:xmpp:forward:0"; + /// XEP-0300: Use of Cryptographic Hash Functions in XMPP pub const HASHES: &'static str = "urn:xmpp:hashes:2"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP From 72a6eee2fd9600c28bb70609b9911074fac509a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 04:37:18 +0100 Subject: [PATCH 0136/1020] Add a RSM parser and serialiser. --- src/lib.rs | 3 + src/ns.rs | 3 + src/rsm.rs | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 src/rsm.rs diff --git a/src/lib.rs b/src/lib.rs index 0dde87afdd43fb72e7e844b82736f58a6271d83d..81f62e2d6c89f9655bf4d21ab3e52f4623990843 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,9 @@ pub mod disco; /// XEP-0047: In-Band Bytestreams pub mod ibb; +/// XEP-0059: Result Set Management +pub mod rsm; + /// XEP-0085: Chat State Notifications pub mod chatstates; diff --git a/src/ns.rs b/src/ns.rs index 16f73394b92a143289840d80aa8bcbe6b5e59dbe..d06aff62351595f8d360450acf75c761c32fd1ba 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -10,6 +10,9 @@ pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; /// XEP-0047: In-Band Bytestreams pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; +/// XEP-0059: Result Set Management +pub const RSM: &'static str = "http://jabber.org/protocol/rsm"; + /// XEP-0085: Chat State Notifications pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; diff --git a/src/rsm.rs b/src/rsm.rs new file mode 100644 index 0000000000000000000000000000000000000000..a70bdd89dc6bbad35a60661f2d74aef3defabe3b --- /dev/null +++ b/src/rsm.rs @@ -0,0 +1,186 @@ +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct First { + pub index: Option, + pub base: String, +} + +#[derive(Debug, Clone)] +pub struct Set { + pub after: Option, + pub before: Option, + pub count: Option, + pub first: Option, + pub index: Option, + pub last: Option, + pub max: Option, +} + +pub fn parse_rsm(root: &Element) -> Result { + if !root.is("set", ns::RSM) { + return Err(Error::ParseError("This is not a RSM element.")); + } + let mut after = None; + let mut before = None; + let mut count = None; + let mut first = None; + let mut index = None; + let mut last = None; + let mut max = None; + for child in root.children() { + if child.is("after", ns::RSM) { + if after.is_some() { + return Err(Error::ParseError("Set can’t have more than one after.")); + } + after = Some(child.text()); + } else if child.is("before", ns::RSM) { + if before.is_some() { + return Err(Error::ParseError("Set can’t have more than one before.")); + } + before = Some(child.text()); + } else if child.is("count", ns::RSM) { + if count.is_some() { + return Err(Error::ParseError("Set can’t have more than one count.")); + } + count = Some(child.text().parse()?); + } else if child.is("first", ns::RSM) { + if first.is_some() { + return Err(Error::ParseError("Set can’t have more than one first.")); + } + first = Some(First { + index: match child.attr("index") { + Some(index) => Some(index.parse()?), + None => None, + }, + base: child.text(), + }); + } else if child.is("index", ns::RSM) { + if index.is_some() { + return Err(Error::ParseError("Set can’t have more than one index.")); + } + index = Some(child.text().parse()?); + } else if child.is("last", ns::RSM) { + if last.is_some() { + return Err(Error::ParseError("Set can’t have more than one last.")); + } + last = Some(child.text()); + } else if child.is("max", ns::RSM) { + if max.is_some() { + return Err(Error::ParseError("Set can’t have more than one max.")); + } + max = Some(child.text().parse()?); + } else { + return Err(Error::ParseError("Unknown child in set element.")); + } + } + Ok(Set { + after: after, + before: before, + count: count, + first: first, + index: index, + last: last, + max: max, + }) +} + +pub fn serialise(rsm: &Set) -> Element { + let mut elem = Element::builder("set") + .ns(ns::RSM) + .build(); + if rsm.after.is_some() { + elem.append_child(Element::builder("after").ns(ns::RSM).append(rsm.after.clone()).build()); + } + if rsm.before.is_some() { + elem.append_child(Element::builder("before").ns(ns::RSM).append(rsm.before.clone()).build()); + } + if rsm.count.is_some() { + elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", rsm.count.unwrap())).build()); + } + if rsm.first.is_some() { + let first = rsm.first.clone().unwrap(); + elem.append_child(Element::builder("first") + .ns(ns::RSM) + .attr("index", match first.index { + Some(index) => Some(format!("{}", index)), + None => None, + }) + .append(first.base.clone()).build()); + } + if rsm.index.is_some() { + elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", rsm.index.unwrap())).build()); + } + if rsm.last.is_some() { + elem.append_child(Element::builder("last").ns(ns::RSM).append(rsm.last.clone()).build()); + } + if rsm.max.is_some() { + elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", rsm.max.unwrap())).build()); + } + elem +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use rsm; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let set = rsm::parse_rsm(&elem).unwrap(); + assert_eq!(set.after, None); + assert_eq!(set.before, None); + assert_eq!(set.count, None); + match set.first { + Some(_) => panic!(), + None => (), + } + assert_eq!(set.index, None); + assert_eq!(set.last, None); + assert_eq!(set.max, None); + } + + #[test] + fn test_unknown() { + let elem: Element = "".parse().unwrap(); + let error = rsm::parse_rsm(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not a RSM element."); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = rsm::parse_rsm(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in set element."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let rsm = rsm::Set { + after: None, + before: None, + count: None, + first: None, + index: None, + last: None, + max: None, + }; + let elem2 = rsm::serialise(&rsm); + assert_eq!(elem, elem2); + } +} From 7cd4a49011fc3f0837eb0de50e7c5c7e5fe45026 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 06:06:41 +0100 Subject: [PATCH 0137/1020] error: Add JidParseError. --- src/error.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/error.rs b/src/error.rs index 07b01c199a792537bfdc4c5cc89b09715b8cc0d8..1f001ca5cd9141e24a375ba44cc49fd3a8fca8ad 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,7 @@ use std::num; use base64; use minidom; +use jid; #[derive(Debug)] pub enum Error { @@ -12,6 +13,7 @@ pub enum Error { XMLError(minidom::Error), Base64Error(base64::DecodeError), ParseIntError(num::ParseIntError), + JidParseError(jid::JidParseError), } impl From for Error { @@ -37,3 +39,9 @@ impl From for Error { Error::ParseIntError(err) } } + +impl From for Error { + fn from(err: jid::JidParseError) -> Error { + Error::JidParseError(err) + } +} From 0b2d46aa3aeae35ab3ef23af2db2f22f1fd82673 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 06:07:00 +0100 Subject: [PATCH 0138/1020] Add a MAM parser and serialiser. --- src/lib.rs | 3 + src/mam.rs | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 3 + 3 files changed, 289 insertions(+) create mode 100644 src/mam.rs diff --git a/src/lib.rs b/src/lib.rs index 81f62e2d6c89f9655bf4d21ab3e52f4623990843..e3b19caee8e942a700f449a810a5ccaa13088818 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,9 @@ pub mod hashes; /// XEP-0308: Last Message Correction pub mod message_correct; +/// XEP-0313: Message Archive Management +pub mod mam; + /// XEP-0359: Unique and Stable Stanza IDs pub mod stanza_id; diff --git a/src/mam.rs b/src/mam.rs new file mode 100644 index 0000000000000000000000000000000000000000..416aefc9ee65dad100c2091486d8627e2528a6d1 --- /dev/null +++ b/src/mam.rs @@ -0,0 +1,283 @@ +use minidom::Element; +use jid::Jid; + +use error::Error; + +use data_forms; +use data_forms::DataForm; +use rsm; +use rsm::Set; +use forwarding; +use forwarding::Forwarded; + +use ns; + +#[derive(Debug, Clone)] +pub struct Query { + pub queryid: Option, + pub node: Option, + pub form: Option, + pub set: Option, +} + +#[derive(Debug, Clone)] +pub struct Result_ { + pub queryid: String, + pub id: String, + pub forwarded: Forwarded, +} + +#[derive(Debug, Clone)] +pub struct Fin { + pub complete: bool, + pub set: Set, +} + +#[derive(Debug, Clone)] +pub enum DefaultPrefs { + Always, + Never, + Roster, +} + +#[derive(Debug, Clone)] +pub struct Prefs { + pub default_: Option, + pub always: Vec, + pub never: Vec, +} + +pub fn parse_query(root: &Element) -> Result { + if !root.is("query", ns::MAM) { + return Err(Error::ParseError("This is not a query element.")); + } + let mut form = None; + let mut set = None; + for child in root.children() { + if child.is("x", ns::DATA_FORMS) { + form = Some(data_forms::parse_data_form(child)?); + } else if child.is("set", ns::RSM) { + set = Some(rsm::parse_rsm(child)?); + } else { + return Err(Error::ParseError("Unknown child in query element.")); + } + } + let queryid = match root.attr("queryid") { + Some(queryid) => Some(queryid.to_owned()), + None => None, + }; + let node = match root.attr("node") { + Some(node) => Some(node.to_owned()), + None => None, + }; + Ok(Query { queryid, node, form, set }) +} + +pub fn parse_result(root: &Element) -> Result { + if !root.is("result", ns::MAM) { + return Err(Error::ParseError("This is not a result element.")); + } + let mut forwarded = None; + for child in root.children() { + if child.is("forwarded", ns::FORWARD) { + forwarded = Some(forwarding::parse_forwarded(child)?); + } else { + return Err(Error::ParseError("Unknown child in result element.")); + } + } + let queryid = match root.attr("queryid") { + Some(queryid) => queryid.to_owned(), + None => return Err(Error::ParseError("No 'queryid' attribute present in result.")), + }; + let id = match root.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in result.")), + }; + if forwarded.is_none() { + return Err(Error::ParseError("Mandatory forwarded element missing in result.")); + } + let forwarded = forwarded.unwrap(); + Ok(Result_ { + queryid, + id, + forwarded, + }) +} + +pub fn parse_fin(root: &Element) -> Result { + if !root.is("fin", ns::MAM) { + return Err(Error::ParseError("This is not a fin element.")); + } + let mut set = None; + for child in root.children() { + if child.is("set", ns::RSM) { + set = Some(rsm::parse_rsm(child)?); + } else { + return Err(Error::ParseError("Unknown child in fin element.")); + } + } + let complete = match root.attr("complete") { + Some(complete) => complete == "true", + None => false, + }; + if set.is_none() { + return Err(Error::ParseError("Mandatory set element missing in fin.")); + } + let set = set.unwrap(); + Ok(Fin { complete, set }) +} + +pub fn parse_prefs(root: &Element) -> Result { + if !root.is("prefs", ns::MAM) { + return Err(Error::ParseError("This is not a prefs element.")); + } + let mut always = vec!(); + let mut never = vec!(); + for child in root.children() { + if child.is("always", ns::MAM) { + for jid_elem in child.children() { + if !jid_elem.is("jid", ns::MAM) { + return Err(Error::ParseError("Invalid jid element in always.")); + } + always.push(jid_elem.text().parse()?); + } + } else if child.is("never", ns::MAM) { + for jid_elem in child.children() { + if !jid_elem.is("jid", ns::MAM) { + return Err(Error::ParseError("Invalid jid element in never.")); + } + never.push(jid_elem.text().parse()?); + } + } else { + return Err(Error::ParseError("Unknown child in prefs element.")); + } + } + let default_ = match root.attr("default") { + Some("always") => Some(DefaultPrefs::Always), + Some("never") => Some(DefaultPrefs::Never), + Some("roster") => Some(DefaultPrefs::Roster), + None => None, + + _ => return Err(Error::ParseError("Invalid 'default' attribute present in prefs.")), + }; + Ok(Prefs { default_, always, never }) +} + +pub fn serialise_query(query: &Query) -> Element { + let mut elem = Element::builder("query") + .ns(ns::MAM) + .attr("queryid", query.queryid.clone()) + .attr("node", query.node.clone()) + .build(); + //if let Some(form) = query.form { + // elem.append_child(data_forms::serialise(&form)); + //} + if let Some(ref set) = query.set { + elem.append_child(rsm::serialise(&set)); + } + elem +} + +pub fn serialise_result(result: &Result_) -> Element { + let mut elem = Element::builder("result") + .ns(ns::MAM) + .attr("queryid", result.queryid.clone()) + .attr("id", result.id.clone()) + .build(); + elem.append_child(forwarding::serialise(&result.forwarded)); + elem +} + +pub fn serialise_fin(fin: &Fin) -> Element { + let mut elem = Element::builder("fin") + .ns(ns::MAM) + .attr("complete", match fin.complete { + true => Some("true"), + false => None, + }) + .build(); + elem.append_child(rsm::serialise(&fin.set)); + elem +} + +pub fn serialise_prefs(prefs: &Prefs) -> Element { + let mut elem = Element::builder("prefs") + .ns(ns::MAM) + .attr("default", match prefs.default_ { + Some(DefaultPrefs::Always) => Some("always"), + Some(DefaultPrefs::Never) => Some("never"), + Some(DefaultPrefs::Roster) => Some("roster"), + None => None, + }) + .build(); + if !prefs.always.is_empty() { + let mut always = Element::builder("always") + .ns(ns::RSM) + .build(); + for jid in prefs.always.clone() { + always.append_child(Element::builder("jid") + .ns(ns::RSM) + .append(String::from(jid)) + .build()); + } + elem.append_child(always); + } + if !prefs.never.is_empty() { + let mut never = Element::builder("never") + .ns(ns::RSM) + .build(); + for jid in prefs.never.clone() { + never.append_child(Element::builder("jid") + .ns(ns::RSM) + .append(String::from(jid)) + .build()); + } + elem.append_child(never); + } + elem +} + +#[cfg(test)] +mod tests { + /* + use minidom::Element; + use error::Error; + use mam; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + mam::parse_query(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = mam::parse_query(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in replace element."); + } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = mam::parse_query(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No 'id' attribute present in replace."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let replace = mam::Query { id: String::from("coucou") }; + let elem2 = mam::serialise(&replace); + assert_eq!(elem, elem2); + } + */ +} diff --git a/src/ns.rs b/src/ns.rs index d06aff62351595f8d360450acf75c761c32fd1ba..b9674c0fc3e2715d028dee178a842338134fda67 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -63,6 +63,9 @@ pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-nam /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; +/// XEP-0313: Message Archive Management +pub const MAM: &'static str = "urn:xmpp:mam:2"; + /// XEP-0359: Unique and Stable Stanza IDs pub const SID: &'static str = "urn:xmpp:sid:0"; From eecb47f4eddf162aa213f2a6fe1745995a9f868b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 06:41:55 +0100 Subject: [PATCH 0139/1020] mam: Add tests. --- src/mam.rs | 117 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 18 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index 416aefc9ee65dad100c2091486d8627e2528a6d1..4c0441beed30322f8b6d121b9bac64954e5e2c79 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -239,45 +239,126 @@ pub fn serialise_prefs(prefs: &Prefs) -> Element { #[cfg(test)] mod tests { - /* use minidom::Element; use error::Error; use mam; #[test] - fn test_simple() { - let elem: Element = "".parse().unwrap(); + fn test_query() { + let elem: Element = "".parse().unwrap(); mam::parse_query(&elem).unwrap(); } #[test] - fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = mam::parse_query(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in replace element."); + fn test_result() { + let elem: Element = r#" + + + + + Hail to thee + + + +"#.parse().unwrap(); + mam::parse_result(&elem).unwrap(); + } + + #[test] + fn test_fin() { + let elem: Element = r#" + + + 28482-98726-73623 + 09af3-cc343-b409f + + +"#.parse().unwrap(); + mam::parse_fin(&elem).unwrap(); + } + + #[test] + fn test_query_x() { + let elem: Element = r#" + + + + urn:xmpp:mam:2 + + + juliet@capulet.lit + + + +"#.parse().unwrap(); + mam::parse_query(&elem).unwrap(); } #[test] - fn test_invalid_id() { - let elem: Element = "".parse().unwrap(); + fn test_query_x_set() { + let elem: Element = r#" + + + + urn:xmpp:mam:2 + + + 2010-08-07T00:00:00Z + + + + 10 + + +"#.parse().unwrap(); + mam::parse_query(&elem).unwrap(); + } + + #[test] + fn test_prefs_get() { + let elem: Element = "".parse().unwrap(); + mam::parse_prefs(&elem).unwrap(); + + let elem: Element = r#" + + + + +"#.parse().unwrap(); + mam::parse_prefs(&elem).unwrap(); + } + + #[test] + fn test_prefs_result() { + let elem: Element = r#" + + + romeo@montague.lit + + + montague@montague.lit + + +"#.parse().unwrap(); + mam::parse_prefs(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); let error = mam::parse_query(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "No 'id' attribute present in replace."); + assert_eq!(message, "Unknown child in query element."); } #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); - let replace = mam::Query { id: String::from("coucou") }; - let elem2 = mam::serialise(&replace); + let elem: Element = "".parse().unwrap(); + let replace = mam::Query { queryid: None, node: None, form: None, set: None }; + let elem2 = mam::serialise_query(&replace); assert_eq!(elem, elem2); } - */ } From 61efeb827fcfcacfbb01e39ebe1c62aeaeb82976 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 06:50:57 +0100 Subject: [PATCH 0140/1020] forwarding: Remove unused imports. --- src/forwarding.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/forwarding.rs b/src/forwarding.rs index d7925d7a9fbc212b78b1ae054a27e7295c584902..d1d47a52d3eb121abf1e4b9f47ee1288891c6ed9 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -1,5 +1,4 @@ -use minidom::{Element, IntoElements}; -use minidom::convert::ElementEmitter; +use minidom::Element; use error::Error; From 7750aae5b54d8bf5dbe301c9e9f174a5cdb558a9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 06:57:20 +0100 Subject: [PATCH 0141/1020] presence, message, jingle_ft: Remove unneeded println!(). --- src/jingle_ft.rs | 1 - src/message.rs | 15 +++++---------- src/presence.rs | 6 ++---- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 4d5a86a687a4bce490df12bbb415ca6851497512..71b671c8cd2cd21886e530eb48e8d4dbf5e719a0 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -173,7 +173,6 @@ pub fn serialise_file(file: &File) -> Element { let mut root = Element::builder("file") .ns(ns::JINGLE_FT) .build(); - println!("{:#?}", file); if let Some(ref date) = file.date { root.append_child(Element::builder("date") .ns(ns::JINGLE_FT) diff --git a/src/message.rs b/src/message.rs index 34dc49b5b61eb132af607a9c4b94d26ba53d2b03..a915b2217dd41ae2ddcd82511fe109dbfc6dde56 100644 --- a/src/message.rs +++ b/src/message.rs @@ -192,31 +192,27 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let message = message::parse_message(&elem).unwrap(); - let message2 = message::Message { + let message = message::Message { from: None, to: None, id: None, type_: message::MessageType::Normal, payloads: vec!(), }; - let elem2 = message::serialise(&message2); + let elem2 = message::serialise(&message); assert_eq!(elem, elem2); - println!("{:#?}", message); } #[test] fn test_body() { let elem: Element = "Hello world!".parse().unwrap(); - let message = message::parse_message(&elem).unwrap(); - println!("{:#?}", message); + message::parse_message(&elem).unwrap(); } #[test] fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); - let message = message::parse_message(&elem).unwrap(); - let message2 = message::Message { + let message = message::Message { from: None, to: Some(Jid::from_str("coucou@example.org").unwrap()), id: None, @@ -225,9 +221,8 @@ mod tests { message::MessagePayloadType::Parsed(message::MessagePayload::Body("Hello world!".to_owned())), ), }; - let elem2 = message::serialise(&message2); + let elem2 = message::serialise(&message); assert_eq!(elem, elem2); - println!("{:#?}", message); } #[test] diff --git a/src/presence.rs b/src/presence.rs index ea2da4a3fcdb1c0932febdc5b2ebafa628001ae2..a144e3716755281674d09a3dd3f8fa85252ccb71 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -174,16 +174,14 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); - let presence2 = presence::Presence { + let presence = presence::Presence { from: None, to: None, id: None, type_: presence::PresenceType::Unavailable, payloads: vec!(), }; - let elem2 = presence::serialise(&presence2); + let elem2 = presence::serialise(&presence); assert_eq!(elem, elem2); - println!("{:#?}", presence); } } From 6f2ee2f0ad846ec9d00ee5f70fc27ce821232df7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 21:41:18 +0100 Subject: [PATCH 0142/1020] Cargo.toml: Add some metadata. --- Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index e236251d960c3c6b32654e879805a25c03c29f99..7118c2037a0fee52a3a6af99c2bfc13612b54ff7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,11 @@ name = "xmpp-parsers" version = "0.1.0" authors = ["Emmanuel Gil Peyrot "] +description = "Collection of parsers and serialisers for XMPP extensions" +homepage = "https://hg.linkmauve.fr/xmpp-parsers" +repository = "https://hg.linkmauve.fr/xmpp-parsers" +keywords = ["xmpp"] +categories = ["parsing", "network-programming"] [dependencies] minidom = "0.2.0" From 407e4cceb4561135eac38aef5644d5ef4891a590 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 22:14:34 +0100 Subject: [PATCH 0143/1020] License the project under MPL-2.0. --- Cargo.toml | 2 + LICENSE | 373 +++++++++++++++++++++++++++++++++++++++++ src/attention.rs | 6 + src/body.rs | 6 + src/chatstates.rs | 6 + src/data_forms.rs | 6 + src/delay.rs | 6 + src/disco.rs | 6 + src/ecaps2.rs | 6 + src/eme.rs | 6 + src/error.rs | 6 + src/forwarding.rs | 6 + src/hashes.rs | 6 + src/ibb.rs | 6 + src/iq.rs | 7 + src/jingle.rs | 6 + src/jingle_ft.rs | 6 + src/jingle_ibb.rs | 6 + src/lib.rs | 6 + src/mam.rs | 6 + src/media_element.rs | 6 + src/message.rs | 6 + src/message_correct.rs | 6 + src/ns.rs | 6 + src/ping.rs | 7 + src/presence.rs | 6 + src/receipts.rs | 6 + src/rsm.rs | 6 + src/stanza_id.rs | 6 + src/status.rs | 6 + 30 files changed, 545 insertions(+) create mode 100644 LICENSE diff --git a/Cargo.toml b/Cargo.toml index 7118c2037a0fee52a3a6af99c2bfc13612b54ff7..bef0ab8f8a820eaa3bb5131f585c07aa0704f6d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ homepage = "https://hg.linkmauve.fr/xmpp-parsers" repository = "https://hg.linkmauve.fr/xmpp-parsers" keywords = ["xmpp"] categories = ["parsing", "network-programming"] +license = "MPL-2.0" +license-file = "LICENSE" [dependencies] minidom = "0.2.0" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..14e2f777f6c395e7e04ab4aa306bbcc4b0c1120e --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/src/attention.rs b/src/attention.rs index f64dbf5bddcda2f09edb94747509103a0e6778f0..2f12545ea436cbea67d58fcc9f6c4a449d5dc18c 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/body.rs b/src/body.rs index 7703d655c48a444a753f254c6447b083911128a4..8f23b56927dad6d04827aad39bc06e2ff92d403f 100644 --- a/src/body.rs +++ b/src/body.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/chatstates.rs b/src/chatstates.rs index 7632086e40f2d052af84ef2159a73de1485b1eb0..d26791ef67968e9f0a2540a5e8eb1b0f2c1c7f4b 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/data_forms.rs b/src/data_forms.rs index f25c88188d1ab3d46456172a53301ada6f02712d..ebd2ff2df3dffdc3818ee645d9fe47d7381c970d 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + extern crate minidom; use std::str::FromStr; diff --git a/src/delay.rs b/src/delay.rs index 6d0634a2a84ecb73e17133dd194be61c9833a81e..8e8e382ea2276189c27c2506445492cb29a5550e 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::{Element, IntoElements}; use minidom::convert::ElementEmitter; diff --git a/src/disco.rs b/src/disco.rs index 5961518c12de4bb80cd6f4b170e4219a927bcc61..5bdaef8b0ddd3c664674bcacf6311d87b136e5e6 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + extern crate minidom; use minidom::Element; diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fade1f97f16d06edc1c9b2154ac2ed29a28221f3..aa2cde7e10ca423e351ecb7b71baae6effe1c894 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use disco::{Feature, Identity, Disco}; use data_forms::DataForm; use hashes; diff --git a/src/eme.rs b/src/eme.rs index 79d8c89b58c60b90f198053ced988e9ea3aaaf24..879ddda0c6684f4f20992c8c21b6a0de3c59d813 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/error.rs b/src/error.rs index 1f001ca5cd9141e24a375ba44cc49fd3a8fca8ad..a65f7e2df4aab5c0355fe569139a0af4007a6d12 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use std::convert::From; use std::io; use std::num; diff --git a/src/forwarding.rs b/src/forwarding.rs index d1d47a52d3eb121abf1e4b9f47ee1288891c6ed9..991d508ada8a4e16e622d0360cb9e42a36b0b000 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/hashes.rs b/src/hashes.rs index febf8b3f7ec65ecf1c08750f9b704c50cfe17bd9..c489a32fcde701f203f219409bd372a3d6d76a2b 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/ibb.rs b/src/ibb.rs index 7152d722ce4f7058b16d5c6940d23a811b5fcd47..d805eb810af168f1e0f416d952cf2d399e71754e 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; diff --git a/src/iq.rs b/src/iq.rs index e2ac6b356717c5f4d51ac92605f83b52417c124e..645c4ba0f96e97d0ad066ab675f6545c5e71642d 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -1,3 +1,10 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use minidom::IntoAttributeValue; diff --git a/src/jingle.rs b/src/jingle.rs index 4c7a17c9f162ad5dfd3174ff1ed9d2ad13b23a49..bd903d084edca8260fd64ec8b1993e372f8c0873 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + extern crate minidom; use std::str::FromStr; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 71b671c8cd2cd21886e530eb48e8d4dbf5e719a0..678bca48359839072c87f947bb4f26515caffac9 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + extern crate minidom; use hashes; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index d6f009a9d6041c586d68863390df9b340f50e0ef..8e0d04ca90b9f4dd6effecaf6d133cd2951cd806 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use std::str::FromStr; use minidom::Element; diff --git a/src/lib.rs b/src/lib.rs index e3b19caee8e942a700f449a810a5ccaa13088818..aadf4f79c9188a066567bc1a06e1672906828822 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,12 @@ //! Parsed structs can then be manipulated internally, and serialised back //! before being sent over the wire. +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + extern crate minidom; extern crate jid; extern crate base64; diff --git a/src/mam.rs b/src/mam.rs index 4c0441beed30322f8b6d121b9bac64954e5e2c79..cb0cc7ac048dce7a73f86daf56d53999bf37d33c 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use jid::Jid; diff --git a/src/media_element.rs b/src/media_element.rs index b48636a507ce0a80ac7e3ec252690b8eb60161a1..a3c360e576d91cffb4bb9aa8b9a2995fdefe45eb 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/message.rs b/src/message.rs index a915b2217dd41ae2ddcd82511fe109dbfc6dde56..a88db4c9d7504c8fb0ba1b211b5bb514b1306005 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue}; diff --git a/src/message_correct.rs b/src/message_correct.rs index 09596ec078b7b0d4f7b4542cd4192056ca8a71f3..27ac7e917fcad7f8186800e8c7855eac95a99d17 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/ns.rs b/src/ns.rs index b9674c0fc3e2715d028dee178a842338134fda67..495c2856681576a1f605128bc7973efed83125f2 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const JABBER_CLIENT: &'static str = "jabber:client"; diff --git a/src/ping.rs b/src/ping.rs index 1d5c89ac52ef77a78de5a305e6b3f674beac1f00..b6b6f617d1c60b3854e0ef3c1af806ee0d310422 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -1,3 +1,10 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/presence.rs b/src/presence.rs index a144e3716755281674d09a3dd3f8fa85252ccb71..0f4379584339b7260959432df5a6e0de4c39abdd 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use std::str::FromStr; use minidom::Element; diff --git a/src/receipts.rs b/src/receipts.rs index 8eb07ff991a5d6f1fefa1c08e5d3ca38fb77691e..3dc3b787606aaf2a08ca9506f4a9a7e30abff7ff 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/rsm.rs b/src/rsm.rs index a70bdd89dc6bbad35a60661f2d74aef3defabe3b..d23fa1b6bc58919b080182f28627e2c67be73460 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 14d842b7c296baecf39714e6aa10c411ee0c9b9e..5d34c3dc5c44b4bb47399e6543167d26572566a4 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use jid::Jid; diff --git a/src/status.rs b/src/status.rs index 6412ffc05a558ce53782866f6345c7a8a1e7afd8..66bde73e8ff3348838d2b66f69a3caac7d401b33 100644 --- a/src/status.rs +++ b/src/status.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::Element; use error::Error; From 4b90c1dc1f467054ef13880388797abfa2dc9f22 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Apr 2017 22:10:38 +0100 Subject: [PATCH 0144/1020] =?UTF-8?q?data=5Fforms,=20disco,=20jingle,=20ji?= =?UTF-8?q?ngle=5Fft:=20Remove=20superfluous=20=E2=80=9Cextern=20crate?= =?UTF-8?q?=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data_forms.rs | 2 -- src/disco.rs | 2 -- src/jingle.rs | 2 -- src/jingle_ft.rs | 2 -- 4 files changed, 8 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index ebd2ff2df3dffdc3818ee645d9fe47d7381c970d..3b3757efea29cd00abeb4ccac66b6b9d0afe8b2a 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -extern crate minidom; - use std::str::FromStr; use minidom::Element; diff --git a/src/disco.rs b/src/disco.rs index 5bdaef8b0ddd3c664674bcacf6311d87b136e5e6..bc448091b830ee903356d444b05c1d046f1bd975 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -extern crate minidom; - use minidom::Element; use error::Error; diff --git a/src/jingle.rs b/src/jingle.rs index bd903d084edca8260fd64ec8b1993e372f8c0873..717a3b77217ebdc8b6f290c60e7df9ce348e86ce 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -extern crate minidom; - use std::str::FromStr; use minidom::{Element, IntoElements}; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 678bca48359839072c87f947bb4f26515caffac9..2028fe930d81db97d0de164b921baa5fbb5249ef 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -extern crate minidom; - use hashes; use hashes::{Hash, parse_hash}; From 0e20810a832adf1523fb3f4056d3b8b1a9d0abe1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 20:33:53 +0100 Subject: [PATCH 0145/1020] status, presence: Merge status into presence. --- src/lib.rs | 2 - src/presence.rs | 108 +++++++++++++++++++++++++++++++++++++++++------- src/status.rs | 86 -------------------------------------- 3 files changed, 93 insertions(+), 103 deletions(-) delete mode 100644 src/status.rs diff --git a/src/lib.rs b/src/lib.rs index aadf4f79c9188a066567bc1a06e1672906828822..0a5eac0ad7150d5d6954fbc86b146b1b8ea7f1b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,8 +35,6 @@ pub mod iq; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod body; -/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence -pub mod status; /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/presence.rs b/src/presence.rs index 0f4379584339b7260959432df5a6e0de4c39abdd..cafb7f6e92c8409ff6ec3476c51fa154faca2209 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -15,14 +15,15 @@ use error::Error; use ns; -use status; use delay; use ecaps2; +pub type Status = String; + /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { - Status(status::Status), + Status(Status), Delay(delay::Delay), ECaps2(ecaps2::ECaps2), } @@ -112,19 +113,25 @@ pub fn parse_presence(root: &Element) -> Result { }; let mut payloads = vec!(); for elem in root.children() { - let payload = if let Ok(status) = status::parse_status(elem) { - Some(PresencePayload::Status(status)) - } else if let Ok(delay) = delay::parse_delay(elem) { - Some(PresencePayload::Delay(delay)) - } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { - Some(PresencePayload::ECaps2(ecaps2)) + if elem.is("status", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in status element.")); + } + let payload = PresencePayload::Status(elem.text()); + payloads.push(PresencePayloadType::Parsed(payload)); } else { - None - }; - payloads.push(match payload { - Some(payload) => PresencePayloadType::Parsed(payload), - None => PresencePayloadType::XML(elem.clone()), - }); + let payload = if let Ok(delay) = delay::parse_delay(elem) { + Some(PresencePayload::Delay(delay)) + } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { + Some(PresencePayload::ECaps2(ecaps2)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => PresencePayloadType::Parsed(payload), + None => PresencePayloadType::XML(elem.clone()), + }); + } } Ok(Presence { from: from, @@ -137,7 +144,12 @@ pub fn parse_presence(root: &Element) -> Result { pub fn serialise_payload(payload: &PresencePayload) -> Element { match *payload { - PresencePayload::Status(ref status) => status::serialise(status), + PresencePayload::Status(ref status) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(status.to_owned()) + .build() + }, PresencePayload::Delay(ref delay) => delay::serialise(delay), PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } @@ -164,7 +176,9 @@ pub fn serialise(presence: &Presence) -> Element { #[cfg(test)] mod tests { use minidom::Element; + use error::Error; use presence; + use ns; #[test] fn test_simple() { @@ -190,4 +204,68 @@ mod tests { let elem2 = presence::serialise(&presence); assert_eq!(elem, elem2); } + + #[test] + fn test_status() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.payloads.len(), 1); + match presence.payloads[0] { + presence::PresencePayloadType::Parsed(presence::PresencePayload::Status(ref status)) => { + assert_eq!(*status, presence::Status::from("")); + }, + _ => panic!("Failed to parse status presence."), + } + } + + #[test] + fn test_unknown_child() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + if let presence::PresencePayloadType::XML(ref payload) = presence.payloads[0] { + assert!(payload.is("test", "invalid")); + } else { + panic!("Did successfully parse an invalid element."); + } + } + + #[test] + #[ignore] + fn test_invalid_status_child() { + let elem: Element = "".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in status element."); + } + + #[test] + #[ignore] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in status element."); + } + + #[test] + fn test_serialise_status() { + let status = presence::Status::from("Hello world!"); + let payloads = vec!(presence::PresencePayloadType::Parsed(presence::PresencePayload::Status(status))); + let presence = presence::Presence { + from: None, + to: None, + id: None, + type_: presence::PresenceType::Unavailable, + payloads: payloads, + }; + let elem = presence::serialise(&presence); + assert!(elem.is("presence", ns::JABBER_CLIENT)); + assert!(elem.children().collect::>()[0].is("status", ns::JABBER_CLIENT)); + } } diff --git a/src/status.rs b/src/status.rs deleted file mode 100644 index 66bde73e8ff3348838d2b66f69a3caac7d401b33..0000000000000000000000000000000000000000 --- a/src/status.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -use minidom::Element; - -use error::Error; - -use ns; - -pub type Status = String; - -pub fn parse_status(root: &Element) -> Result { - // TODO: also support components and servers. - if !root.is("status", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not a status element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in status element.")); - } - Ok(root.text()) -} - -pub fn serialise(status: &Status) -> Element { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(status.to_owned()) - .build() -} - -#[cfg(test)] -mod tests { - use minidom::Element; - use error::Error; - use status; - use ns; - - #[test] - fn test_simple() { - let elem: Element = "".parse().unwrap(); - status::parse_status(&elem).unwrap(); - } - - #[test] - fn test_invalid() { - let elem: Element = "".parse().unwrap(); - let error = status::parse_status(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "This is not a status element."); - } - - #[test] - fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = status::parse_status(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in status element."); - } - - #[test] - #[ignore] - fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = status::parse_status(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in status element."); - } - - #[test] - fn test_serialise() { - let status = status::Status::from("Hello world!"); - let elem = status::serialise(&status); - assert!(elem.is("status", ns::JABBER_CLIENT)); - } -} From 4ec3898c2ff4ffaba27a639904ff71d8bfd45e8e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 21:03:04 +0100 Subject: [PATCH 0146/1020] presence: Implement show parsing. --- src/presence.rs | 83 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index cafb7f6e92c8409ff6ec3476c51fa154faca2209..ad6301ebc729eef58e6bdfd7709dc36900c75e83 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -6,8 +6,8 @@ use std::str::FromStr; -use minidom::Element; -use minidom::IntoAttributeValue; +use minidom::{Element, IntoElements, IntoAttributeValue}; +use minidom::convert::ElementEmitter; use jid::Jid; @@ -18,11 +18,32 @@ use ns; use delay; use ecaps2; +#[derive(Debug, Clone, PartialEq)] +pub enum Show { + Away, + Chat, + Dnd, + Xa, +} + +impl IntoElements for Show { + fn into_elements(self, emitter: &mut ElementEmitter) { + let elem = Element::builder(match self { + Show::Away => "away", + Show::Chat => "chat", + Show::Dnd => "dnd", + Show::Xa => "xa", + }).build(); + emitter.append_child(elem); + } +} + pub type Status = String; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { + Show(Show), Status(Status), Delay(delay::Delay), ECaps2(ecaps2::ECaps2), @@ -113,7 +134,20 @@ pub fn parse_presence(root: &Element) -> Result { }; let mut payloads = vec!(); for elem in root.children() { - if elem.is("status", ns::JABBER_CLIENT) { + if elem.is("show", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in show element.")); + } + let payload = PresencePayload::Show(match elem.text().as_ref() { + "away" => Show::Away, + "chat" => Show::Chat, + "dnd" => Show::Dnd, + "xa" => Show::Xa, + + _ => return Err(Error::ParseError("Invalid value for show.")), + }); + payloads.push(PresencePayloadType::Parsed(payload)); + } else if elem.is("status", ns::JABBER_CLIENT) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } @@ -144,6 +178,12 @@ pub fn parse_presence(root: &Element) -> Result { pub fn serialise_payload(payload: &PresencePayload) -> Element { match *payload { + PresencePayload::Show(ref show) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(show.to_owned()) + .build() + }, PresencePayload::Status(ref status) => { Element::builder("status") .ns(ns::JABBER_CLIENT) @@ -205,6 +245,43 @@ mod tests { assert_eq!(elem, elem2); } + #[test] + fn test_show() { + let elem: Element = "chat".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.payloads.len(), 1); + match presence.payloads[0] { + presence::PresencePayloadType::Parsed(presence::PresencePayload::Show(ref show)) => { + assert_eq!(*show, presence::Show::Chat); + }, + _ => panic!("Failed to parse show presence."), + } + } + + #[test] + fn test_missing_show_value() { + // "online" used to be a pretty common mistake. + let elem: Element = "".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Invalid value for show."); + } + + #[test] + fn test_invalid_show() { + // "online" used to be a pretty common mistake. + let elem: Element = "online".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Invalid value for show."); + } + #[test] fn test_status() { let elem: Element = "".parse().unwrap(); From 21398447e7fe96c287aeed3bc448857fa80b24af Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 21:29:55 +0100 Subject: [PATCH 0147/1020] presence: Implement priority parsing. --- src/presence.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index ad6301ebc729eef58e6bdfd7709dc36900c75e83..3f298333c99592d203e08ea9534ee522c57b8621 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -40,11 +40,14 @@ impl IntoElements for Show { pub type Status = String; +pub type Priority = i8; + /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { Show(Show), Status(Status), + Priority(Priority), Delay(delay::Delay), ECaps2(ecaps2::ECaps2), } @@ -153,6 +156,13 @@ pub fn parse_presence(root: &Element) -> Result { } let payload = PresencePayload::Status(elem.text()); payloads.push(PresencePayloadType::Parsed(payload)); + } else if elem.is("priority", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in priority element.")); + } + let priority = Priority::from_str(elem.text().as_ref())?; + let payload = PresencePayload::Priority(priority); + payloads.push(PresencePayloadType::Parsed(payload)); } else { let payload = if let Ok(delay) = delay::parse_delay(elem) { Some(PresencePayload::Delay(delay)) @@ -190,6 +200,12 @@ pub fn serialise_payload(payload: &PresencePayload) -> Element { .append(status.to_owned()) .build() }, + PresencePayload::Priority(ref priority) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(format!("{}", priority)) + .build() + }, PresencePayload::Delay(ref delay) => delay::serialise(delay), PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } @@ -295,6 +311,29 @@ mod tests { } } + #[test] + fn test_priority() { + let elem: Element = "-1".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.payloads.len(), 1); + match presence.payloads[0] { + presence::PresencePayloadType::Parsed(presence::PresencePayload::Priority(ref priority)) => { + assert_eq!(*priority, presence::Priority::from(-1i8)); + }, + _ => panic!("Failed to parse priority."), + } + } + + #[test] + fn test_invalid_priority() { + let elem: Element = "128".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + match error { + Error::ParseIntError(_) => (), + _ => panic!(), + }; + } + #[test] fn test_unknown_child() { let elem: Element = "".parse().unwrap(); From 0288b937df2ba5ba2e89a389fdba0d8087f9061e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 21:44:02 +0100 Subject: [PATCH 0148/1020] Simplify the Display implementation. --- src/lib.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fead7e730ad0b595c7ad28dad9c48fae5d1c7f77..37d43fa02127dbbdcb38c47623b45d6affc8eb39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,14 +52,7 @@ impl From for String { impl fmt::Display for Jid { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - // TODO: may need escaping - if let Some(ref node) = self.node { - write!(fmt, "{}@", node)?; - } - write!(fmt, "{}", self.domain)?; - if let Some(ref resource) = self.resource { - write!(fmt, "/{}", resource)?; - } + fmt.write_str(String::from(self.clone()).as_ref())?; Ok(()) } } From 1909ae33dcd64f729c37bbe53835bf2567b188e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 22:00:29 +0100 Subject: [PATCH 0149/1020] presence: make show, statuses and priority first-class elements. --- src/presence.rs | 80 +++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 3f298333c99592d203e08ea9534ee522c57b8621..13517929a17fffea7b2f3a5a1be301c614ccfea3 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -118,6 +118,9 @@ pub struct Presence { pub to: Option, pub id: Option, pub type_: PresenceType, + pub show: Option, + pub statuses: Vec, + pub priority: Priority, pub payloads: Vec, } @@ -135,13 +138,19 @@ pub fn parse_presence(root: &Element) -> Result { Some(type_) => type_.parse()?, None => Default::default(), }; + let mut show = None; + let mut statuses = vec!(); + let mut priority = None; let mut payloads = vec!(); for elem in root.children() { if elem.is("show", ns::JABBER_CLIENT) { + if show.is_some() { + return Err(Error::ParseError("More than one show element in a presence.")); + } for _ in elem.children() { return Err(Error::ParseError("Unknown child in show element.")); } - let payload = PresencePayload::Show(match elem.text().as_ref() { + show = Some(match elem.text().as_ref() { "away" => Show::Away, "chat" => Show::Chat, "dnd" => Show::Dnd, @@ -149,20 +158,19 @@ pub fn parse_presence(root: &Element) -> Result { _ => return Err(Error::ParseError("Invalid value for show.")), }); - payloads.push(PresencePayloadType::Parsed(payload)); } else if elem.is("status", ns::JABBER_CLIENT) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } - let payload = PresencePayload::Status(elem.text()); - payloads.push(PresencePayloadType::Parsed(payload)); + statuses.push(elem.text()); } else if elem.is("priority", ns::JABBER_CLIENT) { + if priority.is_some() { + return Err(Error::ParseError("More than one priority element in a presence.")); + } for _ in elem.children() { return Err(Error::ParseError("Unknown child in priority element.")); } - let priority = Priority::from_str(elem.text().as_ref())?; - let payload = PresencePayload::Priority(priority); - payloads.push(PresencePayloadType::Parsed(payload)); + priority = Some(Priority::from_str(elem.text().as_ref())?); } else { let payload = if let Ok(delay) = delay::parse_delay(elem) { Some(PresencePayload::Delay(delay)) @@ -182,6 +190,9 @@ pub fn parse_presence(root: &Element) -> Result { to: to, id: id, type_: type_, + show: show, + statuses: statuses, + priority: priority.unwrap_or(0i8), payloads: payloads, }) } @@ -255,6 +266,9 @@ mod tests { to: None, id: None, type_: presence::PresenceType::Unavailable, + show: None, + statuses: vec!(), + priority: 0i8, payloads: vec!(), }; let elem2 = presence::serialise(&presence); @@ -265,13 +279,8 @@ mod tests { fn test_show() { let elem: Element = "chat".parse().unwrap(); let presence = presence::parse_presence(&elem).unwrap(); - assert_eq!(presence.payloads.len(), 1); - match presence.payloads[0] { - presence::PresencePayloadType::Parsed(presence::PresencePayload::Show(ref show)) => { - assert_eq!(*show, presence::Show::Chat); - }, - _ => panic!("Failed to parse show presence."), - } + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.show, Some(presence::Show::Chat)); } #[test] @@ -298,30 +307,40 @@ mod tests { assert_eq!(message, "Invalid value for show."); } + #[test] + fn test_empty_status() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.statuses.len(), 1); + assert_eq!(presence.statuses[0], ""); + } + #[test] fn test_status() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "Here!".parse().unwrap(); let presence = presence::parse_presence(&elem).unwrap(); - assert_eq!(presence.payloads.len(), 1); - match presence.payloads[0] { - presence::PresencePayloadType::Parsed(presence::PresencePayload::Status(ref status)) => { - assert_eq!(*status, presence::Status::from("")); - }, - _ => panic!("Failed to parse status presence."), - } + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.statuses.len(), 1); + assert_eq!(presence.statuses[0], "Here!"); + } + + #[test] + fn test_multiple_statuses() { + let elem: Element = "Here!Là!".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.statuses.len(), 2); + assert_eq!(presence.statuses[0], "Here!"); + assert_eq!(presence.statuses[1], "Là!"); } #[test] fn test_priority() { let elem: Element = "-1".parse().unwrap(); let presence = presence::parse_presence(&elem).unwrap(); - assert_eq!(presence.payloads.len(), 1); - match presence.payloads[0] { - presence::PresencePayloadType::Parsed(presence::PresencePayload::Priority(ref priority)) => { - assert_eq!(*priority, presence::Priority::from(-1i8)); - }, - _ => panic!("Failed to parse priority."), - } + assert_eq!(presence.payloads.len(), 0); + assert_eq!(presence.priority, -1i8); } #[test] @@ -378,6 +397,9 @@ mod tests { to: None, id: None, type_: presence::PresenceType::Unavailable, + show: None, + statuses: vec!(), + priority: 0i8, payloads: payloads, }; let elem = presence::serialise(&presence); From 0abac5ad2d86f54c3cbdaf498ecec3997c4b5c84 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 22:07:32 +0100 Subject: [PATCH 0150/1020] presence: Make statuses addressable by their xml:lang. --- src/presence.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 13517929a17fffea7b2f3a5a1be301c614ccfea3..16c5f0fc6d2e4244d3752e0a78c4d3cab94bd56b 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::str::FromStr; +use std::collections::BTreeMap; use minidom::{Element, IntoElements, IntoAttributeValue}; use minidom::convert::ElementEmitter; @@ -38,6 +39,7 @@ impl IntoElements for Show { } } +pub type Lang = String; pub type Status = String; pub type Priority = i8; @@ -119,7 +121,7 @@ pub struct Presence { pub id: Option, pub type_: PresenceType, pub show: Option, - pub statuses: Vec, + pub statuses: BTreeMap, pub priority: Priority, pub payloads: Vec, } @@ -139,7 +141,7 @@ pub fn parse_presence(root: &Element) -> Result { None => Default::default(), }; let mut show = None; - let mut statuses = vec!(); + let mut statuses = BTreeMap::new(); let mut priority = None; let mut payloads = vec!(); for elem in root.children() { @@ -162,7 +164,8 @@ pub fn parse_presence(root: &Element) -> Result { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } - statuses.push(elem.text()); + let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + statuses.insert(lang, elem.text()); } else if elem.is("priority", ns::JABBER_CLIENT) { if priority.is_some() { return Err(Error::ParseError("More than one priority element in a presence.")); @@ -242,6 +245,7 @@ pub fn serialise(presence: &Presence) -> Element { #[cfg(test)] mod tests { + use std::collections::BTreeMap; use minidom::Element; use error::Error; use presence; @@ -267,7 +271,7 @@ mod tests { id: None, type_: presence::PresenceType::Unavailable, show: None, - statuses: vec!(), + statuses: BTreeMap::new(), priority: 0i8, payloads: vec!(), }; @@ -313,7 +317,7 @@ mod tests { let presence = presence::parse_presence(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); - assert_eq!(presence.statuses[0], ""); + assert_eq!(presence.statuses[""], ""); } #[test] @@ -322,7 +326,7 @@ mod tests { let presence = presence::parse_presence(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); - assert_eq!(presence.statuses[0], "Here!"); + assert_eq!(presence.statuses[""], "Here!"); } #[test] @@ -331,8 +335,8 @@ mod tests { let presence = presence::parse_presence(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 2); - assert_eq!(presence.statuses[0], "Here!"); - assert_eq!(presence.statuses[1], "Là!"); + assert_eq!(presence.statuses[""], "Here!"); + assert_eq!(presence.statuses["fr"], "Là!"); } #[test] @@ -398,7 +402,7 @@ mod tests { id: None, type_: presence::PresenceType::Unavailable, show: None, - statuses: vec!(), + statuses: BTreeMap::new(), priority: 0i8, payloads: payloads, }; From e03a5a89e78c30951dbfb584c8f2e54adc7a43c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 22:45:39 +0100 Subject: [PATCH 0151/1020] presence: Prevent two statuses from having the same lang. --- src/presence.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/presence.rs b/src/presence.rs index 16c5f0fc6d2e4244d3752e0a78c4d3cab94bd56b..fb26d94f3b4c18c3439b3ee200b3d1055a981004 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -165,7 +165,9 @@ pub fn parse_presence(root: &Element) -> Result { return Err(Error::ParseError("Unknown child in status element.")); } let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); - statuses.insert(lang, elem.text()); + if let Some(_) = statuses.insert(lang, elem.text()) { + return Err(Error::ParseError("Status element present twice for the same xml:lang.")); + } } else if elem.is("priority", ns::JABBER_CLIENT) { if priority.is_some() { return Err(Error::ParseError("More than one priority element in a presence.")); @@ -339,6 +341,17 @@ mod tests { assert_eq!(presence.statuses["fr"], "Là!"); } + #[test] + fn test_invalid_multiple_statuses() { + let elem: Element = "Here!Là!".parse().unwrap(); + let error = presence::parse_presence(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Status element present twice for the same xml:lang."); + } + #[test] fn test_priority() { let elem: Element = "-1".parse().unwrap(); From 875b95bdc91141cd8368f1ad0c40e84488c5d666 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 23:46:29 +0100 Subject: [PATCH 0152/1020] Reexport ElementEmitter from the lib. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 23e234c93f4205249f305a26e331a7dc9b65bd9e..9846e75666de2696fc8506a7e02111e0328d86a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,4 +74,4 @@ pub mod convert; pub use error::Error; pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder}; -pub use convert::{IntoElements, IntoAttributeValue}; +pub use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; From c13cebf02587390dabbd48f04f4e686c63a6c1b2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Apr 2017 21:44:17 +0100 Subject: [PATCH 0153/1020] Implement the Debug trait in a more user-friendly way. --- src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 37d43fa02127dbbdcb38c47623b45d6affc8eb39..04f8c2e18a3da897b665823e0f40664d0e203491 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,7 @@ pub enum JidParseError { /// - A node/name, `node`, which is the optional part before the @. /// - A domain, `domain`, which is the mandatory part after the @ but before the /. /// - A resource, `resource`, which is the optional part after the /. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub struct Jid { /// The node part of the Jabber ID, if it exists, else None. pub node: Option, @@ -50,6 +50,13 @@ impl From for String { } } +impl fmt::Debug for Jid { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "JID({})", self)?; + Ok(()) + } +} + impl fmt::Display for Jid { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt.write_str(String::from(self.clone()).as_ref())?; From 24658859753cbd094f9d585bc2f3fc04455d5396 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:24:45 +0100 Subject: [PATCH 0154/1020] Add a stanza error parser and serialiser. --- src/lib.rs | 2 + src/ns.rs | 2 + src/stanza_error.rs | 275 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 src/stanza_error.rs diff --git a/src/lib.rs b/src/lib.rs index 0a5eac0ad7150d5d6954fbc86b146b1b8ea7f1b2..67a335fc6f3342481831afe4cc387d6af5054518 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,8 @@ pub mod message; pub mod presence; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod iq; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod stanza_error; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod body; diff --git a/src/ns.rs b/src/ns.rs index 495c2856681576a1f605128bc7973efed83125f2..8e7c08b1ae6e2443838d13bac259554a4c27a1f1 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -6,6 +6,8 @@ /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const JABBER_CLIENT: &'static str = "jabber:client"; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub const XMPP_STANZAS: &'static str = "urn:ietf:params:xml:ns:xmpp-stanzas"; /// XEP-0004: Data Forms pub const DATA_FORMS: &'static str = "jabber:x:data"; diff --git a/src/stanza_error.rs b/src/stanza_error.rs new file mode 100644 index 0000000000000000000000000000000000000000..0a1eb42566a042ab0198ed8ead421cfb10b8b1d3 --- /dev/null +++ b/src/stanza_error.rs @@ -0,0 +1,275 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::str::FromStr; +use std::collections::BTreeMap; + +use minidom::Element; + +use error::Error; +use jid::Jid; +use ns; + +#[derive(Debug, Clone, PartialEq)] +pub enum ErrorType { + Auth, + Cancel, + Continue, + Modify, + Wait, +} + +impl FromStr for ErrorType { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "auth" => ErrorType::Auth, + "cancel" => ErrorType::Cancel, + "continue" => ErrorType::Continue, + "modify" => ErrorType::Modify, + "wait" => ErrorType::Wait, + + _ => return Err(Error::ParseError("Unknown error type.")), + }) + } +} + +impl From for String { + fn from(type_: ErrorType) -> String { + String::from(match type_ { + ErrorType::Auth => "auth", + ErrorType::Cancel => "cancel", + ErrorType::Continue => "continue", + ErrorType::Modify => "modify", + ErrorType::Wait => "wait", + }) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DefinedCondition { + BadRequest, + Conflict, + FeatureNotImplemented, + Forbidden, + Gone, + InternalServerError, + ItemNotFound, + JidMalformed, + NotAcceptable, + NotAllowed, + NotAuthorized, + PolicyViolation, + RecipientUnavailable, + Redirect, + RegistrationRequired, + RemoteServerNotFound, + RemoteServerTimeout, + ResourceConstraint, + ServiceUnavailable, + SubscriptionRequired, + UndefinedCondition, + UnexpectedRequest, +} + +impl FromStr for DefinedCondition { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "bad-request" => DefinedCondition::BadRequest, + "conflict" => DefinedCondition::Conflict, + "feature-not-implemented" => DefinedCondition::FeatureNotImplemented, + "forbidden" => DefinedCondition::Forbidden, + "gone" => DefinedCondition::Gone, + "internal-server-error" => DefinedCondition::InternalServerError, + "item-not-found" => DefinedCondition::ItemNotFound, + "jid-malformed" => DefinedCondition::JidMalformed, + "not-acceptable" => DefinedCondition::NotAcceptable, + "not-allowed" => DefinedCondition::NotAllowed, + "not-authorized" => DefinedCondition::NotAuthorized, + "policy-violation" => DefinedCondition::PolicyViolation, + "recipient-unavailable" => DefinedCondition::RecipientUnavailable, + "redirect" => DefinedCondition::Redirect, + "registration-required" => DefinedCondition::RegistrationRequired, + "remote-server-not-found" => DefinedCondition::RemoteServerNotFound, + "remote-server-timeout" => DefinedCondition::RemoteServerTimeout, + "resource-constraint" => DefinedCondition::ResourceConstraint, + "service-unavailable" => DefinedCondition::ServiceUnavailable, + "subscription-required" => DefinedCondition::SubscriptionRequired, + "undefined-condition" => DefinedCondition::UndefinedCondition, + "unexpected-request" => DefinedCondition::UnexpectedRequest, + + _ => return Err(Error::ParseError("Unknown defined-condition.")), + }) + } +} + +impl From for String { + fn from(defined_condition: DefinedCondition) -> String { + String::from(match defined_condition { + DefinedCondition::BadRequest => "bad-request", + DefinedCondition::Conflict => "conflict", + DefinedCondition::FeatureNotImplemented => "feature-not-implemented", + DefinedCondition::Forbidden => "forbidden", + DefinedCondition::Gone => "gone", + DefinedCondition::InternalServerError => "internal-server-error", + DefinedCondition::ItemNotFound => "item-not-found", + DefinedCondition::JidMalformed => "jid-malformed", + DefinedCondition::NotAcceptable => "not-acceptable", + DefinedCondition::NotAllowed => "not-allowed", + DefinedCondition::NotAuthorized => "not-authorized", + DefinedCondition::PolicyViolation => "policy-violation", + DefinedCondition::RecipientUnavailable => "recipient-unavailable", + DefinedCondition::Redirect => "redirect", + DefinedCondition::RegistrationRequired => "registration-required", + DefinedCondition::RemoteServerNotFound => "remote-server-not-found", + DefinedCondition::RemoteServerTimeout => "remote-server-timeout", + DefinedCondition::ResourceConstraint => "resource-constraint", + DefinedCondition::ServiceUnavailable => "service-unavailable", + DefinedCondition::SubscriptionRequired => "subscription-required", + DefinedCondition::UndefinedCondition => "undefined-condition", + DefinedCondition::UnexpectedRequest => "unexpected-request", + }) + } +} + +pub type Lang = String; + +#[derive(Debug, Clone)] +pub struct StanzaError { + pub type_: ErrorType, + pub by: Option, + pub defined_condition: DefinedCondition, + pub texts: BTreeMap, + pub other: Option, +} + +pub fn parse_stanza_error(root: &Element) -> Result { + if !root.is("error", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not an error element.")); + } + + let type_ = root.attr("type") + .ok_or(Error::ParseError("Error must have a 'type' attribute."))? + .parse()?; + let by = root.attr("by") + .and_then(|by| by.parse().ok()); + let mut defined_condition = None; + let mut texts = BTreeMap::new(); + let mut other = None; + + for child in root.children() { + if child.is("text", ns::XMPP_STANZAS) { + for _ in child.children() { + return Err(Error::ParseError("Unknown element in error text.")); + } + let lang = child.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = texts.insert(lang, child.text()) { + return Err(Error::ParseError("Text element present twice for the same xml:lang.")); + } + } else if child.ns() == Some(ns::XMPP_STANZAS) { + if defined_condition.is_some() { + return Err(Error::ParseError("Error must not have more than one defined-condition.")); + } + for _ in child.children() { + return Err(Error::ParseError("Unknown element in defined-condition.")); + } + let condition = DefinedCondition::from_str(child.name())?; + defined_condition = Some(condition); + } else { + if other.is_some() { + return Err(Error::ParseError("Error must not have more than one other element.")); + } + other = Some(child.clone()); + } + } + + if defined_condition.is_none() { + return Err(Error::ParseError("Error must have a defined-condition.")); + } + let defined_condition = defined_condition.unwrap(); + + Ok(StanzaError { + type_: type_, + by: by, + defined_condition: defined_condition, + texts: texts, + other: other, + }) +} + +pub fn serialise(error: &StanzaError) -> Element { + let mut root = Element::builder("error") + .ns(ns::JABBER_CLIENT) + .attr("type", String::from(error.type_.clone())) + .attr("by", match error.by { + Some(ref by) => Some(String::from(by.clone())), + None => None, + }) + .append(Element::builder(error.defined_condition.clone()) + .ns(ns::XMPP_STANZAS) + .build()) + .build(); + for (lang, text) in error.texts.clone() { + let elem = Element::builder("text") + .ns(ns::XMPP_STANZAS) + .attr("xml:lang", lang) + .append(text) + .build(); + root.append_child(elem); + } + if let Some(ref other) = error.other { + root.append_child(other.clone()); + } + root +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use stanza_error; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let error = stanza_error::parse_stanza_error(&elem).unwrap(); + assert_eq!(error.type_, stanza_error::ErrorType::Cancel); + assert_eq!(error.defined_condition, stanza_error::DefinedCondition::UndefinedCondition); + } + + #[test] + fn test_invalid_type() { + let elem: Element = "".parse().unwrap(); + let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Error must have a 'type' attribute."); + + let elem: Element = "".parse().unwrap(); + let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown error type."); + } + + #[test] + fn test_invalid_condition() { + let elem: Element = "".parse().unwrap(); + let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Error must have a defined-condition."); + } +} From 3dd0eb3a9b2adfc3afede97b273633d7513f6cae Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:02:35 +0100 Subject: [PATCH 0155/1020] presence: Wire up stanza_error. --- src/presence.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/presence.rs b/src/presence.rs index fb26d94f3b4c18c3439b3ee200b3d1055a981004..3b3c1faa2fe4793bddc40c75a80c725ead85ee59 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -16,6 +16,7 @@ use error::Error; use ns; +use stanza_error; use delay; use ecaps2; @@ -50,6 +51,7 @@ pub enum PresencePayload { Show(Show), Status(Status), Priority(Priority), + StanzaError(stanza_error::StanzaError), Delay(delay::Delay), ECaps2(ecaps2::ECaps2), } @@ -177,7 +179,9 @@ pub fn parse_presence(root: &Element) -> Result { } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { - let payload = if let Ok(delay) = delay::parse_delay(elem) { + let payload = if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { + Some(PresencePayload::StanzaError(stanza_error)) + } else if let Ok(delay) = delay::parse_delay(elem) { Some(PresencePayload::Delay(delay)) } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { Some(PresencePayload::ECaps2(ecaps2)) @@ -222,6 +226,7 @@ pub fn serialise_payload(payload: &PresencePayload) -> Element { .append(format!("{}", priority)) .build() }, + PresencePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), PresencePayload::Delay(ref delay) => delay::serialise(delay), PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } From 42abbe2927ebd67aa2b9effeb92931c4475274e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:04:15 +0100 Subject: [PATCH 0156/1020] message: Wire up stanza_error. --- src/message.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/message.rs b/src/message.rs index a88db4c9d7504c8fb0ba1b211b5bb514b1306005..9d3ea6fe26bd7e5e51f0e824c397d09d9401b365 100644 --- a/src/message.rs +++ b/src/message.rs @@ -16,6 +16,7 @@ use error::Error; use ns; use body; +use stanza_error; use chatstates; use receipts; use delay; @@ -27,6 +28,7 @@ use eme; #[derive(Debug, Clone)] pub enum MessagePayload { Body(body::Body), + StanzaError(stanza_error::StanzaError), ChatState(chatstates::ChatState), Receipt(receipts::Receipt), Delay(delay::Delay), @@ -111,6 +113,8 @@ pub fn parse_message(root: &Element) -> Result { for elem in root.children() { let payload = if let Ok(body) = body::parse_body(elem) { Some(MessagePayload::Body(body)) + } else if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { + Some(MessagePayload::StanzaError(stanza_error)) } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { Some(MessagePayload::ChatState(chatstate)) } else if let Ok(receipt) = receipts::parse_receipt(elem) { @@ -143,6 +147,7 @@ pub fn parse_message(root: &Element) -> Result { pub fn serialise_payload(payload: &MessagePayload) -> Element { match *payload { MessagePayload::Body(ref body) => body::serialise(body), + MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), MessagePayload::Attention(ref attention) => attention::serialise(attention), MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), MessagePayload::Receipt(ref receipt) => receipts::serialise(receipt), From f3b55350adbf5fe25ac2e6ee0087967216499de6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:23:56 +0100 Subject: [PATCH 0157/1020] iq: Wire up stanza_error. --- src/iq.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 645c4ba0f96e97d0ad066ab675f6545c5e71642d..cf97055343d4c9af49b89a135f918a350a623026 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -14,6 +14,7 @@ use error::Error; use ns; +use stanza_error; use disco; use ibb; use jingle; @@ -39,7 +40,7 @@ pub enum IqType { Get(IqPayloadType), Set(IqPayloadType), Result(Option), - Error(IqPayloadType), + Error(stanza_error::StanzaError), } impl IntoAttributeValue for IqType { @@ -77,13 +78,17 @@ pub fn parse_iq(root: &Element) -> Result { }; let mut payload = None; + let mut error_payload = None; for elem in root.children() { if payload.is_some() { return Err(Error::ParseError("Wrong number of children in iq element.")); } if type_ == "error" { if elem.is("error", ns::JABBER_CLIENT) { - payload = Some(IqPayloadType::XML(elem.clone())); + if error_payload.is_some() { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + error_payload = Some(stanza_error::parse_stanza_error(elem)?); } else if root.children().collect::>().len() != 2 { return Err(Error::ParseError("Wrong number of children in iq element.")); } @@ -126,7 +131,7 @@ pub fn parse_iq(root: &Element) -> Result { IqType::Result(None) } } else if type_ == "error" { - if let Some(payload) = payload.clone() { + if let Some(payload) = error_payload.clone() { IqType::Error(payload.clone()) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); @@ -163,13 +168,12 @@ pub fn serialise(iq: &Iq) -> Element { let elem = match iq.payload.clone() { IqType::Get(IqPayloadType::XML(elem)) | IqType::Set(IqPayloadType::XML(elem)) - | IqType::Result(Some(IqPayloadType::XML(elem))) - | IqType::Error(IqPayloadType::XML(elem)) => elem, + | IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Error(error) => stanza_error::serialise(&error), IqType::Get(IqPayloadType::Parsed(payload)) | IqType::Set(IqPayloadType::Parsed(payload)) | IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), IqType::Result(None) => return stanza, - _ => panic!(), }; stanza.append_child(elem); stanza @@ -180,6 +184,7 @@ mod tests { use minidom::Element; use error::Error; use iq; + use stanza_error; use disco; #[test] @@ -269,10 +274,16 @@ mod tests { assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); - assert!(match iq.payload { - iq::IqType::Error(iq::IqPayloadType::XML(element)) => element == error, - _ => false, - }); + match iq.payload { + iq::IqType::Error(element) => { + assert_eq!(element.type_, stanza_error::ErrorType::Cancel); + assert_eq!(element.by, None); + assert_eq!(element.defined_condition, stanza_error::DefinedCondition::ServiceUnavailable); + assert_eq!(element.texts.len(), 0); + assert_eq!(element.other, None); + }, + _ => panic!(), + } } #[test] From ed458ba694458ebca05442b5e0045d720c35f157 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:39:52 +0100 Subject: [PATCH 0158/1020] Add a ChangeLog file, to let users know about the changes between major versions. --- ChangeLog | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 ChangeLog diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000000000000000000000000000000000000..83fad57c8c820b87e4c3b31269e9ffbaf8d55ca6 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,13 @@ +Version XXX: +2017-0X-XX Emmanuel Gil Peyrot + * New parsers/serialisers: + - Stanza error, as per RFC 6120 §8.3. + * Incompatible changes: + - Presence has got an overhaul, it now hosts show, statuses and + priority in its struct. The status module has also been + dropped. + - Iq now gets a proper StanzaError when the type is error. + +Version 0.1.0: +2017-04-29 Emmanuel Gil Peyrot + * Implement many extensions. From 7b6d444f034f0b67a5f47737f51b8b381e0a977e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:50:18 +0100 Subject: [PATCH 0159/1020] delay: Use Jid for from attribute. --- src/delay.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 8e8e382ea2276189c27c2506445492cb29a5550e..06c980f642ebde26d682e885768b16da9aa75b5d 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -8,12 +8,13 @@ use minidom::{Element, IntoElements}; use minidom::convert::ElementEmitter; use error::Error; +use jid::Jid; use ns; #[derive(Debug, Clone)] pub struct Delay { - pub from: Option, + pub from: Option, pub stamp: String, pub data: Option, } @@ -41,7 +42,7 @@ pub fn parse_delay(root: &Element) -> Result { pub fn serialise(delay: &Delay) -> Element { Element::builder("delay") .ns(ns::DELAY) - .attr("from", delay.from.clone()) + .attr("from", delay.from.clone().and_then(|value| Some(String::from(value)))) .attr("stamp", delay.stamp.clone()) .append(delay.data.clone()) .build() @@ -56,15 +57,17 @@ impl IntoElements for Delay { #[cfg(test)] mod tests { + use std::str::FromStr; use minidom::Element; use error::Error; + use jid::Jid; use delay; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); let delay = delay::parse_delay(&elem).unwrap(); - assert_eq!(delay.from, Some(String::from("capulet.com"))); + assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap())); assert_eq!(delay.stamp, "2002-09-10T23:08:25Z"); assert_eq!(delay.data, None); } @@ -107,7 +110,7 @@ mod tests { fn test_serialise_data() { let elem: Element = "Reason".parse().unwrap(); let delay = delay::Delay { - from: Some(String::from("juliet@example.org")), + from: Some(Jid::from_str("juliet@example.org").unwrap()), stamp: "2002-09-10T23:08:25Z".to_owned(), data: Some(String::from("Reason")), }; From c0b7c9da884555e4b1863e6f7f6a37a7f658d1d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 01:50:38 +0100 Subject: [PATCH 0160/1020] iq: Remove unused variable causing a warning. --- src/iq.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index cf97055343d4c9af49b89a135f918a350a623026..c7728ae2cc05e479f3c59d9fff6288fdf87302d1 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -268,19 +268,16 @@ mod tests { ".parse().unwrap(); let iq = iq::parse_iq(&elem).unwrap(); - let error: Element = " - - ".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); match iq.payload { - iq::IqType::Error(element) => { - assert_eq!(element.type_, stanza_error::ErrorType::Cancel); - assert_eq!(element.by, None); - assert_eq!(element.defined_condition, stanza_error::DefinedCondition::ServiceUnavailable); - assert_eq!(element.texts.len(), 0); - assert_eq!(element.other, None); + iq::IqType::Error(error) => { + assert_eq!(error.type_, stanza_error::ErrorType::Cancel); + assert_eq!(error.by, None); + assert_eq!(error.defined_condition, stanza_error::DefinedCondition::ServiceUnavailable); + assert_eq!(error.texts.len(), 0); + assert_eq!(error.other, None); }, _ => panic!(), } From 765e8c333308d2dd5291b15d3a9625512274a2c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 May 2017 23:49:44 +0100 Subject: [PATCH 0161/1020] attention: Replace parse_* and serialise with TryFrom and Into. --- src/attention.rs | 43 +++++++++++++++++++++++++++---------------- src/lib.rs | 2 ++ src/message.rs | 9 +++++---- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 2f12545ea436cbea67d58fcc9f6c4a449d5dc18c..21f8e0b5d29c3411bb3a1c6cc47df8b184308088 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -13,38 +15,45 @@ use ns; #[derive(Debug, Clone)] pub struct Attention; -pub fn parse_attention(root: &Element) -> Result { - if !root.is("attention", ns::ATTENTION) { - return Err(Error::ParseError("This is not an attention element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in attention element.")); +impl<'a> TryFrom<&'a Element> for Attention { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("attention", ns::ATTENTION) { + return Err(Error::ParseError("This is not an attention element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in attention element.")); + } + Ok(Attention) } - Ok(Attention) } -pub fn serialise(_: &Attention) -> Element { - Element::builder("attention") - .ns(ns::ATTENTION) - .build() +impl<'a> Into for &'a Attention { + fn into(self) -> Element { + Element::builder("attention") + .ns(ns::ATTENTION) + .build() + } } #[cfg(test)] mod tests { + use std::convert::TryFrom; use minidom::Element; use error::Error; - use attention; + use super::Attention; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - attention::parse_attention(&elem).unwrap(); + Attention::try_from(&elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = attention::parse_attention(&elem).unwrap_err(); + let error = Attention::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -55,8 +64,10 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let attention = attention::Attention; - let elem2 = attention::serialise(&attention); + let attention = Attention; + let elem2: Element = (&attention).into(); + let elem3: Element = (&attention).into(); assert_eq!(elem, elem2); + assert_eq!(elem2, elem3); } } diff --git a/src/lib.rs b/src/lib.rs index 67a335fc6f3342481831afe4cc387d6af5054518..3076fccfa3b346d63150393ff1317654da90bdeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#![feature(try_from)] + extern crate minidom; extern crate jid; extern crate base64; diff --git a/src/message.rs b/src/message.rs index 9d3ea6fe26bd7e5e51f0e824c397d09d9401b365..3550e810c1159f82891f75707cc116074794dbd9 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue}; @@ -20,7 +21,7 @@ use stanza_error; use chatstates; use receipts; use delay; -use attention; +use attention::Attention; use message_correct; use eme; @@ -32,7 +33,7 @@ pub enum MessagePayload { ChatState(chatstates::ChatState), Receipt(receipts::Receipt), Delay(delay::Delay), - Attention(attention::Attention), + Attention(Attention), MessageCorrect(message_correct::Replace), ExplicitMessageEncryption(eme::ExplicitMessageEncryption), } @@ -121,7 +122,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::Receipt(receipt)) } else if let Ok(delay) = delay::parse_delay(elem) { Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = attention::parse_attention(elem) { + } else if let Ok(attention) = Attention::try_from(elem) { Some(MessagePayload::Attention(attention)) } else if let Ok(replace) = message_correct::parse_replace(elem) { Some(MessagePayload::MessageCorrect(replace)) @@ -148,7 +149,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { match *payload { MessagePayload::Body(ref body) => body::serialise(body), MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), - MessagePayload::Attention(ref attention) => attention::serialise(attention), + MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), MessagePayload::Receipt(ref receipt) => receipts::serialise(receipt), MessagePayload::Delay(ref delay) => delay::serialise(delay), From e9d33e5e7e1bdeb06f0d49fc088e9e8990b53233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 2 May 2017 16:30:31 +0100 Subject: [PATCH 0162/1020] Implement From for String and use it for fmt::Debug --- src/element.rs | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/element.rs b/src/element.rs index 4d3d59e1f10a1c9e949e6885eff166c78366c39b..ff31b87de02de1df932530007b87668d13b9e684 100644 --- a/src/element.rs +++ b/src/element.rs @@ -10,7 +10,7 @@ use std::fmt; use error::Error; use xml::reader::{XmlEvent as ReaderEvent, EventReader}; -use xml::writer::{XmlEvent as WriterEvent, EventWriter}; +use xml::writer::{XmlEvent as WriterEvent, EventWriter, EmitterConfig}; use xml::name::Name; use xml::namespace::NS_NO_PREFIX; @@ -30,32 +30,19 @@ pub struct Element { } +impl<'a> From<&'a Element> for String { + fn from(elem: &'a Element) -> String { + let mut out = Vec::new(); + let config = EmitterConfig::new() + .write_document_declaration(false); + elem.write_to(&mut EventWriter::new_with_config(&mut out, config)).unwrap(); + String::from_utf8(out).unwrap() + } +} + impl fmt::Debug for Element { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "<{}", self.name)?; - if let Some(ref ns) = self.namespace { - write!(fmt, " xmlns=\"{}\"", ns)?; - } - for attr in &self.attributes { - write!(fmt, " {}=\"{}\"", attr.0, attr.1)?; - } - if self.children.is_empty() { - write!(fmt, "/>")?; - } - else { - write!(fmt, ">")?; - for child in &self.children { - match *child { - Node::Element(ref e) => { - write!(fmt, "{:?}", e)?; - }, - Node::Text(ref s) => { - write!(fmt, "{}", s)?; - }, - } - } - write!(fmt, "", self.name)?; - } + write!(fmt, "{}", String::from(self))?; Ok(()) } } @@ -275,7 +262,7 @@ impl Element { }; let mut start = WriterEvent::start_element(name); if let Some(ref ns) = self.namespace { - start = start.default_ns(ns.as_ref()); + start = start.default_ns(ns.clone()); } for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently start = start.attr(Name::local(&attr.0), &attr.1); From 1bc1437da8a585bf828291470268ab8a9e222114 Mon Sep 17 00:00:00 2001 From: lumi Date: Wed, 3 May 2017 18:37:03 +0200 Subject: [PATCH 0163/1020] bump version to 0.3.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 00705bec73f645b9ed3ccf7af645aee0b1aee9fb..582eb5ae57effe9344463c069da1ec8a04656a1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.2.0" +version = "0.3.0" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] description = "A small, simple DOM implementation on top of xml-rs." homepage = "https://gitlab.com/lumi/minidom-rs" From 33f9dee31fd58dd912a2c8ccc7dfb1ee806988b4 Mon Sep 17 00:00:00 2001 From: lumi Date: Wed, 3 May 2017 18:39:40 +0200 Subject: [PATCH 0164/1020] move use std::iter::FromIterator to tests --- src/element.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/element.rs b/src/element.rs index ff31b87de02de1df932530007b87668d13b9e684..5ac2a1445101083780ec5ebcbd7df04f21585a9f 100644 --- a/src/element.rs +++ b/src/element.rs @@ -3,7 +3,6 @@ use std::io::prelude::*; use std::io::Cursor; use std::collections::BTreeMap; -use std::iter::FromIterator; use std::fmt; @@ -548,6 +547,8 @@ impl ElementBuilder { #[test] fn test_element_new() { + use std::iter::FromIterator; + let elem = Element::new( "name".to_owned() , Some("namespace".to_owned()) , BTreeMap::from_iter(vec![ ("name".to_string(), "value".to_string()) ].into_iter() ) From 1cea1987a21c901e1438f8cb8b30a5b0afe1efff Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 4 May 2017 01:20:01 +0100 Subject: [PATCH 0165/1020] Cargo.toml: Remove superfluous license-file. --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bef0ab8f8a820eaa3bb5131f585c07aa0704f6d6..3f13f303aa98175821340f40d01ef13eca23a404 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ repository = "https://hg.linkmauve.fr/xmpp-parsers" keywords = ["xmpp"] categories = ["parsing", "network-programming"] license = "MPL-2.0" -license-file = "LICENSE" [dependencies] minidom = "0.2.0" From 29725b9d4d553d499fc3686acb39ab2e0d6de572 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 4 May 2017 01:20:22 +0100 Subject: [PATCH 0166/1020] Update to minidom 0.3.0. --- Cargo.toml | 2 +- src/delay.rs | 3 +-- src/jingle_ft.rs | 3 +-- src/message.rs | 3 +-- src/presence.rs | 3 +-- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f13f303aa98175821340f40d01ef13eca23a404..ea7eecef2b81b19673c0ad916bafc0032d0341c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.2.0" +minidom = "0.3.0" jid = "0.2.0" base64 = "0.5.0" digest = "0.5.0" diff --git a/src/delay.rs b/src/delay.rs index 06c980f642ebde26d682e885768b16da9aa75b5d..2b2149eb6a5526b9249ceba9b82bcaca1135ba93 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,8 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoElements}; -use minidom::convert::ElementEmitter; +use minidom::{Element, IntoElements, ElementEmitter}; use error::Error; use jid::Jid; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 2028fe930d81db97d0de164b921baa5fbb5249ef..7cd729e1041530bdc2b8fca0136e97780a307138 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -7,8 +7,7 @@ use hashes; use hashes::{Hash, parse_hash}; -use minidom::{Element, IntoElements}; -use minidom::convert::ElementEmitter; +use minidom::{Element, IntoElements, ElementEmitter}; use error::Error; use ns; diff --git a/src/message.rs b/src/message.rs index 3550e810c1159f82891f75707cc116074794dbd9..6fcc681de1ac2d68ee09b91d593a4bd2847e4a6d 100644 --- a/src/message.rs +++ b/src/message.rs @@ -7,8 +7,7 @@ use std::convert::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue}; -use minidom::convert::ElementEmitter; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use jid::Jid; diff --git a/src/presence.rs b/src/presence.rs index 3b3c1faa2fe4793bddc40c75a80c725ead85ee59..bf7b57dfc495c53d1722e030a126df451cbe3e15 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -7,8 +7,7 @@ use std::str::FromStr; use std::collections::BTreeMap; -use minidom::{Element, IntoElements, IntoAttributeValue}; -use minidom::convert::ElementEmitter; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use jid::Jid; From 6c4fd8da014f99abaf6eabb82eaa567e60b9056a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 4 May 2017 01:20:28 +0100 Subject: [PATCH 0167/1020] jingle: Update to Into and TryFrom. --- src/iq.rs | 10 +- src/jingle.rs | 392 ++++++++++++++++++++++++++------------------------ 2 files changed, 208 insertions(+), 194 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index c7728ae2cc05e479f3c59d9fff6288fdf87302d1..8a4e70e7d69e79882795e2ff3ad20b9be047b710 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -5,6 +5,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use minidom::IntoAttributeValue; @@ -17,7 +19,7 @@ use ns; use stanza_error; use disco; use ibb; -use jingle; +use jingle::Jingle; use ping; /// Lists every known payload of a ``. @@ -25,7 +27,7 @@ use ping; pub enum IqPayload { Disco(disco::Disco), IBB(ibb::IBB), - Jingle(jingle::Jingle), + Jingle(Jingle), Ping(ping::Ping), } @@ -97,7 +99,7 @@ pub fn parse_iq(root: &Element) -> Result { Some(IqPayload::Disco(disco)) } else if let Ok(ibb) = ibb::parse_ibb(elem) { Some(IqPayload::IBB(ibb)) - } else if let Ok(jingle) = jingle::parse_jingle(elem) { + } else if let Ok(jingle) = Jingle::try_from(elem) { Some(IqPayload::Jingle(jingle)) } else if let Ok(ping) = ping::parse_ping(elem) { Some(IqPayload::Ping(ping)) @@ -152,7 +154,7 @@ pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), IqPayload::IBB(ref ibb) => ibb::serialise(ibb), - IqPayload::Jingle(ref jingle) => jingle::serialise(jingle), + IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(_) => ping::serialise_ping(), } } diff --git a/src/jingle.rs b/src/jingle.rs index 717a3b77217ebdc8b6f290c60e7df9ce348e86ce..dfe0d71046138649fb33f97e499a17319ef771f5 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,10 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements}; -use minidom::convert::ElementEmitter; +use minidom::Element; use error::Error; use ns; @@ -204,9 +204,9 @@ impl FromStr for Reason { } } -impl IntoElements for Reason { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = Element::builder(match self { +impl<'a> Into for &'a Reason { + fn into(self) -> Element { + Element::builder(match *self { Reason::AlternativeSession => "alternative-session", Reason::Busy => "busy", Reason::Cancel => "cancel", @@ -224,8 +224,7 @@ impl IntoElements for Reason { Reason::Timeout => "timeout", Reason::UnsupportedApplications => "unsupported-applications", Reason::UnsupportedTransports => "unsupported-transports", - }).build(); - emitter.append_child(elem); + }).build() } } @@ -246,195 +245,208 @@ pub struct Jingle { pub other: Vec, } -pub fn parse_jingle(root: &Element) -> Result { - if !root.is("jingle", ns::JINGLE) { - return Err(Error::ParseError("This is not a Jingle element.")); - } +impl<'a> TryFrom<&'a Element> for Jingle { + type Error = Error; - let mut contents: Vec = vec!(); - - let action = root.attr("action") - .ok_or(Error::ParseError("Jingle must have an 'action' attribute."))? - .parse()?; - let initiator = root.attr("initiator") - .and_then(|initiator| initiator.parse().ok()); - let responder = root.attr("responder") - .and_then(|responder| responder.parse().ok()); - let sid = root.attr("sid") - .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; - let mut reason_element = None; - let mut other = vec!(); - - for child in root.children() { - if child.is("content", ns::JINGLE) { - let creator = child.attr("creator") - .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? - .parse()?; - let disposition = child.attr("disposition") - .unwrap_or("session"); - let name = child.attr("name") - .ok_or(Error::ParseError("Content must have a 'name' attribute."))?; - let senders = child.attr("senders") - .unwrap_or("both") - .parse()?; - let mut description = None; - let mut transport = None; - let mut security = None; - for stuff in child.children() { - if stuff.name() == "description" { - if description.is_some() { - return Err(Error::ParseError("Content must not have more than one description.")); - } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on description element."))?; - description = Some(( - namespace, - stuff.clone(), - )); - } else if stuff.name() == "transport" { - if transport.is_some() { - return Err(Error::ParseError("Content must not have more than one transport.")); - } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on transport element."))?; - transport = Some(( - namespace, - stuff.clone(), - )); - } else if stuff.name() == "security" { - if security.is_some() { - return Err(Error::ParseError("Content must not have more than one security.")); + fn try_from(root: &'a Element) -> Result { + if !root.is("jingle", ns::JINGLE) { + return Err(Error::ParseError("This is not a Jingle element.")); + } + + let mut contents: Vec = vec!(); + + let action = root.attr("action") + .ok_or(Error::ParseError("Jingle must have an 'action' attribute."))? + .parse()?; + let initiator = root.attr("initiator") + .and_then(|initiator| initiator.parse().ok()); + let responder = root.attr("responder") + .and_then(|responder| responder.parse().ok()); + let sid = root.attr("sid") + .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; + let mut reason_element = None; + let mut other = vec!(); + + for child in root.children() { + if child.is("content", ns::JINGLE) { + let creator = child.attr("creator") + .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? + .parse()?; + let disposition = child.attr("disposition") + .unwrap_or("session"); + let name = child.attr("name") + .ok_or(Error::ParseError("Content must have a 'name' attribute."))?; + let senders = child.attr("senders") + .unwrap_or("both") + .parse()?; + let mut description = None; + let mut transport = None; + let mut security = None; + for stuff in child.children() { + if stuff.name() == "description" { + if description.is_some() { + return Err(Error::ParseError("Content must not have more than one description.")); + } + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on description element."))?; + description = Some(( + namespace, + stuff.clone(), + )); + } else if stuff.name() == "transport" { + if transport.is_some() { + return Err(Error::ParseError("Content must not have more than one transport.")); + } + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on transport element."))?; + transport = Some(( + namespace, + stuff.clone(), + )); + } else if stuff.name() == "security" { + if security.is_some() { + return Err(Error::ParseError("Content must not have more than one security.")); + } + let namespace = stuff.ns() + .and_then(|ns| ns.parse().ok()) + // TODO: is this even reachable? + .ok_or(Error::ParseError("Invalid namespace on security element."))?; + security = Some(( + namespace, + stuff.clone(), + )); } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on security element."))?; - security = Some(( - namespace, - stuff.clone(), - )); } - } - if description.is_none() { - return Err(Error::ParseError("Content must have one description.")); - } - if transport.is_none() { - return Err(Error::ParseError("Content must have one transport.")); - } - let description = description.unwrap().to_owned(); - let transport = transport.unwrap().to_owned(); - contents.push(Content { - creator: creator, - disposition: disposition.to_owned(), - name: name.to_owned(), - senders: senders, - description: description, - transport: transport, - security: security, - }); - } else if child.is("reason", ns::JINGLE) { - if reason_element.is_some() { - return Err(Error::ParseError("Jingle must not have more than one reason.")); - } - let mut reason = None; - let mut text = None; - for stuff in child.children() { - if stuff.ns() != Some(ns::JINGLE) { - return Err(Error::ParseError("Reason contains a foreign element.")); + if description.is_none() { + return Err(Error::ParseError("Content must have one description.")); + } + if transport.is_none() { + return Err(Error::ParseError("Content must have one transport.")); + } + let description = description.unwrap().to_owned(); + let transport = transport.unwrap().to_owned(); + contents.push(Content { + creator: creator, + disposition: disposition.to_owned(), + name: name.to_owned(), + senders: senders, + description: description, + transport: transport, + security: security, + }); + } else if child.is("reason", ns::JINGLE) { + if reason_element.is_some() { + return Err(Error::ParseError("Jingle must not have more than one reason.")); } - let name = stuff.name(); - if name == "text" { - if text.is_some() { - return Err(Error::ParseError("Reason must not have more than one text.")); + let mut reason = None; + let mut text = None; + for stuff in child.children() { + if stuff.ns() != Some(ns::JINGLE) { + return Err(Error::ParseError("Reason contains a foreign element.")); + } + let name = stuff.name(); + if name == "text" { + if text.is_some() { + return Err(Error::ParseError("Reason must not have more than one text.")); + } + text = Some(stuff.text()); + } else { + reason = Some(name.parse()?); } - text = Some(stuff.text()); - } else { - reason = Some(name.parse()?); } + if reason.is_none() { + return Err(Error::ParseError("Reason doesn’t contain a valid reason.")); + } + reason_element = Some(ReasonElement { + reason: reason.unwrap(), + text: text, + }); + } else { + other.push(child.clone()); } - if reason.is_none() { - return Err(Error::ParseError("Reason doesn’t contain a valid reason.")); - } - reason_element = Some(ReasonElement { - reason: reason.unwrap(), - text: text, - }); - } else { - other.push(child.clone()); } - } - Ok(Jingle { - action: action, - initiator: initiator, - responder: responder, - sid: sid.to_owned(), - contents: contents, - reason: reason_element, - other: other, - }) + Ok(Jingle { + action: action, + initiator: initiator, + responder: responder, + sid: sid.to_owned(), + contents: contents, + reason: reason_element, + other: other, + }) + } } -pub fn serialise_content(content: &Content) -> Element { - let mut root = Element::builder("content") - .ns(ns::JINGLE) - .attr("creator", String::from(content.creator.clone())) - .attr("disposition", content.disposition.clone()) - .attr("name", content.name.clone()) - .attr("senders", String::from(content.senders.clone())) - .build(); - root.append_child(content.description.1.clone()); - root.append_child(content.transport.1.clone()); - if let Some(security) = content.security.clone() { - root.append_child(security.1.clone()); +impl<'a> Into for &'a Content { + fn into(self) -> Element { + let mut root = Element::builder("content") + .ns(ns::JINGLE) + .attr("creator", String::from(self.creator.clone())) + .attr("disposition", self.disposition.clone()) + .attr("name", self.name.clone()) + .attr("senders", String::from(self.senders.clone())) + .build(); + root.append_child(self.description.1.clone()); + root.append_child(self.transport.1.clone()); + if let Some(security) = self.security.clone() { + root.append_child(security.1.clone()); + } + root } - root } -pub fn serialise(jingle: &Jingle) -> Element { - let mut root = Element::builder("jingle") - .ns(ns::JINGLE) - .attr("action", String::from(jingle.action.clone())) - .attr("initiator", jingle.initiator.clone()) - .attr("responder", jingle.responder.clone()) - .attr("sid", jingle.sid.clone()) - .build(); - for content in jingle.contents.clone() { - let content_elem = serialise_content(&content); - root.append_child(content_elem); +impl<'a> Into for &'a Jingle { + fn into(self) -> Element { + let mut root = Element::builder("jingle") + .ns(ns::JINGLE) + .attr("action", String::from(self.action.clone())) + .attr("initiator", self.initiator.clone()) + .attr("responder", self.responder.clone()) + .attr("sid", self.sid.clone()) + .build(); + for content in self.contents.clone() { + let content_elem = (&content).into(); + root.append_child(content_elem); + } + if let Some(ref reason) = self.reason { + let reason2: Element = (&reason.reason).into(); + let reason_elem = Element::builder("reason") + .append(reason2) + .append(reason.text.clone()) + .build(); + root.append_child(reason_elem); + } + root } - if let Some(ref reason) = jingle.reason { - let reason_elem = Element::builder("reason") - .append(reason.reason.clone()) - .append(reason.text.clone()) - .build(); - root.append_child(reason_elem); +} + +impl Into for Jingle { + fn into(self) -> Element { + (&self).into() } - root } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use jingle; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); - assert_eq!(jingle.action, jingle::Action::SessionInitiate); + let jingle = Jingle::try_from(&elem).unwrap(); + assert_eq!(jingle.action, Action::SessionInitiate); assert_eq!(jingle.sid, "coucou"); } #[test] fn test_invalid_jingle() { let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -442,7 +454,7 @@ mod tests { assert_eq!(message, "Jingle must have an 'action' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -450,7 +462,7 @@ mod tests { assert_eq!(message, "Jingle must have a 'sid' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -461,25 +473,25 @@ mod tests { #[test] fn test_content() { let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); - assert_eq!(jingle.contents[0].creator, jingle::Creator::Initiator); + let jingle = Jingle::try_from(&elem).unwrap(); + assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, "coucou"); - assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); + assert_eq!(jingle.contents[0].senders, Senders::Both); assert_eq!(jingle.contents[0].disposition, "session"); let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); - assert_eq!(jingle.contents[0].senders, jingle::Senders::Both); + let jingle = Jingle::try_from(&elem).unwrap(); + assert_eq!(jingle.contents[0].senders, Senders::Both); let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); + let jingle = Jingle::try_from(&elem).unwrap(); assert_eq!(jingle.contents[0].disposition, "early-session"); } #[test] fn test_invalid_content() { let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -487,7 +499,7 @@ mod tests { assert_eq!(message, "Content must have a 'creator' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -495,7 +507,7 @@ mod tests { assert_eq!(message, "Content must have a 'name' attribute."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -503,7 +515,7 @@ mod tests { assert_eq!(message, "Unknown creator."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -511,7 +523,7 @@ mod tests { assert_eq!(message, "Unknown senders."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -519,7 +531,7 @@ mod tests { assert_eq!(message, "Unknown senders."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -527,7 +539,7 @@ mod tests { assert_eq!(message, "Content must have one description."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -538,22 +550,22 @@ mod tests { #[test] fn test_reason() { let elem: Element = "".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); + let jingle = Jingle::try_from(&elem).unwrap(); let reason = jingle.reason.unwrap(); - assert_eq!(reason.reason, jingle::Reason::Success); + assert_eq!(reason.reason, Reason::Success); assert_eq!(reason.text, None); let elem: Element = "coucou".parse().unwrap(); - let jingle = jingle::parse_jingle(&elem).unwrap(); + let jingle = Jingle::try_from(&elem).unwrap(); let reason = jingle.reason.unwrap(); - assert_eq!(reason.reason, jingle::Reason::Success); + assert_eq!(reason.reason, Reason::Success); assert_eq!(reason.text, Some(String::from("coucou"))); } #[test] fn test_invalid_reason() { let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -561,7 +573,7 @@ mod tests { assert_eq!(message, "Reason doesn’t contain a valid reason."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -569,7 +581,7 @@ mod tests { assert_eq!(message, "Unknown reason."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -577,7 +589,7 @@ mod tests { assert_eq!(message, "Reason contains a foreign element."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -585,7 +597,7 @@ mod tests { assert_eq!(message, "Jingle must not have more than one reason."); let elem: Element = "".parse().unwrap(); - let error = jingle::parse_jingle(&elem).unwrap_err(); + let error = Jingle::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From 033cbe777bc3908d2420f35669d7e9d515b31a5e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 4 May 2017 01:31:13 +0100 Subject: [PATCH 0168/1020] ping: Port to TryFrom/Into. --- src/iq.rs | 8 ++++---- src/ping.rs | 37 ++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 8a4e70e7d69e79882795e2ff3ad20b9be047b710..d1e3eeaa5be33dbf1821a3e2a62481eeac997241 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -20,7 +20,7 @@ use stanza_error; use disco; use ibb; use jingle::Jingle; -use ping; +use ping::Ping; /// Lists every known payload of a ``. #[derive(Debug, Clone)] @@ -28,7 +28,7 @@ pub enum IqPayload { Disco(disco::Disco), IBB(ibb::IBB), Jingle(Jingle), - Ping(ping::Ping), + Ping(Ping), } #[derive(Debug, Clone)] @@ -101,7 +101,7 @@ pub fn parse_iq(root: &Element) -> Result { Some(IqPayload::IBB(ibb)) } else if let Ok(jingle) = Jingle::try_from(elem) { Some(IqPayload::Jingle(jingle)) - } else if let Ok(ping) = ping::parse_ping(elem) { + } else if let Ok(ping) = Ping::try_from(elem) { Some(IqPayload::Ping(ping)) } else { None @@ -155,7 +155,7 @@ pub fn serialise_payload(payload: &IqPayload) -> Element { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), IqPayload::IBB(ref ibb) => ibb::serialise(ibb), IqPayload::Jingle(ref jingle) => jingle.into(), - IqPayload::Ping(_) => ping::serialise_ping(), + IqPayload::Ping(ref ping) => ping.into(), } } diff --git a/src/ping.rs b/src/ping.rs index b6b6f617d1c60b3854e0ef3c1af806ee0d310422..4fe2fd86792ffa9f877ccedb8a7eca6d68dadc53 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,6 +5,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -14,37 +16,42 @@ use ns; #[derive(Debug, Clone)] pub struct Ping; -pub fn parse_ping(root: &Element) -> Result { - if !root.is("ping", ns::PING) { - return Err(Error::ParseError("This is not a ping element.")); - } +impl<'a> TryFrom<&'a Element> for Ping { + type Error = Error; - for _ in root.children() { - return Err(Error::ParseError("Unknown child in ping element.")); + fn try_from(elem: &'a Element) -> Result { + if !elem.is("ping", ns::PING) { + return Err(Error::ParseError("This is not a ping element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in ping element.")); + } + Ok(Ping) } - Ok(Ping { }) } -pub fn serialise_ping() -> Element { - Element::builder("ping").ns(ns::PING).build() +impl<'a> Into for &'a Ping { + fn into(self) -> Element { + Element::builder("ping") + .ns(ns::PING) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use ping; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - ping::parse_ping(&elem).unwrap(); + Ping::try_from(&elem).unwrap(); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = ping::parse_ping(&elem).unwrap_err(); + let error = Ping::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -56,7 +63,7 @@ mod tests { #[ignore] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = ping::parse_ping(&elem).unwrap_err(); + let error = Ping::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From e3acb55b49dcb3d718b1ab47c162285d11a1267d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 4 May 2017 23:11:10 +0100 Subject: [PATCH 0169/1020] ibb: Port to TryFrom/Into. --- src/ibb.rs | 159 +++++++++++++++++++++++++++-------------------------- src/iq.rs | 8 +-- 2 files changed, 86 insertions(+), 81 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index d805eb810af168f1e0f416d952cf2d399e71754e..0156cfee764230ed366373184c996b23bb319f7c 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -64,103 +65,107 @@ pub enum IBB { }, } -fn required_attr(root: &Element, attr: &str, err: Error) -> Result { - root.attr(attr) +fn required_attr(elem: &Element, attr: &str, err: Error) -> Result { + elem.attr(attr) .and_then(|value| value.parse().ok()) .ok_or(err) } -pub fn parse_ibb(root: &Element) -> Result { - if root.is("open", ns::IBB) { - for _ in root.children() { - return Err(Error::ParseError("Unknown child in open element.")); +impl<'a> TryFrom<&'a Element> for IBB { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if elem.is("open", ns::IBB) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in open element.")); + } + let block_size = required_attr(elem, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; + let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; + let stanza = match elem.attr("stanza") { + Some(stanza) => stanza.parse()?, + None => Default::default(), + }; + Ok(IBB::Open { + block_size: block_size, + sid: sid, + stanza: stanza + }) + } else if elem.is("data", ns::IBB) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in data element.")); + } + let seq = required_attr(elem, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; + let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + let data = base64::decode(&elem.text())?; + Ok(IBB::Data { + seq: seq, + sid: sid, + data: data + }) + } else if elem.is("close", ns::IBB) { + let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in close element.")); + } + Ok(IBB::Close { + sid: sid, + }) + } else { + Err(Error::ParseError("This is not an ibb element.")) } - let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; - let stanza = match root.attr("stanza") { - Some(stanza) => stanza.parse()?, - None => Default::default(), - }; - Ok(IBB::Open { - block_size: block_size, - sid: sid, - stanza: stanza - }) - } else if root.is("data", ns::IBB) { - for _ in root.children() { - return Err(Error::ParseError("Unknown child in data element.")); - } - let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; - let data = base64::decode(&root.text())?; - Ok(IBB::Data { - seq: seq, - sid: sid, - data: data - }) - } else if root.is("close", ns::IBB) { - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; - for _ in root.children() { - return Err(Error::ParseError("Unknown child in close element.")); - } - Ok(IBB::Close { - sid: sid, - }) - } else { - Err(Error::ParseError("This is not an ibb element.")) } } -pub fn serialise(ibb: &IBB) -> Element { - match *ibb { - IBB::Open { ref block_size, ref sid, ref stanza } => { - Element::builder("open") - .ns(ns::IBB) - .attr("block-size", format!("{}", block_size)) - .attr("sid", sid.to_owned()) - .attr("stanza", stanza.to_owned()) - .build() - }, - IBB::Data { ref seq, ref sid, ref data } => { - Element::builder("data") - .ns(ns::IBB) - .attr("seq", format!("{}", seq)) - .attr("sid", sid.to_owned()) - .append(base64::encode(&data)) - .build() - }, - IBB::Close { ref sid } => { - Element::builder("close") - .ns(ns::IBB) - .attr("sid", sid.to_owned()) - .build() - }, +impl<'a> Into for &'a IBB { + fn into(self) -> Element { + match *self { + IBB::Open { ref block_size, ref sid, ref stanza } => { + Element::builder("open") + .ns(ns::IBB) + .attr("block-size", format!("{}", block_size)) + .attr("sid", sid.to_owned()) + .attr("stanza", stanza.to_owned()) + .build() + }, + IBB::Data { ref seq, ref sid, ref data } => { + Element::builder("data") + .ns(ns::IBB) + .attr("seq", format!("{}", seq)) + .attr("sid", sid.to_owned()) + .append(base64::encode(&data)) + .build() + }, + IBB::Close { ref sid } => { + Element::builder("close") + .ns(ns::IBB) + .attr("sid", sid.to_owned()) + .build() + }, + } } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use ibb; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let open = ibb::parse_ibb(&elem).unwrap(); + let open = IBB::try_from(&elem).unwrap(); match open { - ibb::IBB::Open { block_size, sid, stanza } => { + IBB::Open { block_size, sid, stanza } => { assert_eq!(block_size, 3); assert_eq!(sid, "coucou"); - assert_eq!(stanza, ibb::Stanza::Iq); + assert_eq!(stanza, Stanza::Iq); }, _ => panic!(), } let elem: Element = "AAAA".parse().unwrap(); - let data = ibb::parse_ibb(&elem).unwrap(); + let data = IBB::try_from(&elem).unwrap(); match data { - ibb::IBB::Data { seq, sid, data } => { + IBB::Data { seq, sid, data } => { assert_eq!(seq, 0); assert_eq!(sid, "coucou"); assert_eq!(data, vec!(0, 0, 0)); @@ -169,9 +174,9 @@ mod tests { } let elem: Element = "".parse().unwrap(); - let close = ibb::parse_ibb(&elem).unwrap(); + let close = IBB::try_from(&elem).unwrap(); match close { - ibb::IBB::Close { sid } => { + IBB::Close { sid } => { assert_eq!(sid, "coucou"); }, _ => panic!(), @@ -181,7 +186,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = ibb::parse_ibb(&elem).unwrap_err(); + let error = IBB::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -190,7 +195,7 @@ mod tests { // TODO: maybe make a better error message here. let elem: Element = "".parse().unwrap(); - let error = ibb::parse_ibb(&elem).unwrap_err(); + let error = IBB::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -198,7 +203,7 @@ mod tests { assert_eq!(message, "Required attribute 'block-size' missing in open element."); let elem: Element = "".parse().unwrap(); - let error = ibb::parse_ibb(&elem).unwrap_err(); + let error = IBB::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -209,7 +214,7 @@ mod tests { #[test] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); - let error = ibb::parse_ibb(&elem).unwrap_err(); + let error = IBB::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/iq.rs b/src/iq.rs index d1e3eeaa5be33dbf1821a3e2a62481eeac997241..65b49cdbce0148609c63fc745dcba0a764f76594 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -18,7 +18,7 @@ use ns; use stanza_error; use disco; -use ibb; +use ibb::IBB; use jingle::Jingle; use ping::Ping; @@ -26,7 +26,7 @@ use ping::Ping; #[derive(Debug, Clone)] pub enum IqPayload { Disco(disco::Disco), - IBB(ibb::IBB), + IBB(IBB), Jingle(Jingle), Ping(Ping), } @@ -97,7 +97,7 @@ pub fn parse_iq(root: &Element) -> Result { } else { let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { Some(IqPayload::Disco(disco)) - } else if let Ok(ibb) = ibb::parse_ibb(elem) { + } else if let Ok(ibb) = IBB::try_from(elem) { Some(IqPayload::IBB(ibb)) } else if let Ok(jingle) = Jingle::try_from(elem) { Some(IqPayload::Jingle(jingle)) @@ -153,7 +153,7 @@ pub fn parse_iq(root: &Element) -> Result { pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { IqPayload::Disco(ref disco) => disco::serialise_disco(disco), - IqPayload::IBB(ref ibb) => ibb::serialise(ibb), + IqPayload::IBB(ref ibb) => ibb.into(), IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(ref ping) => ping.into(), } From 51eecda4fd10025970aafd481a6bd3a52c2231bd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 12:48:42 +0100 Subject: [PATCH 0170/1020] error: Implement From. --- src/error.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/error.rs b/src/error.rs index a65f7e2df4aab5c0355fe569139a0af4007a6d12..2f7b254f40da4948bb7812cb7f9b6d9fdc5f7192 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,6 +7,7 @@ use std::convert::From; use std::io; use std::num; +use std::string; use base64; use minidom; @@ -19,6 +20,7 @@ pub enum Error { XMLError(minidom::Error), Base64Error(base64::DecodeError), ParseIntError(num::ParseIntError), + ParseStringError(string::ParseError), JidParseError(jid::JidParseError), } @@ -46,6 +48,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: string::ParseError) -> Error { + Error::ParseStringError(err) + } +} + impl From for Error { fn from(err: jid::JidParseError) -> Error { Error::JidParseError(err) From 828b88e5b203b9021b4d1dd8dc63cd99ec32ee91 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 12:49:30 +0100 Subject: [PATCH 0171/1020] Add a Jingle SOCKS5 Bytestreams Transport implementation. --- src/jingle_s5b.rs | 315 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 3 + 3 files changed, 321 insertions(+) create mode 100644 src/jingle_s5b.rs diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs new file mode 100644 index 0000000000000000000000000000000000000000..6a6ee50f71a0f511cd5a0dd6763be16df8accb6b --- /dev/null +++ b/src/jingle_s5b.rs @@ -0,0 +1,315 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; +use std::str::FromStr; + +use minidom::{Element, IntoAttributeValue}; + +use error::Error; + +use ns; + +#[derive(Debug, Clone, PartialEq)] +pub enum Type { + Assisted, + Direct, + Proxy, + Tunnel, +} + +impl Default for Type { + fn default() -> Type { + Type::Direct + } +} + +impl FromStr for Type { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "assisted" => Type::Assisted, + "direct" => Type::Direct, + "proxy" => Type::Proxy, + "tunnel" => Type::Tunnel, + + _ => return Err(Error::ParseError("Invalid 'type' attribute in candidate element.")), + }) + } +} + +impl IntoAttributeValue for Type { + fn into_attribute_value(self) -> Option { + Some(match self { + Type::Assisted => String::from("assisted"), + Type::Direct => return None, + Type::Proxy => String::from("proxy"), + Type::Tunnel => String::from("tunnel"), + }) + } +} + +#[derive(Debug, Clone)] +pub struct Candidate { + pub cid: String, + pub host: String, + pub jid: String, + pub port: Option, + pub priority: u32, + pub type_: Type, +} + +impl<'a> Into for &'a Candidate { + fn into(self) -> Element { + Element::builder("candidate") + .ns(ns::JINGLE_S5B) + .attr("cid", self.cid.clone()) + .attr("host", self.host.clone()) + .attr("jid", self.jid.clone()) + .attr("port", match self.port { Some(port) => Some(format!("{}", port)), None => None }) + .attr("priority", format!("{}", self.priority)) + .attr("type", self.type_.clone()) + .build() + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Mode { + Tcp, + Udp, +} + +impl Default for Mode { + fn default() -> Mode { + Mode::Tcp + } +} + +impl FromStr for Mode { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "tcp" => Mode::Tcp, + "udp" => Mode::Udp, + + _ => return Err(Error::ParseError("Invalid 'mode' attribute.")), + }) + } +} + +impl IntoAttributeValue for Mode { + fn into_attribute_value(self) -> Option { + match self { + Mode::Tcp => None, + Mode::Udp => Some(String::from("udp")), + } + } +} + +#[derive(Debug, Clone)] +pub enum TransportPayload { + Activated(String), + Candidates(Vec), + CandidateError, + CandidateUsed(String), + ProxyError, +} + +#[derive(Debug, Clone)] +pub struct Transport { + pub sid: String, + pub dstaddr: Option, + pub mode: Mode, + pub payload: TransportPayload, +} + +impl<'a> TryFrom<&'a Element> for Transport { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if elem.is("transport", ns::JINGLE_S5B) { + let sid = elem.attr("sid") + .ok_or(Error::ParseError("Required attribute 'sid' missing in JingleS5B transport element."))? + .parse()?; + let dstaddr = elem.attr("dstaddr") + .and_then(|value| Some(value.to_owned())); + let mode = match elem.attr("mode") { + None => Default::default(), + Some(mode) => mode.parse()?, + }; + + let mut payload = None; + for child in elem.children() { + payload = Some(if child.is("candidate", ns::JINGLE_S5B) { + let mut candidates = match payload { + Some(TransportPayload::Candidates(candidates)) => candidates, + Some(_) => return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")), + None => vec!(), + }; + let cid = child.attr("cid") + .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B candidate element."))? + .parse()?; + let host = child.attr("host") + .ok_or(Error::ParseError("Required attribute 'host' missing in JingleS5B candidate element."))? + .parse()?; + let jid = child.attr("jid") + .ok_or(Error::ParseError("Required attribute 'jid' missing in JingleS5B candidate element."))? + .parse()?; + let port = match child.attr("port") { + Some(s) => Some(s.parse()?), + None => None, + }; + let priority = child.attr("priority") + .ok_or(Error::ParseError("Required attribute 'priority' missing in JingleS5B candidate element."))? + .parse()?; + let type_ = match child.attr("type") { + Some(s) => s.parse()?, + None => Default::default(), + }; + candidates.push(Candidate { + cid: cid, + host: host, + jid: jid, + port: port, + priority: priority, + type_: type_, + }); + TransportPayload::Candidates(candidates) + } else if child.is("activated", ns::JINGLE_S5B) { + if let Some(_) = payload { + return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); + } + let cid = child.attr("cid") + .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B activated element."))? + .parse()?; + TransportPayload::Activated(cid) + } else if child.is("candidate-error", ns::JINGLE_S5B) { + if let Some(_) = payload { + return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element.")); + } + TransportPayload::CandidateError + } else if child.is("candidate-used", ns::JINGLE_S5B) { + if let Some(_) = payload { + return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); + } + let cid = child.attr("cid") + .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B candidate-used element."))? + .parse()?; + TransportPayload::CandidateUsed(cid) + } else if child.is("proxy-error", ns::JINGLE_S5B) { + if let Some(_) = payload { + return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element.")); + } + TransportPayload::ProxyError + } else { + return Err(Error::ParseError("Unknown child in JingleS5B transport element.")); + }); + } + let payload = payload.ok_or(Error::ParseError("No child in JingleS5B transport element."))?; + Ok(Transport { + sid: sid, + dstaddr: dstaddr, + mode: mode, + payload: payload, + }) + } else { + Err(Error::ParseError("This is not an JingleS5B transport element.")) + } + } +} + +impl<'a> Into for &'a Transport { + fn into(self) -> Element { + Element::builder("transport") + .ns(ns::JINGLE_S5B) + .attr("sid", self.sid.clone()) + .attr("dstaddr", self.dstaddr.clone()) + .attr("mode", self.mode.clone()) + .append(match self.payload { + TransportPayload::Candidates(ref candidates) => { + candidates.iter() + .map(|candidate| -> Element { candidate.into() }) + .collect::>() + }, + TransportPayload::Activated(ref cid) => { + vec!(Element::builder("activated") + .ns(ns::JINGLE_S5B) + .attr("cid", cid.to_owned()) + .build()) + }, + TransportPayload::CandidateError => { + vec!(Element::builder("candidate-error") + .ns(ns::JINGLE_S5B) + .build()) + }, + TransportPayload::CandidateUsed(ref cid) => { + vec!(Element::builder("candidate-used") + .ns(ns::JINGLE_S5B) + .attr("cid", cid.to_owned()) + .build()) + }, + TransportPayload::ProxyError => { + vec!(Element::builder("proxy-error") + .ns(ns::JINGLE_S5B) + .build()) + }, + }) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let transport = Transport::try_from(&elem).unwrap(); + assert_eq!(transport.sid, "coucou"); + assert_eq!(transport.dstaddr, None); + assert_eq!(transport.mode, Mode::Tcp); + match transport.payload { + TransportPayload::ProxyError => (), + _ => panic!("Wrong element inside transport!"), + } + } + + #[test] + fn test_serialise_activated() { + let elem: Element = "".parse().unwrap(); + let transport = Transport { + sid: String::from("coucou"), + dstaddr: None, + mode: Mode::Tcp, + payload: TransportPayload::Activated(String::from("coucou")), + }; + let elem2: Element = (&transport).into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_serialise_candidate() { + let elem: Element = "".parse().unwrap(); + let transport = Transport { + sid: String::from("coucou"), + dstaddr: None, + mode: Mode::Tcp, + payload: TransportPayload::Candidates(vec!(Candidate { + cid: String::from("coucou"), + host: String::from("coucou"), + jid: String::from("coucou@coucou"), + port: None, + priority: 0u32, + type_: Type::Direct, + })), + }; + let elem2: Element = (&transport).into(); + assert_eq!(elem, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3076fccfa3b346d63150393ff1317654da90bdeb..e3fb5356424a05396f7d8c6d3a21b7fd8c4fdd6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,9 @@ pub mod attention; /// XEP-0234: Jingle File Transfer pub mod jingle_ft; +/// XEP-0260: Jingle SOCKS5 Bytestreams Transport Method +pub mod jingle_s5b; + /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub mod jingle_ibb; diff --git a/src/ns.rs b/src/ns.rs index 8e7c08b1ae6e2443838d13bac259554a4c27a1f1..c98bff5839c560fba551fad366bc942358e8f797 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -47,6 +47,9 @@ pub const JINGLE_FT: &'static str = "urn:xmpp:jingle:apps:file-transfer:5"; /// XEP-0234: Jingle File Transfer pub const JINGLE_FT_ERROR: &'static str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; +/// XEP-0260: Jingle SOCKS5 Bytestreams Transport Method +pub const JINGLE_S5B: &'static str = "urn:xmpp:jingle:transports:s5b:1"; + /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; From bb12168c4159edcb6e0d7a8511be171bc02092e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 13:54:16 +0100 Subject: [PATCH 0172/1020] =?UTF-8?q?jingle=5Fs5b:=20Handle=20the=20forgot?= =?UTF-8?q?ten=20case=20of=20an=20empty=20transport=20element,=20see=20?= =?UTF-8?q?=C2=A72.2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jingle_s5b.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 6a6ee50f71a0f511cd5a0dd6763be16df8accb6b..b0d7266f42e16e5ad1c531ef08a7e32307407bfb 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -118,6 +118,7 @@ pub enum TransportPayload { CandidateError, CandidateUsed(String), ProxyError, + None, } #[derive(Debug, Clone)] @@ -210,7 +211,7 @@ impl<'a> TryFrom<&'a Element> for Transport { return Err(Error::ParseError("Unknown child in JingleS5B transport element.")); }); } - let payload = payload.ok_or(Error::ParseError("No child in JingleS5B transport element."))?; + let payload = payload.unwrap_or(TransportPayload::None); Ok(Transport { sid: sid, dstaddr: dstaddr, @@ -258,6 +259,7 @@ impl<'a> Into for &'a Transport { .ns(ns::JINGLE_S5B) .build()) }, + TransportPayload::None => vec!(), }) .build() } @@ -269,13 +271,13 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let transport = Transport::try_from(&elem).unwrap(); assert_eq!(transport.sid, "coucou"); assert_eq!(transport.dstaddr, None); assert_eq!(transport.mode, Mode::Tcp); match transport.payload { - TransportPayload::ProxyError => (), + TransportPayload::None => (), _ => panic!("Wrong element inside transport!"), } } From 8fbda37f6b6990a6db322693e95599ebe4b6ef3b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 14:03:41 +0100 Subject: [PATCH 0173/1020] jingle_ibb: Switch to Into/TryFrom. --- src/jingle_ibb.rs | 70 +++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 8e0d04ca90b9f4dd6effecaf6d133cd2951cd806..4ac847cc70d3d8534a07defe16e8cfb80e207207 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use minidom::Element; @@ -30,55 +31,58 @@ fn required_attr(root: &Element, attr: &str, err: Error) -> Result Result { - if root.is("transport", ns::JINGLE_IBB) { - for _ in root.children() { - return Err(Error::ParseError("Unknown child in JingleIBB element.")); +impl<'a> TryFrom<&'a Element> for Transport { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if elem.is("transport", ns::JINGLE_IBB) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in JingleIBB element.")); + } + let block_size = required_attr(elem, "block-size", Error::ParseError("Required attribute 'block-size' missing in JingleIBB element."))?; + let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in JingleIBB element."))?; + let stanza = elem.attr("stanza") + .unwrap_or("iq") + .parse()?; + Ok(Transport { + block_size: block_size, + sid: sid, + stanza: stanza + }) + } else { + Err(Error::ParseError("This is not an JingleIBB element.")) } - let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in JingleIBB element."))?; - let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in JingleIBB element."))?; - let stanza = root.attr("stanza") - .unwrap_or("iq") - .parse()?; - Ok(Transport { - block_size: block_size, - sid: sid, - stanza: stanza - }) - } else { - Err(Error::ParseError("This is not an JingleIBB element.")) } } -pub fn serialise(transport: &Transport) -> Element { - Element::builder("transport") - .ns(ns::JINGLE_IBB) - .attr("block-size", format!("{}", transport.block_size)) - .attr("sid", transport.sid.clone()) - .attr("stanza", transport.stanza.clone()) - .build() +impl<'a> Into for &'a Transport { + fn into(self) -> Element { + Element::builder("transport") + .ns(ns::JINGLE_IBB) + .attr("block-size", format!("{}", self.block_size)) + .attr("sid", self.sid.clone()) + .attr("stanza", self.stanza.clone()) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use ibb; - use jingle_ibb; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let transport = jingle_ibb::parse_jingle_ibb(&elem).unwrap(); + let transport = Transport::try_from(&elem).unwrap(); assert_eq!(transport.block_size, 3); assert_eq!(transport.sid, "coucou"); - assert_eq!(transport.stanza, ibb::Stanza::Iq); + assert_eq!(transport.stanza, Stanza::Iq); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let error = Transport::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -87,7 +91,7 @@ mod tests { // TODO: maybe make a better error message here. let elem: Element = "".parse().unwrap(); - let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let error = Transport::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -95,7 +99,7 @@ mod tests { assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element."); let elem: Element = "".parse().unwrap(); - let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let error = Transport::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -106,7 +110,7 @@ mod tests { #[test] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); - let error = jingle_ibb::parse_jingle_ibb(&elem).unwrap_err(); + let error = Transport::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From f99c667eabadd9085df00afc2b0f60232a05b151 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 14:58:18 +0100 Subject: [PATCH 0174/1020] jingle: Make description and transport optional in content. --- src/jingle.rs | 67 ++++++++++----------------------------------------- 1 file changed, 13 insertions(+), 54 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index dfe0d71046138649fb33f97e499a17319ef771f5..fb928aee94f0a113f05027a6e8abee5105cd154d 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -150,9 +150,9 @@ pub struct Content { pub disposition: String, pub name: String, pub senders: Senders, - pub description: (String, Element), - pub transport: (String, Element), - pub security: Option<(String, Element)>, + pub description: Option, + pub transport: Option, + pub security: Option, } #[derive(Debug, Clone, PartialEq)] @@ -287,48 +287,19 @@ impl<'a> TryFrom<&'a Element> for Jingle { if description.is_some() { return Err(Error::ParseError("Content must not have more than one description.")); } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on description element."))?; - description = Some(( - namespace, - stuff.clone(), - )); + description = Some(stuff.clone()); } else if stuff.name() == "transport" { if transport.is_some() { return Err(Error::ParseError("Content must not have more than one transport.")); } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on transport element."))?; - transport = Some(( - namespace, - stuff.clone(), - )); + transport = Some(stuff.clone()); } else if stuff.name() == "security" { if security.is_some() { return Err(Error::ParseError("Content must not have more than one security.")); } - let namespace = stuff.ns() - .and_then(|ns| ns.parse().ok()) - // TODO: is this even reachable? - .ok_or(Error::ParseError("Invalid namespace on security element."))?; - security = Some(( - namespace, - stuff.clone(), - )); + security = Some(stuff.clone()); } } - if description.is_none() { - return Err(Error::ParseError("Content must have one description.")); - } - if transport.is_none() { - return Err(Error::ParseError("Content must have one transport.")); - } - let description = description.unwrap().to_owned(); - let transport = transport.unwrap().to_owned(); contents.push(Content { creator: creator, disposition: disposition.to_owned(), @@ -391,10 +362,14 @@ impl<'a> Into for &'a Content { .attr("name", self.name.clone()) .attr("senders", String::from(self.senders.clone())) .build(); - root.append_child(self.description.1.clone()); - root.append_child(self.transport.1.clone()); + if let Some(description) = self.description.clone() { + root.append_child(description); + } + if let Some(transport) = self.transport.clone() { + root.append_child(transport); + } if let Some(security) = self.security.clone() { - root.append_child(security.1.clone()); + root.append_child(security); } root } @@ -529,22 +504,6 @@ mod tests { _ => panic!(), }; assert_eq!(message, "Unknown senders."); - - let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Content must have one description."); - - let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Content must have one transport."); } #[test] From 151635f5fb433fa3aaea4a3614907b928c3fb254 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:07:03 +0100 Subject: [PATCH 0175/1020] receipts: Switch to Into/TryFrom. --- src/message.rs | 8 +++--- src/receipts.rs | 67 ++++++++++++++++++++++++++----------------------- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/message.rs b/src/message.rs index 6fcc681de1ac2d68ee09b91d593a4bd2847e4a6d..48001452a985cf99bc5d5c95c52f8fcf09fcd61a 100644 --- a/src/message.rs +++ b/src/message.rs @@ -18,7 +18,7 @@ use ns; use body; use stanza_error; use chatstates; -use receipts; +use receipts::Receipt; use delay; use attention::Attention; use message_correct; @@ -30,7 +30,7 @@ pub enum MessagePayload { Body(body::Body), StanzaError(stanza_error::StanzaError), ChatState(chatstates::ChatState), - Receipt(receipts::Receipt), + Receipt(Receipt), Delay(delay::Delay), Attention(Attention), MessageCorrect(message_correct::Replace), @@ -117,7 +117,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::StanzaError(stanza_error)) } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = receipts::parse_receipt(elem) { + } else if let Ok(receipt) = Receipt::try_from(elem) { Some(MessagePayload::Receipt(receipt)) } else if let Ok(delay) = delay::parse_delay(elem) { Some(MessagePayload::Delay(delay)) @@ -150,7 +150,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), - MessagePayload::Receipt(ref receipt) => receipts::serialise(receipt), + MessagePayload::Receipt(ref receipt) => receipt.into(), MessagePayload::Delay(ref delay) => delay::serialise(delay), MessagePayload::MessageCorrect(ref replace) => message_correct::serialise(replace), MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme), diff --git a/src/receipts.rs b/src/receipts.rs index 3dc3b787606aaf2a08ca9506f4a9a7e30abff7ff..27392fb8cb161c2f1cea195907754b35d06d876b 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -16,59 +18,62 @@ pub enum Receipt { Received(String), } -pub fn parse_receipt(root: &Element) -> Result { - for _ in root.children() { - return Err(Error::ParseError("Unknown child in receipt element.")); - } - if root.is("request", ns::RECEIPTS) { - Ok(Receipt::Request) - } else if root.is("received", ns::RECEIPTS) { - let id = root.attr("id").unwrap_or("").to_owned(); - Ok(Receipt::Received(id)) - } else { - Err(Error::ParseError("This is not a receipt element.")) +impl<'a> TryFrom<&'a Element> for Receipt { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in receipt element.")); + } + if elem.is("request", ns::RECEIPTS) { + Ok(Receipt::Request) + } else if elem.is("received", ns::RECEIPTS) { + let id = elem.attr("id").unwrap_or("").to_owned(); + Ok(Receipt::Received(id)) + } else { + Err(Error::ParseError("This is not a receipt element.")) + } } } -pub fn serialise(receipt: &Receipt) -> Element { - match *receipt { - Receipt::Request => Element::builder("request") - .ns(ns::RECEIPTS) - .build(), - Receipt::Received(ref id) => Element::builder("received") - .ns(ns::RECEIPTS) - .attr("id", id.clone()) - .build(), +impl<'a> Into for &'a Receipt { + fn into(self) -> Element { + match *self { + Receipt::Request => Element::builder("request") + .ns(ns::RECEIPTS) + .build(), + Receipt::Received(ref id) => Element::builder("received") + .ns(ns::RECEIPTS) + .attr("id", id.clone()) + .build(), + } } } #[cfg(test)] mod tests { - use minidom::Element; - //use error::Error; - use receipts; - use ns; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - receipts::parse_receipt(&elem).unwrap(); + Receipt::try_from(&elem).unwrap(); let elem: Element = "".parse().unwrap(); - receipts::parse_receipt(&elem).unwrap(); + Receipt::try_from(&elem).unwrap(); let elem: Element = "".parse().unwrap(); - receipts::parse_receipt(&elem).unwrap(); + Receipt::try_from(&elem).unwrap(); } #[test] fn test_serialise() { - let receipt = receipts::Receipt::Request; - let elem = receipts::serialise(&receipt); + let receipt = Receipt::Request; + let elem: Element = (&receipt).into(); assert!(elem.is("request", ns::RECEIPTS)); - let receipt = receipts::Receipt::Received("coucou".to_owned()); - let elem = receipts::serialise(&receipt); + let receipt = Receipt::Received("coucou".to_owned()); + let elem: Element = (&receipt).into(); assert!(elem.is("received", ns::RECEIPTS)); assert_eq!(elem.attr("id"), Some("coucou")); } From 7ebabf7e9103b2938caf5c7a1819dad7a095bf7c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:10:35 +0100 Subject: [PATCH 0176/1020] stanza_id: Switch to Into/TryFrom. --- src/stanza_id.rs | 102 ++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 5d34c3dc5c44b4bb47399e6543167d26572566a4..d478b1aae17af0f50f5bceab5febaf3e98689657 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use jid::Jid; @@ -22,61 +24,63 @@ pub enum StanzaId { }, } -pub fn parse_stanza_id(root: &Element) -> Result { - let is_stanza_id = root.is("stanza-id", ns::SID); - if !is_stanza_id && !root.is("origin-id", ns::SID) { - return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); - } - let id = match root.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in stanza-id or origin-id.")), - }; - Ok(if is_stanza_id { - let by = match root.attr("by") { - Some(by) => by.parse().unwrap(), - None => return Err(Error::ParseError("No 'by' attribute present in stanza-id.")), +impl<'a> TryFrom<&'a Element> for StanzaId { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + let is_stanza_id = elem.is("stanza-id", ns::SID); + if !is_stanza_id && !elem.is("origin-id", ns::SID) { + return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); + } + let id = match elem.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in stanza-id or origin-id.")), }; - StanzaId::StanzaId { id, by } - } else { - StanzaId::OriginId { id } - }) + Ok(if is_stanza_id { + let by = match elem.attr("by") { + Some(by) => by.parse().unwrap(), + None => return Err(Error::ParseError("No 'by' attribute present in stanza-id.")), + }; + StanzaId::StanzaId { id, by } + } else { + StanzaId::OriginId { id } + }) + } } -pub fn serialise(stanza_id: &StanzaId) -> Element { - match *stanza_id { - StanzaId::StanzaId { ref id, ref by } => { - Element::builder("stanza-id") - .ns(ns::SID) - .attr("id", id.clone()) - .attr("by", String::from(by.clone())) - .build() - }, - StanzaId::OriginId { ref id } => { - Element::builder("origin-id") - .ns(ns::SID) - .attr("id", id.clone()) - .build() - }, +impl<'a> Into for &'a StanzaId { + fn into(self) -> Element { + match *self { + StanzaId::StanzaId { ref id, ref by } => { + Element::builder("stanza-id") + .ns(ns::SID) + .attr("id", id.clone()) + .attr("by", String::from(by.clone())) + .build() + }, + StanzaId::OriginId { ref id } => { + Element::builder("origin-id") + .ns(ns::SID) + .attr("id", id.clone()) + .build() + }, + } } } #[cfg(test)] mod tests { + use super::*; use std::str::FromStr; - use minidom::Element; - use jid::Jid; - use error::Error; - use stanza_id; - #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let stanza_id = stanza_id::parse_stanza_id(&elem).unwrap(); - if let stanza_id::StanzaId::StanzaId { id, by } = stanza_id { + let stanza_id = StanzaId::try_from(&elem).unwrap(); + if let StanzaId::StanzaId { id, by } = stanza_id { assert_eq!(id, String::from("coucou")); assert_eq!(by, Jid::from_str("coucou@coucou").unwrap()); } else { @@ -84,8 +88,8 @@ mod tests { } let elem: Element = "".parse().unwrap(); - let stanza_id = stanza_id::parse_stanza_id(&elem).unwrap(); - if let stanza_id::StanzaId::OriginId { id } = stanza_id { + let stanza_id = StanzaId::try_from(&elem).unwrap(); + if let StanzaId::OriginId { id } = stanza_id { assert_eq!(id, String::from("coucou")); } else { panic!(); @@ -95,7 +99,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let error = StanzaId::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -106,7 +110,7 @@ mod tests { #[test] fn test_invalid_id() { let elem: Element = "".parse().unwrap(); - let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let error = StanzaId::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -117,7 +121,7 @@ mod tests { #[test] fn test_invalid_by() { let elem: Element = "".parse().unwrap(); - let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let error = StanzaId::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -128,8 +132,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let stanza_id = stanza_id::StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; - let elem2 = stanza_id::serialise(&stanza_id); + let stanza_id = StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; + let elem2 = (&stanza_id).into(); assert_eq!(elem, elem2); } } From de8fe4bf02728b862732dc8bdc54a5490158ab57 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:14:45 +0100 Subject: [PATCH 0177/1020] rsm: Switch to Into/TryFrom. --- src/mam.rs | 11 +-- src/rsm.rs | 212 +++++++++++++++++++++++++++-------------------------- 2 files changed, 115 insertions(+), 108 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index cb0cc7ac048dce7a73f86daf56d53999bf37d33c..77a3b6cf0caf988d0ee8294e2f7ca1519bbafcd3 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use jid::Jid; @@ -11,7 +13,6 @@ use error::Error; use data_forms; use data_forms::DataForm; -use rsm; use rsm::Set; use forwarding; use forwarding::Forwarded; @@ -63,7 +64,7 @@ pub fn parse_query(root: &Element) -> Result { if child.is("x", ns::DATA_FORMS) { form = Some(data_forms::parse_data_form(child)?); } else if child.is("set", ns::RSM) { - set = Some(rsm::parse_rsm(child)?); + set = Some(Set::try_from(child)?); } else { return Err(Error::ParseError("Unknown child in query element.")); } @@ -117,7 +118,7 @@ pub fn parse_fin(root: &Element) -> Result { let mut set = None; for child in root.children() { if child.is("set", ns::RSM) { - set = Some(rsm::parse_rsm(child)?); + set = Some(Set::try_from(child)?); } else { return Err(Error::ParseError("Unknown child in fin element.")); } @@ -179,7 +180,7 @@ pub fn serialise_query(query: &Query) -> Element { // elem.append_child(data_forms::serialise(&form)); //} if let Some(ref set) = query.set { - elem.append_child(rsm::serialise(&set)); + elem.append_child(set.into()); } elem } @@ -202,7 +203,7 @@ pub fn serialise_fin(fin: &Fin) -> Element { false => None, }) .build(); - elem.append_child(rsm::serialise(&fin.set)); + elem.append_child((&fin.set).into()); elem } diff --git a/src/rsm.rs b/src/rsm.rs index d23fa1b6bc58919b080182f28627e2c67be73460..4caab08096b9bc75be4ffb687c931c0d2f82e8a6 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -27,119 +29,123 @@ pub struct Set { pub max: Option, } -pub fn parse_rsm(root: &Element) -> Result { - if !root.is("set", ns::RSM) { - return Err(Error::ParseError("This is not a RSM element.")); - } - let mut after = None; - let mut before = None; - let mut count = None; - let mut first = None; - let mut index = None; - let mut last = None; - let mut max = None; - for child in root.children() { - if child.is("after", ns::RSM) { - if after.is_some() { - return Err(Error::ParseError("Set can’t have more than one after.")); - } - after = Some(child.text()); - } else if child.is("before", ns::RSM) { - if before.is_some() { - return Err(Error::ParseError("Set can’t have more than one before.")); - } - before = Some(child.text()); - } else if child.is("count", ns::RSM) { - if count.is_some() { - return Err(Error::ParseError("Set can’t have more than one count.")); - } - count = Some(child.text().parse()?); - } else if child.is("first", ns::RSM) { - if first.is_some() { - return Err(Error::ParseError("Set can’t have more than one first.")); - } - first = Some(First { - index: match child.attr("index") { - Some(index) => Some(index.parse()?), - None => None, - }, - base: child.text(), - }); - } else if child.is("index", ns::RSM) { - if index.is_some() { - return Err(Error::ParseError("Set can’t have more than one index.")); - } - index = Some(child.text().parse()?); - } else if child.is("last", ns::RSM) { - if last.is_some() { - return Err(Error::ParseError("Set can’t have more than one last.")); - } - last = Some(child.text()); - } else if child.is("max", ns::RSM) { - if max.is_some() { - return Err(Error::ParseError("Set can’t have more than one max.")); +impl<'a> TryFrom<&'a Element> for Set { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("set", ns::RSM) { + return Err(Error::ParseError("This is not a RSM element.")); + } + let mut after = None; + let mut before = None; + let mut count = None; + let mut first = None; + let mut index = None; + let mut last = None; + let mut max = None; + for child in elem.children() { + if child.is("after", ns::RSM) { + if after.is_some() { + return Err(Error::ParseError("Set can’t have more than one after.")); + } + after = Some(child.text()); + } else if child.is("before", ns::RSM) { + if before.is_some() { + return Err(Error::ParseError("Set can’t have more than one before.")); + } + before = Some(child.text()); + } else if child.is("count", ns::RSM) { + if count.is_some() { + return Err(Error::ParseError("Set can’t have more than one count.")); + } + count = Some(child.text().parse()?); + } else if child.is("first", ns::RSM) { + if first.is_some() { + return Err(Error::ParseError("Set can’t have more than one first.")); + } + first = Some(First { + index: match child.attr("index") { + Some(index) => Some(index.parse()?), + None => None, + }, + base: child.text(), + }); + } else if child.is("index", ns::RSM) { + if index.is_some() { + return Err(Error::ParseError("Set can’t have more than one index.")); + } + index = Some(child.text().parse()?); + } else if child.is("last", ns::RSM) { + if last.is_some() { + return Err(Error::ParseError("Set can’t have more than one last.")); + } + last = Some(child.text()); + } else if child.is("max", ns::RSM) { + if max.is_some() { + return Err(Error::ParseError("Set can’t have more than one max.")); + } + max = Some(child.text().parse()?); + } else { + return Err(Error::ParseError("Unknown child in set element.")); } - max = Some(child.text().parse()?); - } else { - return Err(Error::ParseError("Unknown child in set element.")); } + Ok(Set { + after: after, + before: before, + count: count, + first: first, + index: index, + last: last, + max: max, + }) } - Ok(Set { - after: after, - before: before, - count: count, - first: first, - index: index, - last: last, - max: max, - }) } -pub fn serialise(rsm: &Set) -> Element { - let mut elem = Element::builder("set") - .ns(ns::RSM) - .build(); - if rsm.after.is_some() { - elem.append_child(Element::builder("after").ns(ns::RSM).append(rsm.after.clone()).build()); - } - if rsm.before.is_some() { - elem.append_child(Element::builder("before").ns(ns::RSM).append(rsm.before.clone()).build()); - } - if rsm.count.is_some() { - elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", rsm.count.unwrap())).build()); - } - if rsm.first.is_some() { - let first = rsm.first.clone().unwrap(); - elem.append_child(Element::builder("first") - .ns(ns::RSM) - .attr("index", match first.index { - Some(index) => Some(format!("{}", index)), - None => None, - }) - .append(first.base.clone()).build()); - } - if rsm.index.is_some() { - elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", rsm.index.unwrap())).build()); - } - if rsm.last.is_some() { - elem.append_child(Element::builder("last").ns(ns::RSM).append(rsm.last.clone()).build()); - } - if rsm.max.is_some() { - elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", rsm.max.unwrap())).build()); +impl<'a> Into for &'a Set { + fn into(self) -> Element { + let mut elem = Element::builder("set") + .ns(ns::RSM) + .build(); + if self.after.is_some() { + elem.append_child(Element::builder("after").ns(ns::RSM).append(self.after.clone()).build()); + } + if self.before.is_some() { + elem.append_child(Element::builder("before").ns(ns::RSM).append(self.before.clone()).build()); + } + if self.count.is_some() { + elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", self.count.unwrap())).build()); + } + if self.first.is_some() { + let first = self.first.clone().unwrap(); + elem.append_child(Element::builder("first") + .ns(ns::RSM) + .attr("index", match first.index { + Some(index) => Some(format!("{}", index)), + None => None, + }) + .append(first.base.clone()).build()); + } + if self.index.is_some() { + elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", self.index.unwrap())).build()); + } + if self.last.is_some() { + elem.append_child(Element::builder("last").ns(ns::RSM).append(self.last.clone()).build()); + } + if self.max.is_some() { + elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", self.max.unwrap())).build()); + } + elem } - elem } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use rsm; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let set = rsm::parse_rsm(&elem).unwrap(); + let set = Set::try_from(&elem).unwrap(); assert_eq!(set.after, None); assert_eq!(set.before, None); assert_eq!(set.count, None); @@ -155,7 +161,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = rsm::parse_rsm(&elem).unwrap_err(); + let error = Set::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -166,7 +172,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = rsm::parse_rsm(&elem).unwrap_err(); + let error = Set::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -177,7 +183,7 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let rsm = rsm::Set { + let rsm = Set { after: None, before: None, count: None, @@ -186,7 +192,7 @@ mod tests { last: None, max: None, }; - let elem2 = rsm::serialise(&rsm); + let elem2 = (&rsm).into(); assert_eq!(elem, elem2); } } From 4f11a067d82f8ff407c734df2a7b94fcd67d2f56 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:16:45 +0100 Subject: [PATCH 0178/1020] message_correct: Switch to Into/TryFrom. --- src/message.rs | 8 +++---- src/message_correct.rs | 54 +++++++++++++++++++++++------------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/message.rs b/src/message.rs index 48001452a985cf99bc5d5c95c52f8fcf09fcd61a..c513abee9aad85c47c58b273683d5267c8f73727 100644 --- a/src/message.rs +++ b/src/message.rs @@ -21,7 +21,7 @@ use chatstates; use receipts::Receipt; use delay; use attention::Attention; -use message_correct; +use message_correct::Replace; use eme; /// Lists every known payload of a ``. @@ -33,7 +33,7 @@ pub enum MessagePayload { Receipt(Receipt), Delay(delay::Delay), Attention(Attention), - MessageCorrect(message_correct::Replace), + MessageCorrect(Replace), ExplicitMessageEncryption(eme::ExplicitMessageEncryption), } @@ -123,7 +123,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::Delay(delay)) } else if let Ok(attention) = Attention::try_from(elem) { Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = message_correct::parse_replace(elem) { + } else if let Ok(replace) = Replace::try_from(elem) { Some(MessagePayload::MessageCorrect(replace)) } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { Some(MessagePayload::ExplicitMessageEncryption(eme)) @@ -152,7 +152,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), MessagePayload::Receipt(ref receipt) => receipt.into(), MessagePayload::Delay(ref delay) => delay::serialise(delay), - MessagePayload::MessageCorrect(ref replace) => message_correct::serialise(replace), + MessagePayload::MessageCorrect(ref replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme), } } diff --git a/src/message_correct.rs b/src/message_correct.rs index 27ac7e917fcad7f8186800e8c7855eac95a99d17..ecc555cc5aa23681d4e6c8600795a8aaca234d2a 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -15,43 +17,47 @@ pub struct Replace { pub id: String, } -pub fn parse_replace(root: &Element) -> Result { - if !root.is("replace", ns::MESSAGE_CORRECT) { - return Err(Error::ParseError("This is not a replace element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in replace element.")); +impl<'a> TryFrom<&'a Element> for Replace { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("replace", ns::MESSAGE_CORRECT) { + return Err(Error::ParseError("This is not a replace element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in replace element.")); + } + let id = match elem.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in replace.")), + }; + Ok(Replace { id: id }) } - let id = match root.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in replace.")), - }; - Ok(Replace { id: id }) } -pub fn serialise(replace: &Replace) -> Element { - Element::builder("replace") - .ns(ns::MESSAGE_CORRECT) - .attr("id", replace.id.clone()) - .build() +impl<'a> Into for &'a Replace { + fn into(self) -> Element { + Element::builder("replace") + .ns(ns::MESSAGE_CORRECT) + .attr("id", self.id.clone()) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use message_correct; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - message_correct::parse_replace(&elem).unwrap(); + Replace::try_from(&elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = message_correct::parse_replace(&elem).unwrap_err(); + let error = Replace::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -62,7 +68,7 @@ mod tests { #[test] fn test_invalid_id() { let elem: Element = "".parse().unwrap(); - let error = message_correct::parse_replace(&elem).unwrap_err(); + let error = Replace::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -73,8 +79,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let replace = message_correct::Replace { id: String::from("coucou") }; - let elem2 = message_correct::serialise(&replace); + let replace = Replace { id: String::from("coucou") }; + let elem2 = (&replace).into(); assert_eq!(elem, elem2); } } From a3a90e4edad79e1a231c761cab96953b8a4bb2e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:30:52 +0100 Subject: [PATCH 0179/1020] jingle_ft: Switch to Into/TryFrom. --- src/jingle_ft.rs | 270 ++++++++++++++++++++++++----------------------- 1 file changed, 140 insertions(+), 130 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 7cd729e1041530bdc2b8fca0136e97780a307138..f2b981faa45d8f4a967769a243f4da01bfa43e65 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use hashes; use hashes::{Hash, parse_hash}; @@ -85,150 +87,158 @@ impl IntoElements for Received { } } -pub fn parse_jingle_ft(root: &Element) -> Result { - if !root.is("description", ns::JINGLE_FT) { - return Err(Error::ParseError("This is not a JingleFT description element.")); - } - if root.children().collect::>().len() != 1 { - return Err(Error::ParseError("JingleFT description element must have exactly one child.")); - } +impl<'a> TryFrom<&'a Element> for Description { + type Error = Error; - let mut date = None; - let mut media_type = None; - let mut name = None; - let mut desc = None; - let mut size = None; - let mut range = None; - let mut hashes = vec!(); - for description_payload in root.children() { - if !description_payload.is("file", ns::JINGLE_FT) { - return Err(Error::ParseError("Unknown element in JingleFT description.")); + fn try_from(elem: &'a Element) -> Result { + if !elem.is("description", ns::JINGLE_FT) { + return Err(Error::ParseError("This is not a JingleFT description element.")); } - for file_payload in description_payload.children() { - if file_payload.is("date", ns::JINGLE_FT) { - if date.is_some() { - return Err(Error::ParseError("File must not have more than one date.")); - } - date = Some(file_payload.text()); - } else if file_payload.is("media-type", ns::JINGLE_FT) { - if media_type.is_some() { - return Err(Error::ParseError("File must not have more than one media-type.")); - } - media_type = Some(file_payload.text()); - } else if file_payload.is("name", ns::JINGLE_FT) { - if name.is_some() { - return Err(Error::ParseError("File must not have more than one name.")); - } - name = Some(file_payload.text()); - } else if file_payload.is("desc", ns::JINGLE_FT) { - if desc.is_some() { - return Err(Error::ParseError("File must not have more than one desc.")); - } - desc = Some(file_payload.text()); - } else if file_payload.is("size", ns::JINGLE_FT) { - if size.is_some() { - return Err(Error::ParseError("File must not have more than one size.")); - } - size = Some(file_payload.text().parse()?); - } else if file_payload.is("range", ns::JINGLE_FT) { - if range.is_some() { - return Err(Error::ParseError("File must not have more than one range.")); - } - let offset = file_payload.attr("offset").unwrap_or("0").parse()?; - let length = match file_payload.attr("length") { - Some(length) => Some(length.parse()?), - None => None, - }; - let mut range_hashes = vec!(); - for hash_element in file_payload.children() { - if !hash_element.is("hash", ns::HASHES) { - return Err(Error::ParseError("Unknown element in JingleFT range.")); + if elem.children().collect::>().len() != 1 { + return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + } + + let mut date = None; + let mut media_type = None; + let mut name = None; + let mut desc = None; + let mut size = None; + let mut range = None; + let mut hashes = vec!(); + for description_payload in elem.children() { + if !description_payload.is("file", ns::JINGLE_FT) { + return Err(Error::ParseError("Unknown element in JingleFT description.")); + } + for file_payload in description_payload.children() { + if file_payload.is("date", ns::JINGLE_FT) { + if date.is_some() { + return Err(Error::ParseError("File must not have more than one date.")); + } + date = Some(file_payload.text()); + } else if file_payload.is("media-type", ns::JINGLE_FT) { + if media_type.is_some() { + return Err(Error::ParseError("File must not have more than one media-type.")); + } + media_type = Some(file_payload.text()); + } else if file_payload.is("name", ns::JINGLE_FT) { + if name.is_some() { + return Err(Error::ParseError("File must not have more than one name.")); + } + name = Some(file_payload.text()); + } else if file_payload.is("desc", ns::JINGLE_FT) { + if desc.is_some() { + return Err(Error::ParseError("File must not have more than one desc.")); } - range_hashes.push(parse_hash(hash_element)?); + desc = Some(file_payload.text()); + } else if file_payload.is("size", ns::JINGLE_FT) { + if size.is_some() { + return Err(Error::ParseError("File must not have more than one size.")); + } + size = Some(file_payload.text().parse()?); + } else if file_payload.is("range", ns::JINGLE_FT) { + if range.is_some() { + return Err(Error::ParseError("File must not have more than one range.")); + } + let offset = file_payload.attr("offset").unwrap_or("0").parse()?; + let length = match file_payload.attr("length") { + Some(length) => Some(length.parse()?), + None => None, + }; + let mut range_hashes = vec!(); + for hash_element in file_payload.children() { + if !hash_element.is("hash", ns::HASHES) { + return Err(Error::ParseError("Unknown element in JingleFT range.")); + } + range_hashes.push(parse_hash(hash_element)?); + } + range = Some(Range { + offset: offset, + length: length, + hashes: range_hashes, + }); + } else if file_payload.is("hash", ns::HASHES) { + hashes.push(parse_hash(file_payload)?); + } else { + return Err(Error::ParseError("Unknown element in JingleFT file.")); } - range = Some(Range { - offset: offset, - length: length, - hashes: range_hashes, - }); - } else if file_payload.is("hash", ns::HASHES) { - hashes.push(parse_hash(file_payload)?); - } else { - return Err(Error::ParseError("Unknown element in JingleFT file.")); } } - } - Ok(Description { - file: File { - date: date, - media_type: media_type, - name: name, - desc: desc, - size: size, - range: range, - hashes: hashes, - }, - }) + Ok(Description { + file: File { + date: date, + media_type: media_type, + name: name, + desc: desc, + size: size, + range: range, + hashes: hashes, + }, + }) + } } -pub fn serialise_file(file: &File) -> Element { - let mut root = Element::builder("file") - .ns(ns::JINGLE_FT) - .build(); - if let Some(ref date) = file.date { - root.append_child(Element::builder("date") - .ns(ns::JINGLE_FT) - .append(date.clone()) - .build()); - } - if let Some(ref media_type) = file.media_type { - root.append_child(Element::builder("media-type") - .ns(ns::JINGLE_FT) - .append(media_type.clone()) - .build()); - } - if let Some(ref name) = file.name { - root.append_child(Element::builder("name") - .ns(ns::JINGLE_FT) - .append(name.clone()) - .build()); - } - if let Some(ref desc) = file.desc { - root.append_child(Element::builder("desc") - .ns(ns::JINGLE_FT) - .append(desc.clone()) - .build()); - } - if let Some(ref size) = file.size { - root.append_child(Element::builder("size") - .ns(ns::JINGLE_FT) - .append(format!("{}", size)) - .build()); - } - if let Some(ref range) = file.range { - root.append_child(Element::builder("range") - .ns(ns::JINGLE_FT) - .append(range.clone()) - .build()); - } - for hash in file.hashes.clone() { - root.append_child(hashes::serialise(&hash)); +impl<'a> Into for &'a File { + fn into(self) -> Element { + let mut root = Element::builder("file") + .ns(ns::JINGLE_FT) + .build(); + if let Some(ref date) = self.date { + root.append_child(Element::builder("date") + .ns(ns::JINGLE_FT) + .append(date.clone()) + .build()); + } + if let Some(ref media_type) = self.media_type { + root.append_child(Element::builder("media-type") + .ns(ns::JINGLE_FT) + .append(media_type.clone()) + .build()); + } + if let Some(ref name) = self.name { + root.append_child(Element::builder("name") + .ns(ns::JINGLE_FT) + .append(name.clone()) + .build()); + } + if let Some(ref desc) = self.desc { + root.append_child(Element::builder("desc") + .ns(ns::JINGLE_FT) + .append(desc.clone()) + .build()); + } + if let Some(ref size) = self.size { + root.append_child(Element::builder("size") + .ns(ns::JINGLE_FT) + .append(format!("{}", size)) + .build()); + } + if let Some(ref range) = self.range { + root.append_child(Element::builder("range") + .ns(ns::JINGLE_FT) + .append(range.clone()) + .build()); + } + for hash in self.hashes.clone() { + root.append_child(hashes::serialise(&hash)); + } + root } - root } -pub fn serialise(desc: &Description) -> Element { - Element::builder("description") - .ns(ns::JINGLE_FT) - .append(serialise_file(&desc.file)) - .build() +impl<'a> Into for &'a Description { + fn into(self) -> Element { + let file: Element = (&self.file).into(); + Element::builder("description") + .ns(ns::JINGLE_FT) + .append(file) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use jingle_ft; + use super::*; #[test] fn test_description() { @@ -245,7 +255,7 @@ mod tests { "#.parse().unwrap(); - let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); + let desc = Description::try_from(&elem).unwrap(); assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.desc, None); @@ -267,7 +277,7 @@ mod tests { "#.parse().unwrap(); - let desc = jingle_ft::parse_jingle_ft(&elem).unwrap(); + let desc = Description::try_from(&elem).unwrap(); assert_eq!(desc.file.media_type, None); assert_eq!(desc.file.name, None); assert_eq!(desc.file.desc, None); From e45152018771ad1cdb7f83e266fe761f1e5f9e11 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:33:58 +0100 Subject: [PATCH 0180/1020] chatstates: Switch to Into/TryFrom. --- src/chatstates.rs | 75 +++++++++++++++++++++++++---------------------- src/message.rs | 8 ++--- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index d26791ef67968e9f0a2540a5e8eb1b0f2c1c7f4b..9ed2b10c1a2b804e2ce402ae5955fa247a6c3822 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -19,53 +21,56 @@ pub enum ChatState { Paused, } -pub fn parse_chatstate(root: &Element) -> Result { - for _ in root.children() { - return Err(Error::ParseError("Unknown child in chatstate element.")); - } - if root.is("active", ns::CHATSTATES) { - Ok(ChatState::Active) - } else if root.is("composing", ns::CHATSTATES) { - Ok(ChatState::Composing) - } else if root.is("gone", ns::CHATSTATES) { - Ok(ChatState::Gone) - } else if root.is("inactive", ns::CHATSTATES) { - Ok(ChatState::Inactive) - } else if root.is("paused", ns::CHATSTATES) { - Ok(ChatState::Paused) - } else { - Err(Error::ParseError("This is not a chatstate element.")) +impl<'a> TryFrom<&'a Element> for ChatState { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in chatstate element.")); + } + if elem.is("active", ns::CHATSTATES) { + Ok(ChatState::Active) + } else if elem.is("composing", ns::CHATSTATES) { + Ok(ChatState::Composing) + } else if elem.is("gone", ns::CHATSTATES) { + Ok(ChatState::Gone) + } else if elem.is("inactive", ns::CHATSTATES) { + Ok(ChatState::Inactive) + } else if elem.is("paused", ns::CHATSTATES) { + Ok(ChatState::Paused) + } else { + Err(Error::ParseError("This is not a chatstate element.")) + } } } -pub fn serialise(chatstate: &ChatState) -> Element { - Element::builder(match *chatstate { - ChatState::Active => "active", - ChatState::Composing => "composing", - ChatState::Gone => "gone", - ChatState::Inactive => "inactive", - ChatState::Paused => "paused", - }).ns(ns::CHATSTATES) - .build() +impl<'a> Into for &'a ChatState { + fn into(self) -> Element { + Element::builder(match *self { + ChatState::Active => "active", + ChatState::Composing => "composing", + ChatState::Gone => "gone", + ChatState::Inactive => "inactive", + ChatState::Paused => "paused", + }).ns(ns::CHATSTATES) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use chatstates; - use ns; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - chatstates::parse_chatstate(&elem).unwrap(); + ChatState::try_from(&elem).unwrap(); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let error = ChatState::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -76,7 +81,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let error = ChatState::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -88,7 +93,7 @@ mod tests { #[ignore] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = chatstates::parse_chatstate(&elem).unwrap_err(); + let error = ChatState::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -98,8 +103,8 @@ mod tests { #[test] fn test_serialise() { - let chatstate = chatstates::ChatState::Active; - let elem = chatstates::serialise(&chatstate); + let chatstate = ChatState::Active; + let elem: Element = (&chatstate).into(); assert!(elem.is("active", ns::CHATSTATES)); } } diff --git a/src/message.rs b/src/message.rs index c513abee9aad85c47c58b273683d5267c8f73727..dfc5e6eb5655f6f7d5bf4637feb1280cf311a4f0 100644 --- a/src/message.rs +++ b/src/message.rs @@ -17,7 +17,7 @@ use ns; use body; use stanza_error; -use chatstates; +use chatstates::ChatState; use receipts::Receipt; use delay; use attention::Attention; @@ -29,7 +29,7 @@ use eme; pub enum MessagePayload { Body(body::Body), StanzaError(stanza_error::StanzaError), - ChatState(chatstates::ChatState), + ChatState(ChatState), Receipt(Receipt), Delay(delay::Delay), Attention(Attention), @@ -115,7 +115,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::Body(body)) } else if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { Some(MessagePayload::StanzaError(stanza_error)) - } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) { + } else if let Ok(chatstate) = ChatState::try_from(elem) { Some(MessagePayload::ChatState(chatstate)) } else if let Ok(receipt) = Receipt::try_from(elem) { Some(MessagePayload::Receipt(receipt)) @@ -149,7 +149,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { MessagePayload::Body(ref body) => body::serialise(body), MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), MessagePayload::Attention(ref attention) => attention.into(), - MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate), + MessagePayload::ChatState(ref chatstate) => chatstate.into(), MessagePayload::Receipt(ref receipt) => receipt.into(), MessagePayload::Delay(ref delay) => delay::serialise(delay), MessagePayload::MessageCorrect(ref replace) => replace.into(), From 0f58e650b75974d3419df3161d947a64efdf50f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:42:12 +0100 Subject: [PATCH 0181/1020] delay: Switch to Into/TryFrom. --- src/delay.rs | 82 +++++++++++++++++++++++------------------------ src/forwarding.rs | 10 +++--- src/message.rs | 8 ++--- src/presence.rs | 9 +++--- 4 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 2b2149eb6a5526b9249ceba9b82bcaca1135ba93..a15b61bcac8340fc1320e262975fbef2b16d9383 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,7 +4,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoElements, ElementEmitter}; +use std::convert::TryFrom; + +use minidom::Element; use error::Error; use jid::Jid; @@ -18,54 +20,50 @@ pub struct Delay { pub data: Option, } -pub fn parse_delay(root: &Element) -> Result { - if !root.is("delay", ns::DELAY) { - return Err(Error::ParseError("This is not a delay element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in delay element.")); - } - let from = root.attr("from").and_then(|value| value.parse().ok()); - let stamp = root.attr("stamp").ok_or(Error::ParseError("Mandatory argument 'stamp' not present in delay element."))?.to_owned(); - let data = match root.text().as_ref() { - "" => None, - text => Some(text.to_owned()), - }; - Ok(Delay { - from: from, - stamp: stamp, - data: data, - }) -} +impl<'a> TryFrom<&'a Element> for Delay { + type Error = Error; -pub fn serialise(delay: &Delay) -> Element { - Element::builder("delay") - .ns(ns::DELAY) - .attr("from", delay.from.clone().and_then(|value| Some(String::from(value)))) - .attr("stamp", delay.stamp.clone()) - .append(delay.data.clone()) - .build() + fn try_from(elem: &'a Element) -> Result { + if !elem.is("delay", ns::DELAY) { + return Err(Error::ParseError("This is not a delay element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in delay element.")); + } + let from = elem.attr("from").and_then(|value| value.parse().ok()); + let stamp = elem.attr("stamp").ok_or(Error::ParseError("Mandatory argument 'stamp' not present in delay element."))?.to_owned(); + let data = match elem.text().as_ref() { + "" => None, + text => Some(text.to_owned()), + }; + Ok(Delay { + from: from, + stamp: stamp, + data: data, + }) + } } -impl IntoElements for Delay { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = serialise(&self); - emitter.append_child(elem) +impl<'a> Into for &'a Delay { + fn into(self) -> Element { + Element::builder("delay") + .ns(ns::DELAY) + .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) + .attr("stamp", self.stamp.clone()) + .append(self.data.clone()) + .build() } } #[cfg(test)] mod tests { use std::str::FromStr; - use minidom::Element; - use error::Error; - use jid::Jid; - use delay; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let delay = delay::parse_delay(&elem).unwrap(); + let delay = Delay::try_from(&elem).unwrap(); assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap())); assert_eq!(delay.stamp, "2002-09-10T23:08:25Z"); assert_eq!(delay.data, None); @@ -74,7 +72,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = delay::parse_delay(&elem).unwrap_err(); + let error = Delay::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -85,7 +83,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = delay::parse_delay(&elem).unwrap_err(); + let error = Delay::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -96,24 +94,24 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let delay = delay::Delay { + let delay = Delay { from: None, stamp: "2002-09-10T23:08:25Z".to_owned(), data: None, }; - let elem2 = delay::serialise(&delay); + let elem2 = (&delay).into(); assert_eq!(elem, elem2); } #[test] fn test_serialise_data() { let elem: Element = "Reason".parse().unwrap(); - let delay = delay::Delay { + let delay = Delay { from: Some(Jid::from_str("juliet@example.org").unwrap()), stamp: "2002-09-10T23:08:25Z".to_owned(), data: Some(String::from("Reason")), }; - let elem2 = delay::serialise(&delay); + let elem2 = (&delay).into(); assert_eq!(elem, elem2); } } diff --git a/src/forwarding.rs b/src/forwarding.rs index 991d508ada8a4e16e622d0360cb9e42a36b0b000..d34414578e429ad1b7dbdcb0eeb0763b1506c1b9 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -4,18 +4,20 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; -use delay; +use delay::Delay; use message; use ns; #[derive(Debug, Clone)] pub struct Forwarded { - pub delay: Option, + pub delay: Option, // XXX: really? Option? pub stanza: Option, } @@ -28,7 +30,7 @@ pub fn parse_forwarded(root: &Element) -> Result { let mut stanza = None; for child in root.children() { if child.is("delay", ns::DELAY) { - delay = Some(delay::parse_delay(child)?); + delay = Some(Delay::try_from(child)?); } else if child.is("message", ns::JABBER_CLIENT) { stanza = Some(message::parse_message(child)?); // TODO: also handle the five other possibilities. @@ -45,7 +47,7 @@ pub fn parse_forwarded(root: &Element) -> Result { pub fn serialise(forwarded: &Forwarded) -> Element { Element::builder("forwarded") .ns(ns::FORWARD) - .append(forwarded.delay.clone()) + .append(match forwarded.delay { Some(ref delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) .append(forwarded.stanza.clone()) .build() } diff --git a/src/message.rs b/src/message.rs index dfc5e6eb5655f6f7d5bf4637feb1280cf311a4f0..476920a04adf54b360969d2766c879d6034c9be7 100644 --- a/src/message.rs +++ b/src/message.rs @@ -19,7 +19,7 @@ use body; use stanza_error; use chatstates::ChatState; use receipts::Receipt; -use delay; +use delay::Delay; use attention::Attention; use message_correct::Replace; use eme; @@ -31,7 +31,7 @@ pub enum MessagePayload { StanzaError(stanza_error::StanzaError), ChatState(ChatState), Receipt(Receipt), - Delay(delay::Delay), + Delay(Delay), Attention(Attention), MessageCorrect(Replace), ExplicitMessageEncryption(eme::ExplicitMessageEncryption), @@ -119,7 +119,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::ChatState(chatstate)) } else if let Ok(receipt) = Receipt::try_from(elem) { Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = delay::parse_delay(elem) { + } else if let Ok(delay) = Delay::try_from(elem) { Some(MessagePayload::Delay(delay)) } else if let Ok(attention) = Attention::try_from(elem) { Some(MessagePayload::Attention(attention)) @@ -151,7 +151,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstate.into(), MessagePayload::Receipt(ref receipt) => receipt.into(), - MessagePayload::Delay(ref delay) => delay::serialise(delay), + MessagePayload::Delay(ref delay) => delay.into(), MessagePayload::MessageCorrect(ref replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme), } diff --git a/src/presence.rs b/src/presence.rs index bf7b57dfc495c53d1722e030a126df451cbe3e15..de5944c2e7c654443abe2954e0b8bfc9245a900a 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -16,7 +17,7 @@ use error::Error; use ns; use stanza_error; -use delay; +use delay::Delay; use ecaps2; #[derive(Debug, Clone, PartialEq)] @@ -51,7 +52,7 @@ pub enum PresencePayload { Status(Status), Priority(Priority), StanzaError(stanza_error::StanzaError), - Delay(delay::Delay), + Delay(Delay), ECaps2(ecaps2::ECaps2), } @@ -180,7 +181,7 @@ pub fn parse_presence(root: &Element) -> Result { } else { let payload = if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { Some(PresencePayload::StanzaError(stanza_error)) - } else if let Ok(delay) = delay::parse_delay(elem) { + } else if let Ok(delay) = Delay::try_from(elem) { Some(PresencePayload::Delay(delay)) } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { Some(PresencePayload::ECaps2(ecaps2)) @@ -226,7 +227,7 @@ pub fn serialise_payload(payload: &PresencePayload) -> Element { .build() }, PresencePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), - PresencePayload::Delay(ref delay) => delay::serialise(delay), + PresencePayload::Delay(ref delay) => delay.into(), PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), } } From 1ec38066299661bf0ba61b0039353576beb3933d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:46:11 +0100 Subject: [PATCH 0182/1020] hashes: Switch to Into/TryFrom. --- src/ecaps2.rs | 9 ++++---- src/hashes.rs | 60 ++++++++++++++++++++++++++---------------------- src/jingle_ft.rs | 11 ++++----- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index aa2cde7e10ca423e351ecb7b71baae6effe1c894..62242db7e2ef20e336c261fdc58621009935d125 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,10 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use disco::{Feature, Identity, Disco}; use data_forms::DataForm; -use hashes; -use hashes::{Hash, parse_hash}; +use hashes::Hash; use minidom::Element; use error::Error; @@ -31,7 +32,7 @@ pub fn parse_ecaps2(root: &Element) -> Result { let mut hashes = vec!(); for child in root.children() { if child.is("hash", ns::HASHES) { - let hash = parse_hash(child)?; + let hash = Hash::try_from(child)?; hashes.push(hash); } else { return Err(Error::ParseError("Unknown child in ecaps2 element.")); @@ -47,7 +48,7 @@ pub fn serialise(ecaps2: &ECaps2) -> Element { .ns(ns::ECAPS2) .build(); for hash in ecaps2.hashes.clone() { - let hash_elem = hashes::serialise(&hash); + let hash_elem = (&hash).into(); c.append_child(hash_elem); } c diff --git a/src/hashes.rs b/src/hashes.rs index c489a32fcde701f203f219409bd372a3d6d76a2b..6e7bcb2db2c9510b32bc1957e57ffc1666b7672a 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -16,42 +18,46 @@ pub struct Hash { pub hash: String, } -pub fn parse_hash(root: &Element) -> Result { - if !root.is("hash", ns::HASHES) { - return Err(Error::ParseError("This is not a hash element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in hash element.")); +impl<'a> TryFrom<&'a Element> for Hash { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("hash", ns::HASHES) { + return Err(Error::ParseError("This is not a hash element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in hash element.")); + } + let algo = elem.attr("algo").ok_or(Error::ParseError("Mandatory argument 'algo' not present in hash element."))?.to_owned(); + let hash = match elem.text().as_ref() { + "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), + text => text.to_owned(), + }; + Ok(Hash { + algo: algo, + hash: hash, + }) } - let algo = root.attr("algo").ok_or(Error::ParseError("Mandatory argument 'algo' not present in hash element."))?.to_owned(); - let hash = match root.text().as_ref() { - "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), - text => text.to_owned(), - }; - Ok(Hash { - algo: algo, - hash: hash, - }) } -pub fn serialise(hash: &Hash) -> Element { - Element::builder("hash") - .ns(ns::HASHES) - .attr("algo", hash.algo.clone()) - .append(hash.hash.clone()) - .build() +impl<'a> Into for &'a Hash { + fn into(self) -> Element { + Element::builder("hash") + .ns(ns::HASHES) + .attr("algo", self.algo.clone()) + .append(self.hash.clone()) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use hashes; + use super::*; #[test] fn test_simple() { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); - let hash = hashes::parse_hash(&elem).unwrap(); + let hash = Hash::try_from(&elem).unwrap(); assert_eq!(hash.algo, "sha-256"); assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); } @@ -59,7 +65,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = hashes::parse_hash(&elem).unwrap_err(); + let error = Hash::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -70,7 +76,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = hashes::parse_hash(&elem).unwrap_err(); + let error = Hash::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index f2b981faa45d8f4a967769a243f4da01bfa43e65..efbe9c9d8b355a8e3d43b8e3fb3d15c051f0c79d 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -6,8 +6,7 @@ use std::convert::TryFrom; -use hashes; -use hashes::{Hash, parse_hash}; +use hashes::Hash; use minidom::{Element, IntoElements, ElementEmitter}; @@ -32,7 +31,7 @@ impl IntoElements for Range { }) .build(); for hash in self.hashes { - elem.append_child(hashes::serialise(&hash)); + elem.append_child((&hash).into()); } emitter.append_child(elem); } @@ -149,7 +148,7 @@ impl<'a> TryFrom<&'a Element> for Description { if !hash_element.is("hash", ns::HASHES) { return Err(Error::ParseError("Unknown element in JingleFT range.")); } - range_hashes.push(parse_hash(hash_element)?); + range_hashes.push(Hash::try_from(hash_element)?); } range = Some(Range { offset: offset, @@ -157,7 +156,7 @@ impl<'a> TryFrom<&'a Element> for Description { hashes: range_hashes, }); } else if file_payload.is("hash", ns::HASHES) { - hashes.push(parse_hash(file_payload)?); + hashes.push(Hash::try_from(file_payload)?); } else { return Err(Error::ParseError("Unknown element in JingleFT file.")); } @@ -220,7 +219,7 @@ impl<'a> Into for &'a File { .build()); } for hash in self.hashes.clone() { - root.append_child(hashes::serialise(&hash)); + root.append_child((&hash).into()); } root } From 0dd0b444b390bef071644ecea38e46e80a1102e2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:48:41 +0100 Subject: [PATCH 0183/1020] media_element: Switch to Into/TryFrom. --- src/data_forms.rs | 5 ++-- src/media_element.rs | 66 +++++++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 3b3757efea29cd00abeb4ccac66b6b9d0afe8b2a..94dae1cac83d690e3b00a7eefdaaa3c7653edbe6 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use minidom::Element; @@ -11,7 +12,7 @@ use minidom::Element; use error::Error; use ns; -use media_element::{MediaElement, parse_media_element}; +use media_element::MediaElement; #[derive(Debug, Clone)] pub struct Field { @@ -74,7 +75,7 @@ pub fn parse_data_form(root: &Element) -> Result { if element.is("value", ns::DATA_FORMS) { values.push(element.text()); } else if element.is("media", ns::MEDIA_ELEMENT) { - match parse_media_element(element) { + match MediaElement::try_from(element) { Ok(media_element) => media.push(media_element), Err(_) => (), // TODO: is it really nice to swallow this error? } diff --git a/src/media_element.rs b/src/media_element.rs index a3c360e576d91cffb4bb9aa8b9a2995fdefe45eb..be2adda105eb6b31ee96a8626a920293748c9d1c 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -23,40 +25,42 @@ pub struct MediaElement { pub uris: Vec, } -pub fn parse_media_element(root: &Element) -> Result { - if !root.is("media", ns::MEDIA_ELEMENT) { - return Err(Error::ParseError("This is not a media element.")); - } +impl<'a> TryFrom<&'a Element> for MediaElement { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("media", ns::MEDIA_ELEMENT) { + return Err(Error::ParseError("This is not a media element.")); + } - let width = root.attr("width").and_then(|width| width.parse().ok()); - let height = root.attr("height").and_then(|height| height.parse().ok()); - let mut uris = vec!(); - for uri in root.children() { - if uri.is("uri", ns::MEDIA_ELEMENT) { - let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?; - let text = uri.text().trim().to_owned(); - if text == "" { - return Err(Error::ParseError("URI missing in uri.")); + let width = elem.attr("width").and_then(|width| width.parse().ok()); + let height = elem.attr("height").and_then(|height| height.parse().ok()); + let mut uris = vec!(); + for uri in elem.children() { + if uri.is("uri", ns::MEDIA_ELEMENT) { + let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?; + let text = uri.text().trim().to_owned(); + if text == "" { + return Err(Error::ParseError("URI missing in uri.")); + } + uris.push(URI { type_: type_.to_owned(), uri: text }); + } else { + return Err(Error::ParseError("Unknown child in media element.")); } - uris.push(URI { type_: type_.to_owned(), uri: text }); - } else { - return Err(Error::ParseError("Unknown child in media element.")); } + Ok(MediaElement { width: width, height: height, uris: uris }) } - Ok(MediaElement { width: width, height: height, uris: uris }) } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use media_element; + use super::*; use data_forms; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.width.is_none()); assert!(media.height.is_none()); assert!(media.uris.is_empty()); @@ -65,7 +69,7 @@ mod tests { #[test] fn test_width_height() { let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert_eq!(media.width.unwrap(), 32); assert_eq!(media.height.unwrap(), 32); } @@ -73,7 +77,7 @@ mod tests { #[test] fn test_uri() { let elem: Element = "https://example.org/".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert_eq!(media.uris.len(), 1); assert_eq!(media.uris[0].type_, "text/html"); assert_eq!(media.uris[0].uri, "https://example.org/"); @@ -82,26 +86,26 @@ mod tests { #[test] fn test_invalid_width_height() { let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.width.is_none()); let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.width.is_none()); let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.height.is_none()); let elem: Element = "".parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.height.is_none()); } #[test] fn test_unknown_child() { let elem: Element = "".parse().unwrap(); - let error = media_element::parse_media_element(&elem).unwrap_err(); + let error = MediaElement::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -112,7 +116,7 @@ mod tests { #[test] fn test_bad_uri() { let elem: Element = "https://example.org/".parse().unwrap(); - let error = media_element::parse_media_element(&elem).unwrap_err(); + let error = MediaElement::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -120,7 +124,7 @@ mod tests { assert_eq!(message, "Attribute type on uri is mandatory."); let elem: Element = "".parse().unwrap(); - let error = media_element::parse_media_element(&elem).unwrap_err(); + let error = MediaElement::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -142,7 +146,7 @@ mod tests { http://victim.example.com/challenges/speech.mp3?F3A6292C "#.parse().unwrap(); - let media = media_element::parse_media_element(&elem).unwrap(); + let media = MediaElement::try_from(&elem).unwrap(); assert!(media.width.is_none()); assert!(media.height.is_none()); assert_eq!(media.uris.len(), 3); From 8673b8f90e2eabb4e1c50057fcfbd4bbb3eab807 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:51:39 +0100 Subject: [PATCH 0184/1020] data_forms: Switch to Into/TryFrom. --- src/data_forms.rs | 106 ++++++++++++++++++++++--------------------- src/disco.rs | 6 ++- src/mam.rs | 5 +- src/media_element.rs | 4 +- 4 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 94dae1cac83d690e3b00a7eefdaaa3c7653edbe6..a83ebb824b608dc507989048630f8bd4d06e2a9a 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -53,70 +53,72 @@ pub struct DataForm { pub fields: Vec, } -pub fn parse_data_form(root: &Element) -> Result { - if !root.is("x", ns::DATA_FORMS) { - return Err(Error::ParseError("This is not a data form element.")); - } +impl<'a> TryFrom<&'a Element> for DataForm { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("x", ns::DATA_FORMS) { + return Err(Error::ParseError("This is not a data form element.")); + } - let type_: DataFormType = match root.attr("type") { - Some(type_) => type_.parse()?, - None => return Err(Error::ParseError("Type attribute on data form is mandatory.")), - }; - let mut fields = vec!(); - let mut form_type = None; - for field in root.children() { - if field.is("field", ns::DATA_FORMS) { - let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?; - let field_type = field.attr("type").unwrap_or("text-single"); - let label = field.attr("label").and_then(|label| label.parse().ok()); - let mut values = vec!(); - let mut media = vec!(); - for element in field.children() { - if element.is("value", ns::DATA_FORMS) { - values.push(element.text()); - } else if element.is("media", ns::MEDIA_ELEMENT) { - match MediaElement::try_from(element) { - Ok(media_element) => media.push(media_element), - Err(_) => (), // TODO: is it really nice to swallow this error? + let type_: DataFormType = match elem.attr("type") { + Some(type_) => type_.parse()?, + None => return Err(Error::ParseError("Type attribute on data form is mandatory.")), + }; + let mut fields = vec!(); + let mut form_type = None; + for field in elem.children() { + if field.is("field", ns::DATA_FORMS) { + let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?; + let field_type = field.attr("type").unwrap_or("text-single"); + let label = field.attr("label").and_then(|label| label.parse().ok()); + let mut values = vec!(); + let mut media = vec!(); + for element in field.children() { + if element.is("value", ns::DATA_FORMS) { + values.push(element.text()); + } else if element.is("media", ns::MEDIA_ELEMENT) { + match MediaElement::try_from(element) { + Ok(media_element) => media.push(media_element), + Err(_) => (), // TODO: is it really nice to swallow this error? + } + } else { + return Err(Error::ParseError("Field child isn’t a value or media element.")); } - } else { - return Err(Error::ParseError("Field child isn’t a value or media element.")); } - } - if var == "FORM_TYPE" && field_type == "hidden" { - if form_type != None { - return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); - } - if values.len() != 1 { - return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); + if var == "FORM_TYPE" && field_type == "hidden" { + if form_type != None { + return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); + } + if values.len() != 1 { + return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); + } + form_type = Some(values[0].clone()); } - form_type = Some(values[0].clone()); + fields.push(Field { + var: var.to_owned(), + type_: field_type.to_owned(), + label: label, + values: values, + media: media, + }); + } else { + return Err(Error::ParseError("Unknown field type in data form.")); } - fields.push(Field { - var: var.to_owned(), - type_: field_type.to_owned(), - label: label, - values: values, - media: media, - }); - } else { - return Err(Error::ParseError("Unknown field type in data form.")); } + Ok(DataForm { type_: type_, form_type: form_type, fields: fields }) } - Ok(DataForm { type_: type_, form_type: form_type, fields: fields }) } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use data_forms; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let form = data_forms::parse_data_form(&elem).unwrap(); - assert_eq!(form.type_, data_forms::DataFormType::Result_); + let form = DataForm::try_from(&elem).unwrap(); + assert_eq!(form.type_, DataFormType::Result_); assert!(form.form_type.is_none()); assert!(form.fields.is_empty()); } @@ -124,7 +126,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = data_forms::parse_data_form(&elem).unwrap_err(); + let error = DataForm::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -132,7 +134,7 @@ mod tests { assert_eq!(message, "Type attribute on data form is mandatory."); let elem: Element = "".parse().unwrap(); - let error = data_forms::parse_data_form(&elem).unwrap_err(); + let error = DataForm::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -143,7 +145,7 @@ mod tests { #[test] fn test_wrong_child() { let elem: Element = "".parse().unwrap(); - let error = data_forms::parse_data_form(&elem).unwrap_err(); + let error = DataForm::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/disco.rs b/src/disco.rs index bc448091b830ee903356d444b05c1d046f1bd975..9fb529060cd915f7c24ccd1a06809874776045b9 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -4,12 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; use ns; -use data_forms::{DataForm, DataFormType, parse_data_form}; +use data_forms::{DataForm, DataFormType}; #[derive(Debug, Clone, PartialEq)] pub struct Feature { @@ -74,7 +76,7 @@ pub fn parse_disco(root: &Element) -> Result { name: name, }); } else if child.is("x", ns::DATA_FORMS) { - let data_form = parse_data_form(child)?; + let data_form = DataForm::try_from(child)?; match data_form.type_ { DataFormType::Result_ => (), _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")), diff --git a/src/mam.rs b/src/mam.rs index 77a3b6cf0caf988d0ee8294e2f7ca1519bbafcd3..308cc1c0a38a83cf56f8d594d2fe0897c7677691 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -11,7 +11,6 @@ use jid::Jid; use error::Error; -use data_forms; use data_forms::DataForm; use rsm::Set; use forwarding; @@ -62,7 +61,7 @@ pub fn parse_query(root: &Element) -> Result { let mut set = None; for child in root.children() { if child.is("x", ns::DATA_FORMS) { - form = Some(data_forms::parse_data_form(child)?); + form = Some(DataForm::try_from(child)?); } else if child.is("set", ns::RSM) { set = Some(Set::try_from(child)?); } else { @@ -177,7 +176,7 @@ pub fn serialise_query(query: &Query) -> Element { .attr("node", query.node.clone()) .build(); //if let Some(form) = query.form { - // elem.append_child(data_forms::serialise(&form)); + // elem.append_child((&form).into()); //} if let Some(ref set) = query.set { elem.append_child(set.into()); diff --git a/src/media_element.rs b/src/media_element.rs index be2adda105eb6b31ee96a8626a920293748c9d1c..d4d31985caad71b54afa050ddd0594a30a9650f3 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -55,7 +55,7 @@ impl<'a> TryFrom<&'a Element> for MediaElement { #[cfg(test)] mod tests { use super::*; - use data_forms; + use data_forms::DataForm; #[test] fn test_simple() { @@ -177,7 +177,7 @@ mod tests { [ ... ] "#.parse().unwrap(); - let form = data_forms::parse_data_form(&elem).unwrap(); + let form = DataForm::try_from(&elem).unwrap(); assert_eq!(form.fields.len(), 1); assert_eq!(form.fields[0].var, "ocr"); assert_eq!(form.fields[0].media[0].width, Some(290)); From 2f05d02d239ad8cf412d8411c2a03a93d61f8528 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:53:43 +0100 Subject: [PATCH 0185/1020] forwarding: Switch to Into/TryFrom. --- src/forwarding.rs | 66 +++++++++++++++++++++++++---------------------- src/mam.rs | 5 ++-- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/forwarding.rs b/src/forwarding.rs index d34414578e429ad1b7dbdcb0eeb0763b1506c1b9..75b7fbcc8a307946346e9d07298e744c215ce5de 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -22,52 +22,56 @@ pub struct Forwarded { pub stanza: Option, } -pub fn parse_forwarded(root: &Element) -> Result { - if !root.is("forwarded", ns::FORWARD) { - return Err(Error::ParseError("This is not a forwarded element.")); - } - let mut delay = None; - let mut stanza = None; - for child in root.children() { - if child.is("delay", ns::DELAY) { - delay = Some(Delay::try_from(child)?); - } else if child.is("message", ns::JABBER_CLIENT) { - stanza = Some(message::parse_message(child)?); - // TODO: also handle the five other possibilities. - } else { - return Err(Error::ParseError("Unknown child in forwarded element.")); +impl<'a> TryFrom<&'a Element> for Forwarded { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("forwarded", ns::FORWARD) { + return Err(Error::ParseError("This is not a forwarded element.")); } + let mut delay = None; + let mut stanza = None; + for child in elem.children() { + if child.is("delay", ns::DELAY) { + delay = Some(Delay::try_from(child)?); + } else if child.is("message", ns::JABBER_CLIENT) { + stanza = Some(message::parse_message(child)?); + // TODO: also handle the five other possibilities. + } else { + return Err(Error::ParseError("Unknown child in forwarded element.")); + } + } + Ok(Forwarded { + delay: delay, + stanza: stanza, + }) } - Ok(Forwarded { - delay: delay, - stanza: stanza, - }) } -pub fn serialise(forwarded: &Forwarded) -> Element { - Element::builder("forwarded") - .ns(ns::FORWARD) - .append(match forwarded.delay { Some(ref delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) - .append(forwarded.stanza.clone()) - .build() +impl<'a> Into for &'a Forwarded { + fn into(self) -> Element { + Element::builder("forwarded") + .ns(ns::FORWARD) + .append(match self.delay { Some(ref delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) + .append(self.stanza.clone()) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use forwarding; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - forwarding::parse_forwarded(&elem).unwrap(); + Forwarded::try_from(&elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = forwarding::parse_forwarded(&elem).unwrap_err(); + let error = Forwarded::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -78,8 +82,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let forwarded = forwarding::Forwarded { delay: None, stanza: None }; - let elem2 = forwarding::serialise(&forwarded); + let forwarded = Forwarded { delay: None, stanza: None }; + let elem2 = (&forwarded).into(); assert_eq!(elem, elem2); } } diff --git a/src/mam.rs b/src/mam.rs index 308cc1c0a38a83cf56f8d594d2fe0897c7677691..a826decc45e5e2b1111c8cb25ab7db2ed0ca12fc 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -13,7 +13,6 @@ use error::Error; use data_forms::DataForm; use rsm::Set; -use forwarding; use forwarding::Forwarded; use ns; @@ -86,7 +85,7 @@ pub fn parse_result(root: &Element) -> Result { let mut forwarded = None; for child in root.children() { if child.is("forwarded", ns::FORWARD) { - forwarded = Some(forwarding::parse_forwarded(child)?); + forwarded = Some(Forwarded::try_from(child)?); } else { return Err(Error::ParseError("Unknown child in result element.")); } @@ -190,7 +189,7 @@ pub fn serialise_result(result: &Result_) -> Element { .attr("queryid", result.queryid.clone()) .attr("id", result.id.clone()) .build(); - elem.append_child(forwarding::serialise(&result.forwarded)); + elem.append_child((&result.forwarded).into()); elem } From f963715e77c2899a35e3d521609b9696c3da28f8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 20:58:22 +0100 Subject: [PATCH 0186/1020] ecaps2: Switch to Into/TryFrom. --- src/ecaps2.rs | 55 ++++++++++++++++++++++++++----------------------- src/presence.rs | 8 +++---- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 62242db7e2ef20e336c261fdc58621009935d125..fdddfdaad507f73f945d250e8779ee72c717789d 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -25,33 +25,37 @@ pub struct ECaps2 { hashes: Vec, } -pub fn parse_ecaps2(root: &Element) -> Result { - if !root.is("c", ns::ECAPS2) { - return Err(Error::ParseError("This is not an ecaps2 element.")); - } - let mut hashes = vec!(); - for child in root.children() { - if child.is("hash", ns::HASHES) { - let hash = Hash::try_from(child)?; - hashes.push(hash); - } else { - return Err(Error::ParseError("Unknown child in ecaps2 element.")); +impl<'a> TryFrom<&'a Element> for ECaps2 { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("c", ns::ECAPS2) { + return Err(Error::ParseError("This is not an ecaps2 element.")); + } + let mut hashes = vec!(); + for child in elem.children() { + if child.is("hash", ns::HASHES) { + let hash = Hash::try_from(child)?; + hashes.push(hash); + } else { + return Err(Error::ParseError("Unknown child in ecaps2 element.")); + } } + Ok(ECaps2 { + hashes: hashes, + }) } - Ok(ECaps2 { - hashes: hashes, - }) } -pub fn serialise(ecaps2: &ECaps2) -> Element { - let mut c = Element::builder("c") - .ns(ns::ECAPS2) - .build(); - for hash in ecaps2.hashes.clone() { - let hash_elem = (&hash).into(); - c.append_child(hash_elem); +impl<'a> Into for &'a ECaps2 { + fn into(self) -> Element { + Element::builder("c") + .ns(ns::ECAPS2) + .append(self.hashes.iter() + .map(|hash| hash.into()) + .collect::>()) + .build() } - c } fn compute_item(field: &str) -> Vec { @@ -161,8 +165,7 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; + use super::*; use disco; use ecaps2; use base64; @@ -170,7 +173,7 @@ mod tests { #[test] fn test_parse() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); - let ecaps2 = ecaps2::parse_ecaps2(&elem).unwrap(); + let ecaps2 = ECaps2::try_from(&elem).unwrap(); assert_eq!(ecaps2.hashes.len(), 2); assert_eq!(ecaps2.hashes[0].algo, "sha-256"); assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); @@ -181,7 +184,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); - let error = ecaps2::parse_ecaps2(&elem).unwrap_err(); + let error = ECaps2::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/presence.rs b/src/presence.rs index de5944c2e7c654443abe2954e0b8bfc9245a900a..6ae97ecb9ae2371c5dbda2460080d79c7b552047 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -18,7 +18,7 @@ use ns; use stanza_error; use delay::Delay; -use ecaps2; +use ecaps2::ECaps2; #[derive(Debug, Clone, PartialEq)] pub enum Show { @@ -53,7 +53,7 @@ pub enum PresencePayload { Priority(Priority), StanzaError(stanza_error::StanzaError), Delay(Delay), - ECaps2(ecaps2::ECaps2), + ECaps2(ECaps2), } #[derive(Debug, Clone, PartialEq)] @@ -183,7 +183,7 @@ pub fn parse_presence(root: &Element) -> Result { Some(PresencePayload::StanzaError(stanza_error)) } else if let Ok(delay) = Delay::try_from(elem) { Some(PresencePayload::Delay(delay)) - } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) { + } else if let Ok(ecaps2) = ECaps2::try_from(elem) { Some(PresencePayload::ECaps2(ecaps2)) } else { None @@ -228,7 +228,7 @@ pub fn serialise_payload(payload: &PresencePayload) -> Element { }, PresencePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), PresencePayload::Delay(ref delay) => delay.into(), - PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2), + PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), } } From 2b49d8aa539113474707eafd62a72f27f1c5f522 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:01:15 +0100 Subject: [PATCH 0187/1020] disco: Switch to Into/TryFrom. --- src/disco.rs | 218 +++++++++++++++++++++++++------------------------- src/ecaps2.rs | 7 +- src/iq.rs | 8 +- 3 files changed, 118 insertions(+), 115 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 9fb529060cd915f7c24ccd1a06809874776045b9..48f4a2d0db93b5e426a877af91a2c06b56dd0031 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -34,121 +34,125 @@ pub struct Disco { pub extensions: Vec, } -pub fn parse_disco(root: &Element) -> Result { - if !root.is("query", ns::DISCO_INFO) { - return Err(Error::ParseError("This is not a disco#info element.")); - } - - let mut identities: Vec = vec!(); - let mut features: Vec = vec!(); - let mut extensions: Vec = vec!(); - - let node = root.attr("node") - .and_then(|node| node.parse().ok()); - - for child in root.children() { - if child.is("feature", ns::DISCO_INFO) { - let feature = child.attr("var") - .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?; - features.push(Feature { - var: feature.to_owned(), - }); - } else if child.is("identity", ns::DISCO_INFO) { - let category = child.attr("category") - .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?; - if category == "" { - return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) - } +impl<'a> TryFrom<&'a Element> for Disco { + type Error = Error; - let type_ = child.attr("type") - .ok_or(Error::ParseError("Identity must have a 'type' attribute."))?; - if type_ == "" { - return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) - } + fn try_from(elem: &'a Element) -> Result { + if !elem.is("query", ns::DISCO_INFO) { + return Err(Error::ParseError("This is not a disco#info element.")); + } - let xml_lang = child.attr("xml:lang").unwrap_or(""); - let name = child.attr("name") - .and_then(|name| name.parse().ok()); - identities.push(Identity { - category: category.to_owned(), - type_: type_.to_owned(), - xml_lang: xml_lang.to_owned(), - name: name, - }); - } else if child.is("x", ns::DATA_FORMS) { - let data_form = DataForm::try_from(child)?; - match data_form.type_ { - DataFormType::Result_ => (), - _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")), + let mut identities: Vec = vec!(); + let mut features: Vec = vec!(); + let mut extensions: Vec = vec!(); + + let node = elem.attr("node") + .and_then(|node| node.parse().ok()); + + for child in elem.children() { + if child.is("feature", ns::DISCO_INFO) { + let feature = child.attr("var") + .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?; + features.push(Feature { + var: feature.to_owned(), + }); + } else if child.is("identity", ns::DISCO_INFO) { + let category = child.attr("category") + .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?; + if category == "" { + return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) + } + + let type_ = child.attr("type") + .ok_or(Error::ParseError("Identity must have a 'type' attribute."))?; + if type_ == "" { + return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) + } + + let xml_lang = child.attr("xml:lang").unwrap_or(""); + let name = child.attr("name") + .and_then(|name| name.parse().ok()); + identities.push(Identity { + category: category.to_owned(), + type_: type_.to_owned(), + xml_lang: xml_lang.to_owned(), + name: name, + }); + } else if child.is("x", ns::DATA_FORMS) { + let data_form = DataForm::try_from(child)?; + match data_form.type_ { + DataFormType::Result_ => (), + _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")), + } + match data_form.form_type { + Some(_) => extensions.push(data_form), + None => return Err(Error::ParseError("Data form found without a FORM_TYPE.")), + } + } else { + return Err(Error::ParseError("Unknown element in disco#info.")); } - match data_form.form_type { - Some(_) => extensions.push(data_form), - None => return Err(Error::ParseError("Data form found without a FORM_TYPE.")), - } - } else { - return Err(Error::ParseError("Unknown element in disco#info.")); } - } - /* - // TODO: encode these restrictions only for result disco#info, not get ones. - if identities.is_empty() { - return Err(Error::ParseError("There must be at least one identity in disco#info.")); - } - if features.is_empty() { - return Err(Error::ParseError("There must be at least one feature in disco#info.")); - } - if !features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { - return Err(Error::ParseError("disco#info feature not present in disco#info.")); + /* + // TODO: encode these restrictions only for result disco#info, not get ones. + if identities.is_empty() { + return Err(Error::ParseError("There must be at least one identity in disco#info.")); + } + if features.is_empty() { + return Err(Error::ParseError("There must be at least one feature in disco#info.")); + } + if !features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { + return Err(Error::ParseError("disco#info feature not present in disco#info.")); + } + */ + + Ok(Disco { + node: node, + identities: identities, + features: features, + extensions: extensions + }) } - */ - - Ok(Disco { - node: node, - identities: identities, - features: features, - extensions: extensions - }) } -pub fn serialise_disco(disco: &Disco) -> Element { - let mut root = Element::builder("query") - .ns(ns::DISCO_INFO) - .attr("node", disco.node.clone()) - .build(); - for identity in &disco.identities { - let identity_element = Element::builder("identity") - .ns(ns::DISCO_INFO) - .attr("category", identity.category.clone()) - .attr("type", identity.type_.clone()) - .attr("xml:lang", identity.xml_lang.clone()) - .attr("name", identity.name.clone()) - .build(); - root.append_child(identity_element); - } - for feature in &disco.features { - let feature_element = Element::builder("feature") - .ns(ns::DISCO_INFO) - .attr("var", feature.var.clone()) - .build(); - root.append_child(feature_element); - } - for _ in &disco.extensions { - panic!("Not yet implemented!"); +impl<'a> Into for &'a Disco { + fn into(self) -> Element { + let mut root = Element::builder("query") + .ns(ns::DISCO_INFO) + .attr("node", self.node.clone()) + .build(); + for identity in &self.identities { + let identity_element = Element::builder("identity") + .ns(ns::DISCO_INFO) + .attr("category", identity.category.clone()) + .attr("type", identity.type_.clone()) + .attr("xml:lang", identity.xml_lang.clone()) + .attr("name", identity.name.clone()) + .build(); + root.append_child(identity_element); + } + for feature in &self.features { + let feature_element = Element::builder("feature") + .ns(ns::DISCO_INFO) + .attr("var", feature.var.clone()) + .build(); + root.append_child(feature_element); + } + for _ in &self.extensions { + panic!("Not yet implemented!"); + } + root } - root } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use disco; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let query = disco::parse_disco(&elem).unwrap(); + let query = Disco::try_from(&elem).unwrap(); assert!(query.node.is_none()); assert_eq!(query.identities.len(), 1); assert_eq!(query.features.len(), 1); @@ -158,7 +162,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -169,7 +173,7 @@ mod tests { #[test] fn test_invalid_identity() { let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -177,7 +181,7 @@ mod tests { assert_eq!(message, "Identity must have a 'category' attribute."); let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -185,7 +189,7 @@ mod tests { assert_eq!(message, "Identity must have a non-empty 'category' attribute."); let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -193,7 +197,7 @@ mod tests { assert_eq!(message, "Identity must have a 'type' attribute."); let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -204,7 +208,7 @@ mod tests { #[test] fn test_invalid_feature() { let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -216,7 +220,7 @@ mod tests { #[ignore] fn test_invalid_result() { let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -224,7 +228,7 @@ mod tests { assert_eq!(message, "There must be at least one identity in disco#info."); let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -232,7 +236,7 @@ mod tests { assert_eq!(message, "There must be at least one feature in disco#info."); let elem: Element = "".parse().unwrap(); - let error = disco::parse_disco(&elem).unwrap_err(); + let error = Disco::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/ecaps2.rs b/src/ecaps2.rs index fdddfdaad507f73f945d250e8779ee72c717789d..3d7f2c949438b6d0c2f586dcf1f4d6dbaa6b604e 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -166,7 +166,6 @@ pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { #[cfg(test)] mod tests { use super::*; - use disco; use ecaps2; use base64; @@ -195,7 +194,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let disco = disco::parse_disco(&elem).unwrap(); + let disco = Disco::try_from(&elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 54); } @@ -258,7 +257,7 @@ mod tests { 105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, 100, 31, 30, 28, 28]; - let disco = disco::parse_disco(&elem).unwrap(); + let disco = Disco::try_from(&elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); @@ -430,7 +429,7 @@ mod tests { 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; - let disco = disco::parse_disco(&elem).unwrap(); + let disco = Disco::try_from(&elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); diff --git a/src/iq.rs b/src/iq.rs index 65b49cdbce0148609c63fc745dcba0a764f76594..f48026a5670f7d4ab5ea65118ebd554b72765a61 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -17,7 +17,7 @@ use error::Error; use ns; use stanza_error; -use disco; +use disco::Disco; use ibb::IBB; use jingle::Jingle; use ping::Ping; @@ -25,7 +25,7 @@ use ping::Ping; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum IqPayload { - Disco(disco::Disco), + Disco(Disco), IBB(IBB), Jingle(Jingle), Ping(Ping), @@ -95,7 +95,7 @@ pub fn parse_iq(root: &Element) -> Result { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) { + let parsed_payload = if let Ok(disco) = Disco::try_from(elem) { Some(IqPayload::Disco(disco)) } else if let Ok(ibb) = IBB::try_from(elem) { Some(IqPayload::IBB(ibb)) @@ -152,7 +152,7 @@ pub fn parse_iq(root: &Element) -> Result { pub fn serialise_payload(payload: &IqPayload) -> Element { match *payload { - IqPayload::Disco(ref disco) => disco::serialise_disco(disco), + IqPayload::Disco(ref disco) => disco.into(), IqPayload::IBB(ref ibb) => ibb.into(), IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(ref ping) => ping.into(), From 2b96751e5fd59fece078cca80282b28a9f3dd71f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:03:42 +0100 Subject: [PATCH 0188/1020] eme: Switch to Into/TryFrom. --- src/eme.rs | 60 +++++++++++++++++++++++++++----------------------- src/message.rs | 8 +++---- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index 879ddda0c6684f4f20992c8c21b6a0de3c59d813..ad28afc705c643fd61da43f32e181771b13cdad0 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; + use minidom::Element; use error::Error; @@ -16,44 +18,48 @@ pub struct ExplicitMessageEncryption { pub name: Option, } -pub fn parse_explicit_message_encryption(root: &Element) -> Result { - if !root.is("encryption", ns::EME) { - return Err(Error::ParseError("This is not an encryption element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in encryption element.")); +impl<'a> TryFrom<&'a Element> for ExplicitMessageEncryption { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("encryption", ns::EME) { + return Err(Error::ParseError("This is not an encryption element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in encryption element.")); + } + let namespace = elem.attr("namespace").ok_or(Error::ParseError("Mandatory argument 'namespace' not present in encryption element."))?.to_owned(); + let name = elem.attr("name").and_then(|value| value.parse().ok()); + Ok(ExplicitMessageEncryption { + namespace: namespace, + name: name, + }) } - let namespace = root.attr("namespace").ok_or(Error::ParseError("Mandatory argument 'namespace' not present in encryption element."))?.to_owned(); - let name = root.attr("name").and_then(|value| value.parse().ok()); - Ok(ExplicitMessageEncryption { - namespace: namespace, - name: name, - }) } -pub fn serialise(eme: &ExplicitMessageEncryption) -> Element { - Element::builder("encryption") - .ns(ns::EME) - .attr("namespace", eme.namespace.clone()) - .attr("name", eme.name.clone()) - .build() +impl<'a> Into for &'a ExplicitMessageEncryption { + fn into(self) -> Element { + Element::builder("encryption") + .ns(ns::EME) + .attr("namespace", self.namespace.clone()) + .attr("name", self.name.clone()) + .build() + } } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use eme; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let encryption = eme::parse_explicit_message_encryption(&elem).unwrap(); + let encryption = ExplicitMessageEncryption::try_from(&elem).unwrap(); assert_eq!(encryption.namespace, "urn:xmpp:otr:0"); assert_eq!(encryption.name, None); let elem: Element = "".parse().unwrap(); - let encryption = eme::parse_explicit_message_encryption(&elem).unwrap(); + let encryption = ExplicitMessageEncryption::try_from(&elem).unwrap(); assert_eq!(encryption.namespace, "some.unknown.mechanism"); assert_eq!(encryption.name, Some(String::from("SuperMechanism"))); } @@ -61,7 +67,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = eme::parse_explicit_message_encryption(&elem).unwrap_err(); + let error = ExplicitMessageEncryption::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -72,7 +78,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = eme::parse_explicit_message_encryption(&elem).unwrap_err(); + let error = ExplicitMessageEncryption::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -83,8 +89,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let eme = eme::ExplicitMessageEncryption { namespace: String::from("coucou"), name: None }; - let elem2 = eme::serialise(&eme); + let eme = ExplicitMessageEncryption { namespace: String::from("coucou"), name: None }; + let elem2 = (&eme).into(); assert_eq!(elem, elem2); } } diff --git a/src/message.rs b/src/message.rs index 476920a04adf54b360969d2766c879d6034c9be7..ce6622aa9fae19f067deadf83ca9e2584809af4a 100644 --- a/src/message.rs +++ b/src/message.rs @@ -22,7 +22,7 @@ use receipts::Receipt; use delay::Delay; use attention::Attention; use message_correct::Replace; -use eme; +use eme::ExplicitMessageEncryption; /// Lists every known payload of a ``. #[derive(Debug, Clone)] @@ -34,7 +34,7 @@ pub enum MessagePayload { Delay(Delay), Attention(Attention), MessageCorrect(Replace), - ExplicitMessageEncryption(eme::ExplicitMessageEncryption), + ExplicitMessageEncryption(ExplicitMessageEncryption), } #[derive(Debug, Clone, PartialEq)] @@ -125,7 +125,7 @@ pub fn parse_message(root: &Element) -> Result { Some(MessagePayload::Attention(attention)) } else if let Ok(replace) = Replace::try_from(elem) { Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) { + } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { Some(MessagePayload::ExplicitMessageEncryption(eme)) } else { None @@ -153,7 +153,7 @@ pub fn serialise_payload(payload: &MessagePayload) -> Element { MessagePayload::Receipt(ref receipt) => receipt.into(), MessagePayload::Delay(ref delay) => delay.into(), MessagePayload::MessageCorrect(ref replace) => replace.into(), - MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme), + MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), } } From 418956c7209cabc352fa8638fe3c3e2b332b820f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:08:44 +0100 Subject: [PATCH 0189/1020] mam: Switch to Into/TryFrom. --- src/mam.rs | 368 ++++++++++++++++++++++++++++------------------------- 1 file changed, 195 insertions(+), 173 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index a826decc45e5e2b1111c8cb25ab7db2ed0ca12fc..c831a0993d237fd9441101ad02a6fed5f9c7e8c6 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -52,206 +52,228 @@ pub struct Prefs { pub never: Vec, } -pub fn parse_query(root: &Element) -> Result { - if !root.is("query", ns::MAM) { - return Err(Error::ParseError("This is not a query element.")); - } - let mut form = None; - let mut set = None; - for child in root.children() { - if child.is("x", ns::DATA_FORMS) { - form = Some(DataForm::try_from(child)?); - } else if child.is("set", ns::RSM) { - set = Some(Set::try_from(child)?); - } else { - return Err(Error::ParseError("Unknown child in query element.")); +impl<'a> TryFrom<&'a Element> for Query { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("query", ns::MAM) { + return Err(Error::ParseError("This is not a query element.")); } + let mut form = None; + let mut set = None; + for child in elem.children() { + if child.is("x", ns::DATA_FORMS) { + form = Some(DataForm::try_from(child)?); + } else if child.is("set", ns::RSM) { + set = Some(Set::try_from(child)?); + } else { + return Err(Error::ParseError("Unknown child in query element.")); + } + } + let queryid = match elem.attr("queryid") { + Some(queryid) => Some(queryid.to_owned()), + None => None, + }; + let node = match elem.attr("node") { + Some(node) => Some(node.to_owned()), + None => None, + }; + Ok(Query { queryid, node, form, set }) } - let queryid = match root.attr("queryid") { - Some(queryid) => Some(queryid.to_owned()), - None => None, - }; - let node = match root.attr("node") { - Some(node) => Some(node.to_owned()), - None => None, - }; - Ok(Query { queryid, node, form, set }) } -pub fn parse_result(root: &Element) -> Result { - if !root.is("result", ns::MAM) { - return Err(Error::ParseError("This is not a result element.")); - } - let mut forwarded = None; - for child in root.children() { - if child.is("forwarded", ns::FORWARD) { - forwarded = Some(Forwarded::try_from(child)?); - } else { - return Err(Error::ParseError("Unknown child in result element.")); +impl<'a> TryFrom<&'a Element> for Result_ { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("result", ns::MAM) { + return Err(Error::ParseError("This is not a result element.")); } + let mut forwarded = None; + for child in elem.children() { + if child.is("forwarded", ns::FORWARD) { + forwarded = Some(Forwarded::try_from(child)?); + } else { + return Err(Error::ParseError("Unknown child in result element.")); + } + } + let queryid = match elem.attr("queryid") { + Some(queryid) => queryid.to_owned(), + None => return Err(Error::ParseError("No 'queryid' attribute present in result.")), + }; + let id = match elem.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in result.")), + }; + if forwarded.is_none() { + return Err(Error::ParseError("Mandatory forwarded element missing in result.")); + } + let forwarded = forwarded.unwrap(); + Ok(Result_ { + queryid, + id, + forwarded, + }) } - let queryid = match root.attr("queryid") { - Some(queryid) => queryid.to_owned(), - None => return Err(Error::ParseError("No 'queryid' attribute present in result.")), - }; - let id = match root.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in result.")), - }; - if forwarded.is_none() { - return Err(Error::ParseError("Mandatory forwarded element missing in result.")); - } - let forwarded = forwarded.unwrap(); - Ok(Result_ { - queryid, - id, - forwarded, - }) } -pub fn parse_fin(root: &Element) -> Result { - if !root.is("fin", ns::MAM) { - return Err(Error::ParseError("This is not a fin element.")); - } - let mut set = None; - for child in root.children() { - if child.is("set", ns::RSM) { - set = Some(Set::try_from(child)?); - } else { - return Err(Error::ParseError("Unknown child in fin element.")); +impl<'a> TryFrom<&'a Element> for Fin { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("fin", ns::MAM) { + return Err(Error::ParseError("This is not a fin element.")); } + let mut set = None; + for child in elem.children() { + if child.is("set", ns::RSM) { + set = Some(Set::try_from(child)?); + } else { + return Err(Error::ParseError("Unknown child in fin element.")); + } + } + let complete = match elem.attr("complete") { + Some(complete) => complete == "true", + None => false, + }; + if set.is_none() { + return Err(Error::ParseError("Mandatory set element missing in fin.")); + } + let set = set.unwrap(); + Ok(Fin { complete, set }) } - let complete = match root.attr("complete") { - Some(complete) => complete == "true", - None => false, - }; - if set.is_none() { - return Err(Error::ParseError("Mandatory set element missing in fin.")); - } - let set = set.unwrap(); - Ok(Fin { complete, set }) } -pub fn parse_prefs(root: &Element) -> Result { - if !root.is("prefs", ns::MAM) { - return Err(Error::ParseError("This is not a prefs element.")); - } - let mut always = vec!(); - let mut never = vec!(); - for child in root.children() { - if child.is("always", ns::MAM) { - for jid_elem in child.children() { - if !jid_elem.is("jid", ns::MAM) { - return Err(Error::ParseError("Invalid jid element in always.")); +impl<'a> TryFrom<&'a Element> for Prefs { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("prefs", ns::MAM) { + return Err(Error::ParseError("This is not a prefs element.")); + } + let mut always = vec!(); + let mut never = vec!(); + for child in elem.children() { + if child.is("always", ns::MAM) { + for jid_elem in child.children() { + if !jid_elem.is("jid", ns::MAM) { + return Err(Error::ParseError("Invalid jid element in always.")); + } + always.push(jid_elem.text().parse()?); } - always.push(jid_elem.text().parse()?); - } - } else if child.is("never", ns::MAM) { - for jid_elem in child.children() { - if !jid_elem.is("jid", ns::MAM) { - return Err(Error::ParseError("Invalid jid element in never.")); + } else if child.is("never", ns::MAM) { + for jid_elem in child.children() { + if !jid_elem.is("jid", ns::MAM) { + return Err(Error::ParseError("Invalid jid element in never.")); + } + never.push(jid_elem.text().parse()?); } - never.push(jid_elem.text().parse()?); + } else { + return Err(Error::ParseError("Unknown child in prefs element.")); } - } else { - return Err(Error::ParseError("Unknown child in prefs element.")); } + let default_ = match elem.attr("default") { + Some("always") => Some(DefaultPrefs::Always), + Some("never") => Some(DefaultPrefs::Never), + Some("roster") => Some(DefaultPrefs::Roster), + None => None, + + _ => return Err(Error::ParseError("Invalid 'default' attribute present in prefs.")), + }; + Ok(Prefs { default_, always, never }) } - let default_ = match root.attr("default") { - Some("always") => Some(DefaultPrefs::Always), - Some("never") => Some(DefaultPrefs::Never), - Some("roster") => Some(DefaultPrefs::Roster), - None => None, - - _ => return Err(Error::ParseError("Invalid 'default' attribute present in prefs.")), - }; - Ok(Prefs { default_, always, never }) } -pub fn serialise_query(query: &Query) -> Element { - let mut elem = Element::builder("query") - .ns(ns::MAM) - .attr("queryid", query.queryid.clone()) - .attr("node", query.node.clone()) - .build(); - //if let Some(form) = query.form { - // elem.append_child((&form).into()); - //} - if let Some(ref set) = query.set { - elem.append_child(set.into()); +impl<'a> Into for &'a Query { + fn into(self) -> Element { + let mut elem = Element::builder("query") + .ns(ns::MAM) + .attr("queryid", self.queryid.clone()) + .attr("node", self.node.clone()) + .build(); + //if let Some(form) = self.form { + // elem.append_child((&form).into()); + //} + if let Some(ref set) = self.set { + elem.append_child(set.into()); + } + elem } - elem } -pub fn serialise_result(result: &Result_) -> Element { - let mut elem = Element::builder("result") - .ns(ns::MAM) - .attr("queryid", result.queryid.clone()) - .attr("id", result.id.clone()) - .build(); - elem.append_child((&result.forwarded).into()); - elem +impl<'a> Into for &'a Result_ { + fn into(self) -> Element { + let mut elem = Element::builder("result") + .ns(ns::MAM) + .attr("queryid", self.queryid.clone()) + .attr("id", self.id.clone()) + .build(); + elem.append_child((&self.forwarded).into()); + elem + } } -pub fn serialise_fin(fin: &Fin) -> Element { - let mut elem = Element::builder("fin") - .ns(ns::MAM) - .attr("complete", match fin.complete { - true => Some("true"), - false => None, - }) - .build(); - elem.append_child((&fin.set).into()); - elem +impl<'a> Into for &'a Fin { + fn into(self) -> Element { + let mut elem = Element::builder("fin") + .ns(ns::MAM) + .attr("complete", match self.complete { + true => Some("true"), + false => None, + }) + .build(); + elem.append_child((&self.set).into()); + elem + } } -pub fn serialise_prefs(prefs: &Prefs) -> Element { - let mut elem = Element::builder("prefs") - .ns(ns::MAM) - .attr("default", match prefs.default_ { - Some(DefaultPrefs::Always) => Some("always"), - Some(DefaultPrefs::Never) => Some("never"), - Some(DefaultPrefs::Roster) => Some("roster"), - None => None, - }) - .build(); - if !prefs.always.is_empty() { - let mut always = Element::builder("always") - .ns(ns::RSM) - .build(); - for jid in prefs.always.clone() { - always.append_child(Element::builder("jid") - .ns(ns::RSM) - .append(String::from(jid)) - .build()); +impl<'a> Into for &'a Prefs { + fn into(self) -> Element { + let mut elem = Element::builder("prefs") + .ns(ns::MAM) + .attr("default", match self.default_ { + Some(DefaultPrefs::Always) => Some("always"), + Some(DefaultPrefs::Never) => Some("never"), + Some(DefaultPrefs::Roster) => Some("roster"), + None => None, + }) + .build(); + if !self.always.is_empty() { + let mut always = Element::builder("always") + .ns(ns::RSM) + .build(); + for jid in self.always.clone() { + always.append_child(Element::builder("jid") + .ns(ns::RSM) + .append(String::from(jid)) + .build()); + } + elem.append_child(always); } - elem.append_child(always); - } - if !prefs.never.is_empty() { - let mut never = Element::builder("never") - .ns(ns::RSM) - .build(); - for jid in prefs.never.clone() { - never.append_child(Element::builder("jid") - .ns(ns::RSM) - .append(String::from(jid)) - .build()); + if !self.never.is_empty() { + let mut never = Element::builder("never") + .ns(ns::RSM) + .build(); + for jid in self.never.clone() { + never.append_child(Element::builder("jid") + .ns(ns::RSM) + .append(String::from(jid)) + .build()); + } + elem.append_child(never); } - elem.append_child(never); + elem } - elem } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use mam; + use super::*; #[test] fn test_query() { let elem: Element = "".parse().unwrap(); - mam::parse_query(&elem).unwrap(); + Query::try_from(&elem).unwrap(); } #[test] @@ -266,7 +288,7 @@ mod tests { "#.parse().unwrap(); - mam::parse_result(&elem).unwrap(); + Result_::try_from(&elem).unwrap(); } #[test] @@ -279,7 +301,7 @@ mod tests { "#.parse().unwrap(); - mam::parse_fin(&elem).unwrap(); + Fin::try_from(&elem).unwrap(); } #[test] @@ -296,7 +318,7 @@ mod tests { "#.parse().unwrap(); - mam::parse_query(&elem).unwrap(); + Query::try_from(&elem).unwrap(); } #[test] @@ -316,13 +338,13 @@ mod tests { "#.parse().unwrap(); - mam::parse_query(&elem).unwrap(); + Query::try_from(&elem).unwrap(); } #[test] fn test_prefs_get() { let elem: Element = "".parse().unwrap(); - mam::parse_prefs(&elem).unwrap(); + Prefs::try_from(&elem).unwrap(); let elem: Element = r#" @@ -330,7 +352,7 @@ mod tests { "#.parse().unwrap(); - mam::parse_prefs(&elem).unwrap(); + Prefs::try_from(&elem).unwrap(); } #[test] @@ -345,13 +367,13 @@ mod tests { "#.parse().unwrap(); - mam::parse_prefs(&elem).unwrap(); + Prefs::try_from(&elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = mam::parse_query(&elem).unwrap_err(); + let error = Query::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -362,8 +384,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let replace = mam::Query { queryid: None, node: None, form: None, set: None }; - let elem2 = mam::serialise_query(&replace); + let replace = Query { queryid: None, node: None, form: None, set: None }; + let elem2 = (&replace).into(); assert_eq!(elem, elem2); } } From 04d90f22ee306cf8c251e77812b55ea69044dc1f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:13:53 +0100 Subject: [PATCH 0190/1020] stanza_error: Switch to Into/TryFrom. --- src/iq.rs | 20 +++--- src/message.rs | 8 +-- src/presence.rs | 8 +-- src/stanza_error.rs | 161 +++++++++++++++++++++++--------------------- 4 files changed, 100 insertions(+), 97 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index f48026a5670f7d4ab5ea65118ebd554b72765a61..0e0aca7fc76e5b2ae9a49dd7e1d4d8f408a05efa 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -16,7 +16,7 @@ use error::Error; use ns; -use stanza_error; +use stanza_error::StanzaError; use disco::Disco; use ibb::IBB; use jingle::Jingle; @@ -42,7 +42,7 @@ pub enum IqType { Get(IqPayloadType), Set(IqPayloadType), Result(Option), - Error(stanza_error::StanzaError), + Error(StanzaError), } impl IntoAttributeValue for IqType { @@ -90,7 +90,7 @@ pub fn parse_iq(root: &Element) -> Result { if error_payload.is_some() { return Err(Error::ParseError("Wrong number of children in iq element.")); } - error_payload = Some(stanza_error::parse_stanza_error(elem)?); + error_payload = Some(StanzaError::try_from(elem)?); } else if root.children().collect::>().len() != 2 { return Err(Error::ParseError("Wrong number of children in iq element.")); } @@ -171,7 +171,7 @@ pub fn serialise(iq: &Iq) -> Element { IqType::Get(IqPayloadType::XML(elem)) | IqType::Set(IqPayloadType::XML(elem)) | IqType::Result(Some(IqPayloadType::XML(elem))) => elem, - IqType::Error(error) => stanza_error::serialise(&error), + IqType::Error(error) => (&error).into(), IqType::Get(IqPayloadType::Parsed(payload)) | IqType::Set(IqPayloadType::Parsed(payload)) | IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), @@ -183,11 +183,9 @@ pub fn serialise(iq: &Iq) -> Element { #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; + use super::*; use iq; - use stanza_error; - use disco; + use stanza_error::{ErrorType, DefinedCondition}; #[test] fn test_require_type() { @@ -275,9 +273,9 @@ mod tests { assert_eq!(iq.id, None); match iq.payload { iq::IqType::Error(error) => { - assert_eq!(error.type_, stanza_error::ErrorType::Cancel); + assert_eq!(error.type_, ErrorType::Cancel); assert_eq!(error.by, None); - assert_eq!(error.defined_condition, stanza_error::DefinedCondition::ServiceUnavailable); + assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable); assert_eq!(error.texts.len(), 0); assert_eq!(error.other, None); }, @@ -314,7 +312,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let iq = iq::parse_iq(&elem).unwrap(); assert!(match iq.payload { - iq::IqType::Get(iq::IqPayloadType::Parsed(iq::IqPayload::Disco(disco::Disco { .. }))) => true, + IqType::Get(IqPayloadType::Parsed(IqPayload::Disco(Disco { .. }))) => true, _ => false, }); } diff --git a/src/message.rs b/src/message.rs index ce6622aa9fae19f067deadf83ca9e2584809af4a..ee2c64477ce141cf8b33766d1e8655b23ca501e6 100644 --- a/src/message.rs +++ b/src/message.rs @@ -16,7 +16,7 @@ use error::Error; use ns; use body; -use stanza_error; +use stanza_error::StanzaError; use chatstates::ChatState; use receipts::Receipt; use delay::Delay; @@ -28,7 +28,7 @@ use eme::ExplicitMessageEncryption; #[derive(Debug, Clone)] pub enum MessagePayload { Body(body::Body), - StanzaError(stanza_error::StanzaError), + StanzaError(StanzaError), ChatState(ChatState), Receipt(Receipt), Delay(Delay), @@ -113,7 +113,7 @@ pub fn parse_message(root: &Element) -> Result { for elem in root.children() { let payload = if let Ok(body) = body::parse_body(elem) { Some(MessagePayload::Body(body)) - } else if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { + } else if let Ok(stanza_error) = StanzaError::try_from(elem) { Some(MessagePayload::StanzaError(stanza_error)) } else if let Ok(chatstate) = ChatState::try_from(elem) { Some(MessagePayload::ChatState(chatstate)) @@ -147,7 +147,7 @@ pub fn parse_message(root: &Element) -> Result { pub fn serialise_payload(payload: &MessagePayload) -> Element { match *payload { MessagePayload::Body(ref body) => body::serialise(body), - MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), + MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstate.into(), MessagePayload::Receipt(ref receipt) => receipt.into(), diff --git a/src/presence.rs b/src/presence.rs index 6ae97ecb9ae2371c5dbda2460080d79c7b552047..4ec697cbbe1c6cf56da436f72e0778fdac190499 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -16,7 +16,7 @@ use error::Error; use ns; -use stanza_error; +use stanza_error::StanzaError; use delay::Delay; use ecaps2::ECaps2; @@ -51,7 +51,7 @@ pub enum PresencePayload { Show(Show), Status(Status), Priority(Priority), - StanzaError(stanza_error::StanzaError), + StanzaError(StanzaError), Delay(Delay), ECaps2(ECaps2), } @@ -179,7 +179,7 @@ pub fn parse_presence(root: &Element) -> Result { } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { - let payload = if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) { + let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { Some(PresencePayload::StanzaError(stanza_error)) } else if let Ok(delay) = Delay::try_from(elem) { Some(PresencePayload::Delay(delay)) @@ -226,7 +226,7 @@ pub fn serialise_payload(payload: &PresencePayload) -> Element { .append(format!("{}", priority)) .build() }, - PresencePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error), + PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), PresencePayload::Delay(ref delay) => delay.into(), PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 0a1eb42566a042ab0198ed8ead421cfb10b8b1d3..9a0bfa03dc71eb316580db86cc8f6a7bccb3152e 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -149,104 +150,108 @@ pub struct StanzaError { pub other: Option, } -pub fn parse_stanza_error(root: &Element) -> Result { - if !root.is("error", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not an error element.")); - } +impl<'a> TryFrom<&'a Element> for StanzaError { + type Error = Error; - let type_ = root.attr("type") - .ok_or(Error::ParseError("Error must have a 'type' attribute."))? - .parse()?; - let by = root.attr("by") - .and_then(|by| by.parse().ok()); - let mut defined_condition = None; - let mut texts = BTreeMap::new(); - let mut other = None; + fn try_from(elem: &'a Element) -> Result { + if !elem.is("error", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not an error element.")); + } - for child in root.children() { - if child.is("text", ns::XMPP_STANZAS) { - for _ in child.children() { - return Err(Error::ParseError("Unknown element in error text.")); - } - let lang = child.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = texts.insert(lang, child.text()) { - return Err(Error::ParseError("Text element present twice for the same xml:lang.")); - } - } else if child.ns() == Some(ns::XMPP_STANZAS) { - if defined_condition.is_some() { - return Err(Error::ParseError("Error must not have more than one defined-condition.")); - } - for _ in child.children() { - return Err(Error::ParseError("Unknown element in defined-condition.")); - } - let condition = DefinedCondition::from_str(child.name())?; - defined_condition = Some(condition); - } else { - if other.is_some() { - return Err(Error::ParseError("Error must not have more than one other element.")); + let type_ = elem.attr("type") + .ok_or(Error::ParseError("Error must have a 'type' attribute."))? + .parse()?; + let by = elem.attr("by") + .and_then(|by| by.parse().ok()); + let mut defined_condition = None; + let mut texts = BTreeMap::new(); + let mut other = None; + + for child in elem.children() { + if child.is("text", ns::XMPP_STANZAS) { + for _ in child.children() { + return Err(Error::ParseError("Unknown element in error text.")); + } + let lang = child.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = texts.insert(lang, child.text()) { + return Err(Error::ParseError("Text element present twice for the same xml:lang.")); + } + } else if child.ns() == Some(ns::XMPP_STANZAS) { + if defined_condition.is_some() { + return Err(Error::ParseError("Error must not have more than one defined-condition.")); + } + for _ in child.children() { + return Err(Error::ParseError("Unknown element in defined-condition.")); + } + let condition = DefinedCondition::from_str(child.name())?; + defined_condition = Some(condition); + } else { + if other.is_some() { + return Err(Error::ParseError("Error must not have more than one other element.")); + } + other = Some(child.clone()); } - other = Some(child.clone()); } - } - if defined_condition.is_none() { - return Err(Error::ParseError("Error must have a defined-condition.")); - } - let defined_condition = defined_condition.unwrap(); + if defined_condition.is_none() { + return Err(Error::ParseError("Error must have a defined-condition.")); + } + let defined_condition = defined_condition.unwrap(); - Ok(StanzaError { - type_: type_, - by: by, - defined_condition: defined_condition, - texts: texts, - other: other, - }) + Ok(StanzaError { + type_: type_, + by: by, + defined_condition: defined_condition, + texts: texts, + other: other, + }) + } } -pub fn serialise(error: &StanzaError) -> Element { - let mut root = Element::builder("error") - .ns(ns::JABBER_CLIENT) - .attr("type", String::from(error.type_.clone())) - .attr("by", match error.by { - Some(ref by) => Some(String::from(by.clone())), - None => None, - }) - .append(Element::builder(error.defined_condition.clone()) - .ns(ns::XMPP_STANZAS) - .build()) - .build(); - for (lang, text) in error.texts.clone() { - let elem = Element::builder("text") - .ns(ns::XMPP_STANZAS) - .attr("xml:lang", lang) - .append(text) - .build(); - root.append_child(elem); - } - if let Some(ref other) = error.other { - root.append_child(other.clone()); +impl<'a> Into for &'a StanzaError { + fn into(self) -> Element { + let mut root = Element::builder("error") + .ns(ns::JABBER_CLIENT) + .attr("type", String::from(self.type_.clone())) + .attr("by", match self.by { + Some(ref by) => Some(String::from(by.clone())), + None => None, + }) + .append(Element::builder(self.defined_condition.clone()) + .ns(ns::XMPP_STANZAS) + .build()) + .build(); + for (lang, text) in self.texts.clone() { + let elem = Element::builder("text") + .ns(ns::XMPP_STANZAS) + .attr("xml:lang", lang) + .append(text) + .build(); + root.append_child(elem); + } + if let Some(ref other) = self.other { + root.append_child(other.clone()); + } + root } - root } #[cfg(test)] mod tests { - use minidom::Element; - use error::Error; - use stanza_error; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let error = stanza_error::parse_stanza_error(&elem).unwrap(); - assert_eq!(error.type_, stanza_error::ErrorType::Cancel); - assert_eq!(error.defined_condition, stanza_error::DefinedCondition::UndefinedCondition); + let error = StanzaError::try_from(&elem).unwrap(); + assert_eq!(error.type_, ErrorType::Cancel); + assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition); } #[test] fn test_invalid_type() { let elem: Element = "".parse().unwrap(); - let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let error = StanzaError::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -254,7 +259,7 @@ mod tests { assert_eq!(message, "Error must have a 'type' attribute."); let elem: Element = "".parse().unwrap(); - let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let error = StanzaError::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -265,7 +270,7 @@ mod tests { #[test] fn test_invalid_condition() { let elem: Element = "".parse().unwrap(); - let error = stanza_error::parse_stanza_error(&elem).unwrap_err(); + let error = StanzaError::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From fffaca316f460121330635b95ac912b74e6cc2a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:16:56 +0100 Subject: [PATCH 0191/1020] iq: Switch to Into/TryFrom. --- src/iq.rs | 241 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 124 insertions(+), 117 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 0e0aca7fc76e5b2ae9a49dd7e1d4d8f408a05efa..4f61260d8184481c836fbb5165c7683ba5d41437 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -64,133 +64,140 @@ pub struct Iq { pub payload: IqType, } -pub fn parse_iq(root: &Element) -> Result { - if !root.is("iq", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not an iq element.")); - } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_, - None => return Err(Error::ParseError("Iq element requires a 'type' attribute.")), - }; +impl<'a> TryFrom<&'a Element> for Iq { + type Error = Error; - let mut payload = None; - let mut error_payload = None; - for elem in root.children() { - if payload.is_some() { - return Err(Error::ParseError("Wrong number of children in iq element.")); + fn try_from(root: &'a Element) -> Result { + if !root.is("iq", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not an iq element.")); } - if type_ == "error" { - if elem.is("error", ns::JABBER_CLIENT) { - if error_payload.is_some() { - return Err(Error::ParseError("Wrong number of children in iq element.")); - } - error_payload = Some(StanzaError::try_from(elem)?); - } else if root.children().collect::>().len() != 2 { + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_, + None => return Err(Error::ParseError("Iq element requires a 'type' attribute.")), + }; + + let mut payload = None; + let mut error_payload = None; + for elem in root.children() { + if payload.is_some() { return Err(Error::ParseError("Wrong number of children in iq element.")); } - } else { - let parsed_payload = if let Ok(disco) = Disco::try_from(elem) { - Some(IqPayload::Disco(disco)) - } else if let Ok(ibb) = IBB::try_from(elem) { - Some(IqPayload::IBB(ibb)) - } else if let Ok(jingle) = Jingle::try_from(elem) { - Some(IqPayload::Jingle(jingle)) - } else if let Ok(ping) = Ping::try_from(elem) { - Some(IqPayload::Ping(ping)) + if type_ == "error" { + if elem.is("error", ns::JABBER_CLIENT) { + if error_payload.is_some() { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + error_payload = Some(StanzaError::try_from(elem)?); + } else if root.children().collect::>().len() != 2 { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } } else { - None - }; + let parsed_payload = if let Ok(disco) = Disco::try_from(elem) { + Some(IqPayload::Disco(disco)) + } else if let Ok(ibb) = IBB::try_from(elem) { + Some(IqPayload::IBB(ibb)) + } else if let Ok(jingle) = Jingle::try_from(elem) { + Some(IqPayload::Jingle(jingle)) + } else if let Ok(ping) = Ping::try_from(elem) { + Some(IqPayload::Ping(ping)) + } else { + None + }; - payload = match parsed_payload { - Some(payload) => Some(IqPayloadType::Parsed(payload)), - None => Some(IqPayloadType::XML(elem.clone())), - }; + payload = match parsed_payload { + Some(payload) => Some(IqPayloadType::Parsed(payload)), + None => Some(IqPayloadType::XML(elem.clone())), + }; + } } - } - let type_ = if type_ == "get" { - if let Some(payload) = payload.clone() { - IqType::Get(payload.clone()) - } else { - return Err(Error::ParseError("Wrong number of children in iq element.")); - } - } else if type_ == "set" { - if let Some(payload) = payload.clone() { - IqType::Set(payload.clone()) - } else { - return Err(Error::ParseError("Wrong number of children in iq element.")); - } - } else if type_ == "result" { - if let Some(payload) = payload.clone() { - IqType::Result(Some(payload.clone())) - } else { - IqType::Result(None) - } - } else if type_ == "error" { - if let Some(payload) = error_payload.clone() { - IqType::Error(payload.clone()) + let type_ = if type_ == "get" { + if let Some(payload) = payload.clone() { + IqType::Get(payload.clone()) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else if type_ == "set" { + if let Some(payload) = payload.clone() { + IqType::Set(payload.clone()) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else if type_ == "result" { + if let Some(payload) = payload.clone() { + IqType::Result(Some(payload.clone())) + } else { + IqType::Result(None) + } + } else if type_ == "error" { + if let Some(payload) = error_payload.clone() { + IqType::Error(payload.clone()) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } } else { - return Err(Error::ParseError("Wrong number of children in iq element.")); - } - } else { - panic!() - }; + panic!() + }; - Ok(Iq { - from: from, - to: to, - id: id, - payload: type_, - }) + Ok(Iq { + from: from, + to: to, + id: id, + payload: type_, + }) + } } -pub fn serialise_payload(payload: &IqPayload) -> Element { - match *payload { - IqPayload::Disco(ref disco) => disco.into(), - IqPayload::IBB(ref ibb) => ibb.into(), - IqPayload::Jingle(ref jingle) => jingle.into(), - IqPayload::Ping(ref ping) => ping.into(), +impl<'a> Into for &'a IqPayload { + fn into(self) -> Element { + match *self { + IqPayload::Disco(ref disco) => disco.into(), + IqPayload::IBB(ref ibb) => ibb.into(), + IqPayload::Jingle(ref jingle) => jingle.into(), + IqPayload::Ping(ref ping) => ping.into(), + } } } -pub fn serialise(iq: &Iq) -> Element { - let mut stanza = Element::builder("iq") - .ns(ns::JABBER_CLIENT) - .attr("from", iq.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", iq.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", iq.id.clone()) - .attr("type", iq.payload.clone()) - .build(); - let elem = match iq.payload.clone() { - IqType::Get(IqPayloadType::XML(elem)) - | IqType::Set(IqPayloadType::XML(elem)) - | IqType::Result(Some(IqPayloadType::XML(elem))) => elem, - IqType::Error(error) => (&error).into(), - IqType::Get(IqPayloadType::Parsed(payload)) - | IqType::Set(IqPayloadType::Parsed(payload)) - | IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload), - IqType::Result(None) => return stanza, - }; - stanza.append_child(elem); - stanza +impl<'a> Into for &'a Iq { + fn into(self) -> Element { + let mut stanza = Element::builder("iq") + .ns(ns::JABBER_CLIENT) + .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", self.id.clone()) + .attr("type", self.payload.clone()) + .build(); + let elem = match self.payload.clone() { + IqType::Get(IqPayloadType::XML(elem)) + | IqType::Set(IqPayloadType::XML(elem)) + | IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Error(error) => (&error).into(), + IqType::Get(IqPayloadType::Parsed(payload)) + | IqType::Set(IqPayloadType::Parsed(payload)) + | IqType::Result(Some(IqPayloadType::Parsed(payload))) => (&payload).into(), + IqType::Result(None) => return stanza, + }; + stanza.append_child(elem); + stanza + } } #[cfg(test)] mod tests { use super::*; - use iq; use stanza_error::{ErrorType, DefinedCondition}; #[test] fn test_require_type() { let elem: Element = "".parse().unwrap(); - let error = iq::parse_iq(&elem).unwrap_err(); + let error = Iq::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -203,13 +210,13 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - iq::IqType::Get(iq::IqPayloadType::XML(element)) => element == query, + IqType::Get(IqPayloadType::XML(element)) => element == query, _ => false }); } @@ -219,13 +226,13 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); let vcard: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - iq::IqType::Set(iq::IqPayloadType::XML(element)) => element == vcard, + IqType::Set(IqPayloadType::XML(element)) => element == vcard, _ => false }); } @@ -233,12 +240,12 @@ mod tests { #[test] fn test_result_empty() { let elem: Element = "".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - iq::IqType::Result(None) => true, + IqType::Result(None) => true, _ => false, }); } @@ -248,13 +255,13 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - iq::IqType::Result(Some(iq::IqPayloadType::XML(element))) => element == query, + IqType::Result(Some(IqPayloadType::XML(element))) => element == query, _ => false, }); } @@ -267,12 +274,12 @@ mod tests { ".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); match iq.payload { - iq::IqType::Error(error) => { + IqType::Error(error) => { assert_eq!(error.type_, ErrorType::Cancel); assert_eq!(error.by, None); assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable); @@ -286,7 +293,7 @@ mod tests { #[test] fn test_children_invalid() { let elem: Element = "".parse().unwrap(); - let error = iq::parse_iq(&elem).unwrap_err(); + let error = Iq::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -297,20 +304,20 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let iq2 = iq::Iq { + let iq2 = Iq { from: None, to: None, id: None, - payload: iq::IqType::Result(None), + payload: IqType::Result(None), }; - let elem2 = iq::serialise(&iq2); + let elem2 = (&iq2).into(); assert_eq!(elem, elem2); } #[test] fn test_disco() { let elem: Element = "".parse().unwrap(); - let iq = iq::parse_iq(&elem).unwrap(); + let iq = Iq::try_from(&elem).unwrap(); assert!(match iq.payload { IqType::Get(IqPayloadType::Parsed(IqPayload::Disco(Disco { .. }))) => true, _ => false, From f971cbd5c9f541b2857e437ad8887daad3aeadf2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:21:34 +0100 Subject: [PATCH 0192/1020] message: Switch to Into/TryFrom. --- src/forwarding.rs | 8 +- src/message.rs | 186 +++++++++++++++++++++++----------------------- 2 files changed, 96 insertions(+), 98 deletions(-) diff --git a/src/forwarding.rs b/src/forwarding.rs index 75b7fbcc8a307946346e9d07298e744c215ce5de..a2162e06b110209c8b9e8001d0da0518c90255ad 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -11,7 +11,7 @@ use minidom::Element; use error::Error; use delay::Delay; -use message; +use message::Message; use ns; @@ -19,7 +19,7 @@ use ns; pub struct Forwarded { pub delay: Option, // XXX: really? Option? - pub stanza: Option, + pub stanza: Option, } impl<'a> TryFrom<&'a Element> for Forwarded { @@ -35,7 +35,7 @@ impl<'a> TryFrom<&'a Element> for Forwarded { if child.is("delay", ns::DELAY) { delay = Some(Delay::try_from(child)?); } else if child.is("message", ns::JABBER_CLIENT) { - stanza = Some(message::parse_message(child)?); + stanza = Some(Message::try_from(child)?); // TODO: also handle the five other possibilities. } else { return Err(Error::ParseError("Unknown child in forwarded element.")); @@ -53,7 +53,7 @@ impl<'a> Into for &'a Forwarded { Element::builder("forwarded") .ns(ns::FORWARD) .append(match self.delay { Some(ref delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) - .append(self.stanza.clone()) + .append(match self.stanza { Some(ref stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) .build() } } diff --git a/src/message.rs b/src/message.rs index ee2c64477ce141cf8b33766d1e8655b23ca501e6..51baf9b9ffb8a0cd4d2dbaec8d67816a6ed83afa 100644 --- a/src/message.rs +++ b/src/message.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -95,152 +95,150 @@ pub struct Message { pub payloads: Vec, } -pub fn parse_message(root: &Element) -> Result { - if !root.is("message", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not a message element.")); - } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_.parse()?, - None => Default::default(), - }; - let mut payloads = vec!(); - for elem in root.children() { - let payload = if let Ok(body) = body::parse_body(elem) { - Some(MessagePayload::Body(body)) - } else if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(MessagePayload::StanzaError(stanza_error)) - } else if let Ok(chatstate) = ChatState::try_from(elem) { - Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = Receipt::try_from(elem) { - Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = Attention::try_from(elem) { - Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = Replace::try_from(elem) { - Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { - Some(MessagePayload::ExplicitMessageEncryption(eme)) - } else { - None +impl<'a> TryFrom<&'a Element> for Message { + type Error = Error; + + fn try_from(root: &'a Element) -> Result { + if !root.is("message", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a message element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_.parse()?, + None => Default::default(), }; - payloads.push(match payload { - Some(payload) => MessagePayloadType::Parsed(payload), - None => MessagePayloadType::XML(elem.clone()), - }); - } - Ok(Message { - from: from, - to: to, - id: id, - type_: type_, - payloads: payloads, - }) -} - -pub fn serialise_payload(payload: &MessagePayload) -> Element { - match *payload { - MessagePayload::Body(ref body) => body::serialise(body), - MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), - MessagePayload::Attention(ref attention) => attention.into(), - MessagePayload::ChatState(ref chatstate) => chatstate.into(), - MessagePayload::Receipt(ref receipt) => receipt.into(), - MessagePayload::Delay(ref delay) => delay.into(), - MessagePayload::MessageCorrect(ref replace) => replace.into(), - MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), + let mut payloads = vec!(); + for elem in root.children() { + let payload = if let Ok(body) = body::parse_body(elem) { + Some(MessagePayload::Body(body)) + } else if let Ok(stanza_error) = StanzaError::try_from(elem) { + Some(MessagePayload::StanzaError(stanza_error)) + } else if let Ok(chatstate) = ChatState::try_from(elem) { + Some(MessagePayload::ChatState(chatstate)) + } else if let Ok(receipt) = Receipt::try_from(elem) { + Some(MessagePayload::Receipt(receipt)) + } else if let Ok(delay) = Delay::try_from(elem) { + Some(MessagePayload::Delay(delay)) + } else if let Ok(attention) = Attention::try_from(elem) { + Some(MessagePayload::Attention(attention)) + } else if let Ok(replace) = Replace::try_from(elem) { + Some(MessagePayload::MessageCorrect(replace)) + } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { + Some(MessagePayload::ExplicitMessageEncryption(eme)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => MessagePayloadType::Parsed(payload), + None => MessagePayloadType::XML(elem.clone()), + }); + } + Ok(Message { + from: from, + to: to, + id: id, + type_: type_, + payloads: payloads, + }) } } -pub fn serialise(message: &Message) -> Element { - let mut stanza = Element::builder("message") - .ns(ns::JABBER_CLIENT) - .attr("from", message.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", message.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", message.id.clone()) - .attr("type", message.type_.clone()) - .build(); - for child in message.payloads.clone() { - let elem = match child { - MessagePayloadType::XML(elem) => elem, - MessagePayloadType::Parsed(payload) => serialise_payload(&payload), - }; - stanza.append_child(elem); +impl<'a> Into for &'a MessagePayload { + fn into(self) -> Element { + match *self { + MessagePayload::Body(ref body) => body::serialise(body), + MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), + MessagePayload::Attention(ref attention) => attention.into(), + MessagePayload::ChatState(ref chatstate) => chatstate.into(), + MessagePayload::Receipt(ref receipt) => receipt.into(), + MessagePayload::Delay(ref delay) => delay.into(), + MessagePayload::MessageCorrect(ref replace) => replace.into(), + MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), + } } - stanza } -impl IntoElements for Message { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = serialise(&self); - emitter.append_child(elem); +impl<'a> Into for &'a Message { + fn into(self) -> Element { + let mut stanza = Element::builder("message") + .ns(ns::JABBER_CLIENT) + .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", self.id.clone()) + .attr("type", self.type_.clone()) + .build(); + for child in self.payloads.clone() { + let elem = match child { + MessagePayloadType::XML(elem) => elem, + MessagePayloadType::Parsed(payload) => (&payload).into(), + }; + stanza.append_child(elem); + } + stanza } } #[cfg(test)] mod tests { - use std::str::FromStr; - use minidom::Element; - use jid::Jid; - use message; + use super::*; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let message = message::parse_message(&elem).unwrap(); + let message = Message::try_from(&elem).unwrap(); assert_eq!(message.from, None); assert_eq!(message.to, None); assert_eq!(message.id, None); - assert_eq!(message.type_, message::MessageType::Normal); + assert_eq!(message.type_, MessageType::Normal); assert!(message.payloads.is_empty()); } #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let message = message::Message { + let message = Message { from: None, to: None, id: None, - type_: message::MessageType::Normal, + type_: MessageType::Normal, payloads: vec!(), }; - let elem2 = message::serialise(&message); + let elem2 = (&message).into(); assert_eq!(elem, elem2); } #[test] fn test_body() { let elem: Element = "Hello world!".parse().unwrap(); - message::parse_message(&elem).unwrap(); + Message::try_from(&elem).unwrap(); } #[test] fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); - let message = message::Message { + let message = Message { from: None, to: Some(Jid::from_str("coucou@example.org").unwrap()), id: None, - type_: message::MessageType::Chat, + type_: MessageType::Chat, payloads: vec!( - message::MessagePayloadType::Parsed(message::MessagePayload::Body("Hello world!".to_owned())), + MessagePayloadType::Parsed(MessagePayload::Body("Hello world!".to_owned())), ), }; - let elem2 = message::serialise(&message); + let elem2 = (&message).into(); assert_eq!(elem, elem2); } #[test] fn test_attention() { let elem: Element = "".parse().unwrap(); - let message = message::parse_message(&elem).unwrap(); - let elem2 = message::serialise(&message); + let message = Message::try_from(&elem).unwrap(); + let elem2 = (&message).into(); assert_eq!(elem, elem2); } } From 69cfb14c77fe93fe0a8c92aeaf114581867ea715 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:24:17 +0100 Subject: [PATCH 0193/1020] presence: Switch to Into/TryFrom. --- src/presence.rs | 282 ++++++++++++++++++++++++------------------------ 1 file changed, 144 insertions(+), 138 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 4ec697cbbe1c6cf56da436f72e0778fdac190499..cfaf5e71ab90caf5d6acf00fe377f7789f2265f4 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -128,177 +128,183 @@ pub struct Presence { pub payloads: Vec, } -pub fn parse_presence(root: &Element) -> Result { - if !root.is("presence", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not a presence element.")); - } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_.parse()?, - None => Default::default(), - }; - let mut show = None; - let mut statuses = BTreeMap::new(); - let mut priority = None; - let mut payloads = vec!(); - for elem in root.children() { - if elem.is("show", ns::JABBER_CLIENT) { - if show.is_some() { - return Err(Error::ParseError("More than one show element in a presence.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in show element.")); - } - show = Some(match elem.text().as_ref() { - "away" => Show::Away, - "chat" => Show::Chat, - "dnd" => Show::Dnd, - "xa" => Show::Xa, - - _ => return Err(Error::ParseError("Invalid value for show.")), - }); - } else if elem.is("status", ns::JABBER_CLIENT) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in status element.")); - } - let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = statuses.insert(lang, elem.text()) { - return Err(Error::ParseError("Status element present twice for the same xml:lang.")); - } - } else if elem.is("priority", ns::JABBER_CLIENT) { - if priority.is_some() { - return Err(Error::ParseError("More than one priority element in a presence.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in priority element.")); - } - priority = Some(Priority::from_str(elem.text().as_ref())?); - } else { - let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(PresencePayload::StanzaError(stanza_error)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(PresencePayload::Delay(delay)) - } else if let Ok(ecaps2) = ECaps2::try_from(elem) { - Some(PresencePayload::ECaps2(ecaps2)) +impl<'a> TryFrom<&'a Element> for Presence { + type Error = Error; + + fn try_from(root: &'a Element) -> Result { + if !root.is("presence", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a presence element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_.parse()?, + None => Default::default(), + }; + let mut show = None; + let mut statuses = BTreeMap::new(); + let mut priority = None; + let mut payloads = vec!(); + for elem in root.children() { + if elem.is("show", ns::JABBER_CLIENT) { + if show.is_some() { + return Err(Error::ParseError("More than one show element in a presence.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in show element.")); + } + show = Some(match elem.text().as_ref() { + "away" => Show::Away, + "chat" => Show::Chat, + "dnd" => Show::Dnd, + "xa" => Show::Xa, + + _ => return Err(Error::ParseError("Invalid value for show.")), + }); + } else if elem.is("status", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in status element.")); + } + let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = statuses.insert(lang, elem.text()) { + return Err(Error::ParseError("Status element present twice for the same xml:lang.")); + } + } else if elem.is("priority", ns::JABBER_CLIENT) { + if priority.is_some() { + return Err(Error::ParseError("More than one priority element in a presence.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in priority element.")); + } + priority = Some(Priority::from_str(elem.text().as_ref())?); } else { - None - }; - payloads.push(match payload { - Some(payload) => PresencePayloadType::Parsed(payload), - None => PresencePayloadType::XML(elem.clone()), - }); + let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { + Some(PresencePayload::StanzaError(stanza_error)) + } else if let Ok(delay) = Delay::try_from(elem) { + Some(PresencePayload::Delay(delay)) + } else if let Ok(ecaps2) = ECaps2::try_from(elem) { + Some(PresencePayload::ECaps2(ecaps2)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => PresencePayloadType::Parsed(payload), + None => PresencePayloadType::XML(elem.clone()), + }); + } } + Ok(Presence { + from: from, + to: to, + id: id, + type_: type_, + show: show, + statuses: statuses, + priority: priority.unwrap_or(0i8), + payloads: payloads, + }) } - Ok(Presence { - from: from, - to: to, - id: id, - type_: type_, - show: show, - statuses: statuses, - priority: priority.unwrap_or(0i8), - payloads: payloads, - }) } -pub fn serialise_payload(payload: &PresencePayload) -> Element { - match *payload { - PresencePayload::Show(ref show) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(show.to_owned()) - .build() - }, - PresencePayload::Status(ref status) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(status.to_owned()) - .build() - }, - PresencePayload::Priority(ref priority) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(format!("{}", priority)) - .build() - }, - PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), - PresencePayload::Delay(ref delay) => delay.into(), - PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), +impl<'a> Into for &'a PresencePayload { + fn into(self) -> Element { + match *self { + PresencePayload::Show(ref show) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(show.to_owned()) + .build() + }, + PresencePayload::Status(ref status) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(status.to_owned()) + .build() + }, + PresencePayload::Priority(ref priority) => { + Element::builder("status") + .ns(ns::JABBER_CLIENT) + .append(format!("{}", priority)) + .build() + }, + PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), + PresencePayload::Delay(ref delay) => delay.into(), + PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), + } } } -pub fn serialise(presence: &Presence) -> Element { - let mut stanza = Element::builder("presence") - .ns(ns::JABBER_CLIENT) - .attr("from", presence.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", presence.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", presence.id.clone()) - .attr("type", presence.type_.clone()) - .build(); - for child in presence.payloads.clone() { - let elem = match child { - PresencePayloadType::XML(elem) => elem, - PresencePayloadType::Parsed(payload) => serialise_payload(&payload), - }; - stanza.append_child(elem); +impl<'a> Into for &'a Presence { + fn into(self) -> Element { + let mut stanza = Element::builder("presence") + .ns(ns::JABBER_CLIENT) + .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", self.id.clone()) + .attr("type", self.type_.clone()) + .build(); + for child in self.payloads.clone() { + let elem = match child { + PresencePayloadType::XML(elem) => elem, + PresencePayloadType::Parsed(payload) => (&payload).into(), + }; + stanza.append_child(elem); + } + stanza } - stanza } #[cfg(test)] mod tests { use std::collections::BTreeMap; - use minidom::Element; - use error::Error; - use presence; + use super::*; use ns; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.from, None); assert_eq!(presence.to, None); assert_eq!(presence.id, None); - assert_eq!(presence.type_, presence::PresenceType::Available); + assert_eq!(presence.type_, PresenceType::Available); assert!(presence.payloads.is_empty()); } #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let presence = presence::Presence { + let presence = Presence { from: None, to: None, id: None, - type_: presence::PresenceType::Unavailable, + type_: PresenceType::Unavailable, show: None, statuses: BTreeMap::new(), priority: 0i8, payloads: vec!(), }; - let elem2 = presence::serialise(&presence); + let elem2 = (&presence).into(); assert_eq!(elem, elem2); } #[test] fn test_show() { let elem: Element = "chat".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); - assert_eq!(presence.show, Some(presence::Show::Chat)); + assert_eq!(presence.show, Some(Show::Chat)); } #[test] fn test_missing_show_value() { // "online" used to be a pretty common mistake. let elem: Element = "".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -310,7 +316,7 @@ mod tests { fn test_invalid_show() { // "online" used to be a pretty common mistake. let elem: Element = "online".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -321,7 +327,7 @@ mod tests { #[test] fn test_empty_status() { let elem: Element = "".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); assert_eq!(presence.statuses[""], ""); @@ -330,7 +336,7 @@ mod tests { #[test] fn test_status() { let elem: Element = "Here!".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); assert_eq!(presence.statuses[""], "Here!"); @@ -339,7 +345,7 @@ mod tests { #[test] fn test_multiple_statuses() { let elem: Element = "Here!Là!".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 2); assert_eq!(presence.statuses[""], "Here!"); @@ -349,7 +355,7 @@ mod tests { #[test] fn test_invalid_multiple_statuses() { let elem: Element = "Here!Là!".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -360,7 +366,7 @@ mod tests { #[test] fn test_priority() { let elem: Element = "-1".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); + let presence = Presence::try_from(&elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.priority, -1i8); } @@ -368,7 +374,7 @@ mod tests { #[test] fn test_invalid_priority() { let elem: Element = "128".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); match error { Error::ParseIntError(_) => (), _ => panic!(), @@ -378,8 +384,8 @@ mod tests { #[test] fn test_unknown_child() { let elem: Element = "".parse().unwrap(); - let presence = presence::parse_presence(&elem).unwrap(); - if let presence::PresencePayloadType::XML(ref payload) = presence.payloads[0] { + let presence = Presence::try_from(&elem).unwrap(); + if let PresencePayloadType::XML(ref payload) = presence.payloads[0] { assert!(payload.is("test", "invalid")); } else { panic!("Did successfully parse an invalid element."); @@ -390,7 +396,7 @@ mod tests { #[ignore] fn test_invalid_status_child() { let elem: Element = "".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -402,7 +408,7 @@ mod tests { #[ignore] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = presence::parse_presence(&elem).unwrap_err(); + let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -412,19 +418,19 @@ mod tests { #[test] fn test_serialise_status() { - let status = presence::Status::from("Hello world!"); - let payloads = vec!(presence::PresencePayloadType::Parsed(presence::PresencePayload::Status(status))); - let presence = presence::Presence { + let status = Status::from("Hello world!"); + let payloads = vec!(PresencePayloadType::Parsed(PresencePayload::Status(status))); + let presence = Presence { from: None, to: None, id: None, - type_: presence::PresenceType::Unavailable, + type_: PresenceType::Unavailable, show: None, statuses: BTreeMap::new(), priority: 0i8, payloads: payloads, }; - let elem = presence::serialise(&presence); + let elem: Element = (&presence).into(); assert!(elem.is("presence", ns::JABBER_CLIENT)); assert!(elem.children().collect::>()[0].is("status", ns::JABBER_CLIENT)); } From 414210796507c03c71ded2dfda77a8a60da25a48 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:38:23 +0100 Subject: [PATCH 0194/1020] message: Merge body in this module, and make it support xml:lang. --- src/body.rs | 86 -------------------------------------------------- src/lib.rs | 3 -- src/message.rs | 81 +++++++++++++++++++++++++++++++---------------- 3 files changed, 53 insertions(+), 117 deletions(-) delete mode 100644 src/body.rs diff --git a/src/body.rs b/src/body.rs deleted file mode 100644 index 8f23b56927dad6d04827aad39bc06e2ff92d403f..0000000000000000000000000000000000000000 --- a/src/body.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -use minidom::Element; - -use error::Error; - -use ns; - -pub type Body = String; - -pub fn parse_body(root: &Element) -> Result { - // TODO: also support components and servers. - if !root.is("body", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not a body element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in body element.")); - } - Ok(root.text()) -} - -pub fn serialise(body: &Body) -> Element { - Element::builder("body") - .ns(ns::JABBER_CLIENT) - .append(body.to_owned()) - .build() -} - -#[cfg(test)] -mod tests { - use minidom::Element; - use error::Error; - use body; - use ns; - - #[test] - fn test_simple() { - let elem: Element = "".parse().unwrap(); - body::parse_body(&elem).unwrap(); - } - - #[test] - fn test_invalid() { - let elem: Element = "".parse().unwrap(); - let error = body::parse_body(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "This is not a body element."); - } - - #[test] - fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = body::parse_body(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in body element."); - } - - #[test] - #[ignore] - fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = body::parse_body(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in body element."); - } - - #[test] - fn test_serialise() { - let body = body::Body::from("Hello world!"); - let elem = body::serialise(&body); - assert!(elem.is("body", ns::JABBER_CLIENT)); - } -} diff --git a/src/lib.rs b/src/lib.rs index e3fb5356424a05396f7d8c6d3a21b7fd8c4fdd6d..67c06296c339d8f0fcab94fe1d279e254088c428 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,9 +37,6 @@ pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; -/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence -pub mod body; - /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/message.rs b/src/message.rs index 51baf9b9ffb8a0cd4d2dbaec8d67816a6ed83afa..c54b0ab4d00b57891af31b6a9eed95a08211cc3f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -6,6 +6,7 @@ use std::convert::TryFrom; use std::str::FromStr; +use std::collections::BTreeMap; use minidom::{Element, IntoAttributeValue}; @@ -15,7 +16,6 @@ use error::Error; use ns; -use body; use stanza_error::StanzaError; use chatstates::ChatState; use receipts::Receipt; @@ -27,7 +27,6 @@ use eme::ExplicitMessageEncryption; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum MessagePayload { - Body(body::Body), StanzaError(StanzaError), ChatState(ChatState), Receipt(Receipt), @@ -86,12 +85,16 @@ pub enum MessagePayloadType { Parsed(MessagePayload), } +type Lang = String; +type Body = String; + #[derive(Debug, Clone)] pub struct Message { pub from: Option, pub to: Option, pub id: Option, pub type_: MessageType, + pub bodies: BTreeMap, pub payloads: Vec, } @@ -112,37 +115,47 @@ impl<'a> TryFrom<&'a Element> for Message { Some(type_) => type_.parse()?, None => Default::default(), }; + let mut bodies = BTreeMap::new(); let mut payloads = vec!(); for elem in root.children() { - let payload = if let Ok(body) = body::parse_body(elem) { - Some(MessagePayload::Body(body)) - } else if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(MessagePayload::StanzaError(stanza_error)) - } else if let Ok(chatstate) = ChatState::try_from(elem) { - Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = Receipt::try_from(elem) { - Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = Attention::try_from(elem) { - Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = Replace::try_from(elem) { - Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { - Some(MessagePayload::ExplicitMessageEncryption(eme)) + if elem.is("body", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in body element.")); + } + let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = bodies.insert(lang, elem.text()) { + return Err(Error::ParseError("Body element present twice for the same xml:lang.")); + } } else { - None - }; - payloads.push(match payload { - Some(payload) => MessagePayloadType::Parsed(payload), - None => MessagePayloadType::XML(elem.clone()), - }); + let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { + Some(MessagePayload::StanzaError(stanza_error)) + } else if let Ok(chatstate) = ChatState::try_from(elem) { + Some(MessagePayload::ChatState(chatstate)) + } else if let Ok(receipt) = Receipt::try_from(elem) { + Some(MessagePayload::Receipt(receipt)) + } else if let Ok(delay) = Delay::try_from(elem) { + Some(MessagePayload::Delay(delay)) + } else if let Ok(attention) = Attention::try_from(elem) { + Some(MessagePayload::Attention(attention)) + } else if let Ok(replace) = Replace::try_from(elem) { + Some(MessagePayload::MessageCorrect(replace)) + } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { + Some(MessagePayload::ExplicitMessageEncryption(eme)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => MessagePayloadType::Parsed(payload), + None => MessagePayloadType::XML(elem.clone()), + }); + } } Ok(Message { from: from, to: to, id: id, type_: type_, + bodies: BTreeMap::new(), payloads: payloads, }) } @@ -151,7 +164,6 @@ impl<'a> TryFrom<&'a Element> for Message { impl<'a> Into for &'a MessagePayload { fn into(self) -> Element { match *self { - MessagePayload::Body(ref body) => body::serialise(body), MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstate.into(), @@ -171,6 +183,17 @@ impl<'a> Into for &'a Message { .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) .attr("id", self.id.clone()) .attr("type", self.type_.clone()) + .append(self.bodies.iter() + .map(|(lang, body)| { + Element::builder("body") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(body.clone()) + .build() }) + .collect::>()) .build(); for child in self.payloads.clone() { let elem = match child { @@ -206,6 +229,7 @@ mod tests { to: None, id: None, type_: MessageType::Normal, + bodies: BTreeMap::new(), payloads: vec!(), }; let elem2 = (&message).into(); @@ -221,14 +245,15 @@ mod tests { #[test] fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); + let mut bodies = BTreeMap::new(); + bodies.insert(String::from(""), String::from("Hello world!")); let message = Message { from: None, to: Some(Jid::from_str("coucou@example.org").unwrap()), id: None, type_: MessageType::Chat, - payloads: vec!( - MessagePayloadType::Parsed(MessagePayload::Body("Hello world!".to_owned())), - ), + bodies: bodies, + payloads: vec!(), }; let elem2 = (&message).into(); assert_eq!(elem, elem2); From 3dfb05aab3e6835c5572de015203e27a045085ed Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:41:33 +0100 Subject: [PATCH 0195/1020] lib: Fix documentation. --- src/lib.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 67c06296c339d8f0fcab94fe1d279e254088c428..7faf06c6c8f0e672d8a9715668d1ffe96e0ba0bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,10 @@ //! A crate parsing common XMPP elements into Rust structures. //! -//! Each module implements a `parse` function, which takes a minidom -//! `Element` reference and returns `Some(MessagePayload)` if the parsing -//! succeeded, None otherwise. +//! Each module implements the `TryFrom<&minidom::Element>` trait, which takes +//! a minidom `Element` reference and returns a `Result`. //! -//! Parsed structs can then be manipulated internally, and serialised back -//! before being sent over the wire. +//! Parsed structs can then be manipulated manually, and must be serialised +//! back before being sent over the wire. // Copyright (c) 2017 Emmanuel Gil Peyrot // From 8f85c95a5255b8ef6039fc9e0bebaf8038d6bbf6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:54:12 +0100 Subject: [PATCH 0196/1020] Release version 0.2.0! --- Cargo.toml | 4 ++-- ChangeLog | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ea7eecef2b81b19673c0ad916bafc0032d0341c3..85e089c9c0e3a6c605c68ba018efda29001f7204 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "xmpp-parsers" -version = "0.1.0" +version = "0.2.0" authors = ["Emmanuel Gil Peyrot "] description = "Collection of parsers and serialisers for XMPP extensions" homepage = "https://hg.linkmauve.fr/xmpp-parsers" repository = "https://hg.linkmauve.fr/xmpp-parsers" -keywords = ["xmpp"] +keywords = ["xmpp", "xml"] categories = ["parsing", "network-programming"] license = "MPL-2.0" diff --git a/ChangeLog b/ChangeLog index 83fad57c8c820b87e4c3b31269e9ffbaf8d55ca6..d8e85e11df28f7d4fe9955a72dde69553b21afc8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,12 +1,22 @@ -Version XXX: -2017-0X-XX Emmanuel Gil Peyrot +Version 0.2.0: +2017-05-06 Emmanuel Gil Peyrot * New parsers/serialisers: - Stanza error, as per RFC 6120 §8.3. + - Jingle SOCKS5 Transport, XEP-0260. * Incompatible changes: + - Parsers and serialisers now all implement TryFrom + and Into, instead of the old parse_* and serialise_* + functions. - Presence has got an overhaul, it now hosts show, statuses and priority in its struct. The status module has also been dropped. + - Message now supports multiple bodies, each in a different + language. The body module has also been dropped. - Iq now gets a proper StanzaError when the type is error. + - Fix bogus Jingle payload, which was requiring both + description and transport. + * Crate updates: + - minidom 0.3.0 Version 0.1.0: 2017-04-29 Emmanuel Gil Peyrot From 4278c8ce2b7de2acdf40265bf0906b91b833862b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 7 May 2017 15:10:04 +0100 Subject: [PATCH 0198/1020] message: Add support for the element. --- src/message.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/message.rs b/src/message.rs index c54b0ab4d00b57891af31b6a9eed95a08211cc3f..2c8959b8bb6efcc7d1d85a567849c24a3395b376 100644 --- a/src/message.rs +++ b/src/message.rs @@ -87,6 +87,7 @@ pub enum MessagePayloadType { type Lang = String; type Body = String; +type Subject = String; #[derive(Debug, Clone)] pub struct Message { @@ -95,6 +96,7 @@ pub struct Message { pub id: Option, pub type_: MessageType, pub bodies: BTreeMap, + pub subjects: BTreeMap, pub payloads: Vec, } @@ -116,6 +118,7 @@ impl<'a> TryFrom<&'a Element> for Message { None => Default::default(), }; let mut bodies = BTreeMap::new(); + let mut subjects = BTreeMap::new(); let mut payloads = vec!(); for elem in root.children() { if elem.is("body", ns::JABBER_CLIENT) { @@ -126,6 +129,14 @@ impl<'a> TryFrom<&'a Element> for Message { if let Some(_) = bodies.insert(lang, elem.text()) { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } + } else if elem.is("subject", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in subject element.")); + } + let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = subjects.insert(lang, elem.text()) { + return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); + } } else { let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { Some(MessagePayload::StanzaError(stanza_error)) @@ -156,6 +167,7 @@ impl<'a> TryFrom<&'a Element> for Message { id: id, type_: type_, bodies: BTreeMap::new(), + subjects: subjects, payloads: payloads, }) } @@ -183,6 +195,17 @@ impl<'a> Into for &'a Message { .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) .attr("id", self.id.clone()) .attr("type", self.type_.clone()) + .append(self.subjects.iter() + .map(|(lang, subject)| { + Element::builder("subject") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(subject.clone()) + .build() }) + .collect::>()) .append(self.bodies.iter() .map(|(lang, body)| { Element::builder("body") @@ -230,6 +253,7 @@ mod tests { id: None, type_: MessageType::Normal, bodies: BTreeMap::new(), + subjects: BTreeMap::new(), payloads: vec!(), }; let elem2 = (&message).into(); @@ -253,12 +277,23 @@ mod tests { id: None, type_: MessageType::Chat, bodies: bodies, + subjects: BTreeMap::new(), payloads: vec!(), }; let elem2 = (&message).into(); assert_eq!(elem, elem2); } + #[test] + fn test_subject() { + let elem: Element = "Hello world!".parse().unwrap(); + let message = Message::try_from(&elem).unwrap(); + assert_eq!(message.subjects[""], "Hello world!"); + + let elem2 = (&message).into(); + assert_eq!(elem, elem2); + } + #[test] fn test_attention() { let elem: Element = "".parse().unwrap(); From da31e7235718ee7eaf1f9fcb276e9b3ae78bd369 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 7 May 2017 15:06:11 +0100 Subject: [PATCH 0199/1020] message: Fix wrong parsing of the element, and add a test for it. --- src/message.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/message.rs b/src/message.rs index 2c8959b8bb6efcc7d1d85a567849c24a3395b376..3248451c2651a8beb5d4ac9bedae296d4d2885da 100644 --- a/src/message.rs +++ b/src/message.rs @@ -166,7 +166,7 @@ impl<'a> TryFrom<&'a Element> for Message { to: to, id: id, type_: type_, - bodies: BTreeMap::new(), + bodies: bodies, subjects: subjects, payloads: payloads, }) @@ -263,7 +263,11 @@ mod tests { #[test] fn test_body() { let elem: Element = "Hello world!".parse().unwrap(); - Message::try_from(&elem).unwrap(); + let message = Message::try_from(&elem).unwrap(); + assert_eq!(message.bodies[""], "Hello world!"); + + let elem2 = (&message).into(); + assert_eq!(elem, elem2); } #[test] From 2b29748e6b2dfff871b9bb1e14be53fc9493c4e3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 7 May 2017 15:09:18 +0100 Subject: [PATCH 0200/1020] message: Add support for the element. --- src/message.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/message.rs b/src/message.rs index 3248451c2651a8beb5d4ac9bedae296d4d2885da..e7972c3405c423eb30e3a93a7694b70c1340b5d7 100644 --- a/src/message.rs +++ b/src/message.rs @@ -88,6 +88,7 @@ pub enum MessagePayloadType { type Lang = String; type Body = String; type Subject = String; +type Thread = String; #[derive(Debug, Clone)] pub struct Message { @@ -97,6 +98,7 @@ pub struct Message { pub type_: MessageType, pub bodies: BTreeMap, pub subjects: BTreeMap, + pub thread: Option, pub payloads: Vec, } @@ -119,6 +121,7 @@ impl<'a> TryFrom<&'a Element> for Message { }; let mut bodies = BTreeMap::new(); let mut subjects = BTreeMap::new(); + let mut thread = None; let mut payloads = vec!(); for elem in root.children() { if elem.is("body", ns::JABBER_CLIENT) { @@ -137,6 +140,14 @@ impl<'a> TryFrom<&'a Element> for Message { if let Some(_) = subjects.insert(lang, elem.text()) { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); } + } else if elem.is("thread", ns::JABBER_CLIENT) { + if thread.is_some() { + return Err(Error::ParseError("Thread element present twice.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in thread element.")); + } + thread = Some(elem.text()); } else { let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { Some(MessagePayload::StanzaError(stanza_error)) @@ -168,6 +179,7 @@ impl<'a> TryFrom<&'a Element> for Message { type_: type_, bodies: bodies, subjects: subjects, + thread: thread, payloads: payloads, }) } @@ -254,6 +266,7 @@ mod tests { type_: MessageType::Normal, bodies: BTreeMap::new(), subjects: BTreeMap::new(), + thread: None, payloads: vec!(), }; let elem2 = (&message).into(); @@ -282,6 +295,7 @@ mod tests { type_: MessageType::Chat, bodies: bodies, subjects: BTreeMap::new(), + thread: None, payloads: vec!(), }; let elem2 = (&message).into(); From 4ec92b16acff01004a7b6945234c36dc6e1b95e9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 7 May 2017 15:23:06 +0100 Subject: [PATCH 0201/1020] Fix most clippy warnings. --- src/jingle_s5b.rs | 8 ++++---- src/mam.rs | 5 +---- src/message.rs | 4 ++-- src/presence.rs | 2 +- src/stanza_error.rs | 2 +- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index b0d7266f42e16e5ad1c531ef08a7e32307407bfb..4c55d76583f9effa538dba86b11b9b405d90f028 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -182,7 +182,7 @@ impl<'a> TryFrom<&'a Element> for Transport { }); TransportPayload::Candidates(candidates) } else if child.is("activated", ns::JINGLE_S5B) { - if let Some(_) = payload { + if payload.is_some() { return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); } let cid = child.attr("cid") @@ -190,12 +190,12 @@ impl<'a> TryFrom<&'a Element> for Transport { .parse()?; TransportPayload::Activated(cid) } else if child.is("candidate-error", ns::JINGLE_S5B) { - if let Some(_) = payload { + if payload.is_some() { return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element.")); } TransportPayload::CandidateError } else if child.is("candidate-used", ns::JINGLE_S5B) { - if let Some(_) = payload { + if payload.is_some() { return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); } let cid = child.attr("cid") @@ -203,7 +203,7 @@ impl<'a> TryFrom<&'a Element> for Transport { .parse()?; TransportPayload::CandidateUsed(cid) } else if child.is("proxy-error", ns::JINGLE_S5B) { - if let Some(_) = payload { + if payload.is_some() { return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element.")); } TransportPayload::ProxyError diff --git a/src/mam.rs b/src/mam.rs index c831a0993d237fd9441101ad02a6fed5f9c7e8c6..640a3c042ae1dc61b675d3c5bb901b65a77fe5ca 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -217,10 +217,7 @@ impl<'a> Into for &'a Fin { fn into(self) -> Element { let mut elem = Element::builder("fin") .ns(ns::MAM) - .attr("complete", match self.complete { - true => Some("true"), - false => None, - }) + .attr("complete", if self.complete { Some("true") } else { None }) .build(); elem.append_child((&self.set).into()); elem diff --git a/src/message.rs b/src/message.rs index e7972c3405c423eb30e3a93a7694b70c1340b5d7..bb8e8b46abe1f646265cbec0adf9dfc4155cad03 100644 --- a/src/message.rs +++ b/src/message.rs @@ -129,7 +129,7 @@ impl<'a> TryFrom<&'a Element> for Message { return Err(Error::ParseError("Unknown child in body element.")); } let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = bodies.insert(lang, elem.text()) { + if bodies.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } } else if elem.is("subject", ns::JABBER_CLIENT) { @@ -137,7 +137,7 @@ impl<'a> TryFrom<&'a Element> for Message { return Err(Error::ParseError("Unknown child in subject element.")); } let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = subjects.insert(lang, elem.text()) { + if subjects.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); } } else if elem.is("thread", ns::JABBER_CLIENT) { diff --git a/src/presence.rs b/src/presence.rs index cfaf5e71ab90caf5d6acf00fe377f7789f2265f4..f354d3101e4ced006933811e5feefcb12b765d45 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -170,7 +170,7 @@ impl<'a> TryFrom<&'a Element> for Presence { return Err(Error::ParseError("Unknown child in status element.")); } let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = statuses.insert(lang, elem.text()) { + if statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); } } else if elem.is("priority", ns::JABBER_CLIENT) { diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 9a0bfa03dc71eb316580db86cc8f6a7bccb3152e..2f509ee21f6edb84cb1252104cafca57a5f8271f 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -173,7 +173,7 @@ impl<'a> TryFrom<&'a Element> for StanzaError { return Err(Error::ParseError("Unknown element in error text.")); } let lang = child.attr("xml:lang").unwrap_or("").to_owned(); - if let Some(_) = texts.insert(lang, child.text()) { + if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); } } else if child.ns() == Some(ns::XMPP_STANZAS) { From 83d80dd2353f0144da76aea95153103c33f7c8f0 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 14 May 2017 16:38:56 +0200 Subject: [PATCH 0202/1020] Add iterators over attributes, nodes and text nodes. Clean up lots of code. Add as_element and as_text on Node. Add some inline annotations. --- src/element.rs | 268 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 227 insertions(+), 41 deletions(-) diff --git a/src/element.rs b/src/element.rs index 5ac2a1445101083780ec5ebcbd7df04f21585a9f..71bd391129144580ccfe57a642879c4305b45a7e 100644 --- a/src/element.rs +++ b/src/element.rs @@ -3,6 +3,7 @@ use std::io::prelude::*; use std::io::Cursor; use std::collections::BTreeMap; +use std::collections::btree_map; use std::fmt; @@ -19,6 +20,57 @@ use std::slice; use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; +/// A node in an element tree. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Node { + /// An `Element`. + Element(Element), + /// A text node. + Text(String), +} + +impl Node { + /// Turns this into an `Element` if possible, else returns None. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_element().unwrap().name(), "meow"); + /// assert_eq!(txt.as_element(), None); + /// ``` + pub fn as_element<'a>(&'a self) -> Option<&'a Element> { + match *self { + Node::Element(ref e) => Some(e), + Node::Text(_) => None, + } + } + + /// Turns this into a `String` if possible, else returns None. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_text(), None); + /// assert_eq!(txt.as_text().unwrap(), "meow"); + /// ``` + pub fn as_text<'a>(&'a self) -> Option<&'a str> { + match *self { + Node::Element(_) => None, + Node::Text(ref s) => Some(s), + } + } +} + #[derive(Clone, PartialEq, Eq)] /// A struct representing a DOM Element. pub struct Element { @@ -28,7 +80,6 @@ pub struct Element { children: Vec, } - impl<'a> From<&'a Element> for String { fn from(elem: &'a Element) -> String { let mut out = Vec::new(); @@ -55,15 +106,6 @@ impl FromStr for Element { } } -/// A node in an element tree. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Node { - /// An `Element`. - Element(Element), - /// A text node. - Text(String), -} - impl Element { fn new(name: String, namespace: Option, attributes: BTreeMap, children: Vec) -> Element { Element { @@ -78,7 +120,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let elem = Element::builder("name") @@ -103,7 +145,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let bare = Element::bare("name"); @@ -141,6 +183,34 @@ impl Element { None } + /// Returns an iterator over the attributes of this element. + /// + /// # Example + /// + /// ```rust + /// use minidom::Element; + /// + /// let elm: Element = "".parse().unwrap(); + /// + /// let mut iter = elm.attrs(); + /// + /// assert_eq!(iter.next().unwrap(), ("a", "b")); + /// assert_eq!(iter.next(), None); + /// ``` + pub fn attrs<'a>(&'a self) -> Attrs<'a> { + Attrs { + iter: self.attributes.iter(), + } + } + + /// Returns an iterator over the attributes of this element, with the value being a mutable + /// reference. + pub fn attrs_mut<'a>(&'a mut self) -> AttrsMut<'a> { + AttrsMut { + iter: self.attributes.iter_mut(), + } + } + /// Modifies the value of an attribute. pub fn set_attr, V: IntoAttributeValue>(&mut self, name: S, val: V) { let name = name.into(); @@ -160,7 +230,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let elem = Element::builder("name").ns("namespace").build(); @@ -281,14 +351,41 @@ impl Element { Ok(()) } - /// Returns an iterator over references to the children of this element. + /// Returns an iterator over references to every child node of this element. /// /// # Examples /// + /// ```rust + /// use minidom::{Element, Node}; + /// + /// let elem: Element = "abc".parse().unwrap(); + /// + /// let mut iter = elem.nodes(); + /// + /// assert_eq!(iter.next().unwrap().as_text().unwrap(), "a"); + /// assert_eq!(iter.next().unwrap().as_element().unwrap().name(), "c1"); + /// assert_eq!(iter.next().unwrap().as_text().unwrap(), "b"); + /// assert_eq!(iter.next().unwrap().as_element().unwrap().name(), "c2"); + /// assert_eq!(iter.next().unwrap().as_text().unwrap(), "c"); + /// assert_eq!(iter.next(), None); /// ``` + #[inline] pub fn nodes<'a>(&'a self) -> Nodes<'a> { + self.children.iter() + } + + /// Returns an iterator over mutable references to every child node of this element. + #[inline] pub fn nodes_mut<'a>(&'a mut self) -> NodesMut<'a> { + self.children.iter_mut() + } + + /// Returns an iterator over references to every child element of this element. + /// + /// # Examples + /// + /// ```rust /// use minidom::Element; /// - /// let elem: Element = "".parse().unwrap(); + /// let elem: Element = "hellothisisignored".parse().unwrap(); /// /// let mut iter = elem.children(); /// assert_eq!(iter.next().unwrap().name(), "child1"); @@ -296,26 +393,43 @@ impl Element { /// assert_eq!(iter.next().unwrap().name(), "child3"); /// assert_eq!(iter.next(), None); /// ``` - pub fn children<'a>(&'a self) -> Children<'a> { + #[inline] pub fn children<'a>(&'a self) -> Children<'a> { Children { iter: self.children.iter(), } } - /// Returns an iterator over mutable references to the children of this element. - pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { + /// Returns an iterator over mutable references to every child element of this element. + #[inline] pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { ChildrenMut { iter: self.children.iter_mut(), } } - fn propagate_namespaces(&mut self) { - let ns = self.namespace.clone(); - for child in self.children_mut() { - if child.namespace.is_none() { - child.namespace = ns.clone(); - child.propagate_namespaces(); - } + /// Returns an iterator over references to every text node of this element. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Element; + /// + /// let elem: Element = "hello world!".parse().unwrap(); + /// + /// let mut iter = elem.texts(); + /// assert_eq!(iter.next().unwrap(), "hello"); + /// assert_eq!(iter.next().unwrap(), " world!"); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] pub fn texts<'a>(&'a self) -> Texts<'a> { + Texts { + iter: self.children.iter(), + } + } + + /// Returns an iterator over mutable references to every text node of this element. + #[inline] pub fn texts_mut<'a>(&'a mut self) -> TextsMut<'a> { + TextsMut { + iter: self.children.iter_mut(), } } @@ -323,7 +437,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let mut elem = Element::bare("root"); @@ -356,11 +470,21 @@ impl Element { } } + fn propagate_namespaces(&mut self) { + let ns = self.namespace.clone(); + for child in self.children_mut() { + if child.namespace.is_none() { + child.namespace = ns.clone(); + child.propagate_namespaces(); + } + } + } + /// Appends a text node to an `Element`. /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let mut elem = Element::bare("node"); @@ -379,7 +503,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::{Element, Node}; /// /// let mut elem = Element::bare("node"); @@ -396,21 +520,15 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// - /// let elem: Element = "hello, world!".parse().unwrap(); + /// let elem: Element = "hello, world!".parse().unwrap(); /// /// assert_eq!(elem.text(), "hello, world!"); /// ``` pub fn text(&self) -> String { - let mut ret = String::new(); - for fork in &self.children { - if let Node::Text(ref s) = *fork { - ret += s; - } - } - ret + self.texts().fold(String::new(), |ret, new| ret + new) } /// Returns a reference to the first child element with the specific name and namespace, if it @@ -418,7 +536,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let elem: Element = r#""#.parse().unwrap(); @@ -459,7 +577,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let elem: Element = r#""#.parse().unwrap(); @@ -476,7 +594,7 @@ impl Element { } } -/// An iterator over references to children of an `Element`. +/// An iterator over references to child elements of an `Element`. pub struct Children<'a> { iter: slice::Iter<'a, Node>, } @@ -494,7 +612,7 @@ impl<'a> Iterator for Children<'a> { } } -/// An iterator over mutable references to children of an `Element`. +/// An iterator over mutable references to child elements of an `Element`. pub struct ChildrenMut<'a> { iter: slice::IterMut<'a, Node>, } @@ -512,6 +630,74 @@ impl<'a> Iterator for ChildrenMut<'a> { } } +/// An iterator over references to child text nodes of an `Element`. +pub struct Texts<'a> { + iter: slice::Iter<'a, Node>, +} + +impl<'a> Iterator for Texts<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option<&'a str> { + while let Some(item) = self.iter.next() { + if let Node::Text(ref child) = *item { + return Some(child); + } + } + None + } +} + +/// An iterator over mutable references to child text nodes of an `Element`. +pub struct TextsMut<'a> { + iter: slice::IterMut<'a, Node>, +} + +impl<'a> Iterator for TextsMut<'a> { + type Item = &'a mut String; + + fn next(&mut self) -> Option<&'a mut String> { + while let Some(item) = self.iter.next() { + if let Node::Text(ref mut child) = *item { + return Some(child); + } + } + None + } +} + +/// An iterator over references to all child nodes of an `Element`. +pub type Nodes<'a> = slice::Iter<'a, Node>; + +/// An iterator over mutable references to all child nodes of an `Element`. +pub type NodesMut<'a> = slice::IterMut<'a, Node>; + +/// An iterator over the attributes of an `Element`. +pub struct Attrs<'a> { + iter: btree_map::Iter<'a, String, String>, +} + +impl<'a> Iterator for Attrs<'a> { + type Item = (&'a str, &'a str); + + fn next(&mut self) -> Option { + self.iter.next().map(|(x, y)| (x.as_ref(), y.as_ref())) + } +} + +/// An iterator over the attributes of an `Element`, with the values mutable. +pub struct AttrsMut<'a> { + iter: btree_map::IterMut<'a, String, String>, +} + +impl<'a> Iterator for AttrsMut<'a> { + type Item = (&'a str, &'a mut String); + + fn next(&mut self) -> Option { + self.iter.next().map(|(x, y)| (x.as_ref(), y)) + } +} + /// A builder for `Element`s. pub struct ElementBuilder { root: Element, From b22acff15ec8a9eddd5e7a37e65d18992d06610b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:09:29 +0100 Subject: [PATCH 0203/1020] hashes, ecaps2, jingle_ft: Make the algorithm a proper enum. --- src/ecaps2.rs | 113 ++++++++++++++++++++++++----------------------- src/hashes.rs | 59 +++++++++++++++++++++++-- src/jingle_ft.rs | 5 ++- 3 files changed, 116 insertions(+), 61 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 3d7f2c949438b6d0c2f586dcf1f4d6dbaa6b604e..e397a4558dc29d32c80b8abdf645e692bf8d7abb 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use disco::{Feature, Identity, Disco}; use data_forms::DataForm; -use hashes::Hash; +use hashes::{Hash, Algo}; use minidom::Element; use error::Error; @@ -118,49 +118,52 @@ pub fn compute_disco(disco: &Disco) -> Vec { final_string } -// TODO: make algo into an enum. -pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { - match algo { - "sha-256" => { - let mut hasher = Sha256::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) +pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { + Ok(Hash { + algo: algo.clone(), + hash: match algo { + Algo::Sha_256 => { + let mut hasher = Sha256::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Sha_512 => { + let mut hasher = Sha512::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Sha3_256 => { + let mut hasher = Sha3_256::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Sha3_512 => { + let mut hasher = Sha3_512::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Blake2b_256 => { + let mut hasher = Blake2b::default(); + hasher.input(data); + let mut buf: [u8; 32] = [0; 32]; + let hash = hasher.variable_result(&mut buf).unwrap(); + base64::encode(hash) + }, + Algo::Blake2b_512 => { + let mut hasher = Blake2b::default(); + hasher.input(data); + let mut buf: [u8; 64] = [0; 64]; + let hash = hasher.variable_result(&mut buf).unwrap(); + base64::encode(hash) + }, + Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), + Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, - "sha-512" => { - let mut hasher = Sha512::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) - }, - "sha3-256" => { - let mut hasher = Sha3_256::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) - }, - "sha3-512" => { - let mut hasher = Sha3_512::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) - }, - "blake2b-256" => { - let mut hasher = Blake2b::default(); - hasher.input(data); - let mut buf: [u8; 32] = [0; 32]; - let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(hash) - }, - "blake2b-512" => { - let mut hasher = Blake2b::default(); - hasher.input(data); - let mut buf: [u8; 64] = [0; 64]; - let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(hash) - }, - _ => panic!(), - } + }) } #[cfg(test)] @@ -174,9 +177,9 @@ mod tests { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); let ecaps2 = ECaps2::try_from(&elem).unwrap(); assert_eq!(ecaps2.hashes.len(), 2); - assert_eq!(ecaps2.hashes[0].algo, "sha-256"); + assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256); assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); - assert_eq!(ecaps2.hashes[1].algo, "sha3-256"); + assert_eq!(ecaps2.hashes[1].algo, Algo::Sha3_256); assert_eq!(ecaps2.hashes[1].hash, "+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8="); } @@ -262,10 +265,10 @@ mod tests { assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, "sha-256"); - assert_eq!(sha_256, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); - assert_eq!(sha3_256, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); + assert_eq!(sha_256.hash, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); + assert_eq!(sha3_256.hash, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); } #[test] @@ -434,15 +437,15 @@ mod tests { assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, "sha-256"); - assert_eq!(sha_256, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); - assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); + assert_eq!(sha_256.hash, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); + assert_eq!(sha3_256.hash, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); } #[test] fn test_blake2b_512() { - let hash = ecaps2::hash_ecaps2("abc".as_bytes(), "blake2b-512"); + let hash = ecaps2::hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); let known_hash: Vec = vec!( 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, @@ -450,6 +453,6 @@ mod tests { 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, ); let known_hash = base64::encode(&known_hash); - assert_eq!(hash, known_hash); + assert_eq!(hash.hash, known_hash); } } diff --git a/src/hashes.rs b/src/hashes.rs index 6e7bcb2db2c9510b32bc1957e57ffc1666b7672a..403cb0c90eca8989d8ad678a72ddf3b1d0479e28 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -5,16 +5,64 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::convert::TryFrom; +use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use error::Error; use ns; +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, PartialEq)] +pub enum Algo { + Sha_1, + Sha_256, + Sha_512, + Sha3_256, + Sha3_512, + Blake2b_256, + Blake2b_512, + Unknown(String), +} + +impl FromStr for Algo { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "" => return Err(Error::ParseError("'algo' argument can’t be empty.")), + + "sha-1" => Algo::Sha_1, + "sha-256" => Algo::Sha_256, + "sha-512" => Algo::Sha_512, + "sha3-256" => Algo::Sha3_256, + "sha3-512" => Algo::Sha3_512, + "blake2b-256" => Algo::Blake2b_256, + "blake2b-512" => Algo::Blake2b_512, + value => Algo::Unknown(value.to_owned()), + }) + } +} + +impl IntoAttributeValue for Algo { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Algo::Sha_1 => "sha-1", + Algo::Sha_256 => "sha-256", + Algo::Sha_512 => "sha-512", + Algo::Sha3_256 => "sha3-256", + Algo::Sha3_512 => "sha3-512", + Algo::Blake2b_256 => "blake2b-256", + Algo::Blake2b_512 => "blake2b-512", + Algo::Unknown(text) => return Some(text), + })) + } +} + #[derive(Debug, Clone, PartialEq)] pub struct Hash { - pub algo: String, + pub algo: Algo, pub hash: String, } @@ -28,7 +76,10 @@ impl<'a> TryFrom<&'a Element> for Hash { for _ in elem.children() { return Err(Error::ParseError("Unknown child in hash element.")); } - let algo = elem.attr("algo").ok_or(Error::ParseError("Mandatory argument 'algo' not present in hash element."))?.to_owned(); + let algo = match elem.attr("algo") { + None => Err(Error::ParseError("Mandatory argument 'algo' not present in hash element.")), + Some(text) => Algo::from_str(text), + }?; let hash = match elem.text().as_ref() { "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), text => text.to_owned(), @@ -58,7 +109,7 @@ mod tests { fn test_simple() { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); let hash = Hash::try_from(&elem).unwrap(); - assert_eq!(hash.algo, "sha-256"); + assert_eq!(hash.algo, Algo::Sha_256); assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index efbe9c9d8b355a8e3d43b8e3fb3d15c051f0c79d..9dd69df1026826ab7a2dcc685b4b5f311476fcde 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -238,6 +238,7 @@ impl<'a> Into for &'a Description { #[cfg(test)] mod tests { use super::*; + use hashes::Algo; #[test] fn test_description() { @@ -261,7 +262,7 @@ mod tests { assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); - assert_eq!(desc.file.hashes[0].algo, "sha-1"); + assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); } @@ -283,7 +284,7 @@ mod tests { assert_eq!(desc.file.date, None); assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); - assert_eq!(desc.file.hashes[0].algo, "sha-1"); + assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); } } From 36b0bead49d9fe5a180fd61e1ac378d27183677e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:09:30 +0100 Subject: [PATCH 0204/1020] message: Add stanza-id as a direct payload. --- src/message.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/message.rs b/src/message.rs index bb8e8b46abe1f646265cbec0adf9dfc4155cad03..136f558570fb1cde51b653252ad3eac2ddc95cf4 100644 --- a/src/message.rs +++ b/src/message.rs @@ -23,6 +23,7 @@ use delay::Delay; use attention::Attention; use message_correct::Replace; use eme::ExplicitMessageEncryption; +use stanza_id::StanzaId; /// Lists every known payload of a ``. #[derive(Debug, Clone)] @@ -34,6 +35,7 @@ pub enum MessagePayload { Attention(Attention), MessageCorrect(Replace), ExplicitMessageEncryption(ExplicitMessageEncryption), + StanzaId(StanzaId), } #[derive(Debug, Clone, PartialEq)] @@ -163,6 +165,8 @@ impl<'a> TryFrom<&'a Element> for Message { Some(MessagePayload::MessageCorrect(replace)) } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { Some(MessagePayload::ExplicitMessageEncryption(eme)) + } else if let Ok(stanza_id) = StanzaId::try_from(elem) { + Some(MessagePayload::StanzaId(stanza_id)) } else { None }; @@ -195,6 +199,7 @@ impl<'a> Into for &'a MessagePayload { MessagePayload::Delay(ref delay) => delay.into(), MessagePayload::MessageCorrect(ref replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), + MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), } } } From bbdf38d58b6c3367c6e26176cb60937fcab4eaf3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:06:22 +0100 Subject: [PATCH 0205/1020] message: Stop parsing the payloads automatically. --- src/message.rs | 106 ++++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/src/message.rs b/src/message.rs index 136f558570fb1cde51b653252ad3eac2ddc95cf4..f76824ed95cc77539112e21f05a9ecdaea22037f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -38,6 +38,59 @@ pub enum MessagePayload { StanzaId(StanzaId), } +impl<'a> TryFrom<&'a Element> for MessagePayload { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + ("error", ns::JABBER_CLIENT) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), + + // XEP-0085 + ("active", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + ("inactive", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + ("composing", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + ("paused", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + ("gone", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + + // XEP-0184 + ("request", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), + ("received", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), + + // XEP-0203 + ("delay", ns::DELAY) => MessagePayload::Delay(Delay::try_from(elem)?), + + // XEP-0224 + ("attention", ns::ATTENTION) => MessagePayload::Attention(Attention::try_from(elem)?), + + // XEP-0308 + ("replace", ns::MESSAGE_CORRECT) => MessagePayload::MessageCorrect(Replace::try_from(elem)?), + + // XEP-0359 + ("stanza-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), + + // XEP-0380 + ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), + + _ => return Err(Error::ParseError("Unknown message payload.")) + }) + } +} + +impl<'a> Into for &'a MessagePayload { + fn into(self) -> Element { + match *self { + MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), + MessagePayload::Attention(ref attention) => attention.into(), + MessagePayload::ChatState(ref chatstate) => chatstate.into(), + MessagePayload::Receipt(ref receipt) => receipt.into(), + MessagePayload::Delay(ref delay) => delay.into(), + MessagePayload::MessageCorrect(ref replace) => replace.into(), + MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), + MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum MessageType { Chat, @@ -81,12 +134,6 @@ impl IntoAttributeValue for MessageType { } } -#[derive(Debug, Clone)] -pub enum MessagePayloadType { - XML(Element), - Parsed(MessagePayload), -} - type Lang = String; type Body = String; type Subject = String; @@ -101,7 +148,7 @@ pub struct Message { pub bodies: BTreeMap, pub subjects: BTreeMap, pub thread: Option, - pub payloads: Vec, + pub payloads: Vec, } impl<'a> TryFrom<&'a Element> for Message { @@ -151,29 +198,7 @@ impl<'a> TryFrom<&'a Element> for Message { } thread = Some(elem.text()); } else { - let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(MessagePayload::StanzaError(stanza_error)) - } else if let Ok(chatstate) = ChatState::try_from(elem) { - Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = Receipt::try_from(elem) { - Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = Attention::try_from(elem) { - Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = Replace::try_from(elem) { - Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { - Some(MessagePayload::ExplicitMessageEncryption(eme)) - } else if let Ok(stanza_id) = StanzaId::try_from(elem) { - Some(MessagePayload::StanzaId(stanza_id)) - } else { - None - }; - payloads.push(match payload { - Some(payload) => MessagePayloadType::Parsed(payload), - None => MessagePayloadType::XML(elem.clone()), - }); + payloads.push(elem.clone()) } } Ok(Message { @@ -189,21 +214,6 @@ impl<'a> TryFrom<&'a Element> for Message { } } -impl<'a> Into for &'a MessagePayload { - fn into(self) -> Element { - match *self { - MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), - MessagePayload::Attention(ref attention) => attention.into(), - MessagePayload::ChatState(ref chatstate) => chatstate.into(), - MessagePayload::Receipt(ref receipt) => receipt.into(), - MessagePayload::Delay(ref delay) => delay.into(), - MessagePayload::MessageCorrect(ref replace) => replace.into(), - MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), - MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), - } - } -} - impl<'a> Into for &'a Message { fn into(self) -> Element { let mut stanza = Element::builder("message") @@ -236,11 +246,7 @@ impl<'a> Into for &'a Message { .collect::>()) .build(); for child in self.payloads.clone() { - let elem = match child { - MessagePayloadType::XML(elem) => elem, - MessagePayloadType::Parsed(payload) => (&payload).into(), - }; - stanza.append_child(elem); + stanza.append_child(child); } stanza } From f18043231c450c91cda41c37e38dbead8e228410 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:12:45 +0100 Subject: [PATCH 0206/1020] ecaps2: Avoid a useless clone. --- src/ecaps2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index e397a4558dc29d32c80b8abdf645e692bf8d7abb..b69a6449d5efb0eab126dfd5d1e6bcd567a6df42 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -120,7 +120,6 @@ pub fn compute_disco(disco: &Disco) -> Vec { pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { Ok(Hash { - algo: algo.clone(), hash: match algo { Algo::Sha_256 => { let mut hasher = Sha256::default(); @@ -163,6 +162,7 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, + algo: algo, }) } From e52817e5cda7540f0d49e9ca173bb14051858006 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:14:07 +0100 Subject: [PATCH 0207/1020] message: Avoid identical match branches. --- src/message.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/message.rs b/src/message.rs index f76824ed95cc77539112e21f05a9ecdaea22037f..a900502b0b05369fb0d2020bd547f56d00d45146 100644 --- a/src/message.rs +++ b/src/message.rs @@ -46,15 +46,15 @@ impl<'a> TryFrom<&'a Element> for MessagePayload { ("error", ns::JABBER_CLIENT) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), // XEP-0085 - ("active", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), - ("inactive", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), - ("composing", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), - ("paused", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), - ("gone", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), + ("active", ns::CHATSTATES) + | ("inactive", ns::CHATSTATES) + | ("composing", ns::CHATSTATES) + | ("paused", ns::CHATSTATES) + | ("gone", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), // XEP-0184 - ("request", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), - ("received", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), + ("request", ns::RECEIPTS) + | ("received", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), // XEP-0203 ("delay", ns::DELAY) => MessagePayload::Delay(Delay::try_from(elem)?), From fe8dccd5df3effd319260109c35f000c7313c116 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:32:26 +0100 Subject: [PATCH 0208/1020] presence: Remove now-unused enum values. --- src/presence.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index f354d3101e4ced006933811e5feefcb12b765d45..e1212c7442efb60aafff40b5ca632aaa82d0dc5b 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -48,9 +48,6 @@ pub type Priority = i8; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum PresencePayload { - Show(Show), - Status(Status), - Priority(Priority), StanzaError(StanzaError), Delay(Delay), ECaps2(ECaps2), @@ -213,24 +210,6 @@ impl<'a> TryFrom<&'a Element> for Presence { impl<'a> Into for &'a PresencePayload { fn into(self) -> Element { match *self { - PresencePayload::Show(ref show) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(show.to_owned()) - .build() - }, - PresencePayload::Status(ref status) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(status.to_owned()) - .build() - }, - PresencePayload::Priority(ref priority) => { - Element::builder("status") - .ns(ns::JABBER_CLIENT) - .append(format!("{}", priority)) - .build() - }, PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), PresencePayload::Delay(ref delay) => delay.into(), PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), From 0ad6893d5238b4d81a2f7bf49bcbd4e1beba8563 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:32:44 +0100 Subject: [PATCH 0209/1020] presence: Stop parsing payloads automatically. --- src/presence.rs | 92 ++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index e1212c7442efb60aafff40b5ca632aaa82d0dc5b..67ca5fa026818a47dc262029332393bf2e9e33f3 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -53,6 +53,34 @@ pub enum PresencePayload { ECaps2(ECaps2), } +impl<'a> TryFrom<&'a Element> for PresencePayload { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + ("error", ns::JABBER_CLIENT) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), + + // XEP-0203 + ("delay", ns::DELAY) => PresencePayload::Delay(Delay::try_from(elem)?), + + // XEP-0390 + ("c", ns::ECAPS2) => PresencePayload::ECaps2(ECaps2::try_from(elem)?), + + _ => return Err(Error::ParseError("Unknown presence payload.")) + }) + } +} + +impl<'a> Into for &'a PresencePayload { + fn into(self) -> Element { + match *self { + PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), + PresencePayload::Delay(ref delay) => delay.into(), + PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum PresenceType { /// This value is not an acceptable 'type' attribute, it is only used @@ -107,12 +135,6 @@ impl IntoAttributeValue for PresenceType { } } -#[derive(Debug, Clone)] -pub enum PresencePayloadType { - XML(Element), - Parsed(PresencePayload), -} - #[derive(Debug, Clone)] pub struct Presence { pub from: Option, @@ -122,7 +144,7 @@ pub struct Presence { pub show: Option, pub statuses: BTreeMap, pub priority: Priority, - pub payloads: Vec, + pub payloads: Vec, } impl<'a> TryFrom<&'a Element> for Presence { @@ -179,19 +201,7 @@ impl<'a> TryFrom<&'a Element> for Presence { } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { - let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(PresencePayload::StanzaError(stanza_error)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(PresencePayload::Delay(delay)) - } else if let Ok(ecaps2) = ECaps2::try_from(elem) { - Some(PresencePayload::ECaps2(ecaps2)) - } else { - None - }; - payloads.push(match payload { - Some(payload) => PresencePayloadType::Parsed(payload), - None => PresencePayloadType::XML(elem.clone()), - }); + payloads.push(elem.clone()); } } Ok(Presence { @@ -207,16 +217,6 @@ impl<'a> TryFrom<&'a Element> for Presence { } } -impl<'a> Into for &'a PresencePayload { - fn into(self) -> Element { - match *self { - PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), - PresencePayload::Delay(ref delay) => delay.into(), - PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), - } - } -} - impl<'a> Into for &'a Presence { fn into(self) -> Element { let mut stanza = Element::builder("presence") @@ -225,13 +225,20 @@ impl<'a> Into for &'a Presence { .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) .attr("id", self.id.clone()) .attr("type", self.type_.clone()) + .append(self.show.clone()) + .append(self.statuses.iter().map(|(lang, status)| { + Element::builder("status") + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(status.clone()) + .build() + }).collect::>()) + .append(if self.priority == 0 { None } else { Some(format!("{}", self.priority)) }) .build(); for child in self.payloads.clone() { - let elem = match child { - PresencePayloadType::XML(elem) => elem, - PresencePayloadType::Parsed(payload) => (&payload).into(), - }; - stanza.append_child(elem); + stanza.append_child(child); } stanza } @@ -241,7 +248,6 @@ impl<'a> Into for &'a Presence { mod tests { use std::collections::BTreeMap; use super::*; - use ns; #[test] fn test_simple() { @@ -364,11 +370,8 @@ mod tests { fn test_unknown_child() { let elem: Element = "".parse().unwrap(); let presence = Presence::try_from(&elem).unwrap(); - if let PresencePayloadType::XML(ref payload) = presence.payloads[0] { - assert!(payload.is("test", "invalid")); - } else { - panic!("Did successfully parse an invalid element."); - } + let payload = &presence.payloads[0]; + assert!(payload.is("test", "invalid")); } #[test] @@ -398,16 +401,17 @@ mod tests { #[test] fn test_serialise_status() { let status = Status::from("Hello world!"); - let payloads = vec!(PresencePayloadType::Parsed(PresencePayload::Status(status))); + let mut statuses = BTreeMap::new(); + statuses.insert(String::from(""), status); let presence = Presence { from: None, to: None, id: None, type_: PresenceType::Unavailable, show: None, - statuses: BTreeMap::new(), + statuses: statuses, priority: 0i8, - payloads: payloads, + payloads: vec!(), }; let elem: Element = (&presence).into(); assert!(elem.is("presence", ns::JABBER_CLIENT)); From 44071830108df7e691eea16004278016a0441452 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:50:08 +0100 Subject: [PATCH 0210/1020] iq: Move IqPayload parsing into its own Into implementation. --- src/iq.rs | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 4f61260d8184481c836fbb5165c7683ba5d41437..1bf3ba14659a9d5b178f44110fc82b585905c26a 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -31,6 +31,30 @@ pub enum IqPayload { Ping(Ping), } +impl<'a> TryFrom<&'a Element> for IqPayload { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + // XEP-0030 + ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?), + + // XEP-0047 + ("open", ns::IBB) + | ("data", ns::IBB) + | ("close", ns::IBB) => IqPayload::IBB(IBB::try_from(elem)?), + + // XEP-0166 + ("jingle", ns::JINGLE) => IqPayload::Jingle(Jingle::try_from(elem)?), + + // XEP-0199 + ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?), + + _ => return Err(Error::ParseError("Unknown iq payload.")) + }) + } +} + #[derive(Debug, Clone)] pub enum IqPayloadType { XML(Element), @@ -98,22 +122,11 @@ impl<'a> TryFrom<&'a Element> for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - let parsed_payload = if let Ok(disco) = Disco::try_from(elem) { - Some(IqPayload::Disco(disco)) - } else if let Ok(ibb) = IBB::try_from(elem) { - Some(IqPayload::IBB(ibb)) - } else if let Ok(jingle) = Jingle::try_from(elem) { - Some(IqPayload::Jingle(jingle)) - } else if let Ok(ping) = Ping::try_from(elem) { - Some(IqPayload::Ping(ping)) - } else { - None - }; - - payload = match parsed_payload { - Some(payload) => Some(IqPayloadType::Parsed(payload)), - None => Some(IqPayloadType::XML(elem.clone())), - }; + payload = match IqPayload::try_from(elem) { + Ok(payload) => Some(IqPayloadType::Parsed(payload)), + // TODO: fix the API to avoid having to swallow the error here. + Err(_) => Some(IqPayloadType::XML(elem.clone())), + } } } From 65469db1e3b8835c483f3a1bf849c36e09eb38ee Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 00:04:42 +0100 Subject: [PATCH 0211/1020] iq: Stop parsing the payload automatically. --- src/iq.rs | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 1bf3ba14659a9d5b178f44110fc82b585905c26a..13efbaac0dd870bdec4e6c72580bd6d92af9ee44 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -55,17 +55,11 @@ impl<'a> TryFrom<&'a Element> for IqPayload { } } -#[derive(Debug, Clone)] -pub enum IqPayloadType { - XML(Element), - Parsed(IqPayload), -} - #[derive(Debug, Clone)] pub enum IqType { - Get(IqPayloadType), - Set(IqPayloadType), - Result(Option), + Get(Element), + Set(Element), + Result(Option), Error(StanzaError), } @@ -122,11 +116,7 @@ impl<'a> TryFrom<&'a Element> for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - payload = match IqPayload::try_from(elem) { - Ok(payload) => Some(IqPayloadType::Parsed(payload)), - // TODO: fix the API to avoid having to swallow the error here. - Err(_) => Some(IqPayloadType::XML(elem.clone())), - } + payload = Some(elem); } } @@ -188,13 +178,10 @@ impl<'a> Into for &'a Iq { .attr("type", self.payload.clone()) .build(); let elem = match self.payload.clone() { - IqType::Get(IqPayloadType::XML(elem)) - | IqType::Set(IqPayloadType::XML(elem)) - | IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Get(elem) + | IqType::Set(elem) + | IqType::Result(Some(elem)) => elem, IqType::Error(error) => (&error).into(), - IqType::Get(IqPayloadType::Parsed(payload)) - | IqType::Set(IqPayloadType::Parsed(payload)) - | IqType::Result(Some(IqPayloadType::Parsed(payload))) => (&payload).into(), IqType::Result(None) => return stanza, }; stanza.append_child(elem); @@ -229,7 +216,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Get(IqPayloadType::XML(element)) => element == query, + IqType::Get(element) => element == query, _ => false }); } @@ -245,7 +232,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Set(IqPayloadType::XML(element)) => element == vcard, + IqType::Set(element) => element == vcard, _ => false }); } @@ -274,7 +261,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Result(Some(IqPayloadType::XML(element))) => element == query, + IqType::Result(Some(element)) => element == query, _ => false, }); } @@ -331,8 +318,12 @@ mod tests { fn test_disco() { let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(&elem).unwrap(); - assert!(match iq.payload { - IqType::Get(IqPayloadType::Parsed(IqPayload::Disco(Disco { .. }))) => true, + let payload = match iq.payload { + IqType::Get(ref payload) => IqPayload::try_from(payload).unwrap(), + _ => panic!(), + }; + assert!(match payload { + IqPayload::Disco(Disco { .. }) => true, _ => false, }); } From 6df3c4a679b5a939c6eae740bde4533d57859b14 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 00:07:51 +0100 Subject: [PATCH 0212/1020] iq: Remove a bunch of clones. --- src/iq.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 13efbaac0dd870bdec4e6c72580bd6d92af9ee44..f040b1bde2218da1872a21da6ac040c19f6aecde 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -116,31 +116,31 @@ impl<'a> TryFrom<&'a Element> for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - payload = Some(elem); + payload = Some(elem.clone()); } } let type_ = if type_ == "get" { - if let Some(payload) = payload.clone() { - IqType::Get(payload.clone()) + if let Some(payload) = payload { + IqType::Get(payload) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else if type_ == "set" { - if let Some(payload) = payload.clone() { - IqType::Set(payload.clone()) + if let Some(payload) = payload { + IqType::Set(payload) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else if type_ == "result" { - if let Some(payload) = payload.clone() { - IqType::Result(Some(payload.clone())) + if let Some(payload) = payload { + IqType::Result(Some(payload)) } else { IqType::Result(None) } } else if type_ == "error" { if let Some(payload) = error_payload.clone() { - IqType::Error(payload.clone()) + IqType::Error(payload) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); } From dcb7ac8db21548be2a08d2327fb32e02e23006b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 00:10:23 +0100 Subject: [PATCH 0213/1020] iq: Remove a panic! --- src/iq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iq.rs b/src/iq.rs index f040b1bde2218da1872a21da6ac040c19f6aecde..b62168983f465922b1898dec935c84c70817c0ba 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -145,7 +145,7 @@ impl<'a> TryFrom<&'a Element> for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { - panic!() + return Err(Error::ParseError("Unknown iq type.")); }; Ok(Iq { From 2b8bcebfd95338fbee37fd2df018095e24b55e73 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 02:09:23 +0100 Subject: [PATCH 0214/1020] message, iq, presence: Return an Unknown instead of an error when the element is unknown. --- src/iq.rs | 6 +++++- src/message.rs | 6 +++++- src/presence.rs | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index b62168983f465922b1898dec935c84c70817c0ba..436247505a9ca1310a23c8a8af4b65a6cd9cd1e5 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -29,6 +29,8 @@ pub enum IqPayload { IBB(IBB), Jingle(Jingle), Ping(Ping), + + Unknown(Element), } impl<'a> TryFrom<&'a Element> for IqPayload { @@ -50,7 +52,7 @@ impl<'a> TryFrom<&'a Element> for IqPayload { // XEP-0199 ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?), - _ => return Err(Error::ParseError("Unknown iq payload.")) + _ => IqPayload::Unknown(elem.clone()), }) } } @@ -164,6 +166,8 @@ impl<'a> Into for &'a IqPayload { IqPayload::IBB(ref ibb) => ibb.into(), IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(ref ping) => ping.into(), + + IqPayload::Unknown(ref elem) => elem.clone(), } } } diff --git a/src/message.rs b/src/message.rs index a900502b0b05369fb0d2020bd547f56d00d45146..96153cfabf72e3a30fecd54ab3004127bac99d61 100644 --- a/src/message.rs +++ b/src/message.rs @@ -36,6 +36,8 @@ pub enum MessagePayload { MessageCorrect(Replace), ExplicitMessageEncryption(ExplicitMessageEncryption), StanzaId(StanzaId), + + Unknown(Element), } impl<'a> TryFrom<&'a Element> for MessagePayload { @@ -71,7 +73,7 @@ impl<'a> TryFrom<&'a Element> for MessagePayload { // XEP-0380 ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), - _ => return Err(Error::ParseError("Unknown message payload.")) + _ => MessagePayload::Unknown(elem.clone()), }) } } @@ -87,6 +89,8 @@ impl<'a> Into for &'a MessagePayload { MessagePayload::MessageCorrect(ref replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), + + MessagePayload::Unknown(ref elem) => elem.clone(), } } } diff --git a/src/presence.rs b/src/presence.rs index 67ca5fa026818a47dc262029332393bf2e9e33f3..7eb53b411b4b2efe63e0bebd2b839f6266b228fc 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -51,6 +51,8 @@ pub enum PresencePayload { StanzaError(StanzaError), Delay(Delay), ECaps2(ECaps2), + + Unknown(Element), } impl<'a> TryFrom<&'a Element> for PresencePayload { @@ -66,7 +68,7 @@ impl<'a> TryFrom<&'a Element> for PresencePayload { // XEP-0390 ("c", ns::ECAPS2) => PresencePayload::ECaps2(ECaps2::try_from(elem)?), - _ => return Err(Error::ParseError("Unknown presence payload.")) + _ => PresencePayload::Unknown(elem.clone()), }) } } @@ -77,6 +79,8 @@ impl<'a> Into for &'a PresencePayload { PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), PresencePayload::Delay(ref delay) => delay.into(), PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), + + PresencePayload::Unknown(ref elem) => elem.clone(), } } } From 967d4af8430f0c3be6ad5a5ec240c073a9d54320 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 02:58:18 +0100 Subject: [PATCH 0215/1020] rsm: Rename First id to make it more explicit. --- src/rsm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rsm.rs b/src/rsm.rs index 4caab08096b9bc75be4ffb687c931c0d2f82e8a6..6a16d38559c6140c89d0e6a297fe536ec0a3a1fe 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -15,7 +15,7 @@ use ns; #[derive(Debug, Clone)] pub struct First { pub index: Option, - pub base: String, + pub id: String, } #[derive(Debug, Clone)] @@ -68,7 +68,7 @@ impl<'a> TryFrom<&'a Element> for Set { Some(index) => Some(index.parse()?), None => None, }, - base: child.text(), + id: child.text(), }); } else if child.is("index", ns::RSM) { if index.is_some() { @@ -123,7 +123,7 @@ impl<'a> Into for &'a Set { Some(index) => Some(format!("{}", index)), None => None, }) - .append(first.base.clone()).build()); + .append(first.id.clone()).build()); } if self.index.is_some() { elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", self.index.unwrap())).build()); From d680c31cf929052e19c3d9eeea3ab09b9c44a19c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 19 May 2017 02:58:35 +0100 Subject: [PATCH 0216/1020] iq, message: Wire up MAM to the payloads. --- src/iq.rs | 12 ++++++++++++ src/message.rs | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 436247505a9ca1310a23c8a8af4b65a6cd9cd1e5..d2d4a6899d0848feadcb6194ad104ffa77a2f756 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -21,6 +21,7 @@ use disco::Disco; use ibb::IBB; use jingle::Jingle; use ping::Ping; +use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; /// Lists every known payload of a ``. #[derive(Debug, Clone)] @@ -29,6 +30,9 @@ pub enum IqPayload { IBB(IBB), Jingle(Jingle), Ping(Ping), + MamQuery(MamQuery), + MamFin(MamFin), + MamPrefs(MamPrefs), Unknown(Element), } @@ -52,6 +56,11 @@ impl<'a> TryFrom<&'a Element> for IqPayload { // XEP-0199 ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?), + // XEP-0313 + ("query", ns::MAM) => IqPayload::MamQuery(MamQuery::try_from(elem)?), + ("fin", ns::MAM) => IqPayload::MamFin(MamFin::try_from(elem)?), + ("prefs", ns::MAM) => IqPayload::MamPrefs(MamPrefs::try_from(elem)?), + _ => IqPayload::Unknown(elem.clone()), }) } @@ -166,6 +175,9 @@ impl<'a> Into for &'a IqPayload { IqPayload::IBB(ref ibb) => ibb.into(), IqPayload::Jingle(ref jingle) => jingle.into(), IqPayload::Ping(ref ping) => ping.into(), + IqPayload::MamQuery(ref query) => query.into(), + IqPayload::MamFin(ref fin) => fin.into(), + IqPayload::MamPrefs(ref prefs) => prefs.into(), IqPayload::Unknown(ref elem) => elem.clone(), } diff --git a/src/message.rs b/src/message.rs index 96153cfabf72e3a30fecd54ab3004127bac99d61..dae600e8d3a5c0dd1013c8878838da4c7634193b 100644 --- a/src/message.rs +++ b/src/message.rs @@ -24,6 +24,7 @@ use attention::Attention; use message_correct::Replace; use eme::ExplicitMessageEncryption; use stanza_id::StanzaId; +use mam::Result_ as MamResult; /// Lists every known payload of a ``. #[derive(Debug, Clone)] @@ -36,6 +37,7 @@ pub enum MessagePayload { MessageCorrect(Replace), ExplicitMessageEncryption(ExplicitMessageEncryption), StanzaId(StanzaId), + MamResult(MamResult), Unknown(Element), } @@ -67,6 +69,9 @@ impl<'a> TryFrom<&'a Element> for MessagePayload { // XEP-0308 ("replace", ns::MESSAGE_CORRECT) => MessagePayload::MessageCorrect(Replace::try_from(elem)?), + // XEP-0313 + ("result", ns::MAM) => MessagePayload::MamResult(MamResult::try_from(elem)?), + // XEP-0359 ("stanza-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), @@ -89,6 +94,7 @@ impl<'a> Into for &'a MessagePayload { MessagePayload::MessageCorrect(ref replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), + MessagePayload::MamResult(ref result) => result.into(), MessagePayload::Unknown(ref elem) => elem.clone(), } From cb17ca24c111ec7aa8ade07f41a005b97a455472 Mon Sep 17 00:00:00 2001 From: lumi Date: Fri, 19 May 2017 13:12:46 +0200 Subject: [PATCH 0217/1020] bump version to 0.3.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 582eb5ae57effe9344463c069da1ec8a04656a1b..2ad76a9cec0c030b2eba80c5a0b308e7086d8b98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.3.0" +version = "0.3.1" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] description = "A small, simple DOM implementation on top of xml-rs." homepage = "https://gitlab.com/lumi/minidom-rs" From 61839042bd6a1759cc81a93efafbcb70df4d6ade Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 15:30:22 +0100 Subject: [PATCH 0218/1020] rsm: Split First into two options, and generate Set earlier during parsing. --- src/rsm.rs | 83 ++++++++++++++++++++++-------------------------------- 1 file changed, 34 insertions(+), 49 deletions(-) diff --git a/src/rsm.rs b/src/rsm.rs index 6a16d38559c6140c89d0e6a297fe536ec0a3a1fe..a6aec04de438fd4733401960502986d418f83016 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -12,18 +12,13 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct First { - pub index: Option, - pub id: String, -} - #[derive(Debug, Clone)] pub struct Set { pub after: Option, pub before: Option, pub count: Option, - pub first: Option, + pub first: Option, + pub first_index: Option, pub index: Option, pub last: Option, pub max: Option, @@ -36,68 +31,61 @@ impl<'a> TryFrom<&'a Element> for Set { if !elem.is("set", ns::RSM) { return Err(Error::ParseError("This is not a RSM element.")); } - let mut after = None; - let mut before = None; - let mut count = None; - let mut first = None; - let mut index = None; - let mut last = None; - let mut max = None; + let mut set = Set { + after: None, + before: None, + count: None, + first: None, + first_index: None, + index: None, + last: None, + max: None, + }; for child in elem.children() { if child.is("after", ns::RSM) { - if after.is_some() { + if set.after.is_some() { return Err(Error::ParseError("Set can’t have more than one after.")); } - after = Some(child.text()); + set.after = Some(child.text()); } else if child.is("before", ns::RSM) { - if before.is_some() { + if set.before.is_some() { return Err(Error::ParseError("Set can’t have more than one before.")); } - before = Some(child.text()); + set.before = Some(child.text()); } else if child.is("count", ns::RSM) { - if count.is_some() { + if set.count.is_some() { return Err(Error::ParseError("Set can’t have more than one count.")); } - count = Some(child.text().parse()?); + set.count = Some(child.text().parse()?); } else if child.is("first", ns::RSM) { - if first.is_some() { + if set.first.is_some() { return Err(Error::ParseError("Set can’t have more than one first.")); } - first = Some(First { - index: match child.attr("index") { - Some(index) => Some(index.parse()?), - None => None, - }, - id: child.text(), - }); + set.first_index = match child.attr("index") { + Some(index) => Some(index.parse()?), + None => None, + }; + set.first = Some(child.text()); } else if child.is("index", ns::RSM) { - if index.is_some() { + if set.index.is_some() { return Err(Error::ParseError("Set can’t have more than one index.")); } - index = Some(child.text().parse()?); + set.index = Some(child.text().parse()?); } else if child.is("last", ns::RSM) { - if last.is_some() { + if set.last.is_some() { return Err(Error::ParseError("Set can’t have more than one last.")); } - last = Some(child.text()); + set.last = Some(child.text()); } else if child.is("max", ns::RSM) { - if max.is_some() { + if set.max.is_some() { return Err(Error::ParseError("Set can’t have more than one max.")); } - max = Some(child.text().parse()?); + set.max = Some(child.text().parse()?); } else { return Err(Error::ParseError("Unknown child in set element.")); } } - Ok(Set { - after: after, - before: before, - count: count, - first: first, - index: index, - last: last, - max: max, - }) + Ok(set) } } @@ -116,14 +104,10 @@ impl<'a> Into for &'a Set { elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", self.count.unwrap())).build()); } if self.first.is_some() { - let first = self.first.clone().unwrap(); elem.append_child(Element::builder("first") .ns(ns::RSM) - .attr("index", match first.index { - Some(index) => Some(format!("{}", index)), - None => None, - }) - .append(first.id.clone()).build()); + .attr("index", self.first_index.map(|index| format!("{}", index))) + .append(self.first.clone()).build()); } if self.index.is_some() { elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", self.index.unwrap())).build()); @@ -188,6 +172,7 @@ mod tests { before: None, count: None, first: None, + first_index: None, index: None, last: None, max: None, From 16899f8c23834df950f8777c5b921309ff16db3b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 15:41:16 +0100 Subject: [PATCH 0219/1020] rsm: Add a test for . --- src/rsm.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/rsm.rs b/src/rsm.rs index a6aec04de438fd4733401960502986d418f83016..7e139feb5788e03ecc08427f400b1a707e1c1f2f 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -180,4 +180,25 @@ mod tests { let elem2 = (&rsm).into(); assert_eq!(elem, elem2); } + + #[test] + fn test_first_index() { + let elem: Element = "coucou".parse().unwrap(); + let set = Set::try_from(&elem).unwrap(); + assert_eq!(set.first, Some(String::from("coucou"))); + assert_eq!(set.first_index, Some(4)); + + let set2 = Set { + after: None, + before: None, + count: None, + first: Some(String::from("coucou")), + first_index: Some(4), + index: None, + last: None, + max: None, + }; + let elem2 = (&set2).into(); + assert_eq!(elem, elem2); + } } From 3c083709cbd1bb2f6854ffd2190322b9643390b7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 16:03:17 +0100 Subject: [PATCH 0220/1020] rsm, ibb: Write and use a macro to parse attributes. --- src/ibb.rs | 33 ++++++++++++--------------------- src/lib.rs | 21 +++++++++++++++++++++ src/rsm.rs | 5 +---- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index 0156cfee764230ed366373184c996b23bb319f7c..4d11ec11be37d592a39dcfc4187227f924ba0aec 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -65,12 +65,6 @@ pub enum IBB { }, } -fn required_attr(elem: &Element, attr: &str, err: Error) -> Result { - elem.attr(attr) - .and_then(|value| value.parse().ok()) - .ok_or(err) -} - impl<'a> TryFrom<&'a Element> for IBB { type Error = Error; @@ -79,12 +73,9 @@ impl<'a> TryFrom<&'a Element> for IBB { for _ in elem.children() { return Err(Error::ParseError("Unknown child in open element.")); } - let block_size = required_attr(elem, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; - let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; - let stanza = match elem.attr("stanza") { - Some(stanza) => stanza.parse()?, - None => Default::default(), - }; + let block_size = get_attr!(elem, "block-size", required, block_size, block_size.parse()?); + let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); + let stanza = get_attr!(elem, "stanza", default, stanza, stanza.parse()?); Ok(IBB::Open { block_size: block_size, sid: sid, @@ -94,8 +85,8 @@ impl<'a> TryFrom<&'a Element> for IBB { for _ in elem.children() { return Err(Error::ParseError("Unknown child in data element.")); } - let seq = required_attr(elem, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; - let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; + let seq = get_attr!(elem, "seq", required, seq, seq.parse()?); + let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); let data = base64::decode(&elem.text())?; Ok(IBB::Data { seq: seq, @@ -103,10 +94,10 @@ impl<'a> TryFrom<&'a Element> for IBB { data: data }) } else if elem.is("close", ns::IBB) { - let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; for _ in elem.children() { return Err(Error::ParseError("Unknown child in close element.")); } + let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); Ok(IBB::Close { sid: sid, }) @@ -148,6 +139,7 @@ impl<'a> Into for &'a IBB { #[cfg(test)] mod tests { use super::*; + use std::error::Error as StdError; #[test] fn test_simple() { @@ -191,24 +183,23 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Required attribute 'block-size' missing in open element."); + assert_eq!(message, "Required attribute 'block-size' missing."); - // TODO: maybe make a better error message here. let elem: Element = "".parse().unwrap(); let error = IBB::try_from(&elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + Error::ParseIntError(error) => error, _ => panic!(), }; - assert_eq!(message, "Required attribute 'block-size' missing in open element."); + assert_eq!(message.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); let error = IBB::try_from(&elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + Error::ParseError(error) => error, _ => panic!(), }; - assert_eq!(message, "Required attribute 'sid' missing in open element."); + assert_eq!(message, "Required attribute 'sid' missing."); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 7faf06c6c8f0e672d8a9715668d1ffe96e0ba0bf..248902521a9f92d0029458261a8c93fde03e6228 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,27 @@ extern crate sha2; extern crate sha3; extern crate blake2; +macro_rules! get_attr { + ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => Some($func), + None => None, + } + ); + ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => $func, + None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), + } + ); + ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => $func, + None => Default::default(), + } + ); +} + /// Error type returned by every parser on failure. pub mod error; /// XML namespace definitions used through XMPP. diff --git a/src/rsm.rs b/src/rsm.rs index 7e139feb5788e03ecc08427f400b1a707e1c1f2f..99f18930de9d6603808d60a0b9d065d0190f4336 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -61,10 +61,7 @@ impl<'a> TryFrom<&'a Element> for Set { if set.first.is_some() { return Err(Error::ParseError("Set can’t have more than one first.")); } - set.first_index = match child.attr("index") { - Some(index) => Some(index.parse()?), - None => None, - }; + set.first_index = get_attr!(child, "index", optional, index, index.parse()?); set.first = Some(child.text()); } else if child.is("index", ns::RSM) { if set.index.is_some() { From a4f50f2d43d0e9a2168b0d6bcb2b8eb51d84e525 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 16:08:25 +0100 Subject: [PATCH 0221/1020] jingle_ibb, ibb, rsm: Simplify attribute parsing. --- src/ibb.rs | 12 ++++++------ src/jingle_ibb.rs | 36 ++++++++++++++++-------------------- src/lib.rs | 3 +++ src/rsm.rs | 2 +- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index 4d11ec11be37d592a39dcfc4187227f924ba0aec..ffdb907e15fcabdd4ffba3410c270d217c4c5db6 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -73,9 +73,9 @@ impl<'a> TryFrom<&'a Element> for IBB { for _ in elem.children() { return Err(Error::ParseError("Unknown child in open element.")); } - let block_size = get_attr!(elem, "block-size", required, block_size, block_size.parse()?); - let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); - let stanza = get_attr!(elem, "stanza", default, stanza, stanza.parse()?); + let block_size = get_attr!(elem, "block-size", required); + let sid = get_attr!(elem, "sid", required); + let stanza = get_attr!(elem, "stanza", default); Ok(IBB::Open { block_size: block_size, sid: sid, @@ -85,8 +85,8 @@ impl<'a> TryFrom<&'a Element> for IBB { for _ in elem.children() { return Err(Error::ParseError("Unknown child in data element.")); } - let seq = get_attr!(elem, "seq", required, seq, seq.parse()?); - let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); + let seq = get_attr!(elem, "seq", required); + let sid = get_attr!(elem, "sid", required); let data = base64::decode(&elem.text())?; Ok(IBB::Data { seq: seq, @@ -97,7 +97,7 @@ impl<'a> TryFrom<&'a Element> for IBB { for _ in elem.children() { return Err(Error::ParseError("Unknown child in close element.")); } - let sid = get_attr!(elem, "sid", required, sid, sid.parse()?); + let sid = get_attr!(elem, "sid", required); Ok(IBB::Close { sid: sid, }) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 4ac847cc70d3d8534a07defe16e8cfb80e207207..73544c26666b6eb2efbff481b42c24a741ca0411 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::convert::TryFrom; -use std::str::FromStr; use minidom::Element; @@ -22,15 +21,6 @@ pub struct Transport { pub stanza: Stanza, } -fn optional_attr(root: &Element, attr: &str) -> Option { - root.attr(attr) - .and_then(|value| value.parse().ok()) -} - -fn required_attr(root: &Element, attr: &str, err: Error) -> Result { - optional_attr(root, attr).ok_or(err) -} - impl<'a> TryFrom<&'a Element> for Transport { type Error = Error; @@ -39,11 +29,9 @@ impl<'a> TryFrom<&'a Element> for Transport { for _ in elem.children() { return Err(Error::ParseError("Unknown child in JingleIBB element.")); } - let block_size = required_attr(elem, "block-size", Error::ParseError("Required attribute 'block-size' missing in JingleIBB element."))?; - let sid = required_attr(elem, "sid", Error::ParseError("Required attribute 'sid' missing in JingleIBB element."))?; - let stanza = elem.attr("stanza") - .unwrap_or("iq") - .parse()?; + let block_size = get_attr!(elem, "block-size", required); + let sid = get_attr!(elem, "sid", required); + let stanza = get_attr!(elem, "stanza", default); Ok(Transport { block_size: block_size, sid: sid, @@ -69,6 +57,7 @@ impl<'a> Into for &'a Transport { #[cfg(test)] mod tests { use super::*; + use std::error::Error as StdError; #[test] fn test_simple() { @@ -87,16 +76,23 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element."); + assert_eq!(message, "Required attribute 'block-size' missing."); + + let elem: Element = "".parse().unwrap(); + let error = Transport::try_from(&elem).unwrap_err(); + let message = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(message.description(), "number too large to fit in target type"); - // TODO: maybe make a better error message here. let elem: Element = "".parse().unwrap(); let error = Transport::try_from(&elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + Error::ParseIntError(error) => error, _ => panic!(), }; - assert_eq!(message, "Required attribute 'block-size' missing in JingleIBB element."); + assert_eq!(message.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); let error = Transport::try_from(&elem).unwrap_err(); @@ -104,7 +100,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Required attribute 'sid' missing in JingleIBB element."); + assert_eq!(message, "Required attribute 'sid' missing."); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 248902521a9f92d0029458261a8c93fde03e6228..db9b95aa1dc0b6d89caeb1dc1927b73ed4472196 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,9 @@ extern crate sha3; extern crate blake2; macro_rules! get_attr { + ($elem:ident, $attr:tt, $type:tt) => ( + get_attr!($elem, $attr, $type, value, value.parse()?) + ); ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( match $elem.attr($attr) { Some($value) => Some($func), diff --git a/src/rsm.rs b/src/rsm.rs index 99f18930de9d6603808d60a0b9d065d0190f4336..a697b3705f3fe6a19fa345b4acfc0101a6bb6ae1 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -61,7 +61,7 @@ impl<'a> TryFrom<&'a Element> for Set { if set.first.is_some() { return Err(Error::ParseError("Set can’t have more than one first.")); } - set.first_index = get_attr!(child, "index", optional, index, index.parse()?); + set.first_index = get_attr!(child, "index", optional); set.first = Some(child.text()); } else if child.is("index", ns::RSM) { if set.index.is_some() { From 814a3bd882114ac8490e94c9d5be3958e19a931d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 16:41:29 +0100 Subject: [PATCH 0222/1020] data_forms: Add support for required, options, title and instructions. --- src/data_forms.rs | 138 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 107 insertions(+), 31 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index a83ebb824b608dc507989048630f8bd4d06e2a9a..bb443c9baf7ffec886f9a8f8413dbab349b03a3a 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -14,11 +14,19 @@ use ns; use media_element::MediaElement; +#[derive(Debug, Clone)] +pub struct Option_ { + pub label: Option, + pub value: String, +} + #[derive(Debug, Clone)] pub struct Field { pub var: String, pub type_: String, // TODO: use an enum here. pub label: Option, + pub required: bool, + pub options: Vec, pub values: Vec, pub media: Vec, } @@ -50,6 +58,8 @@ impl FromStr for DataFormType { pub struct DataForm { pub type_: DataFormType, pub form_type: Option, + pub title: Option, + pub instructions: Option, pub fields: Vec, } @@ -60,53 +70,119 @@ impl<'a> TryFrom<&'a Element> for DataForm { if !elem.is("x", ns::DATA_FORMS) { return Err(Error::ParseError("This is not a data form element.")); } - - let type_: DataFormType = match elem.attr("type") { - Some(type_) => type_.parse()?, - None => return Err(Error::ParseError("Type attribute on data form is mandatory.")), + let type_ = get_attr!(elem, "type", required); + let mut form = DataForm { + type_: type_, + form_type: None, + title: None, + instructions: None, + fields: vec!(), }; - let mut fields = vec!(); - let mut form_type = None; - for field in elem.children() { - if field.is("field", ns::DATA_FORMS) { - let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?; - let field_type = field.attr("type").unwrap_or("text-single"); - let label = field.attr("label").and_then(|label| label.parse().ok()); - let mut values = vec!(); - let mut media = vec!(); - for element in field.children() { + for child in elem.children() { + if child.is("title", ns::DATA_FORMS) { + if form.title.is_some() { + return Err(Error::ParseError("More than one title in form element.")); + } + for _ in child.children() { + return Err(Error::ParseError("Title element must not have any child.")); + } + for _ in child.attrs() { + return Err(Error::ParseError("Title element must not have any attribute.")); + } + form.title = Some(child.text()); + } else if child.is("instructions", ns::DATA_FORMS) { + if form.instructions.is_some() { + return Err(Error::ParseError("More than one instructions in form element.")); + } + for _ in child.children() { + return Err(Error::ParseError("instructions element must not have any child.")); + } + for _ in child.attrs() { + return Err(Error::ParseError("instructions element must not have any attribute.")); + } + form.instructions = Some(child.text()); + } else if child.is("field", ns::DATA_FORMS) { + let var: String = get_attr!(child, "var", required); + // TODO: use Default instead. + let field_type: String = get_attr!(child, "type", optional).unwrap_or(String::from("text-single")); + let label = get_attr!(child, "label", optional); + + let is_form_type = var == "FORM_TYPE" && field_type == "hidden"; + let is_list = field_type == "list-single" || field_type == "list-multi"; + let mut field = Field { + var: var, + type_: field_type, + label: label, + required: false, + options: vec!(), + values: vec!(), + media: vec!(), + }; + for element in child.children() { if element.is("value", ns::DATA_FORMS) { - values.push(element.text()); + for _ in element.children() { + return Err(Error::ParseError("Value element must not have any child.")); + } + for _ in element.attrs() { + return Err(Error::ParseError("Value element must not have any attribute.")); + } + field.values.push(element.text()); + } else if element.is("required", ns::DATA_FORMS) { + if field.required { + return Err(Error::ParseError("More than one required element.")); + } + for _ in element.children() { + return Err(Error::ParseError("Required element must not have any child.")); + } + for _ in element.attrs() { + return Err(Error::ParseError("Required element must not have any attribute.")); + } + field.required = true; + } else if element.is("option", ns::DATA_FORMS) { + if !is_list { + return Err(Error::ParseError("Option element found in non-list field.")); + } + let label = get_attr!(element, "label", optional); + let mut value = None; + for child2 in element.children() { + if child2.is("value", ns::DATA_FORMS) { + if value.is_some() { + return Err(Error::ParseError("More than one value element in option element")); + } + value = Some(child2.text()); + } else { + return Err(Error::ParseError("Non-value element in option element")); + } + } + let value = value.ok_or(Error::ParseError("No value element in option element"))?; + field.options.push(Option_ { + label: label, + value: value, + }); } else if element.is("media", ns::MEDIA_ELEMENT) { match MediaElement::try_from(element) { - Ok(media_element) => media.push(media_element), + Ok(media_element) => field.media.push(media_element), Err(_) => (), // TODO: is it really nice to swallow this error? } } else { return Err(Error::ParseError("Field child isn’t a value or media element.")); } } - if var == "FORM_TYPE" && field_type == "hidden" { - if form_type != None { + if is_form_type { + if form.form_type.is_some() { return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); } - if values.len() != 1 { + if field.values.len() != 1 { return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); } - form_type = Some(values[0].clone()); + form.form_type = Some(field.values[0].clone()); } - fields.push(Field { - var: var.to_owned(), - type_: field_type.to_owned(), - label: label, - values: values, - media: media, - }); + form.fields.push(field); } else { - return Err(Error::ParseError("Unknown field type in data form.")); + return Err(Error::ParseError("Unknown child in data form element.")); } } - Ok(DataForm { type_: type_, form_type: form_type, fields: fields }) + Ok(form) } } @@ -131,7 +207,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Type attribute on data form is mandatory."); + assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); let error = DataForm::try_from(&elem).unwrap_err(); @@ -150,6 +226,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown field type in data form."); + assert_eq!(message, "Unknown child in data form element."); } } From 80695edb7257c8d1ff75568b43c3285075c8018b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 16:44:35 +0100 Subject: [PATCH 0223/1020] chatstates, ping, presence: Check for extraneous attributes. --- Cargo.toml | 2 +- src/chatstates.rs | 4 +++- src/ping.rs | 4 +++- src/presence.rs | 17 +++++++++++++---- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 85e089c9c0e3a6c605c68ba018efda29001f7204..59cafb26c08b2a036504c8a183518a03f52cf535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.3.0" +minidom = "0.3.1" jid = "0.2.0" base64 = "0.5.0" digest = "0.5.0" diff --git a/src/chatstates.rs b/src/chatstates.rs index 9ed2b10c1a2b804e2ce402ae5955fa247a6c3822..f9fdb4f3d26e47dfd853a18ff4f22a1fd9b22e59 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -28,6 +28,9 @@ impl<'a> TryFrom<&'a Element> for ChatState { for _ in elem.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in chatstate element.")); + } if elem.is("active", ns::CHATSTATES) { Ok(ChatState::Active) } else if elem.is("composing", ns::CHATSTATES) { @@ -90,7 +93,6 @@ mod tests { } #[test] - #[ignore] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); let error = ChatState::try_from(&elem).unwrap_err(); diff --git a/src/ping.rs b/src/ping.rs index 4fe2fd86792ffa9f877ccedb8a7eca6d68dadc53..baebf68b133a9f4ebc07bdf769ece76cd1f360d8 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -26,6 +26,9 @@ impl<'a> TryFrom<&'a Element> for Ping { for _ in elem.children() { return Err(Error::ParseError("Unknown child in ping element.")); } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in ping element.")); + } Ok(Ping) } } @@ -60,7 +63,6 @@ mod tests { } #[test] - #[ignore] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); let error = Ping::try_from(&elem).unwrap_err(); diff --git a/src/presence.rs b/src/presence.rs index 7eb53b411b4b2efe63e0bebd2b839f6266b228fc..d780d0044af379d02dbb9863e1be61a8214a54e5 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -180,6 +180,9 @@ impl<'a> TryFrom<&'a Element> for Presence { for _ in elem.children() { return Err(Error::ParseError("Unknown child in show element.")); } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in show element.")); + } show = Some(match elem.text().as_ref() { "away" => Show::Away, "chat" => Show::Chat, @@ -192,6 +195,11 @@ impl<'a> TryFrom<&'a Element> for Presence { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } + for (attr, _) in elem.attrs() { + if attr != "xml:lang" { + return Err(Error::ParseError("Unknown attribute in status element.")); + } + } let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); if statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); @@ -203,6 +211,9 @@ impl<'a> TryFrom<&'a Element> for Presence { for _ in elem.children() { return Err(Error::ParseError("Unknown child in priority element.")); } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in priority element.")); + } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { payloads.push(elem.clone()); @@ -379,9 +390,8 @@ mod tests { } #[test] - #[ignore] fn test_invalid_status_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -391,9 +401,8 @@ mod tests { } #[test] - #[ignore] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let error = Presence::try_from(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, From 7cdb46b90a7b2e31790f301e62a082b1bb308296 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 17:08:05 +0100 Subject: [PATCH 0224/1020] data_forms: Replace field_type String with a proper enum. --- src/data_forms.rs | 50 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index bb443c9baf7ffec886f9a8f8413dbab349b03a3a..6d4e3bc41fa450cf1c833af52e6345dc79bfc18f 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -14,6 +14,47 @@ use ns; use media_element::MediaElement; +#[derive(Debug, Clone, PartialEq)] +pub enum FieldType { + Boolean, + Fixed, + Hidden, + JidMulti, + JidSingle, + ListMulti, + ListSingle, + TextMulti, + TextPrivate, + TextSingle, +} + +impl Default for FieldType { + fn default() -> FieldType { + FieldType::TextSingle + } +} + +impl FromStr for FieldType { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "boolean" => FieldType::Boolean, + "fixed" => FieldType::Fixed, + "hidden" => FieldType::Hidden, + "jid-multi" => FieldType::JidMulti, + "jid-single" => FieldType::JidSingle, + "list-multi" => FieldType::ListMulti, + "list-single" => FieldType::ListSingle, + "text-multi" => FieldType::TextMulti, + "text-private" => FieldType::TextPrivate, + "text-single" => FieldType::TextSingle, + + _ => return Err(Error::ParseError("Invalid 'type' attribute in field element.")), + }) + } +} + #[derive(Debug, Clone)] pub struct Option_ { pub label: Option, @@ -23,7 +64,7 @@ pub struct Option_ { #[derive(Debug, Clone)] pub struct Field { pub var: String, - pub type_: String, // TODO: use an enum here. + pub type_: FieldType, pub label: Option, pub required: bool, pub options: Vec, @@ -103,12 +144,11 @@ impl<'a> TryFrom<&'a Element> for DataForm { form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { let var: String = get_attr!(child, "var", required); - // TODO: use Default instead. - let field_type: String = get_attr!(child, "type", optional).unwrap_or(String::from("text-single")); + let field_type = get_attr!(child, "type", default); let label = get_attr!(child, "label", optional); - let is_form_type = var == "FORM_TYPE" && field_type == "hidden"; - let is_list = field_type == "list-single" || field_type == "list-multi"; + let is_form_type = var == "FORM_TYPE" && field_type == FieldType::Hidden; + let is_list = field_type == FieldType::ListSingle || field_type == FieldType::ListMulti; let mut field = Field { var: var, type_: field_type, From 545ce292824fc89b5d217cce4490add277189f45 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 20:15:39 +0100 Subject: [PATCH 0225/1020] message: Add forgotten origin-id payload. --- src/message.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/message.rs b/src/message.rs index dae600e8d3a5c0dd1013c8878838da4c7634193b..23e6686ef26990007867952b70fcef839b1066ff 100644 --- a/src/message.rs +++ b/src/message.rs @@ -73,7 +73,8 @@ impl<'a> TryFrom<&'a Element> for MessagePayload { ("result", ns::MAM) => MessagePayload::MamResult(MamResult::try_from(elem)?), // XEP-0359 - ("stanza-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), + ("stanza-id", ns::SID) + | ("origin-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), // XEP-0380 ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), From dc530a1912f191ca43874f747b9702ea5d482d1f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 20:22:48 +0100 Subject: [PATCH 0226/1020] Add an idle parser. --- src/idle.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ 3 files changed, 91 insertions(+) create mode 100644 src/idle.rs diff --git a/src/idle.rs b/src/idle.rs new file mode 100644 index 0000000000000000000000000000000000000000..5261788e1c10abcee5417873753d87471a82184c --- /dev/null +++ b/src/idle.rs @@ -0,0 +1,85 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; + +use minidom::Element; + +use error::Error; + +use ns; + +type Date = String; + +#[derive(Debug, Clone)] +pub struct Idle { + pub since: Date, +} + +impl<'a> TryFrom<&'a Element> for Idle { + type Error = Error; + + fn try_from(elem: &'a Element) -> Result { + if !elem.is("idle", ns::IDLE) { + return Err(Error::ParseError("This is not an idle element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in idle element.")); + } + let since = get_attr!(elem, "since", required); + Ok(Idle { since: since }) + } +} + +impl<'a> Into for &'a Idle { + fn into(self) -> Element { + Element::builder("idle") + .ns(ns::IDLE) + .attr("since", self.since.clone()) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + Idle::try_from(&elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in idle element."); + } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'since' missing."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let idle = Idle { since: Date::from("2017-05-21T20:19:55+01:00") }; + let elem2 = (&idle).into(); + assert_eq!(elem, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index db9b95aa1dc0b6d89caeb1dc1927b73ed4472196..2eb2aecfeff51af28115c326204354775c4efa2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,9 @@ pub mod message_correct; /// XEP-0313: Message Archive Management pub mod mam; +/// XEP-0319: Last User Interaction in Presence +pub mod idle; + /// XEP-0359: Unique and Stable Stanza IDs pub mod stanza_id; diff --git a/src/ns.rs b/src/ns.rs index c98bff5839c560fba551fad366bc942358e8f797..b51c6530ed89f05f19132e761ab4626cc6fd246c 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -77,6 +77,9 @@ pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; /// XEP-0313: Message Archive Management pub const MAM: &'static str = "urn:xmpp:mam:2"; +/// XEP-0319: Last User Interaction in Presence +pub const IDLE: &'static str = "urn:xmpp:idle:1"; + /// XEP-0359: Unique and Stable Stanza IDs pub const SID: &'static str = "urn:xmpp:sid:0"; From 34ee6d52dc1eda6937f6dea7f5738926f258e83b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 20:30:42 +0100 Subject: [PATCH 0227/1020] presence: Wire up idle as a payload. --- src/presence.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index d780d0044af379d02dbb9863e1be61a8214a54e5..fd6547b16d0836ccd0adf95d212500af28109800 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -18,6 +18,7 @@ use ns; use stanza_error::StanzaError; use delay::Delay; +use idle::Idle; use ecaps2::ECaps2; #[derive(Debug, Clone, PartialEq)] @@ -50,6 +51,7 @@ pub type Priority = i8; pub enum PresencePayload { StanzaError(StanzaError), Delay(Delay), + Idle(Idle), ECaps2(ECaps2), Unknown(Element), @@ -65,6 +67,9 @@ impl<'a> TryFrom<&'a Element> for PresencePayload { // XEP-0203 ("delay", ns::DELAY) => PresencePayload::Delay(Delay::try_from(elem)?), + // XEP-0319 + ("idle", ns::IDLE) => PresencePayload::Idle(Idle::try_from(elem)?), + // XEP-0390 ("c", ns::ECAPS2) => PresencePayload::ECaps2(ECaps2::try_from(elem)?), @@ -78,6 +83,7 @@ impl<'a> Into for &'a PresencePayload { match *self { PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), PresencePayload::Delay(ref delay) => delay.into(), + PresencePayload::Idle(ref idle) => idle.into(), PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), PresencePayload::Unknown(ref elem) => elem.clone(), From 185bd79c72f7e6c442a2cf1891c1e485d54c1d51 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 20:45:50 +0100 Subject: [PATCH 0228/1020] mam: Simplify attribute management, and make default mandatory. --- src/mam.rs | 81 ++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index 640a3c042ae1dc61b675d3c5bb901b65a77fe5ca..b673154bd20685a25c160669ce026546ac8c5074 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -5,8 +5,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::convert::TryFrom; +use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; use error::Error; @@ -45,9 +46,33 @@ pub enum DefaultPrefs { Roster, } +impl FromStr for DefaultPrefs { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "always" => DefaultPrefs::Always, + "never" => DefaultPrefs::Never, + "roster" => DefaultPrefs::Roster, + + _ => return Err(Error::ParseError("Invalid 'default' attribute.")), + }) + } +} + +impl<'a> IntoAttributeValue for &'a DefaultPrefs { + fn into_attribute_value(self) -> Option { + Some(String::from(match *self { + DefaultPrefs::Always => "always", + DefaultPrefs::Never => "never", + DefaultPrefs::Roster => "roster", + })) + } +} + #[derive(Debug, Clone)] pub struct Prefs { - pub default_: Option, + pub default_: DefaultPrefs, pub always: Vec, pub never: Vec, } @@ -70,14 +95,8 @@ impl<'a> TryFrom<&'a Element> for Query { return Err(Error::ParseError("Unknown child in query element.")); } } - let queryid = match elem.attr("queryid") { - Some(queryid) => Some(queryid.to_owned()), - None => None, - }; - let node = match elem.attr("node") { - Some(node) => Some(node.to_owned()), - None => None, - }; + let queryid = get_attr!(elem, "queryid", optional); + let node = get_attr!(elem, "node", optional); Ok(Query { queryid, node, form, set }) } } @@ -97,18 +116,9 @@ impl<'a> TryFrom<&'a Element> for Result_ { return Err(Error::ParseError("Unknown child in result element.")); } } - let queryid = match elem.attr("queryid") { - Some(queryid) => queryid.to_owned(), - None => return Err(Error::ParseError("No 'queryid' attribute present in result.")), - }; - let id = match elem.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in result.")), - }; - if forwarded.is_none() { - return Err(Error::ParseError("Mandatory forwarded element missing in result.")); - } - let forwarded = forwarded.unwrap(); + let forwarded = forwarded.ok_or(Error::ParseError("Mandatory forwarded element missing in result."))?; + let queryid = get_attr!(elem, "queryid", required); + let id = get_attr!(elem, "id", required); Ok(Result_ { queryid, id, @@ -132,14 +142,13 @@ impl<'a> TryFrom<&'a Element> for Fin { return Err(Error::ParseError("Unknown child in fin element.")); } } + let set = set.ok_or(Error::ParseError("Mandatory set element missing in fin."))?; let complete = match elem.attr("complete") { - Some(complete) => complete == "true", + Some(complete) if complete == "true" => true, + Some(complete) if complete == "false" => false, None => false, + Some(_) => return Err(Error::ParseError("Invalid value for 'complete' attribute.")), }; - if set.is_none() { - return Err(Error::ParseError("Mandatory set element missing in fin.")); - } - let set = set.unwrap(); Ok(Fin { complete, set }) } } @@ -172,14 +181,7 @@ impl<'a> TryFrom<&'a Element> for Prefs { return Err(Error::ParseError("Unknown child in prefs element.")); } } - let default_ = match elem.attr("default") { - Some("always") => Some(DefaultPrefs::Always), - Some("never") => Some(DefaultPrefs::Never), - Some("roster") => Some(DefaultPrefs::Roster), - None => None, - - _ => return Err(Error::ParseError("Invalid 'default' attribute present in prefs.")), - }; + let default_ = get_attr!(elem, "default", required); Ok(Prefs { default_, always, never }) } } @@ -228,12 +230,7 @@ impl<'a> Into for &'a Prefs { fn into(self) -> Element { let mut elem = Element::builder("prefs") .ns(ns::MAM) - .attr("default", match self.default_ { - Some(DefaultPrefs::Always) => Some("always"), - Some(DefaultPrefs::Never) => Some("never"), - Some(DefaultPrefs::Roster) => Some("roster"), - None => None, - }) + .attr("default", &self.default_) .build(); if !self.always.is_empty() { let mut always = Element::builder("always") @@ -340,7 +337,7 @@ mod tests { #[test] fn test_prefs_get() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); Prefs::try_from(&elem).unwrap(); let elem: Element = r#" From 8c53d6e415fe420aef4375eef765ceb56774329a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 20:56:04 +0100 Subject: [PATCH 0229/1020] stanza_id: Use the new get_attr! macro to get attributes. --- src/stanza_id.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/stanza_id.rs b/src/stanza_id.rs index d478b1aae17af0f50f5bceab5febaf3e98689657..2b87890bf338daf2f1f1595614b4d5a6981b6be7 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -35,15 +35,9 @@ impl<'a> TryFrom<&'a Element> for StanzaId { for _ in elem.children() { return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); } - let id = match elem.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in stanza-id or origin-id.")), - }; + let id = get_attr!(elem, "id", required); Ok(if is_stanza_id { - let by = match elem.attr("by") { - Some(by) => by.parse().unwrap(), - None => return Err(Error::ParseError("No 'by' attribute present in stanza-id.")), - }; + let by = get_attr!(elem, "by", required); StanzaId::StanzaId { id, by } } else { StanzaId::OriginId { id } @@ -115,7 +109,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "No 'id' attribute present in stanza-id or origin-id."); + assert_eq!(message, "Required attribute 'id' missing."); } #[test] @@ -126,7 +120,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "No 'by' attribute present in stanza-id."); + assert_eq!(message, "Required attribute 'by' missing."); } #[test] From ca9a250efd3926921d6e5c7f00e379bf581a0529 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 21:00:34 +0100 Subject: [PATCH 0230/1020] jingle_s5b: Obtain attributes using the get_attr! macro. --- src/jingle_s5b.rs | 46 +++++++++++----------------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 4c55d76583f9effa538dba86b11b9b405d90f028..cb43e743c9fc0d60a37870fe5d2e4d747208ccff 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -134,15 +134,9 @@ impl<'a> TryFrom<&'a Element> for Transport { fn try_from(elem: &'a Element) -> Result { if elem.is("transport", ns::JINGLE_S5B) { - let sid = elem.attr("sid") - .ok_or(Error::ParseError("Required attribute 'sid' missing in JingleS5B transport element."))? - .parse()?; - let dstaddr = elem.attr("dstaddr") - .and_then(|value| Some(value.to_owned())); - let mode = match elem.attr("mode") { - None => Default::default(), - Some(mode) => mode.parse()?, - }; + let sid = get_attr!(elem, "sid", required); + let dstaddr = get_attr!(elem, "dstaddr", optional); + let mode = get_attr!(elem, "mode", default); let mut payload = None; for child in elem.children() { @@ -152,26 +146,12 @@ impl<'a> TryFrom<&'a Element> for Transport { Some(_) => return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")), None => vec!(), }; - let cid = child.attr("cid") - .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B candidate element."))? - .parse()?; - let host = child.attr("host") - .ok_or(Error::ParseError("Required attribute 'host' missing in JingleS5B candidate element."))? - .parse()?; - let jid = child.attr("jid") - .ok_or(Error::ParseError("Required attribute 'jid' missing in JingleS5B candidate element."))? - .parse()?; - let port = match child.attr("port") { - Some(s) => Some(s.parse()?), - None => None, - }; - let priority = child.attr("priority") - .ok_or(Error::ParseError("Required attribute 'priority' missing in JingleS5B candidate element."))? - .parse()?; - let type_ = match child.attr("type") { - Some(s) => s.parse()?, - None => Default::default(), - }; + let cid = get_attr!(child, "cid", required); + let host = get_attr!(child, "host", required); + let jid = get_attr!(child, "jid", required); + let port = get_attr!(child, "port", optional); + let priority = get_attr!(child, "priority", required); + let type_ = get_attr!(child, "type", default); candidates.push(Candidate { cid: cid, host: host, @@ -185,9 +165,7 @@ impl<'a> TryFrom<&'a Element> for Transport { if payload.is_some() { return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); } - let cid = child.attr("cid") - .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B activated element."))? - .parse()?; + let cid = get_attr!(child, "cid", required); TransportPayload::Activated(cid) } else if child.is("candidate-error", ns::JINGLE_S5B) { if payload.is_some() { @@ -198,9 +176,7 @@ impl<'a> TryFrom<&'a Element> for Transport { if payload.is_some() { return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); } - let cid = child.attr("cid") - .ok_or(Error::ParseError("Required attribute 'cid' missing in JingleS5B candidate-used element."))? - .parse()?; + let cid = get_attr!(child, "cid", required); TransportPayload::CandidateUsed(cid) } else if child.is("proxy-error", ns::JINGLE_S5B) { if payload.is_some() { From e7e4284a0dfa59ea649a4c4a6eb31d9070d13aba Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 21:02:06 +0100 Subject: [PATCH 0231/1020] jingle_ft: Obtain attributes using the get_attr! macro. --- src/jingle_ft.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 9dd69df1026826ab7a2dcc685b4b5f311476fcde..454ce8d4a54d1c026e1fcb356515624638d522fc 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -138,11 +138,8 @@ impl<'a> TryFrom<&'a Element> for Description { if range.is_some() { return Err(Error::ParseError("File must not have more than one range.")); } - let offset = file_payload.attr("offset").unwrap_or("0").parse()?; - let length = match file_payload.attr("length") { - Some(length) => Some(length.parse()?), - None => None, - }; + let offset = get_attr!(file_payload, "offset", default); + let length = get_attr!(file_payload, "length", optional); let mut range_hashes = vec!(); for hash_element in file_payload.children() { if !hash_element.is("hash", ns::HASHES) { From f4e5f5380ec6dc8e3c54bfc6ed131691ebe7528d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 21 May 2017 21:07:37 +0100 Subject: [PATCH 0232/1020] implement IntoElements and IntoAttributeValue for &String --- src/convert.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/convert.rs b/src/convert.rs index 83c187713fd98177a8a998a563e7ad928464a8ec..f3f2ae0975088e9ae7aecb2a3f5e03aca0f0be08 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -70,6 +70,12 @@ impl IntoElements for String { } } +impl<'a> IntoElements for &'a String { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_text_node(self.to_owned()); + } +} + impl<'a> IntoElements for &'a str { fn into_elements(self, emitter: &mut ElementEmitter) { emitter.append_text_node(self.to_owned()); @@ -88,6 +94,12 @@ impl IntoAttributeValue for String { } } +impl<'a> IntoAttributeValue for &'a String { + fn into_attribute_value(self) -> Option { + Some(self.to_owned()) + } +} + impl<'a> IntoAttributeValue for &'a str { fn into_attribute_value(self) -> Option { Some((*self).to_owned()) From f6ad64c910bf8364d5008e44bdf8ff04c0253371 Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 22 May 2017 16:09:04 +0200 Subject: [PATCH 0233/1020] now at version 0.3.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2ad76a9cec0c030b2eba80c5a0b308e7086d8b98..66d9055ce7ac588e9c0eb2228a9a1d6464238bf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.3.1" +version = "0.3.2" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] description = "A small, simple DOM implementation on top of xml-rs." homepage = "https://gitlab.com/lumi/minidom-rs" From 49d25968cbf45b5e936508e6b38863e77c24f71a Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 22 May 2017 16:15:04 +0200 Subject: [PATCH 0234/1020] no need to clone that --- src/convert.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/convert.rs b/src/convert.rs index f3f2ae0975088e9ae7aecb2a3f5e03aca0f0be08..fb6fbfb03159c8964dc559a9844619fb986fc6ac 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -90,7 +90,7 @@ pub trait IntoAttributeValue { impl IntoAttributeValue for String { fn into_attribute_value(self) -> Option { - Some(self.clone()) + Some(self) } } From 3223fc11fe048d3a47edb6f4e6c809b7900e2556 Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 22 May 2017 16:15:26 +0200 Subject: [PATCH 0235/1020] this will already get auto-dereferenced --- src/convert.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/convert.rs b/src/convert.rs index fb6fbfb03159c8964dc559a9844619fb986fc6ac..ebca8e2a0629070ceb0ca1c239ca21d861637014 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -102,7 +102,7 @@ impl<'a> IntoAttributeValue for &'a String { impl<'a> IntoAttributeValue for &'a str { fn into_attribute_value(self) -> Option { - Some((*self).to_owned()) + Some(self.to_owned()) } } From dbcbe7cd9b0a30816b8e9c37a122c708a82b8574 Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 22 May 2017 18:45:43 +0200 Subject: [PATCH 0236/1020] make clippy happy --- src/convert.rs | 5 ++--- src/element.rs | 36 +++++++++++++++++++----------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/convert.rs b/src/convert.rs index ebca8e2a0629070ceb0ca1c239ca21d861637014..3e3ed4162c0e3e8a3676fdf26720fa9938bb17f4 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -45,9 +45,8 @@ impl<'a, T: IntoElements + Clone> IntoElements for &'a [T] { impl IntoElements for Option { fn into_elements(self, emitter: &mut ElementEmitter) { - match self { - Some(e) => e.into_elements(emitter), - None => (), + if let Some(e) = self { + e.into_elements(emitter); } } } diff --git a/src/element.rs b/src/element.rs index 71bd391129144580ccfe57a642879c4305b45a7e..31e0a16ff5c05e3f2a664368ed98ca1d3771f040 100644 --- a/src/element.rs +++ b/src/element.rs @@ -43,7 +43,7 @@ impl Node { /// assert_eq!(elm.as_element().unwrap().name(), "meow"); /// assert_eq!(txt.as_element(), None); /// ``` - pub fn as_element<'a>(&'a self) -> Option<&'a Element> { + pub fn as_element(&self) -> Option<&Element> { match *self { Node::Element(ref e) => Some(e), Node::Text(_) => None, @@ -63,7 +63,7 @@ impl Node { /// assert_eq!(elm.as_text(), None); /// assert_eq!(txt.as_text().unwrap(), "meow"); /// ``` - pub fn as_text<'a>(&'a self) -> Option<&'a str> { + pub fn as_text(&self) -> Option<&str> { match *self { Node::Element(_) => None, Node::Text(ref s) => Some(s), @@ -178,7 +178,7 @@ impl Element { /// Returns a reference to the value of the given attribute, if it exists, else `None`. pub fn attr(&self, name: &str) -> Option<&str> { if let Some(value) = self.attributes.get(name) { - return Some(&value) + return Some(value) } None } @@ -197,7 +197,7 @@ impl Element { /// assert_eq!(iter.next().unwrap(), ("a", "b")); /// assert_eq!(iter.next(), None); /// ``` - pub fn attrs<'a>(&'a self) -> Attrs<'a> { + pub fn attrs(&self) -> Attrs { Attrs { iter: self.attributes.iter(), } @@ -205,7 +205,7 @@ impl Element { /// Returns an iterator over the attributes of this element, with the value being a mutable /// reference. - pub fn attrs_mut<'a>(&'a mut self) -> AttrsMut<'a> { + pub fn attrs_mut(&mut self) -> AttrsMut { AttrsMut { iter: self.attributes.iter_mut(), } @@ -279,6 +279,7 @@ impl Element { } } + #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] fn from_reader_inner(&mut self, reader: &mut EventReader) -> Result<(), Error> { loop { let e = reader.next()?; @@ -307,10 +308,7 @@ impl Element { // TODO: may want to check whether we're closing the correct element return Ok(()); }, - ReaderEvent::Characters(s) => { - self.append_text_node(s); - }, - ReaderEvent::CData(s) => { + ReaderEvent::Characters(s) | ReaderEvent::CData(s) => { self.append_text_node(s); }, ReaderEvent::EndDocument => { @@ -324,7 +322,7 @@ impl Element { /// Output a document to an `EventWriter`. pub fn write_to(&self, writer: &mut EventWriter) -> Result<(), Error> { let name = if let Some(ref ns) = self.namespace { - Name::qualified(&self.name, &ns, None) + Name::qualified(&self.name, ns, None) } else { Name::local(&self.name) @@ -334,7 +332,7 @@ impl Element { start = start.default_ns(ns.clone()); } for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently - start = start.attr(Name::local(&attr.0), &attr.1); + start = start.attr(Name::local(attr.0), attr.1); } writer.write(start)?; for child in &self.children { @@ -369,12 +367,12 @@ impl Element { /// assert_eq!(iter.next().unwrap().as_text().unwrap(), "c"); /// assert_eq!(iter.next(), None); /// ``` - #[inline] pub fn nodes<'a>(&'a self) -> Nodes<'a> { + #[inline] pub fn nodes(&self) -> Nodes { self.children.iter() } /// Returns an iterator over mutable references to every child node of this element. - #[inline] pub fn nodes_mut<'a>(&'a mut self) -> NodesMut<'a> { + #[inline] pub fn nodes_mut(&mut self) -> NodesMut { self.children.iter_mut() } @@ -393,14 +391,14 @@ impl Element { /// assert_eq!(iter.next().unwrap().name(), "child3"); /// assert_eq!(iter.next(), None); /// ``` - #[inline] pub fn children<'a>(&'a self) -> Children<'a> { + #[inline] pub fn children(&self) -> Children { Children { iter: self.children.iter(), } } /// Returns an iterator over mutable references to every child element of this element. - #[inline] pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { + #[inline] pub fn children_mut(&mut self) -> ChildrenMut { ChildrenMut { iter: self.children.iter_mut(), } @@ -420,14 +418,14 @@ impl Element { /// assert_eq!(iter.next().unwrap(), " world!"); /// assert_eq!(iter.next(), None); /// ``` - #[inline] pub fn texts<'a>(&'a self) -> Texts<'a> { + #[inline] pub fn texts(&self) -> Texts { Texts { iter: self.children.iter(), } } /// Returns an iterator over mutable references to every text node of this element. - #[inline] pub fn texts_mut<'a>(&'a mut self) -> TextsMut<'a> { + #[inline] pub fn texts_mut(&mut self) -> TextsMut { TextsMut { iter: self.children.iter_mut(), } @@ -602,6 +600,7 @@ pub struct Children<'a> { impl<'a> Iterator for Children<'a> { type Item = &'a Element; + #[cfg_attr(feature = "cargo-clippy", allow(while_let_on_iterator))] fn next(&mut self) -> Option<&'a Element> { while let Some(item) = self.iter.next() { if let Node::Element(ref child) = *item { @@ -620,6 +619,7 @@ pub struct ChildrenMut<'a> { impl<'a> Iterator for ChildrenMut<'a> { type Item = &'a mut Element; + #[cfg_attr(feature = "cargo-clippy", allow(while_let_on_iterator))] fn next(&mut self) -> Option<&'a mut Element> { while let Some(item) = self.iter.next() { if let Node::Element(ref mut child) = *item { @@ -638,6 +638,7 @@ pub struct Texts<'a> { impl<'a> Iterator for Texts<'a> { type Item = &'a str; + #[cfg_attr(feature = "cargo-clippy", allow(while_let_on_iterator))] fn next(&mut self) -> Option<&'a str> { while let Some(item) = self.iter.next() { if let Node::Text(ref child) = *item { @@ -656,6 +657,7 @@ pub struct TextsMut<'a> { impl<'a> Iterator for TextsMut<'a> { type Item = &'a mut String; + #[cfg_attr(feature = "cargo-clippy", allow(while_let_on_iterator))] fn next(&mut self) -> Option<&'a mut String> { while let Some(item) = self.iter.next() { if let Node::Text(ref mut child) = *item { From 3341d7e69204233075d7be89bea320297947a780 Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 22 May 2017 18:49:24 +0200 Subject: [PATCH 0237/1020] turns out i was wrong and clippy was right, all hail clippy --- src/element.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/element.rs b/src/element.rs index 31e0a16ff5c05e3f2a664368ed98ca1d3771f040..c77ed0967ab08d16138fedb9596dd2734a1ee650 100644 --- a/src/element.rs +++ b/src/element.rs @@ -600,9 +600,8 @@ pub struct Children<'a> { impl<'a> Iterator for Children<'a> { type Item = &'a Element; - #[cfg_attr(feature = "cargo-clippy", allow(while_let_on_iterator))] fn next(&mut self) -> Option<&'a Element> { - while let Some(item) = self.iter.next() { + for item in &mut self.iter { if let Node::Element(ref child) = *item { return Some(child); } @@ -619,9 +618,8 @@ pub struct ChildrenMut<'a> { impl<'a> Iterator for ChildrenMut<'a> { type Item = &'a mut Element; - #[cfg_attr(feature = "cargo-clippy", allow(while_let_on_iterator))] fn next(&mut self) -> Option<&'a mut Element> { - while let Some(item) = self.iter.next() { + for item in &mut self.iter { if let Node::Element(ref mut child) = *item { return Some(child); } @@ -638,9 +636,8 @@ pub struct Texts<'a> { impl<'a> Iterator for Texts<'a> { type Item = &'a str; - #[cfg_attr(feature = "cargo-clippy", allow(while_let_on_iterator))] fn next(&mut self) -> Option<&'a str> { - while let Some(item) = self.iter.next() { + for item in &mut self.iter { if let Node::Text(ref child) = *item { return Some(child); } @@ -657,9 +654,8 @@ pub struct TextsMut<'a> { impl<'a> Iterator for TextsMut<'a> { type Item = &'a mut String; - #[cfg_attr(feature = "cargo-clippy", allow(while_let_on_iterator))] fn next(&mut self) -> Option<&'a mut String> { - while let Some(item) = self.iter.next() { + for item in &mut self.iter { if let Node::Text(ref mut child) = *item { return Some(child); } From 6323529cd70ff9a57fb2ee33e7a64ad1e34e4364 Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 22 May 2017 18:50:43 +0200 Subject: [PATCH 0238/1020] bump version to 0.3.3 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 66d9055ce7ac588e9c0eb2228a9a1d6464238bf8..b5ee1a1381735c7226e209252280c16fb1779565 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.3.2" +version = "0.3.3" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] description = "A small, simple DOM implementation on top of xml-rs." homepage = "https://gitlab.com/lumi/minidom-rs" From 508c9714160c74863fecce75c4e5d5b882306bbe Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 22 May 2017 19:20:01 +0200 Subject: [PATCH 0239/1020] port error.rs to error_chain, bump version to 0.4.0 --- Cargo.toml | 3 ++- src/element.rs | 14 +++++++------- src/error.rs | 43 +++++++++++++++++-------------------------- src/lib.rs | 1 + 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b5ee1a1381735c7226e209252280c16fb1779565..8b6cf5e41440241e064f9cceb2d2f7e4a1a81b3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.3.3" +version = "0.4.0" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] description = "A small, simple DOM implementation on top of xml-rs." homepage = "https://gitlab.com/lumi/minidom-rs" @@ -15,3 +15,4 @@ gitlab = { repository = "lumi/minidom-rs" } [dependencies] xml-rs = "0.4.1" +error-chain = "0.10.0" diff --git a/src/element.rs b/src/element.rs index c77ed0967ab08d16138fedb9596dd2734a1ee650..3c5c9d522076cf593bedb57345a32fa29b1aad0c 100644 --- a/src/element.rs +++ b/src/element.rs @@ -7,7 +7,7 @@ use std::collections::btree_map; use std::fmt; -use error::Error; +use error::{Error, ErrorKind, Result}; use xml::reader::{XmlEvent as ReaderEvent, EventReader}; use xml::writer::{XmlEvent as WriterEvent, EventWriter, EmitterConfig}; @@ -100,7 +100,7 @@ impl fmt::Debug for Element { impl FromStr for Element { type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { let mut reader = EventReader::new(Cursor::new(s)); Element::from_reader(&mut reader) } @@ -246,7 +246,7 @@ impl Element { } /// Parse a document from an `EventReader`. - pub fn from_reader(reader: &mut EventReader) -> Result { + pub fn from_reader(reader: &mut EventReader) -> Result { loop { let e = reader.next()?; match e { @@ -272,7 +272,7 @@ impl Element { return Ok(root); }, ReaderEvent::EndDocument => { - return Err(Error::EndOfDocument); + bail!(ErrorKind::EndOfDocument); }, _ => () // TODO: may need more errors } @@ -280,7 +280,7 @@ impl Element { } #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] - fn from_reader_inner(&mut self, reader: &mut EventReader) -> Result<(), Error> { + fn from_reader_inner(&mut self, reader: &mut EventReader) -> Result<()> { loop { let e = reader.next()?; match e { @@ -312,7 +312,7 @@ impl Element { self.append_text_node(s); }, ReaderEvent::EndDocument => { - return Err(Error::EndOfDocument); + bail!(ErrorKind::EndOfDocument); }, _ => (), // TODO: may need to implement more } @@ -320,7 +320,7 @@ impl Element { } /// Output a document to an `EventWriter`. - pub fn write_to(&self, writer: &mut EventWriter) -> Result<(), Error> { + pub fn write_to(&self, writer: &mut EventWriter) -> Result<()> { let name = if let Some(ref ns) = self.namespace { Name::qualified(&self.name, ns, None) } diff --git a/src/error.rs b/src/error.rs index dc818e2f651e8452d548182f5957b57663147aa1..4e76c6a9a1be50012fe4f115f05c325e16dcda76 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,33 +7,24 @@ use std::convert::From; use xml::writer::Error as WriterError; use xml::reader::Error as ReaderError; -/// An enum representing the possible errors. -#[derive(Debug)] -pub enum Error { - /// An io::Error. - IoError(io::Error), - /// An error in the xml-rs `EventWriter`. - XmlWriterError(WriterError), - /// An error in the xml-rs `EventReader`. - XmlReaderError(ReaderError), - /// The end of the document has been reached unexpectedly. - EndOfDocument, -} - -impl From for Error { - fn from(err: io::Error) -> Error { - Error::IoError(err) - } -} - -impl From for Error { - fn from(err: WriterError) -> Error { - Error::XmlWriterError(err) +error_chain! { + foreign_links { + XmlWriterError(WriterError) + /// An error with writing an XML event, from xml::writer::EventWriter. + ; + XmlReaderError(ReaderError) + /// An error with reading an XML event, from xml::reader::EventReader. + ; + IoError(io::Error) + /// An I/O error, from std::io. + ; } -} -impl From for Error { - fn from(err: ReaderError) -> Error { - Error::XmlReaderError(err) + errors { + /// En error which is returned when the end of the document was reached prematurely. + EndOfDocument { + description("the end of the document has been reached prematurely") + display("the end of the document has been reached prematurely") + } } } diff --git a/src/lib.rs b/src/lib.rs index 9846e75666de2696fc8506a7e02111e0328d86a5..3539c99b4644b64ad28a0a890f475fe7271361d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,6 +65,7 @@ //! ``` extern crate xml; +#[macro_use] extern crate error_chain; pub mod error; pub mod element; From 9d45230c17b2b7b90f99674f294beec5bb054e76 Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 22 May 2017 19:30:52 +0200 Subject: [PATCH 0240/1020] should re-export more things under the error module --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8b6cf5e41440241e064f9cceb2d2f7e4a1a81b3c..bc53cd5a57a77ba225dfa2641dbc48b157e8eae1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.4.0" +version = "0.4.1" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] description = "A small, simple DOM implementation on top of xml-rs." homepage = "https://gitlab.com/lumi/minidom-rs" diff --git a/src/lib.rs b/src/lib.rs index 3539c99b4644b64ad28a0a890f475fe7271361d3..d51de436e8250786a1b24288daf74c08d14899e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,6 +73,6 @@ pub mod convert; #[cfg(test)] mod tests; -pub use error::Error; +pub use error::{Error, ErrorKind, Result, ResultExt}; pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder}; pub use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; From d61d09f5b7d28e3d5ce2cab19a94362ef3e4c83d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 22 May 2017 19:00:04 +0100 Subject: [PATCH 0241/1020] hashes, presence, message, iq, disco: Use get_attr!. --- src/disco.rs | 36 +++++++++++++++--------------------- src/hashes.rs | 5 +---- src/iq.rs | 16 +++++----------- src/message.rs | 18 ++++++------------ src/presence.rs | 16 +++++----------- 5 files changed, 32 insertions(+), 59 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 48f4a2d0db93b5e426a877af91a2c06b56dd0031..9610ad7e7daa58aa8f0e4f5262936ac0aac12f43 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -46,43 +46,37 @@ impl<'a> TryFrom<&'a Element> for Disco { let mut features: Vec = vec!(); let mut extensions: Vec = vec!(); - let node = elem.attr("node") - .and_then(|node| node.parse().ok()); + let node = get_attr!(elem, "node", optional); for child in elem.children() { if child.is("feature", ns::DISCO_INFO) { - let feature = child.attr("var") - .ok_or(Error::ParseError("Feature must have a 'var' attribute."))?; + let feature = get_attr!(child, "var", required); features.push(Feature { - var: feature.to_owned(), + var: feature, }); } else if child.is("identity", ns::DISCO_INFO) { - let category = child.attr("category") - .ok_or(Error::ParseError("Identity must have a 'category' attribute."))?; + let category = get_attr!(child, "category", required); if category == "" { return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) } - let type_ = child.attr("type") - .ok_or(Error::ParseError("Identity must have a 'type' attribute."))?; + let type_ = get_attr!(child, "type", required); if type_ == "" { return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) } - let xml_lang = child.attr("xml:lang").unwrap_or(""); - let name = child.attr("name") - .and_then(|name| name.parse().ok()); + let lang = get_attr!(child, "xml:lang", default); + let name = get_attr!(child, "name", optional); identities.push(Identity { - category: category.to_owned(), - type_: type_.to_owned(), - xml_lang: xml_lang.to_owned(), + category: category, + type_: type_, + xml_lang: lang, name: name, }); } else if child.is("x", ns::DATA_FORMS) { let data_form = DataForm::try_from(child)?; - match data_form.type_ { - DataFormType::Result_ => (), - _ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")), + if data_form.type_ != DataFormType::Result_ { + return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")); } match data_form.form_type { Some(_) => extensions.push(data_form), @@ -178,7 +172,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Identity must have a 'category' attribute."); + assert_eq!(message, "Required attribute 'category' missing."); let elem: Element = "".parse().unwrap(); let error = Disco::try_from(&elem).unwrap_err(); @@ -194,7 +188,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Identity must have a 'type' attribute."); + assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); let error = Disco::try_from(&elem).unwrap_err(); @@ -213,7 +207,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Feature must have a 'var' attribute."); + assert_eq!(message, "Required attribute 'var' missing."); } #[test] diff --git a/src/hashes.rs b/src/hashes.rs index 403cb0c90eca8989d8ad678a72ddf3b1d0479e28..da16788b9578af631dde3f584f8e38bdc6b97cf5 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -76,10 +76,7 @@ impl<'a> TryFrom<&'a Element> for Hash { for _ in elem.children() { return Err(Error::ParseError("Unknown child in hash element.")); } - let algo = match elem.attr("algo") { - None => Err(Error::ParseError("Mandatory argument 'algo' not present in hash element.")), - Some(text) => Algo::from_str(text), - }?; + let algo = get_attr!(elem, "algo", required); let hash = match elem.text().as_ref() { "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), text => text.to_owned(), diff --git a/src/iq.rs b/src/iq.rs index d2d4a6899d0848feadcb6194ad104ffa77a2f756..b1767735662b6ef4245166c4557fd925c4d3290d 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -100,16 +100,10 @@ impl<'a> TryFrom<&'a Element> for Iq { if !root.is("iq", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not an iq element.")); } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_, - None => return Err(Error::ParseError("Iq element requires a 'type' attribute.")), - }; + let from = get_attr!(root, "from", optional); + let to = get_attr!(root, "to", optional); + let id = get_attr!(root, "id", optional); + let type_: String = get_attr!(root, "type", required); let mut payload = None; let mut error_payload = None; @@ -218,7 +212,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Iq element requires a 'type' attribute."); + assert_eq!(message, "Required attribute 'type' missing."); } #[test] diff --git a/src/message.rs b/src/message.rs index 23e6686ef26990007867952b70fcef839b1066ff..a309fa015ff4a4490148efd8c4218b85685861c5 100644 --- a/src/message.rs +++ b/src/message.rs @@ -169,16 +169,10 @@ impl<'a> TryFrom<&'a Element> for Message { if !root.is("message", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a message element.")); } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_.parse()?, - None => Default::default(), - }; + let from = get_attr!(root, "from", optional); + let to = get_attr!(root, "to", optional); + let id = get_attr!(root, "id", optional); + let type_ = get_attr!(root, "type", default); let mut bodies = BTreeMap::new(); let mut subjects = BTreeMap::new(); let mut thread = None; @@ -188,7 +182,7 @@ impl<'a> TryFrom<&'a Element> for Message { for _ in elem.children() { return Err(Error::ParseError("Unknown child in body element.")); } - let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + let lang = get_attr!(root, "xml:lang", default); if bodies.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } @@ -196,7 +190,7 @@ impl<'a> TryFrom<&'a Element> for Message { for _ in elem.children() { return Err(Error::ParseError("Unknown child in subject element.")); } - let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + let lang = get_attr!(root, "xml:lang", default); if subjects.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); } diff --git a/src/presence.rs b/src/presence.rs index fd6547b16d0836ccd0adf95d212500af28109800..c15950a90082e339e91bfbb56ce03e1d5904cfbb 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -164,16 +164,10 @@ impl<'a> TryFrom<&'a Element> for Presence { if !root.is("presence", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a presence element.")); } - let from = root.attr("from") - .and_then(|value| value.parse().ok()); - let to = root.attr("to") - .and_then(|value| value.parse().ok()); - let id = root.attr("id") - .and_then(|value| value.parse().ok()); - let type_ = match root.attr("type") { - Some(type_) => type_.parse()?, - None => Default::default(), - }; + let from = get_attr!(root, "from", optional); + let to = get_attr!(root, "to", optional); + let id = get_attr!(root, "id", optional); + let type_ = get_attr!(root, "type", default); let mut show = None; let mut statuses = BTreeMap::new(); let mut priority = None; @@ -206,7 +200,7 @@ impl<'a> TryFrom<&'a Element> for Presence { return Err(Error::ParseError("Unknown attribute in status element.")); } } - let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + let lang = get_attr!(elem, "xml:lang", default); if statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); } From e3f1f31718c8c5095d11e264ee31dbe02cbc9e03 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 23 May 2017 01:02:23 +0100 Subject: [PATCH 0242/1020] delay, eme, stanza_error: Use get_attr!. --- src/delay.rs | 4 ++-- src/eme.rs | 4 ++-- src/message_correct.rs | 9 +++------ src/stanza_error.rs | 11 ++++------- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index a15b61bcac8340fc1320e262975fbef2b16d9383..79424dcb5e4a01e3129e00c4c80555d1718c7046 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -30,8 +30,8 @@ impl<'a> TryFrom<&'a Element> for Delay { for _ in elem.children() { return Err(Error::ParseError("Unknown child in delay element.")); } - let from = elem.attr("from").and_then(|value| value.parse().ok()); - let stamp = elem.attr("stamp").ok_or(Error::ParseError("Mandatory argument 'stamp' not present in delay element."))?.to_owned(); + let from = get_attr!(elem, "from", optional); + let stamp = get_attr!(elem, "stamp", required); let data = match elem.text().as_ref() { "" => None, text => Some(text.to_owned()), diff --git a/src/eme.rs b/src/eme.rs index ad28afc705c643fd61da43f32e181771b13cdad0..217517cef0710c13e89920d15205ba5afe40d4ae 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -28,8 +28,8 @@ impl<'a> TryFrom<&'a Element> for ExplicitMessageEncryption { for _ in elem.children() { return Err(Error::ParseError("Unknown child in encryption element.")); } - let namespace = elem.attr("namespace").ok_or(Error::ParseError("Mandatory argument 'namespace' not present in encryption element."))?.to_owned(); - let name = elem.attr("name").and_then(|value| value.parse().ok()); + let namespace = get_attr!(elem, "namespace", required); + let name = get_attr!(elem, "name", optional); Ok(ExplicitMessageEncryption { namespace: namespace, name: name, diff --git a/src/message_correct.rs b/src/message_correct.rs index ecc555cc5aa23681d4e6c8600795a8aaca234d2a..ab0d3b2b9532a9cecd16bd59bba6e7cb72eb9df9 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -27,11 +27,8 @@ impl<'a> TryFrom<&'a Element> for Replace { for _ in elem.children() { return Err(Error::ParseError("Unknown child in replace element.")); } - let id = match elem.attr("id") { - Some(id) => id.to_owned(), - None => return Err(Error::ParseError("No 'id' attribute present in replace.")), - }; - Ok(Replace { id: id }) + let id = get_attr!(elem, "id", required); + Ok(Replace { id }) } } @@ -73,7 +70,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "No 'id' attribute present in replace."); + assert_eq!(message, "Required attribute 'id' missing."); } #[test] diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 2f509ee21f6edb84cb1252104cafca57a5f8271f..85f21298635c99dbae616a859b423e3b4c4c0e9f 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -158,11 +158,8 @@ impl<'a> TryFrom<&'a Element> for StanzaError { return Err(Error::ParseError("This is not an error element.")); } - let type_ = elem.attr("type") - .ok_or(Error::ParseError("Error must have a 'type' attribute."))? - .parse()?; - let by = elem.attr("by") - .and_then(|by| by.parse().ok()); + let type_ = get_attr!(elem, "type", required); + let by = get_attr!(elem, "by", optional); let mut defined_condition = None; let mut texts = BTreeMap::new(); let mut other = None; @@ -172,7 +169,7 @@ impl<'a> TryFrom<&'a Element> for StanzaError { for _ in child.children() { return Err(Error::ParseError("Unknown element in error text.")); } - let lang = child.attr("xml:lang").unwrap_or("").to_owned(); + let lang = get_attr!(elem, "xml:lang", default); if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); } @@ -256,7 +253,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Error must have a 'type' attribute."); + assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(&elem).unwrap_err(); From c1e62cf69bb833d477d0fa0b53d4def10ab841c8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 23 May 2017 23:28:56 +0100 Subject: [PATCH 0243/1020] Cargo.toml: Update minidom, probably nice things in it! --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 59cafb26c08b2a036504c8a183518a03f52cf535..41d4a464fedf3e5b83258b0f9608007f6d2433d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.3.1" +minidom = "0.4.1" jid = "0.2.0" base64 = "0.5.0" digest = "0.5.0" From 16e43c0b01c512d25b9d5c56948f487cb3ecd1b6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 23 May 2017 23:31:33 +0100 Subject: [PATCH 0244/1020] Convert all of the parsers/serialisers into consuming their parameter. --- src/attention.rs | 14 ++++----- src/chatstates.rs | 18 +++++------ src/data_forms.rs | 14 ++++----- src/delay.rs | 16 +++++----- src/disco.rs | 34 ++++++++++---------- src/ecaps2.rs | 20 ++++++------ src/eme.rs | 16 +++++----- src/forwarding.rs | 20 ++++++------ src/hashes.rs | 14 ++++----- src/ibb.rs | 36 +++++++++++----------- src/idle.rs | 14 ++++----- src/iq.rs | 58 +++++++++++++++++----------------- src/jingle.rs | 66 ++++++++++++++++++--------------------- src/jingle_ft.rs | 46 +++++++++++++-------------- src/jingle_ibb.rs | 18 +++++------ src/jingle_s5b.rs | 22 ++++++------- src/mam.rs | 70 +++++++++++++++++++++--------------------- src/media_element.rs | 28 ++++++++--------- src/message.rs | 63 +++++++++++++++++++------------------ src/message_correct.rs | 14 ++++----- src/ping.rs | 12 ++++---- src/presence.rs | 56 ++++++++++++++++----------------- src/receipts.rs | 26 ++++++++-------- src/rsm.rs | 21 +++++++------ src/stanza_error.rs | 14 ++++----- src/stanza_id.rs | 30 +++++++++--------- 26 files changed, 378 insertions(+), 382 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 21f8e0b5d29c3411bb3a1c6cc47df8b184308088..b28b5cc300967f03c455814f1008cd06c93c264b 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -15,10 +15,10 @@ use ns; #[derive(Debug, Clone)] pub struct Attention; -impl<'a> TryFrom<&'a Element> for Attention { +impl TryFrom for Attention { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("attention", ns::ATTENTION) { return Err(Error::ParseError("This is not an attention element.")); } @@ -29,7 +29,7 @@ impl<'a> TryFrom<&'a Element> for Attention { } } -impl<'a> Into for &'a Attention { +impl Into for Attention { fn into(self) -> Element { Element::builder("attention") .ns(ns::ATTENTION) @@ -47,13 +47,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Attention::try_from(&elem).unwrap(); + Attention::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Attention::try_from(&elem).unwrap_err(); + let error = Attention::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -65,9 +65,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let attention = Attention; - let elem2: Element = (&attention).into(); - let elem3: Element = (&attention).into(); + let elem2: Element = attention.into(); assert_eq!(elem, elem2); - assert_eq!(elem2, elem3); } } diff --git a/src/chatstates.rs b/src/chatstates.rs index f9fdb4f3d26e47dfd853a18ff4f22a1fd9b22e59..54f0aefe7e93d343be9c46e8df6fc2d83e4f7f2e 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -21,10 +21,10 @@ pub enum ChatState { Paused, } -impl<'a> TryFrom<&'a Element> for ChatState { +impl TryFrom for ChatState { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { for _ in elem.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); } @@ -47,9 +47,9 @@ impl<'a> TryFrom<&'a Element> for ChatState { } } -impl<'a> Into for &'a ChatState { +impl Into for ChatState { fn into(self) -> Element { - Element::builder(match *self { + Element::builder(match self { ChatState::Active => "active", ChatState::Composing => "composing", ChatState::Gone => "gone", @@ -67,13 +67,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - ChatState::try_from(&elem).unwrap(); + ChatState::try_from(elem).unwrap(); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = ChatState::try_from(&elem).unwrap_err(); + let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -84,7 +84,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = ChatState::try_from(&elem).unwrap_err(); + let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -95,7 +95,7 @@ mod tests { #[test] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = ChatState::try_from(&elem).unwrap_err(); + let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -106,7 +106,7 @@ mod tests { #[test] fn test_serialise() { let chatstate = ChatState::Active; - let elem: Element = (&chatstate).into(); + let elem: Element = chatstate.into(); assert!(elem.is("active", ns::CHATSTATES)); } } diff --git a/src/data_forms.rs b/src/data_forms.rs index 6d4e3bc41fa450cf1c833af52e6345dc79bfc18f..21d2ab11db6276b488936edaf54dc4fb4501d054 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -104,10 +104,10 @@ pub struct DataForm { pub fields: Vec, } -impl<'a> TryFrom<&'a Element> for DataForm { +impl TryFrom for DataForm { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("x", ns::DATA_FORMS) { return Err(Error::ParseError("This is not a data form element.")); } @@ -200,7 +200,7 @@ impl<'a> TryFrom<&'a Element> for DataForm { value: value, }); } else if element.is("media", ns::MEDIA_ELEMENT) { - match MediaElement::try_from(element) { + match MediaElement::try_from(element.clone()) { Ok(media_element) => field.media.push(media_element), Err(_) => (), // TODO: is it really nice to swallow this error? } @@ -233,7 +233,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let form = DataForm::try_from(&elem).unwrap(); + let form = DataForm::try_from(elem).unwrap(); assert_eq!(form.type_, DataFormType::Result_); assert!(form.form_type.is_none()); assert!(form.fields.is_empty()); @@ -242,7 +242,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = DataForm::try_from(&elem).unwrap_err(); + let error = DataForm::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -250,7 +250,7 @@ mod tests { assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); - let error = DataForm::try_from(&elem).unwrap_err(); + let error = DataForm::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -261,7 +261,7 @@ mod tests { #[test] fn test_wrong_child() { let elem: Element = "".parse().unwrap(); - let error = DataForm::try_from(&elem).unwrap_err(); + let error = DataForm::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/delay.rs b/src/delay.rs index 79424dcb5e4a01e3129e00c4c80555d1718c7046..08b406c5c7d04b58bf53eeeb73c1cd63a48b4875 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -20,10 +20,10 @@ pub struct Delay { pub data: Option, } -impl<'a> TryFrom<&'a Element> for Delay { +impl TryFrom for Delay { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("delay", ns::DELAY) { return Err(Error::ParseError("This is not a delay element.")); } @@ -44,7 +44,7 @@ impl<'a> TryFrom<&'a Element> for Delay { } } -impl<'a> Into for &'a Delay { +impl Into for Delay { fn into(self) -> Element { Element::builder("delay") .ns(ns::DELAY) @@ -63,7 +63,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let delay = Delay::try_from(&elem).unwrap(); + let delay = Delay::try_from(elem).unwrap(); assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap())); assert_eq!(delay.stamp, "2002-09-10T23:08:25Z"); assert_eq!(delay.data, None); @@ -72,7 +72,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = Delay::try_from(&elem).unwrap_err(); + let error = Delay::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -83,7 +83,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Delay::try_from(&elem).unwrap_err(); + let error = Delay::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -99,7 +99,7 @@ mod tests { stamp: "2002-09-10T23:08:25Z".to_owned(), data: None, }; - let elem2 = (&delay).into(); + let elem2 = delay.into(); assert_eq!(elem, elem2); } @@ -111,7 +111,7 @@ mod tests { stamp: "2002-09-10T23:08:25Z".to_owned(), data: Some(String::from("Reason")), }; - let elem2 = (&delay).into(); + let elem2 = delay.into(); assert_eq!(elem, elem2); } } diff --git a/src/disco.rs b/src/disco.rs index 9610ad7e7daa58aa8f0e4f5262936ac0aac12f43..acd2d4872f78411fe40f86b22d18cab1ec7144a9 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -34,10 +34,10 @@ pub struct Disco { pub extensions: Vec, } -impl<'a> TryFrom<&'a Element> for Disco { +impl TryFrom for Disco { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("query", ns::DISCO_INFO) { return Err(Error::ParseError("This is not a disco#info element.")); } @@ -74,7 +74,7 @@ impl<'a> TryFrom<&'a Element> for Disco { name: name, }); } else if child.is("x", ns::DATA_FORMS) { - let data_form = DataForm::try_from(child)?; + let data_form = DataForm::try_from(child.clone())?; if data_form.type_ != DataFormType::Result_ { return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")); } @@ -109,13 +109,13 @@ impl<'a> TryFrom<&'a Element> for Disco { } } -impl<'a> Into for &'a Disco { +impl Into for Disco { fn into(self) -> Element { let mut root = Element::builder("query") .ns(ns::DISCO_INFO) .attr("node", self.node.clone()) .build(); - for identity in &self.identities { + for identity in self.identities { let identity_element = Element::builder("identity") .ns(ns::DISCO_INFO) .attr("category", identity.category.clone()) @@ -125,14 +125,14 @@ impl<'a> Into for &'a Disco { .build(); root.append_child(identity_element); } - for feature in &self.features { + for feature in self.features { let feature_element = Element::builder("feature") .ns(ns::DISCO_INFO) .attr("var", feature.var.clone()) .build(); root.append_child(feature_element); } - for _ in &self.extensions { + for _ in self.extensions { panic!("Not yet implemented!"); } root @@ -146,7 +146,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let query = Disco::try_from(&elem).unwrap(); + let query = Disco::try_from(elem).unwrap(); assert!(query.node.is_none()); assert_eq!(query.identities.len(), 1); assert_eq!(query.features.len(), 1); @@ -156,7 +156,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -167,7 +167,7 @@ mod tests { #[test] fn test_invalid_identity() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -175,7 +175,7 @@ mod tests { assert_eq!(message, "Required attribute 'category' missing."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -183,7 +183,7 @@ mod tests { assert_eq!(message, "Identity must have a non-empty 'category' attribute."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -191,7 +191,7 @@ mod tests { assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -202,7 +202,7 @@ mod tests { #[test] fn test_invalid_feature() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -214,7 +214,7 @@ mod tests { #[ignore] fn test_invalid_result() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -222,7 +222,7 @@ mod tests { assert_eq!(message, "There must be at least one identity in disco#info."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -230,7 +230,7 @@ mod tests { assert_eq!(message, "There must be at least one feature in disco#info."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(&elem).unwrap_err(); + let error = Disco::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/ecaps2.rs b/src/ecaps2.rs index b69a6449d5efb0eab126dfd5d1e6bcd567a6df42..ac9df22edc6f3e3478963578b41b689037d4dcaa 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -25,17 +25,17 @@ pub struct ECaps2 { hashes: Vec, } -impl<'a> TryFrom<&'a Element> for ECaps2 { +impl TryFrom for ECaps2 { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("c", ns::ECAPS2) { return Err(Error::ParseError("This is not an ecaps2 element.")); } let mut hashes = vec!(); for child in elem.children() { if child.is("hash", ns::HASHES) { - let hash = Hash::try_from(child)?; + let hash = Hash::try_from(child.clone())?; hashes.push(hash); } else { return Err(Error::ParseError("Unknown child in ecaps2 element.")); @@ -47,12 +47,12 @@ impl<'a> TryFrom<&'a Element> for ECaps2 { } } -impl<'a> Into for &'a ECaps2 { +impl Into for ECaps2 { fn into(self) -> Element { Element::builder("c") .ns(ns::ECAPS2) .append(self.hashes.iter() - .map(|hash| hash.into()) + .map(|hash| hash.clone().into()) .collect::>()) .build() } @@ -175,7 +175,7 @@ mod tests { #[test] fn test_parse() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); - let ecaps2 = ECaps2::try_from(&elem).unwrap(); + let ecaps2 = ECaps2::try_from(elem).unwrap(); assert_eq!(ecaps2.hashes.len(), 2); assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256); assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); @@ -186,7 +186,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); - let error = ECaps2::try_from(&elem).unwrap_err(); + let error = ECaps2::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -197,7 +197,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let disco = Disco::try_from(&elem).unwrap(); + let disco = Disco::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 54); } @@ -260,7 +260,7 @@ mod tests { 105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, 100, 31, 30, 28, 28]; - let disco = Disco::try_from(&elem).unwrap(); + let disco = Disco::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); @@ -432,7 +432,7 @@ mod tests { 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; - let disco = Disco::try_from(&elem).unwrap(); + let disco = Disco::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); diff --git a/src/eme.rs b/src/eme.rs index 217517cef0710c13e89920d15205ba5afe40d4ae..edf6cd359ba443022f60d9227ed0e032728116a9 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -18,10 +18,10 @@ pub struct ExplicitMessageEncryption { pub name: Option, } -impl<'a> TryFrom<&'a Element> for ExplicitMessageEncryption { +impl TryFrom for ExplicitMessageEncryption { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("encryption", ns::EME) { return Err(Error::ParseError("This is not an encryption element.")); } @@ -37,7 +37,7 @@ impl<'a> TryFrom<&'a Element> for ExplicitMessageEncryption { } } -impl<'a> Into for &'a ExplicitMessageEncryption { +impl Into for ExplicitMessageEncryption { fn into(self) -> Element { Element::builder("encryption") .ns(ns::EME) @@ -54,12 +54,12 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let encryption = ExplicitMessageEncryption::try_from(&elem).unwrap(); + let encryption = ExplicitMessageEncryption::try_from(elem).unwrap(); assert_eq!(encryption.namespace, "urn:xmpp:otr:0"); assert_eq!(encryption.name, None); let elem: Element = "".parse().unwrap(); - let encryption = ExplicitMessageEncryption::try_from(&elem).unwrap(); + let encryption = ExplicitMessageEncryption::try_from(elem).unwrap(); assert_eq!(encryption.namespace, "some.unknown.mechanism"); assert_eq!(encryption.name, Some(String::from("SuperMechanism"))); } @@ -67,7 +67,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = ExplicitMessageEncryption::try_from(&elem).unwrap_err(); + let error = ExplicitMessageEncryption::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -78,7 +78,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = ExplicitMessageEncryption::try_from(&elem).unwrap_err(); + let error = ExplicitMessageEncryption::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -90,7 +90,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let eme = ExplicitMessageEncryption { namespace: String::from("coucou"), name: None }; - let elem2 = (&eme).into(); + let elem2 = eme.into(); assert_eq!(elem, elem2); } } diff --git a/src/forwarding.rs b/src/forwarding.rs index a2162e06b110209c8b9e8001d0da0518c90255ad..9ad416b8fcf5ea23775bcac763b37235763c46c6 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -22,10 +22,10 @@ pub struct Forwarded { pub stanza: Option, } -impl<'a> TryFrom<&'a Element> for Forwarded { +impl TryFrom for Forwarded { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("forwarded", ns::FORWARD) { return Err(Error::ParseError("This is not a forwarded element.")); } @@ -33,9 +33,9 @@ impl<'a> TryFrom<&'a Element> for Forwarded { let mut stanza = None; for child in elem.children() { if child.is("delay", ns::DELAY) { - delay = Some(Delay::try_from(child)?); + delay = Some(Delay::try_from(child.clone())?); } else if child.is("message", ns::JABBER_CLIENT) { - stanza = Some(Message::try_from(child)?); + stanza = Some(Message::try_from(child.clone())?); // TODO: also handle the five other possibilities. } else { return Err(Error::ParseError("Unknown child in forwarded element.")); @@ -48,12 +48,12 @@ impl<'a> TryFrom<&'a Element> for Forwarded { } } -impl<'a> Into for &'a Forwarded { +impl Into for Forwarded { fn into(self) -> Element { Element::builder("forwarded") .ns(ns::FORWARD) - .append(match self.delay { Some(ref delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) - .append(match self.stanza { Some(ref stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) + .append(match self.delay { Some(delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) + .append(match self.stanza { Some(stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) .build() } } @@ -65,13 +65,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Forwarded::try_from(&elem).unwrap(); + Forwarded::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Forwarded::try_from(&elem).unwrap_err(); + let error = Forwarded::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -83,7 +83,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let forwarded = Forwarded { delay: None, stanza: None }; - let elem2 = (&forwarded).into(); + let elem2 = forwarded.into(); assert_eq!(elem, elem2); } } diff --git a/src/hashes.rs b/src/hashes.rs index da16788b9578af631dde3f584f8e38bdc6b97cf5..af9c3a053c81626161b09b2fe0b011249b9899b4 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -66,10 +66,10 @@ pub struct Hash { pub hash: String, } -impl<'a> TryFrom<&'a Element> for Hash { +impl TryFrom for Hash { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("hash", ns::HASHES) { return Err(Error::ParseError("This is not a hash element.")); } @@ -88,11 +88,11 @@ impl<'a> TryFrom<&'a Element> for Hash { } } -impl<'a> Into for &'a Hash { +impl Into for Hash { fn into(self) -> Element { Element::builder("hash") .ns(ns::HASHES) - .attr("algo", self.algo.clone()) + .attr("algo", self.algo) .append(self.hash.clone()) .build() } @@ -105,7 +105,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); - let hash = Hash::try_from(&elem).unwrap(); + let hash = Hash::try_from(elem).unwrap(); assert_eq!(hash.algo, Algo::Sha_256); assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); } @@ -113,7 +113,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = Hash::try_from(&elem).unwrap_err(); + let error = Hash::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -124,7 +124,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Hash::try_from(&elem).unwrap_err(); + let error = Hash::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/ibb.rs b/src/ibb.rs index ffdb907e15fcabdd4ffba3410c270d217c4c5db6..71bf69511aa1bcd4143340dd53b6bff328fad5c9 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -65,10 +65,10 @@ pub enum IBB { }, } -impl<'a> TryFrom<&'a Element> for IBB { +impl TryFrom for IBB { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if elem.is("open", ns::IBB) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in open element.")); @@ -107,29 +107,29 @@ impl<'a> TryFrom<&'a Element> for IBB { } } -impl<'a> Into for &'a IBB { +impl Into for IBB { fn into(self) -> Element { - match *self { - IBB::Open { ref block_size, ref sid, ref stanza } => { + match self { + IBB::Open { block_size, sid, stanza } => { Element::builder("open") .ns(ns::IBB) .attr("block-size", format!("{}", block_size)) - .attr("sid", sid.to_owned()) - .attr("stanza", stanza.to_owned()) + .attr("sid", sid) + .attr("stanza", stanza) .build() }, - IBB::Data { ref seq, ref sid, ref data } => { + IBB::Data { seq, sid, data } => { Element::builder("data") .ns(ns::IBB) .attr("seq", format!("{}", seq)) - .attr("sid", sid.to_owned()) + .attr("sid", sid) .append(base64::encode(&data)) .build() }, - IBB::Close { ref sid } => { + IBB::Close { sid } => { Element::builder("close") .ns(ns::IBB) - .attr("sid", sid.to_owned()) + .attr("sid", sid) .build() }, } @@ -144,7 +144,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let open = IBB::try_from(&elem).unwrap(); + let open = IBB::try_from(elem).unwrap(); match open { IBB::Open { block_size, sid, stanza } => { assert_eq!(block_size, 3); @@ -155,7 +155,7 @@ mod tests { } let elem: Element = "AAAA".parse().unwrap(); - let data = IBB::try_from(&elem).unwrap(); + let data = IBB::try_from(elem).unwrap(); match data { IBB::Data { seq, sid, data } => { assert_eq!(seq, 0); @@ -166,7 +166,7 @@ mod tests { } let elem: Element = "".parse().unwrap(); - let close = IBB::try_from(&elem).unwrap(); + let close = IBB::try_from(elem).unwrap(); match close { IBB::Close { sid } => { assert_eq!(sid, "coucou"); @@ -178,7 +178,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(&elem).unwrap_err(); + let error = IBB::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -186,7 +186,7 @@ mod tests { assert_eq!(message, "Required attribute 'block-size' missing."); let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(&elem).unwrap_err(); + let error = IBB::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, _ => panic!(), @@ -194,7 +194,7 @@ mod tests { assert_eq!(message.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(&elem).unwrap_err(); + let error = IBB::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(error) => error, _ => panic!(), @@ -205,7 +205,7 @@ mod tests { #[test] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(&elem).unwrap_err(); + let error = IBB::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/idle.rs b/src/idle.rs index 5261788e1c10abcee5417873753d87471a82184c..2800a0e1854b05c1db54970a8ca982eb21f81da2 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -19,10 +19,10 @@ pub struct Idle { pub since: Date, } -impl<'a> TryFrom<&'a Element> for Idle { +impl TryFrom for Idle { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("idle", ns::IDLE) { return Err(Error::ParseError("This is not an idle element.")); } @@ -34,7 +34,7 @@ impl<'a> TryFrom<&'a Element> for Idle { } } -impl<'a> Into for &'a Idle { +impl Into for Idle { fn into(self) -> Element { Element::builder("idle") .ns(ns::IDLE) @@ -50,13 +50,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Idle::try_from(&elem).unwrap(); + Idle::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Idle::try_from(&elem).unwrap_err(); + let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -67,7 +67,7 @@ mod tests { #[test] fn test_invalid_id() { let elem: Element = "".parse().unwrap(); - let error = Idle::try_from(&elem).unwrap_err(); + let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -79,7 +79,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let idle = Idle { since: Date::from("2017-05-21T20:19:55+01:00") }; - let elem2 = (&idle).into(); + let elem2 = idle.into(); assert_eq!(elem, elem2); } } diff --git a/src/iq.rs b/src/iq.rs index b1767735662b6ef4245166c4557fd925c4d3290d..aaf7b027998ce5bf2818645892a9a06c79023b00 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -37,13 +37,13 @@ pub enum IqPayload { Unknown(Element), } -impl<'a> TryFrom<&'a Element> for IqPayload { +impl TryFrom for IqPayload { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { // XEP-0030 - ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?), + ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem.clone())?), // XEP-0047 ("open", ns::IBB) @@ -93,10 +93,10 @@ pub struct Iq { pub payload: IqType, } -impl<'a> TryFrom<&'a Element> for Iq { +impl TryFrom for Iq { type Error = Error; - fn try_from(root: &'a Element) -> Result { + fn try_from(root: Element) -> Result { if !root.is("iq", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not an iq element.")); } @@ -116,7 +116,7 @@ impl<'a> TryFrom<&'a Element> for Iq { if error_payload.is_some() { return Err(Error::ParseError("Wrong number of children in iq element.")); } - error_payload = Some(StanzaError::try_from(elem)?); + error_payload = Some(StanzaError::try_from(elem.clone())?); } else if root.children().collect::>().len() != 2 { return Err(Error::ParseError("Wrong number of children in iq element.")); } @@ -162,23 +162,23 @@ impl<'a> TryFrom<&'a Element> for Iq { } } -impl<'a> Into for &'a IqPayload { +impl Into for IqPayload { fn into(self) -> Element { - match *self { - IqPayload::Disco(ref disco) => disco.into(), - IqPayload::IBB(ref ibb) => ibb.into(), - IqPayload::Jingle(ref jingle) => jingle.into(), - IqPayload::Ping(ref ping) => ping.into(), - IqPayload::MamQuery(ref query) => query.into(), - IqPayload::MamFin(ref fin) => fin.into(), - IqPayload::MamPrefs(ref prefs) => prefs.into(), - - IqPayload::Unknown(ref elem) => elem.clone(), + match self { + IqPayload::Disco(disco) => disco.into(), + IqPayload::IBB(ibb) => ibb.into(), + IqPayload::Jingle(jingle) => jingle.into(), + IqPayload::Ping(ping) => ping.into(), + IqPayload::MamQuery(query) => query.into(), + IqPayload::MamFin(fin) => fin.into(), + IqPayload::MamPrefs(prefs) => prefs.into(), + + IqPayload::Unknown(elem) => elem.clone(), } } } -impl<'a> Into for &'a Iq { +impl Into for Iq { fn into(self) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) @@ -191,7 +191,7 @@ impl<'a> Into for &'a Iq { IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem, - IqType::Error(error) => (&error).into(), + IqType::Error(error) => error.into(), IqType::Result(None) => return stanza, }; stanza.append_child(elem); @@ -207,7 +207,7 @@ mod tests { #[test] fn test_require_type() { let elem: Element = "".parse().unwrap(); - let error = Iq::try_from(&elem).unwrap_err(); + let error = Iq::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -220,7 +220,7 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -236,7 +236,7 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); let vcard: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -250,7 +250,7 @@ mod tests { #[test] fn test_result_empty() { let elem: Element = "".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); @@ -265,7 +265,7 @@ mod tests { let elem: Element = " ".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -284,7 +284,7 @@ mod tests { ".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); @@ -303,7 +303,7 @@ mod tests { #[test] fn test_children_invalid() { let elem: Element = "".parse().unwrap(); - let error = Iq::try_from(&elem).unwrap_err(); + let error = Iq::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -320,16 +320,16 @@ mod tests { id: None, payload: IqType::Result(None), }; - let elem2 = (&iq2).into(); + let elem2 = iq2.into(); assert_eq!(elem, elem2); } #[test] fn test_disco() { let elem: Element = "".parse().unwrap(); - let iq = Iq::try_from(&elem).unwrap(); + let iq = Iq::try_from(elem).unwrap(); let payload = match iq.payload { - IqType::Get(ref payload) => IqPayload::try_from(payload).unwrap(), + IqType::Get(payload) => IqPayload::try_from(payload).unwrap(), _ => panic!(), }; assert!(match payload { diff --git a/src/jingle.rs b/src/jingle.rs index fb928aee94f0a113f05027a6e8abee5105cd154d..4796ae41bcfa926d4f2b859926677fe9c7bfa420 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -204,9 +204,9 @@ impl FromStr for Reason { } } -impl<'a> Into for &'a Reason { +impl Into for Reason { fn into(self) -> Element { - Element::builder(match *self { + Element::builder(match self { Reason::AlternativeSession => "alternative-session", Reason::Busy => "busy", Reason::Cancel => "cancel", @@ -245,10 +245,10 @@ pub struct Jingle { pub other: Vec, } -impl<'a> TryFrom<&'a Element> for Jingle { +impl TryFrom for Jingle { type Error = Error; - fn try_from(root: &'a Element) -> Result { + fn try_from(root: Element) -> Result { if !root.is("jingle", ns::JINGLE) { return Err(Error::ParseError("This is not a Jingle element.")); } @@ -353,7 +353,7 @@ impl<'a> TryFrom<&'a Element> for Jingle { } } -impl<'a> Into for &'a Content { +impl Into for Content { fn into(self) -> Element { let mut root = Element::builder("content") .ns(ns::JINGLE) @@ -375,7 +375,7 @@ impl<'a> Into for &'a Content { } } -impl<'a> Into for &'a Jingle { +impl Into for Jingle { fn into(self) -> Element { let mut root = Element::builder("jingle") .ns(ns::JINGLE) @@ -384,15 +384,15 @@ impl<'a> Into for &'a Jingle { .attr("responder", self.responder.clone()) .attr("sid", self.sid.clone()) .build(); - for content in self.contents.clone() { - let content_elem = (&content).into(); + for content in self.contents { + let content_elem = content.into(); root.append_child(content_elem); } - if let Some(ref reason) = self.reason { - let reason2: Element = (&reason.reason).into(); + if let Some(reason) = self.reason { + let reason2: Element = reason.reason.into(); let reason_elem = Element::builder("reason") .append(reason2) - .append(reason.text.clone()) + .append(reason.text) .build(); root.append_child(reason_elem); } @@ -400,12 +400,6 @@ impl<'a> Into for &'a Jingle { } } -impl Into for Jingle { - fn into(self) -> Element { - (&self).into() - } -} - #[cfg(test)] mod tests { use super::*; @@ -413,7 +407,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.action, Action::SessionInitiate); assert_eq!(jingle.sid, "coucou"); } @@ -421,7 +415,7 @@ mod tests { #[test] fn test_invalid_jingle() { let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -429,7 +423,7 @@ mod tests { assert_eq!(message, "Jingle must have an 'action' attribute."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -437,7 +431,7 @@ mod tests { assert_eq!(message, "Jingle must have a 'sid' attribute."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -448,25 +442,25 @@ mod tests { #[test] fn test_content() { let elem: Element = "".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, "coucou"); assert_eq!(jingle.contents[0].senders, Senders::Both); assert_eq!(jingle.contents[0].disposition, "session"); let elem: Element = "".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].senders, Senders::Both); let elem: Element = "".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].disposition, "early-session"); } #[test] fn test_invalid_content() { let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -474,7 +468,7 @@ mod tests { assert_eq!(message, "Content must have a 'creator' attribute."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -482,7 +476,7 @@ mod tests { assert_eq!(message, "Content must have a 'name' attribute."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -490,7 +484,7 @@ mod tests { assert_eq!(message, "Unknown creator."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -498,7 +492,7 @@ mod tests { assert_eq!(message, "Unknown senders."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -509,13 +503,13 @@ mod tests { #[test] fn test_reason() { let elem: Element = "".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); let reason = jingle.reason.unwrap(); assert_eq!(reason.reason, Reason::Success); assert_eq!(reason.text, None); let elem: Element = "coucou".parse().unwrap(); - let jingle = Jingle::try_from(&elem).unwrap(); + let jingle = Jingle::try_from(elem).unwrap(); let reason = jingle.reason.unwrap(); assert_eq!(reason.reason, Reason::Success); assert_eq!(reason.text, Some(String::from("coucou"))); @@ -524,7 +518,7 @@ mod tests { #[test] fn test_invalid_reason() { let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -532,7 +526,7 @@ mod tests { assert_eq!(message, "Reason doesn’t contain a valid reason."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -540,7 +534,7 @@ mod tests { assert_eq!(message, "Unknown reason."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -548,7 +542,7 @@ mod tests { assert_eq!(message, "Reason contains a foreign element."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -556,7 +550,7 @@ mod tests { assert_eq!(message, "Jingle must not have more than one reason."); let elem: Element = "".parse().unwrap(); - let error = Jingle::try_from(&elem).unwrap_err(); + let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 454ce8d4a54d1c026e1fcb356515624638d522fc..57cf626cf6ce235ea06be2b761dd0e5ca8677ae3 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -31,7 +31,7 @@ impl IntoElements for Range { }) .build(); for hash in self.hashes { - elem.append_child((&hash).into()); + elem.append_child(hash.into()); } emitter.append_child(elem); } @@ -86,10 +86,10 @@ impl IntoElements for Received { } } -impl<'a> TryFrom<&'a Element> for Description { +impl TryFrom for Description { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("description", ns::JINGLE_FT) { return Err(Error::ParseError("This is not a JingleFT description element.")); } @@ -145,7 +145,7 @@ impl<'a> TryFrom<&'a Element> for Description { if !hash_element.is("hash", ns::HASHES) { return Err(Error::ParseError("Unknown element in JingleFT range.")); } - range_hashes.push(Hash::try_from(hash_element)?); + range_hashes.push(Hash::try_from(hash_element.clone())?); } range = Some(Range { offset: offset, @@ -153,7 +153,7 @@ impl<'a> TryFrom<&'a Element> for Description { hashes: range_hashes, }); } else if file_payload.is("hash", ns::HASHES) { - hashes.push(Hash::try_from(file_payload)?); + hashes.push(Hash::try_from(file_payload.clone())?); } else { return Err(Error::ParseError("Unknown element in JingleFT file.")); } @@ -174,57 +174,57 @@ impl<'a> TryFrom<&'a Element> for Description { } } -impl<'a> Into for &'a File { +impl Into for File { fn into(self) -> Element { let mut root = Element::builder("file") .ns(ns::JINGLE_FT) .build(); - if let Some(ref date) = self.date { + if let Some(date) = self.date { root.append_child(Element::builder("date") .ns(ns::JINGLE_FT) - .append(date.clone()) + .append(date) .build()); } - if let Some(ref media_type) = self.media_type { + if let Some(media_type) = self.media_type { root.append_child(Element::builder("media-type") .ns(ns::JINGLE_FT) - .append(media_type.clone()) + .append(media_type) .build()); } - if let Some(ref name) = self.name { + if let Some(name) = self.name { root.append_child(Element::builder("name") .ns(ns::JINGLE_FT) - .append(name.clone()) + .append(name) .build()); } - if let Some(ref desc) = self.desc { + if let Some(desc) = self.desc { root.append_child(Element::builder("desc") .ns(ns::JINGLE_FT) - .append(desc.clone()) + .append(desc) .build()); } - if let Some(ref size) = self.size { + if let Some(size) = self.size { root.append_child(Element::builder("size") .ns(ns::JINGLE_FT) .append(format!("{}", size)) .build()); } - if let Some(ref range) = self.range { + if let Some(range) = self.range { root.append_child(Element::builder("range") .ns(ns::JINGLE_FT) - .append(range.clone()) + .append(range) .build()); } - for hash in self.hashes.clone() { - root.append_child((&hash).into()); + for hash in self.hashes { + root.append_child(hash.into()); } root } } -impl<'a> Into for &'a Description { +impl Into for Description { fn into(self) -> Element { - let file: Element = (&self.file).into(); + let file: Element = self.file.into(); Element::builder("description") .ns(ns::JINGLE_FT) .append(file) @@ -252,7 +252,7 @@ mod tests { "#.parse().unwrap(); - let desc = Description::try_from(&elem).unwrap(); + let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.desc, None); @@ -274,7 +274,7 @@ mod tests { "#.parse().unwrap(); - let desc = Description::try_from(&elem).unwrap(); + let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, None); assert_eq!(desc.file.name, None); assert_eq!(desc.file.desc, None); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 73544c26666b6eb2efbff481b42c24a741ca0411..d89df221f80ce5a79d810d0ae334c03b999e053e 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -21,10 +21,10 @@ pub struct Transport { pub stanza: Stanza, } -impl<'a> TryFrom<&'a Element> for Transport { +impl TryFrom for Transport { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if elem.is("transport", ns::JINGLE_IBB) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in JingleIBB element.")); @@ -43,7 +43,7 @@ impl<'a> TryFrom<&'a Element> for Transport { } } -impl<'a> Into for &'a Transport { +impl Into for Transport { fn into(self) -> Element { Element::builder("transport") .ns(ns::JINGLE_IBB) @@ -62,7 +62,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let transport = Transport::try_from(&elem).unwrap(); + let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.block_size, 3); assert_eq!(transport.sid, "coucou"); assert_eq!(transport.stanza, Stanza::Iq); @@ -71,7 +71,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = Transport::try_from(&elem).unwrap_err(); + let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -79,7 +79,7 @@ mod tests { assert_eq!(message, "Required attribute 'block-size' missing."); let elem: Element = "".parse().unwrap(); - let error = Transport::try_from(&elem).unwrap_err(); + let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, _ => panic!(), @@ -87,7 +87,7 @@ mod tests { assert_eq!(message.description(), "number too large to fit in target type"); let elem: Element = "".parse().unwrap(); - let error = Transport::try_from(&elem).unwrap_err(); + let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, _ => panic!(), @@ -95,7 +95,7 @@ mod tests { assert_eq!(message.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); - let error = Transport::try_from(&elem).unwrap_err(); + let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -106,7 +106,7 @@ mod tests { #[test] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); - let error = Transport::try_from(&elem).unwrap_err(); + let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index cb43e743c9fc0d60a37870fe5d2e4d747208ccff..fa5ee667e6bb3b1b7c1d939c944808ac2f2f0167 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -63,7 +63,7 @@ pub struct Candidate { pub type_: Type, } -impl<'a> Into for &'a Candidate { +impl Into for Candidate { fn into(self) -> Element { Element::builder("candidate") .ns(ns::JINGLE_S5B) @@ -129,10 +129,10 @@ pub struct Transport { pub payload: TransportPayload, } -impl<'a> TryFrom<&'a Element> for Transport { +impl TryFrom for Transport { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if elem.is("transport", ns::JINGLE_S5B) { let sid = get_attr!(elem, "sid", required); let dstaddr = get_attr!(elem, "dstaddr", optional); @@ -200,7 +200,7 @@ impl<'a> TryFrom<&'a Element> for Transport { } } -impl<'a> Into for &'a Transport { +impl Into for Transport { fn into(self) -> Element { Element::builder("transport") .ns(ns::JINGLE_S5B) @@ -208,15 +208,15 @@ impl<'a> Into for &'a Transport { .attr("dstaddr", self.dstaddr.clone()) .attr("mode", self.mode.clone()) .append(match self.payload { - TransportPayload::Candidates(ref candidates) => { + TransportPayload::Candidates(candidates) => { candidates.iter() - .map(|candidate| -> Element { candidate.into() }) + .map(|candidate| -> Element { candidate.clone().into() }) .collect::>() }, - TransportPayload::Activated(ref cid) => { + TransportPayload::Activated(cid) => { vec!(Element::builder("activated") .ns(ns::JINGLE_S5B) - .attr("cid", cid.to_owned()) + .attr("cid", cid) .build()) }, TransportPayload::CandidateError => { @@ -248,7 +248,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let transport = Transport::try_from(&elem).unwrap(); + let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.sid, "coucou"); assert_eq!(transport.dstaddr, None); assert_eq!(transport.mode, Mode::Tcp); @@ -267,7 +267,7 @@ mod tests { mode: Mode::Tcp, payload: TransportPayload::Activated(String::from("coucou")), }; - let elem2: Element = (&transport).into(); + let elem2: Element = transport.into(); assert_eq!(elem, elem2); } @@ -287,7 +287,7 @@ mod tests { type_: Type::Direct, })), }; - let elem2: Element = (&transport).into(); + let elem2: Element = transport.into(); assert_eq!(elem, elem2); } } diff --git a/src/mam.rs b/src/mam.rs index b673154bd20685a25c160669ce026546ac8c5074..22ec0576fa4f1d45da3eb2fcf1858269a9c8b83a 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -60,9 +60,9 @@ impl FromStr for DefaultPrefs { } } -impl<'a> IntoAttributeValue for &'a DefaultPrefs { +impl IntoAttributeValue for DefaultPrefs { fn into_attribute_value(self) -> Option { - Some(String::from(match *self { + Some(String::from(match self { DefaultPrefs::Always => "always", DefaultPrefs::Never => "never", DefaultPrefs::Roster => "roster", @@ -77,10 +77,10 @@ pub struct Prefs { pub never: Vec, } -impl<'a> TryFrom<&'a Element> for Query { +impl TryFrom for Query { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("query", ns::MAM) { return Err(Error::ParseError("This is not a query element.")); } @@ -88,9 +88,9 @@ impl<'a> TryFrom<&'a Element> for Query { let mut set = None; for child in elem.children() { if child.is("x", ns::DATA_FORMS) { - form = Some(DataForm::try_from(child)?); + form = Some(DataForm::try_from(child.clone())?); } else if child.is("set", ns::RSM) { - set = Some(Set::try_from(child)?); + set = Some(Set::try_from(child.clone())?); } else { return Err(Error::ParseError("Unknown child in query element.")); } @@ -101,17 +101,17 @@ impl<'a> TryFrom<&'a Element> for Query { } } -impl<'a> TryFrom<&'a Element> for Result_ { +impl TryFrom for Result_ { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("result", ns::MAM) { return Err(Error::ParseError("This is not a result element.")); } let mut forwarded = None; for child in elem.children() { if child.is("forwarded", ns::FORWARD) { - forwarded = Some(Forwarded::try_from(child)?); + forwarded = Some(Forwarded::try_from(child.clone())?); } else { return Err(Error::ParseError("Unknown child in result element.")); } @@ -127,17 +127,17 @@ impl<'a> TryFrom<&'a Element> for Result_ { } } -impl<'a> TryFrom<&'a Element> for Fin { +impl TryFrom for Fin { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("fin", ns::MAM) { return Err(Error::ParseError("This is not a fin element.")); } let mut set = None; for child in elem.children() { if child.is("set", ns::RSM) { - set = Some(Set::try_from(child)?); + set = Some(Set::try_from(child.clone())?); } else { return Err(Error::ParseError("Unknown child in fin element.")); } @@ -153,10 +153,10 @@ impl<'a> TryFrom<&'a Element> for Fin { } } -impl<'a> TryFrom<&'a Element> for Prefs { +impl TryFrom for Prefs { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("prefs", ns::MAM) { return Err(Error::ParseError("This is not a prefs element.")); } @@ -186,7 +186,7 @@ impl<'a> TryFrom<&'a Element> for Prefs { } } -impl<'a> Into for &'a Query { +impl Into for Query { fn into(self) -> Element { let mut elem = Element::builder("query") .ns(ns::MAM) @@ -194,43 +194,43 @@ impl<'a> Into for &'a Query { .attr("node", self.node.clone()) .build(); //if let Some(form) = self.form { - // elem.append_child((&form).into()); + // elem.append_child(form.into()); //} - if let Some(ref set) = self.set { + if let Some(set) = self.set { elem.append_child(set.into()); } elem } } -impl<'a> Into for &'a Result_ { +impl Into for Result_ { fn into(self) -> Element { let mut elem = Element::builder("result") .ns(ns::MAM) - .attr("queryid", self.queryid.clone()) - .attr("id", self.id.clone()) + .attr("queryid", self.queryid) + .attr("id", self.id) .build(); - elem.append_child((&self.forwarded).into()); + elem.append_child(self.forwarded.into()); elem } } -impl<'a> Into for &'a Fin { +impl Into for Fin { fn into(self) -> Element { let mut elem = Element::builder("fin") .ns(ns::MAM) .attr("complete", if self.complete { Some("true") } else { None }) .build(); - elem.append_child((&self.set).into()); + elem.append_child(self.set.into()); elem } } -impl<'a> Into for &'a Prefs { +impl Into for Prefs { fn into(self) -> Element { let mut elem = Element::builder("prefs") .ns(ns::MAM) - .attr("default", &self.default_) + .attr("default", self.default_) .build(); if !self.always.is_empty() { let mut always = Element::builder("always") @@ -267,7 +267,7 @@ mod tests { #[test] fn test_query() { let elem: Element = "".parse().unwrap(); - Query::try_from(&elem).unwrap(); + Query::try_from(elem).unwrap(); } #[test] @@ -282,7 +282,7 @@ mod tests { "#.parse().unwrap(); - Result_::try_from(&elem).unwrap(); + Result_::try_from(elem).unwrap(); } #[test] @@ -295,7 +295,7 @@ mod tests { "#.parse().unwrap(); - Fin::try_from(&elem).unwrap(); + Fin::try_from(elem).unwrap(); } #[test] @@ -312,7 +312,7 @@ mod tests { "#.parse().unwrap(); - Query::try_from(&elem).unwrap(); + Query::try_from(elem).unwrap(); } #[test] @@ -332,13 +332,13 @@ mod tests { "#.parse().unwrap(); - Query::try_from(&elem).unwrap(); + Query::try_from(elem).unwrap(); } #[test] fn test_prefs_get() { let elem: Element = "".parse().unwrap(); - Prefs::try_from(&elem).unwrap(); + Prefs::try_from(elem).unwrap(); let elem: Element = r#" @@ -346,7 +346,7 @@ mod tests { "#.parse().unwrap(); - Prefs::try_from(&elem).unwrap(); + Prefs::try_from(elem).unwrap(); } #[test] @@ -361,13 +361,13 @@ mod tests { "#.parse().unwrap(); - Prefs::try_from(&elem).unwrap(); + Prefs::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Query::try_from(&elem).unwrap_err(); + let error = Query::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -379,7 +379,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let replace = Query { queryid: None, node: None, form: None, set: None }; - let elem2 = (&replace).into(); + let elem2 = replace.into(); assert_eq!(elem, elem2); } } diff --git a/src/media_element.rs b/src/media_element.rs index d4d31985caad71b54afa050ddd0594a30a9650f3..54ffee132eb5a6ee7b20bbd4012e166b6d7d8cb4 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -25,10 +25,10 @@ pub struct MediaElement { pub uris: Vec, } -impl<'a> TryFrom<&'a Element> for MediaElement { +impl TryFrom for MediaElement { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("media", ns::MEDIA_ELEMENT) { return Err(Error::ParseError("This is not a media element.")); } @@ -60,7 +60,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.width.is_none()); assert!(media.height.is_none()); assert!(media.uris.is_empty()); @@ -69,7 +69,7 @@ mod tests { #[test] fn test_width_height() { let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert_eq!(media.width.unwrap(), 32); assert_eq!(media.height.unwrap(), 32); } @@ -77,7 +77,7 @@ mod tests { #[test] fn test_uri() { let elem: Element = "https://example.org/".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert_eq!(media.uris.len(), 1); assert_eq!(media.uris[0].type_, "text/html"); assert_eq!(media.uris[0].uri, "https://example.org/"); @@ -86,26 +86,26 @@ mod tests { #[test] fn test_invalid_width_height() { let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.width.is_none()); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.width.is_none()); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.height.is_none()); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.height.is_none()); } #[test] fn test_unknown_child() { let elem: Element = "".parse().unwrap(); - let error = MediaElement::try_from(&elem).unwrap_err(); + let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -116,7 +116,7 @@ mod tests { #[test] fn test_bad_uri() { let elem: Element = "https://example.org/".parse().unwrap(); - let error = MediaElement::try_from(&elem).unwrap_err(); + let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -124,7 +124,7 @@ mod tests { assert_eq!(message, "Attribute type on uri is mandatory."); let elem: Element = "".parse().unwrap(); - let error = MediaElement::try_from(&elem).unwrap_err(); + let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -146,7 +146,7 @@ mod tests { http://victim.example.com/challenges/speech.mp3?F3A6292C "#.parse().unwrap(); - let media = MediaElement::try_from(&elem).unwrap(); + let media = MediaElement::try_from(elem).unwrap(); assert!(media.width.is_none()); assert!(media.height.is_none()); assert_eq!(media.uris.len(), 3); @@ -177,7 +177,7 @@ mod tests { [ ... ] "#.parse().unwrap(); - let form = DataForm::try_from(&elem).unwrap(); + let form = DataForm::try_from(elem).unwrap(); assert_eq!(form.fields.len(), 1); assert_eq!(form.fields[0].var, "ocr"); assert_eq!(form.fields[0].media[0].width, Some(290)); diff --git a/src/message.rs b/src/message.rs index a309fa015ff4a4490148efd8c4218b85685861c5..be47756a5c2d622dafc77936184c9e13e6621d7f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -42,10 +42,10 @@ pub enum MessagePayload { Unknown(Element), } -impl<'a> TryFrom<&'a Element> for MessagePayload { +impl TryFrom for MessagePayload { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { ("error", ns::JABBER_CLIENT) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), @@ -84,20 +84,20 @@ impl<'a> TryFrom<&'a Element> for MessagePayload { } } -impl<'a> Into for &'a MessagePayload { +impl Into for MessagePayload { fn into(self) -> Element { - match *self { - MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), - MessagePayload::Attention(ref attention) => attention.into(), - MessagePayload::ChatState(ref chatstate) => chatstate.into(), - MessagePayload::Receipt(ref receipt) => receipt.into(), - MessagePayload::Delay(ref delay) => delay.into(), - MessagePayload::MessageCorrect(ref replace) => replace.into(), - MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(), - MessagePayload::StanzaId(ref stanza_id) => stanza_id.into(), - MessagePayload::MamResult(ref result) => result.into(), - - MessagePayload::Unknown(ref elem) => elem.clone(), + match self { + MessagePayload::StanzaError(stanza_error) => stanza_error.into(), + MessagePayload::Attention(attention) => attention.into(), + MessagePayload::ChatState(chatstate) => chatstate.into(), + MessagePayload::Receipt(receipt) => receipt.into(), + MessagePayload::Delay(delay) => delay.into(), + MessagePayload::MessageCorrect(replace) => replace.into(), + MessagePayload::ExplicitMessageEncryption(eme) => eme.into(), + MessagePayload::StanzaId(stanza_id) => stanza_id.into(), + MessagePayload::MamResult(result) => result.into(), + + MessagePayload::Unknown(elem) => elem.clone(), } } } @@ -162,10 +162,10 @@ pub struct Message { pub payloads: Vec, } -impl<'a> TryFrom<&'a Element> for Message { +impl TryFrom for Message { type Error = Error; - fn try_from(root: &'a Element) -> Result { + fn try_from(root: Element) -> Result { if !root.is("message", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a message element.")); } @@ -219,7 +219,7 @@ impl<'a> TryFrom<&'a Element> for Message { } } -impl<'a> Into for &'a Message { +impl Into for Message { fn into(self) -> Element { let mut stanza = Element::builder("message") .ns(ns::JABBER_CLIENT) @@ -264,7 +264,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let message = Message::try_from(&elem).unwrap(); + let message = Message::try_from(elem).unwrap(); assert_eq!(message.from, None); assert_eq!(message.to, None); assert_eq!(message.id, None); @@ -285,18 +285,19 @@ mod tests { thread: None, payloads: vec!(), }; - let elem2 = (&message).into(); + let elem2 = message.into(); assert_eq!(elem, elem2); } #[test] fn test_body() { let elem: Element = "Hello world!".parse().unwrap(); - let message = Message::try_from(&elem).unwrap(); + let elem1 = elem.clone(); + let message = Message::try_from(elem).unwrap(); assert_eq!(message.bodies[""], "Hello world!"); - let elem2 = (&message).into(); - assert_eq!(elem, elem2); + let elem2 = message.into(); + assert_eq!(elem1, elem2); } #[test] @@ -314,25 +315,27 @@ mod tests { thread: None, payloads: vec!(), }; - let elem2 = (&message).into(); + let elem2 = message.into(); assert_eq!(elem, elem2); } #[test] fn test_subject() { let elem: Element = "Hello world!".parse().unwrap(); - let message = Message::try_from(&elem).unwrap(); + let elem1 = elem.clone(); + let message = Message::try_from(elem).unwrap(); assert_eq!(message.subjects[""], "Hello world!"); - let elem2 = (&message).into(); - assert_eq!(elem, elem2); + let elem2 = message.into(); + assert_eq!(elem1, elem2); } #[test] fn test_attention() { let elem: Element = "".parse().unwrap(); - let message = Message::try_from(&elem).unwrap(); - let elem2 = (&message).into(); - assert_eq!(elem, elem2); + let elem1 = elem.clone(); + let message = Message::try_from(elem).unwrap(); + let elem2 = message.into(); + assert_eq!(elem1, elem2); } } diff --git a/src/message_correct.rs b/src/message_correct.rs index ab0d3b2b9532a9cecd16bd59bba6e7cb72eb9df9..35494f810b04c59ad3365049a79338655967307c 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -17,10 +17,10 @@ pub struct Replace { pub id: String, } -impl<'a> TryFrom<&'a Element> for Replace { +impl TryFrom for Replace { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("replace", ns::MESSAGE_CORRECT) { return Err(Error::ParseError("This is not a replace element.")); } @@ -32,7 +32,7 @@ impl<'a> TryFrom<&'a Element> for Replace { } } -impl<'a> Into for &'a Replace { +impl Into for Replace { fn into(self) -> Element { Element::builder("replace") .ns(ns::MESSAGE_CORRECT) @@ -48,13 +48,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Replace::try_from(&elem).unwrap(); + Replace::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Replace::try_from(&elem).unwrap_err(); + let error = Replace::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -65,7 +65,7 @@ mod tests { #[test] fn test_invalid_id() { let elem: Element = "".parse().unwrap(); - let error = Replace::try_from(&elem).unwrap_err(); + let error = Replace::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -77,7 +77,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let replace = Replace { id: String::from("coucou") }; - let elem2 = (&replace).into(); + let elem2 = replace.into(); assert_eq!(elem, elem2); } } diff --git a/src/ping.rs b/src/ping.rs index baebf68b133a9f4ebc07bdf769ece76cd1f360d8..84a7cebf0d9796abc09046bb29346a617c84b99c 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -16,10 +16,10 @@ use ns; #[derive(Debug, Clone)] pub struct Ping; -impl<'a> TryFrom<&'a Element> for Ping { +impl TryFrom for Ping { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("ping", ns::PING) { return Err(Error::ParseError("This is not a ping element.")); } @@ -33,7 +33,7 @@ impl<'a> TryFrom<&'a Element> for Ping { } } -impl<'a> Into for &'a Ping { +impl Into for Ping { fn into(self) -> Element { Element::builder("ping") .ns(ns::PING) @@ -48,13 +48,13 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Ping::try_from(&elem).unwrap(); + Ping::try_from(elem).unwrap(); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = Ping::try_from(&elem).unwrap_err(); + let error = Ping::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -65,7 +65,7 @@ mod tests { #[test] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = Ping::try_from(&elem).unwrap_err(); + let error = Ping::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/presence.rs b/src/presence.rs index c15950a90082e339e91bfbb56ce03e1d5904cfbb..9dd1c62ca20708e25ce8bedc72412f064b983be2 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -57,10 +57,10 @@ pub enum PresencePayload { Unknown(Element), } -impl<'a> TryFrom<&'a Element> for PresencePayload { +impl TryFrom for PresencePayload { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { ("error", ns::JABBER_CLIENT) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), @@ -73,20 +73,20 @@ impl<'a> TryFrom<&'a Element> for PresencePayload { // XEP-0390 ("c", ns::ECAPS2) => PresencePayload::ECaps2(ECaps2::try_from(elem)?), - _ => PresencePayload::Unknown(elem.clone()), + _ => PresencePayload::Unknown(elem), }) } } -impl<'a> Into for &'a PresencePayload { +impl Into for PresencePayload { fn into(self) -> Element { - match *self { - PresencePayload::StanzaError(ref stanza_error) => stanza_error.into(), - PresencePayload::Delay(ref delay) => delay.into(), - PresencePayload::Idle(ref idle) => idle.into(), - PresencePayload::ECaps2(ref ecaps2) => ecaps2.into(), + match self { + PresencePayload::StanzaError(stanza_error) => stanza_error.into(), + PresencePayload::Delay(delay) => delay.into(), + PresencePayload::Idle(idle) => idle.into(), + PresencePayload::ECaps2(ecaps2) => ecaps2.into(), - PresencePayload::Unknown(ref elem) => elem.clone(), + PresencePayload::Unknown(elem) => elem.clone(), } } } @@ -157,10 +157,10 @@ pub struct Presence { pub payloads: Vec, } -impl<'a> TryFrom<&'a Element> for Presence { +impl TryFrom for Presence { type Error = Error; - fn try_from(root: &'a Element) -> Result { + fn try_from(root: Element) -> Result { if !root.is("presence", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a presence element.")); } @@ -232,7 +232,7 @@ impl<'a> TryFrom<&'a Element> for Presence { } } -impl<'a> Into for &'a Presence { +impl Into for Presence { fn into(self) -> Element { let mut stanza = Element::builder("presence") .ns(ns::JABBER_CLIENT) @@ -267,7 +267,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.from, None); assert_eq!(presence.to, None); assert_eq!(presence.id, None); @@ -288,14 +288,14 @@ mod tests { priority: 0i8, payloads: vec!(), }; - let elem2 = (&presence).into(); + let elem2 = presence.into(); assert_eq!(elem, elem2); } #[test] fn test_show() { let elem: Element = "chat".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.show, Some(Show::Chat)); } @@ -304,7 +304,7 @@ mod tests { fn test_missing_show_value() { // "online" used to be a pretty common mistake. let elem: Element = "".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -316,7 +316,7 @@ mod tests { fn test_invalid_show() { // "online" used to be a pretty common mistake. let elem: Element = "online".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -327,7 +327,7 @@ mod tests { #[test] fn test_empty_status() { let elem: Element = "".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); assert_eq!(presence.statuses[""], ""); @@ -336,7 +336,7 @@ mod tests { #[test] fn test_status() { let elem: Element = "Here!".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); assert_eq!(presence.statuses[""], "Here!"); @@ -345,7 +345,7 @@ mod tests { #[test] fn test_multiple_statuses() { let elem: Element = "Here!Là!".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 2); assert_eq!(presence.statuses[""], "Here!"); @@ -355,7 +355,7 @@ mod tests { #[test] fn test_invalid_multiple_statuses() { let elem: Element = "Here!Là!".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -366,7 +366,7 @@ mod tests { #[test] fn test_priority() { let elem: Element = "-1".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.priority, -1i8); } @@ -374,7 +374,7 @@ mod tests { #[test] fn test_invalid_priority() { let elem: Element = "128".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); match error { Error::ParseIntError(_) => (), _ => panic!(), @@ -384,7 +384,7 @@ mod tests { #[test] fn test_unknown_child() { let elem: Element = "".parse().unwrap(); - let presence = Presence::try_from(&elem).unwrap(); + let presence = Presence::try_from(elem).unwrap(); let payload = &presence.payloads[0]; assert!(payload.is("test", "invalid")); } @@ -392,7 +392,7 @@ mod tests { #[test] fn test_invalid_status_child() { let elem: Element = "".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -403,7 +403,7 @@ mod tests { #[test] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); - let error = Presence::try_from(&elem).unwrap_err(); + let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -426,7 +426,7 @@ mod tests { priority: 0i8, payloads: vec!(), }; - let elem: Element = (&presence).into(); + let elem: Element = presence.into(); assert!(elem.is("presence", ns::JABBER_CLIENT)); assert!(elem.children().collect::>()[0].is("status", ns::JABBER_CLIENT)); } diff --git a/src/receipts.rs b/src/receipts.rs index 27392fb8cb161c2f1cea195907754b35d06d876b..f7c1d50d2af1352210c98d6c3317742ef04b7e47 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -18,10 +18,10 @@ pub enum Receipt { Received(String), } -impl<'a> TryFrom<&'a Element> for Receipt { +impl TryFrom for Receipt { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { for _ in elem.children() { return Err(Error::ParseError("Unknown child in receipt element.")); } @@ -36,16 +36,16 @@ impl<'a> TryFrom<&'a Element> for Receipt { } } -impl<'a> Into for &'a Receipt { +impl Into for Receipt { fn into(self) -> Element { - match *self { + match self { Receipt::Request => Element::builder("request") .ns(ns::RECEIPTS) .build(), - Receipt::Received(ref id) => Element::builder("received") - .ns(ns::RECEIPTS) - .attr("id", id.clone()) - .build(), + Receipt::Received(id) => Element::builder("received") + .ns(ns::RECEIPTS) + .attr("id", id) + .build(), } } } @@ -57,23 +57,23 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Receipt::try_from(&elem).unwrap(); + Receipt::try_from(elem).unwrap(); let elem: Element = "".parse().unwrap(); - Receipt::try_from(&elem).unwrap(); + Receipt::try_from(elem).unwrap(); let elem: Element = "".parse().unwrap(); - Receipt::try_from(&elem).unwrap(); + Receipt::try_from(elem).unwrap(); } #[test] fn test_serialise() { let receipt = Receipt::Request; - let elem: Element = (&receipt).into(); + let elem: Element = receipt.into(); assert!(elem.is("request", ns::RECEIPTS)); let receipt = Receipt::Received("coucou".to_owned()); - let elem: Element = (&receipt).into(); + let elem: Element = receipt.into(); assert!(elem.is("received", ns::RECEIPTS)); assert_eq!(elem.attr("id"), Some("coucou")); } diff --git a/src/rsm.rs b/src/rsm.rs index a697b3705f3fe6a19fa345b4acfc0101a6bb6ae1..7ac80b192b711f1f1828b7aa632a7446423d2ef2 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -24,10 +24,10 @@ pub struct Set { pub max: Option, } -impl<'a> TryFrom<&'a Element> for Set { +impl TryFrom for Set { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("set", ns::RSM) { return Err(Error::ParseError("This is not a RSM element.")); } @@ -86,7 +86,7 @@ impl<'a> TryFrom<&'a Element> for Set { } } -impl<'a> Into for &'a Set { +impl Into for Set { fn into(self) -> Element { let mut elem = Element::builder("set") .ns(ns::RSM) @@ -126,7 +126,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let set = Set::try_from(&elem).unwrap(); + let set = Set::try_from(elem).unwrap(); assert_eq!(set.after, None); assert_eq!(set.before, None); assert_eq!(set.count, None); @@ -142,7 +142,7 @@ mod tests { #[test] fn test_unknown() { let elem: Element = "".parse().unwrap(); - let error = Set::try_from(&elem).unwrap_err(); + let error = Set::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -153,7 +153,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = Set::try_from(&elem).unwrap_err(); + let error = Set::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -174,14 +174,15 @@ mod tests { last: None, max: None, }; - let elem2 = (&rsm).into(); + let elem2 = rsm.into(); assert_eq!(elem, elem2); } #[test] fn test_first_index() { let elem: Element = "coucou".parse().unwrap(); - let set = Set::try_from(&elem).unwrap(); + let elem1 = elem.clone(); + let set = Set::try_from(elem).unwrap(); assert_eq!(set.first, Some(String::from("coucou"))); assert_eq!(set.first_index, Some(4)); @@ -195,7 +196,7 @@ mod tests { last: None, max: None, }; - let elem2 = (&set2).into(); - assert_eq!(elem, elem2); + let elem2 = set2.into(); + assert_eq!(elem1, elem2); } } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 85f21298635c99dbae616a859b423e3b4c4c0e9f..4f30f3a44171529b4066c3c931d942ae710de766 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -150,10 +150,10 @@ pub struct StanzaError { pub other: Option, } -impl<'a> TryFrom<&'a Element> for StanzaError { +impl TryFrom for StanzaError { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("error", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not an error element.")); } @@ -205,7 +205,7 @@ impl<'a> TryFrom<&'a Element> for StanzaError { } } -impl<'a> Into for &'a StanzaError { +impl Into for StanzaError { fn into(self) -> Element { let mut root = Element::builder("error") .ns(ns::JABBER_CLIENT) @@ -240,7 +240,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let error = StanzaError::try_from(&elem).unwrap(); + let error = StanzaError::try_from(elem).unwrap(); assert_eq!(error.type_, ErrorType::Cancel); assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition); } @@ -248,7 +248,7 @@ mod tests { #[test] fn test_invalid_type() { let elem: Element = "".parse().unwrap(); - let error = StanzaError::try_from(&elem).unwrap_err(); + let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -256,7 +256,7 @@ mod tests { assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); - let error = StanzaError::try_from(&elem).unwrap_err(); + let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -267,7 +267,7 @@ mod tests { #[test] fn test_invalid_condition() { let elem: Element = "".parse().unwrap(); - let error = StanzaError::try_from(&elem).unwrap_err(); + let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 2b87890bf338daf2f1f1595614b4d5a6981b6be7..38008d0c5c8230050c7a12c8f200e9dccebe88fc 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -24,10 +24,10 @@ pub enum StanzaId { }, } -impl<'a> TryFrom<&'a Element> for StanzaId { +impl TryFrom for StanzaId { type Error = Error; - fn try_from(elem: &'a Element) -> Result { + fn try_from(elem: Element) -> Result { let is_stanza_id = elem.is("stanza-id", ns::SID); if !is_stanza_id && !elem.is("origin-id", ns::SID) { return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); @@ -45,20 +45,20 @@ impl<'a> TryFrom<&'a Element> for StanzaId { } } -impl<'a> Into for &'a StanzaId { +impl Into for StanzaId { fn into(self) -> Element { - match *self { - StanzaId::StanzaId { ref id, ref by } => { + match self { + StanzaId::StanzaId { id, by } => { Element::builder("stanza-id") .ns(ns::SID) - .attr("id", id.clone()) - .attr("by", String::from(by.clone())) + .attr("id", id) + .attr("by", String::from(by)) .build() }, - StanzaId::OriginId { ref id } => { + StanzaId::OriginId { id } => { Element::builder("origin-id") .ns(ns::SID) - .attr("id", id.clone()) + .attr("id", id) .build() }, } @@ -73,7 +73,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let stanza_id = StanzaId::try_from(&elem).unwrap(); + let stanza_id = StanzaId::try_from(elem).unwrap(); if let StanzaId::StanzaId { id, by } = stanza_id { assert_eq!(id, String::from("coucou")); assert_eq!(by, Jid::from_str("coucou@coucou").unwrap()); @@ -82,7 +82,7 @@ mod tests { } let elem: Element = "".parse().unwrap(); - let stanza_id = StanzaId::try_from(&elem).unwrap(); + let stanza_id = StanzaId::try_from(elem).unwrap(); if let StanzaId::OriginId { id } = stanza_id { assert_eq!(id, String::from("coucou")); } else { @@ -93,7 +93,7 @@ mod tests { #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); - let error = StanzaId::try_from(&elem).unwrap_err(); + let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -104,7 +104,7 @@ mod tests { #[test] fn test_invalid_id() { let elem: Element = "".parse().unwrap(); - let error = StanzaId::try_from(&elem).unwrap_err(); + let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -115,7 +115,7 @@ mod tests { #[test] fn test_invalid_by() { let elem: Element = "".parse().unwrap(); - let error = StanzaId::try_from(&elem).unwrap_err(); + let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -127,7 +127,7 @@ mod tests { fn test_serialise() { let elem: Element = "".parse().unwrap(); let stanza_id = StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; - let elem2 = (&stanza_id).into(); + let elem2 = stanza_id.into(); assert_eq!(elem, elem2); } } From cde19967a62686a2fdc3ce137ed72c2bb7379a5a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 23 May 2017 23:50:00 +0100 Subject: [PATCH 0245/1020] Release version 0.3.0! --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 41d4a464fedf3e5b83258b0f9608007f6d2433d4..a0d95aa30586e6afa8aa98a760d6069b5770399e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.2.0" +version = "0.3.0" authors = ["Emmanuel Gil Peyrot "] description = "Collection of parsers and serialisers for XMPP extensions" homepage = "https://hg.linkmauve.fr/xmpp-parsers" From 6f7e504c8a82fa64b0ed53221204f82fe9e6755a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 00:12:16 +0100 Subject: [PATCH 0247/1020] ChangeLog: Forgotten update for 0.3.0. --- ChangeLog | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ChangeLog b/ChangeLog index d8e85e11df28f7d4fe9955a72dde69553b21afc8..e8c42e2a450e16e1ee37f80a0ac2d2d6015cb07c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +Version 0.3.0: +2017-05-23 Emmanuel Gil Peyrot + * Big changes: + - All parsers and serialisers now consume their argument, this + makes the API way more efficient, but you will have to clone + before passing your structs in it if you want to keep them. + - Payloads of stanzas are not parsed automatically anymore, to + let applications which want to forward them as-is do so more + easily. Parsing now always succeeds on unknown payloads, it + just puts them into an Unknown value containing the existing + minidom Element. + * New parsers/serialisers: + - Last User Interaction in Presence, XEP-0319. + * Improved parsers/serialisers: + - Message now supports subject, bodies and threads as per + RFC 6121 §5.2. + - Replace most attribute reads with a nice macro. + - Use enums for more enum-like things, for example Algo in + Hash, or FieldType in DataForm. + - Wire up stanza-id and origin-id to MessagePayload. + - Wire up MAM elements to message and iq payloads. + - Changes in the RSM API. + - Add support for more data forms elements, but still not the + complete set. + - Thanks to minidom 0.3.1, check for explicitly disallowed + extra attributes in some elements. + * Crate updates: + - minidom 0.4.1 + Version 0.2.0: 2017-05-06 Emmanuel Gil Peyrot * New parsers/serialisers: From fbeeae5c64ec2e4bc2034e50846b3bf56a8345c0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:30:33 +0100 Subject: [PATCH 0248/1020] presence: Simplify the code. --- src/presence.rs | 128 +++++++++++++++++++++++++----------------------- 1 file changed, 67 insertions(+), 61 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 9dd1c62ca20708e25ce8bedc72412f064b983be2..6b4b1ac4e587d4b70ca89fe7da13b925d57858eb 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -29,15 +29,31 @@ pub enum Show { Xa, } -impl IntoElements for Show { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = Element::builder(match self { - Show::Away => "away", - Show::Chat => "chat", - Show::Dnd => "dnd", - Show::Xa => "xa", - }).build(); - emitter.append_child(elem); +impl FromStr for Show { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "away" => Show::Away, + "chat" => Show::Chat, + "dnd" => Show::Dnd, + "xa" => Show::Xa, + + _ => return Err(Error::ParseError("Invalid value for show.")), + }) + } +} + +impl Into for Show { + fn into(self) -> Element { + Element::builder("show") + .append(match self { + Show::Away => "away", + Show::Chat => "chat", + Show::Dnd => "dnd", + Show::Xa => "xa", + }) + .build() } } @@ -86,7 +102,7 @@ impl Into for PresencePayload { PresencePayload::Idle(idle) => idle.into(), PresencePayload::ECaps2(ecaps2) => ecaps2.into(), - PresencePayload::Unknown(elem) => elem.clone(), + PresencePayload::Unknown(elem) => elem, } } } @@ -164,17 +180,20 @@ impl TryFrom for Presence { if !root.is("presence", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a presence element.")); } - let from = get_attr!(root, "from", optional); - let to = get_attr!(root, "to", optional); - let id = get_attr!(root, "id", optional); - let type_ = get_attr!(root, "type", default); - let mut show = None; - let mut statuses = BTreeMap::new(); let mut priority = None; - let mut payloads = vec!(); + let mut presence = Presence { + from: get_attr!(root, "from", optional), + to: get_attr!(root, "to", optional), + id: get_attr!(root, "id", optional), + type_: get_attr!(root, "type", default), + show: None, + statuses: BTreeMap::new(), + priority: 0i8, + payloads: vec!(), + }; for elem in root.children() { if elem.is("show", ns::JABBER_CLIENT) { - if show.is_some() { + if presence.show.is_some() { return Err(Error::ParseError("More than one show element in a presence.")); } for _ in elem.children() { @@ -183,14 +202,7 @@ impl TryFrom for Presence { for _ in elem.attrs() { return Err(Error::ParseError("Unknown attribute in show element.")); } - show = Some(match elem.text().as_ref() { - "away" => Show::Away, - "chat" => Show::Chat, - "dnd" => Show::Dnd, - "xa" => Show::Xa, - - _ => return Err(Error::ParseError("Invalid value for show.")), - }); + presence.show = Some(Show::from_str(elem.text().as_ref())?); } else if elem.is("status", ns::JABBER_CLIENT) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); @@ -201,7 +213,7 @@ impl TryFrom for Presence { } } let lang = get_attr!(elem, "xml:lang", default); - if statuses.insert(lang, elem.text()).is_some() { + if presence.statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); } } else if elem.is("priority", ns::JABBER_CLIENT) { @@ -216,46 +228,40 @@ impl TryFrom for Presence { } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { - payloads.push(elem.clone()); + presence.payloads.push(elem.clone()); } } - Ok(Presence { - from: from, - to: to, - id: id, - type_: type_, - show: show, - statuses: statuses, - priority: priority.unwrap_or(0i8), - payloads: payloads, - }) + if let Some(priority) = priority { + presence.priority = priority; + } + Ok(presence) } } impl Into for Presence { fn into(self) -> Element { - let mut stanza = Element::builder("presence") - .ns(ns::JABBER_CLIENT) - .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", self.id.clone()) - .attr("type", self.type_.clone()) - .append(self.show.clone()) - .append(self.statuses.iter().map(|(lang, status)| { - Element::builder("status") - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(status.clone()) - .build() - }).collect::>()) - .append(if self.priority == 0 { None } else { Some(format!("{}", self.priority)) }) - .build(); - for child in self.payloads.clone() { - stanza.append_child(child); - } - stanza + Element::builder("presence") + .ns(ns::JABBER_CLIENT) + .attr("from", self.from.and_then(|value| Some(String::from(value)))) + .attr("to", self.to.and_then(|value| Some(String::from(value)))) + .attr("id", self.id) + .attr("type", self.type_) + .append(match self.show { + Some(show) => Some({ let elem: Element = show.into(); elem }), + None => None + }) + .append(self.statuses.iter().map(|(lang, status)| { + Element::builder("status") + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(status) + .build() + }).collect::>()) + .append(if self.priority == 0 { None } else { Some(format!("{}", self.priority)) }) + .append(self.payloads) + .build() } } From 21b92621f0f7bbf611ab4618f5723399fcad924f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:32:04 +0100 Subject: [PATCH 0249/1020] iq, message: Remove useless clone on unknown element. --- src/iq.rs | 2 +- src/message.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index aaf7b027998ce5bf2818645892a9a06c79023b00..28b49efbaf6bba033b9830def28aef95cb29b76d 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -61,7 +61,7 @@ impl TryFrom for IqPayload { ("fin", ns::MAM) => IqPayload::MamFin(MamFin::try_from(elem)?), ("prefs", ns::MAM) => IqPayload::MamPrefs(MamPrefs::try_from(elem)?), - _ => IqPayload::Unknown(elem.clone()), + _ => IqPayload::Unknown(elem), }) } } diff --git a/src/message.rs b/src/message.rs index be47756a5c2d622dafc77936184c9e13e6621d7f..bc8a003516340bebe499b8e0c70f1798fd87bcbf 100644 --- a/src/message.rs +++ b/src/message.rs @@ -79,7 +79,7 @@ impl TryFrom for MessagePayload { // XEP-0380 ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), - _ => MessagePayload::Unknown(elem.clone()), + _ => MessagePayload::Unknown(elem), }) } } From 8182213666cfb10607c1e420537a5633c0a20088 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:35:09 +0100 Subject: [PATCH 0250/1020] iq: Reintroduce a reference Into to get the type from the payload. --- src/iq.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 28b49efbaf6bba033b9830def28aef95cb29b76d..7ab3309fc74b52cb6c7ed1263146afa76b59da14 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -43,7 +43,7 @@ impl TryFrom for IqPayload { fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { // XEP-0030 - ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem.clone())?), + ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?), // XEP-0047 ("open", ns::IBB) @@ -74,9 +74,9 @@ pub enum IqType { Error(StanzaError), } -impl IntoAttributeValue for IqType { +impl<'a> IntoAttributeValue for &'a IqType { fn into_attribute_value(self) -> Option { - Some(match self { + Some(match *self { IqType::Get(_) => "get", IqType::Set(_) => "set", IqType::Result(_) => "result", @@ -144,7 +144,7 @@ impl TryFrom for Iq { IqType::Result(None) } } else if type_ == "error" { - if let Some(payload) = error_payload.clone() { + if let Some(payload) = error_payload { IqType::Error(payload) } else { return Err(Error::ParseError("Wrong number of children in iq element.")); @@ -173,7 +173,7 @@ impl Into for IqPayload { IqPayload::MamFin(fin) => fin.into(), IqPayload::MamPrefs(prefs) => prefs.into(), - IqPayload::Unknown(elem) => elem.clone(), + IqPayload::Unknown(elem) => elem, } } } @@ -182,12 +182,12 @@ impl Into for Iq { fn into(self) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) - .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", self.id.clone()) - .attr("type", self.payload.clone()) + .attr("from", self.from.and_then(|value| Some(String::from(value)))) + .attr("to", self.to.and_then(|value| Some(String::from(value)))) + .attr("id", self.id) + .attr("type", &self.payload) .build(); - let elem = match self.payload.clone() { + let elem = match self.payload { IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem, From 453a3635fda8034c9f8a6da817f5be87d8cded20 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:40:11 +0100 Subject: [PATCH 0251/1020] message: Remove extra clones, and simplify Into. --- src/message.rs | 65 ++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/src/message.rs b/src/message.rs index bc8a003516340bebe499b8e0c70f1798fd87bcbf..13caed356d623307feedd049e2c79a7ca46c2f5f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -97,7 +97,7 @@ impl Into for MessagePayload { MessagePayload::StanzaId(stanza_id) => stanza_id.into(), MessagePayload::MamResult(result) => result.into(), - MessagePayload::Unknown(elem) => elem.clone(), + MessagePayload::Unknown(elem) => elem, } } } @@ -221,39 +221,36 @@ impl TryFrom for Message { impl Into for Message { fn into(self) -> Element { - let mut stanza = Element::builder("message") - .ns(ns::JABBER_CLIENT) - .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) - .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) - .attr("id", self.id.clone()) - .attr("type", self.type_.clone()) - .append(self.subjects.iter() - .map(|(lang, subject)| { - Element::builder("subject") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(subject.clone()) - .build() }) - .collect::>()) - .append(self.bodies.iter() - .map(|(lang, body)| { - Element::builder("body") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(body.clone()) - .build() }) - .collect::>()) - .build(); - for child in self.payloads.clone() { - stanza.append_child(child); - } - stanza + Element::builder("message") + .ns(ns::JABBER_CLIENT) + .attr("from", self.from.and_then(|value| Some(String::from(value)))) + .attr("to", self.to.and_then(|value| Some(String::from(value)))) + .attr("id", self.id) + .attr("type", self.type_) + .append(self.subjects.iter() + .map(|(lang, subject)| { + Element::builder("subject") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(subject) + .build() }) + .collect::>()) + .append(self.bodies.iter() + .map(|(lang, body)| { + Element::builder("body") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(body) + .build() }) + .collect::>()) + .append(self.payloads) + .build() } } From 6952f3adfc6575e71a6333d5f439aa51e2b47be0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:42:29 +0100 Subject: [PATCH 0252/1020] message_correct: Check for unwanted attributes. --- src/message_correct.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/message_correct.rs b/src/message_correct.rs index 35494f810b04c59ad3365049a79338655967307c..8ce747f114d9139b843c58bf34d52d8a46ec5043 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -27,6 +27,11 @@ impl TryFrom for Replace { for _ in elem.children() { return Err(Error::ParseError("Unknown child in replace element.")); } + for (attr, _) in elem.attrs() { + if attr != "id" { + return Err(Error::ParseError("Unknown attribute in replace element.")); + } + } let id = get_attr!(elem, "id", required); Ok(Replace { id }) } @@ -36,7 +41,7 @@ impl Into for Replace { fn into(self) -> Element { Element::builder("replace") .ns(ns::MESSAGE_CORRECT) - .attr("id", self.id.clone()) + .attr("id", self.id) .build() } } @@ -51,6 +56,17 @@ mod tests { Replace::try_from(elem).unwrap(); } + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = Replace::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in replace element."); + } + #[test] fn test_invalid_child() { let elem: Element = "".parse().unwrap(); From ecd98251bfd7a0421b4ecd791c3efbec617431a1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 21:44:35 +0100 Subject: [PATCH 0253/1020] rsm: Remove useless clones. --- src/rsm.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rsm.rs b/src/rsm.rs index 7ac80b192b711f1f1828b7aa632a7446423d2ef2..21d86ee790ad189768e48f087d4b13636c0e1397 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -92,10 +92,10 @@ impl Into for Set { .ns(ns::RSM) .build(); if self.after.is_some() { - elem.append_child(Element::builder("after").ns(ns::RSM).append(self.after.clone()).build()); + elem.append_child(Element::builder("after").ns(ns::RSM).append(self.after).build()); } if self.before.is_some() { - elem.append_child(Element::builder("before").ns(ns::RSM).append(self.before.clone()).build()); + elem.append_child(Element::builder("before").ns(ns::RSM).append(self.before).build()); } if self.count.is_some() { elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", self.count.unwrap())).build()); @@ -104,13 +104,13 @@ impl Into for Set { elem.append_child(Element::builder("first") .ns(ns::RSM) .attr("index", self.first_index.map(|index| format!("{}", index))) - .append(self.first.clone()).build()); + .append(self.first).build()); } if self.index.is_some() { elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", self.index.unwrap())).build()); } if self.last.is_some() { - elem.append_child(Element::builder("last").ns(ns::RSM).append(self.last.clone()).build()); + elem.append_child(Element::builder("last").ns(ns::RSM).append(self.last).build()); } if self.max.is_some() { elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", self.max.unwrap())).build()); From 00f3f3eee63eda60ffa1fad4b0d59b306e5381ec Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 22:28:54 +0100 Subject: [PATCH 0254/1020] jingle: Simplify parsing and serialisation. --- src/jingle.rs | 298 ++++++++++++++++++++++++++------------------------ 1 file changed, 155 insertions(+), 143 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 4796ae41bcfa926d4f2b859926677fe9c7bfa420..7f925bf80af06a9d52469c3f6480615a5596b52c 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -7,7 +7,8 @@ use std::convert::TryFrom; use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use jid::Jid; use error::Error; use ns; @@ -57,9 +58,9 @@ impl FromStr for Action { } } -impl From for String { - fn from(action: Action) -> String { - String::from(match action { +impl IntoAttributeValue for Action { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { Action::ContentAccept => "content-accept", Action::ContentAdd => "content-add", Action::ContentModify => "content-modify", @@ -75,13 +76,10 @@ impl From for String { Action::TransportInfo => "transport-info", Action::TransportReject => "transport-reject", Action::TransportReplace => "transport-replace", - }) + })) } } -// TODO: use a real JID type. -type Jid = String; - #[derive(Debug, Clone, PartialEq)] pub enum Creator { Initiator, @@ -118,6 +116,12 @@ pub enum Senders { Responder, } +impl Default for Senders { + fn default() -> Senders { + Senders::Both + } +} + impl FromStr for Senders { type Err = Error; @@ -155,6 +159,66 @@ pub struct Content { pub security: Option, } +impl TryFrom for Content { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("content", ns::JINGLE) { + return Err(Error::ParseError("This is not a content element.")); + } + + let mut content = Content { + creator: get_attr!(elem, "creator", required), + disposition: get_attr!(elem, "disposition", optional).unwrap_or(String::from("session")), + name: get_attr!(elem, "name", required), + senders: get_attr!(elem, "senders", default), + description: None, + transport: None, + security: None, + }; + for child in elem.children() { + if child.name() == "description" { + if content.description.is_some() { + return Err(Error::ParseError("Content must not have more than one description.")); + } + content.description = Some(child.clone()); + } else if child.name() == "transport" { + if content.transport.is_some() { + return Err(Error::ParseError("Content must not have more than one transport.")); + } + content.transport = Some(child.clone()); + } else if child.name() == "security" { + if content.security.is_some() { + return Err(Error::ParseError("Content must not have more than one security.")); + } + content.security = Some(child.clone()); + } + } + Ok(content) + } +} + +impl Into for Content { + fn into(self) -> Element { + Element::builder("content") + .ns(ns::JINGLE) + .attr("creator", String::from(self.creator)) + .attr("disposition", self.disposition) + .attr("name", self.name) + .attr("senders", String::from(self.senders)) + .append(self.description) + .append(self.transport) + .append(self.security) + .build() + } +} + +impl IntoElements for Content { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Reason { AlternativeSession, //(String), @@ -234,6 +298,58 @@ pub struct ReasonElement { pub text: Option, } +impl TryFrom for ReasonElement { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("reason", ns::JINGLE) { + return Err(Error::ParseError("This is not a reason element.")); + } + let mut reason = None; + let mut text = None; + for child in elem.children() { + if child.ns() != Some(ns::JINGLE) { + return Err(Error::ParseError("Reason contains a foreign element.")); + } + match child.name() { + "text" => { + if text.is_some() { + return Err(Error::ParseError("Reason must not have more than one text.")); + } + text = Some(child.text()); + }, + name => { + if reason.is_some() { + return Err(Error::ParseError("Reason must not have more than one reason.")); + } + reason = Some(name.parse()?); + }, + } + } + let reason = reason.ok_or(Error::ParseError("Reason doesn’t contain a valid reason."))?; + Ok(ReasonElement { + reason: reason, + text: text, + }) + } +} + +impl Into for ReasonElement { + fn into(self) -> Element { + let reason: Element = self.reason.into(); + Element::builder("reason") + .append(reason) + .append(self.text) + .build() + } +} + +impl IntoElements for ReasonElement { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone)] pub struct Jingle { pub action: Action, @@ -253,150 +369,46 @@ impl TryFrom for Jingle { return Err(Error::ParseError("This is not a Jingle element.")); } - let mut contents: Vec = vec!(); - - let action = root.attr("action") - .ok_or(Error::ParseError("Jingle must have an 'action' attribute."))? - .parse()?; - let initiator = root.attr("initiator") - .and_then(|initiator| initiator.parse().ok()); - let responder = root.attr("responder") - .and_then(|responder| responder.parse().ok()); - let sid = root.attr("sid") - .ok_or(Error::ParseError("Jingle must have a 'sid' attribute."))?; - let mut reason_element = None; - let mut other = vec!(); - - for child in root.children() { + let mut jingle = Jingle { + action: get_attr!(root, "action", required), + initiator: get_attr!(root, "initiator", optional), + responder: get_attr!(root, "responder", optional), + sid: get_attr!(root, "sid", required), + contents: vec!(), + reason: None, + other: vec!(), + }; + + for child in root.children().cloned() { if child.is("content", ns::JINGLE) { - let creator = child.attr("creator") - .ok_or(Error::ParseError("Content must have a 'creator' attribute."))? - .parse()?; - let disposition = child.attr("disposition") - .unwrap_or("session"); - let name = child.attr("name") - .ok_or(Error::ParseError("Content must have a 'name' attribute."))?; - let senders = child.attr("senders") - .unwrap_or("both") - .parse()?; - let mut description = None; - let mut transport = None; - let mut security = None; - for stuff in child.children() { - if stuff.name() == "description" { - if description.is_some() { - return Err(Error::ParseError("Content must not have more than one description.")); - } - description = Some(stuff.clone()); - } else if stuff.name() == "transport" { - if transport.is_some() { - return Err(Error::ParseError("Content must not have more than one transport.")); - } - transport = Some(stuff.clone()); - } else if stuff.name() == "security" { - if security.is_some() { - return Err(Error::ParseError("Content must not have more than one security.")); - } - security = Some(stuff.clone()); - } - } - contents.push(Content { - creator: creator, - disposition: disposition.to_owned(), - name: name.to_owned(), - senders: senders, - description: description, - transport: transport, - security: security, - }); + let content = Content::try_from(child.clone())?; + jingle.contents.push(content); } else if child.is("reason", ns::JINGLE) { - if reason_element.is_some() { + if jingle.reason.is_some() { return Err(Error::ParseError("Jingle must not have more than one reason.")); } - let mut reason = None; - let mut text = None; - for stuff in child.children() { - if stuff.ns() != Some(ns::JINGLE) { - return Err(Error::ParseError("Reason contains a foreign element.")); - } - let name = stuff.name(); - if name == "text" { - if text.is_some() { - return Err(Error::ParseError("Reason must not have more than one text.")); - } - text = Some(stuff.text()); - } else { - reason = Some(name.parse()?); - } - } - if reason.is_none() { - return Err(Error::ParseError("Reason doesn’t contain a valid reason.")); - } - reason_element = Some(ReasonElement { - reason: reason.unwrap(), - text: text, - }); + let reason = ReasonElement::try_from(child.clone())?; + jingle.reason = Some(reason); } else { - other.push(child.clone()); + jingle.other.push(child.clone()); } } - Ok(Jingle { - action: action, - initiator: initiator, - responder: responder, - sid: sid.to_owned(), - contents: contents, - reason: reason_element, - other: other, - }) - } -} - -impl Into for Content { - fn into(self) -> Element { - let mut root = Element::builder("content") - .ns(ns::JINGLE) - .attr("creator", String::from(self.creator.clone())) - .attr("disposition", self.disposition.clone()) - .attr("name", self.name.clone()) - .attr("senders", String::from(self.senders.clone())) - .build(); - if let Some(description) = self.description.clone() { - root.append_child(description); - } - if let Some(transport) = self.transport.clone() { - root.append_child(transport); - } - if let Some(security) = self.security.clone() { - root.append_child(security); - } - root + Ok(jingle) } } impl Into for Jingle { fn into(self) -> Element { - let mut root = Element::builder("jingle") - .ns(ns::JINGLE) - .attr("action", String::from(self.action.clone())) - .attr("initiator", self.initiator.clone()) - .attr("responder", self.responder.clone()) - .attr("sid", self.sid.clone()) - .build(); - for content in self.contents { - let content_elem = content.into(); - root.append_child(content_elem); - } - if let Some(reason) = self.reason { - let reason2: Element = reason.reason.into(); - let reason_elem = Element::builder("reason") - .append(reason2) - .append(reason.text) - .build(); - root.append_child(reason_elem); - } - root + Element::builder("jingle") + .ns(ns::JINGLE) + .attr("action", self.action) + .attr("initiator", match self.initiator { Some(initiator) => Some(String::from(initiator)), None => None }) + .attr("responder", match self.responder { Some(responder) => Some(String::from(responder)), None => None }) + .attr("sid", self.sid) + .append(self.contents) + .append(self.reason) + .build() } } @@ -420,7 +432,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Jingle must have an 'action' attribute."); + assert_eq!(message, "Required attribute 'action' missing."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); @@ -428,7 +440,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Jingle must have a 'sid' attribute."); + assert_eq!(message, "Required attribute 'sid' missing."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); @@ -465,7 +477,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Content must have a 'creator' attribute."); + assert_eq!(message, "Required attribute 'creator' missing."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); @@ -473,7 +485,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Content must have a 'name' attribute."); + assert_eq!(message, "Required attribute 'name' missing."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); From 47fc1169063d582796f64a53770e7aa7d0adaaa0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 22:41:09 +0100 Subject: [PATCH 0255/1020] media_element: Stop swallowing integer parsing errors. --- src/media_element.rs | 49 ++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/media_element.rs b/src/media_element.rs index 54ffee132eb5a6ee7b20bbd4012e166b6d7d8cb4..931cab6f1d59dab90aed45034ed4d9e1e741d0ec 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -33,22 +33,24 @@ impl TryFrom for MediaElement { return Err(Error::ParseError("This is not a media element.")); } - let width = elem.attr("width").and_then(|width| width.parse().ok()); - let height = elem.attr("height").and_then(|height| height.parse().ok()); - let mut uris = vec!(); + let mut media = MediaElement { + width: get_attr!(elem, "width", optional), + height: get_attr!(elem, "height", optional), + uris: vec!(), + }; for uri in elem.children() { if uri.is("uri", ns::MEDIA_ELEMENT) { - let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?; + let type_ = get_attr!(uri, "type", required); let text = uri.text().trim().to_owned(); if text == "" { return Err(Error::ParseError("URI missing in uri.")); } - uris.push(URI { type_: type_.to_owned(), uri: text }); + media.uris.push(URI { type_: type_, uri: text }); } else { return Err(Error::ParseError("Unknown child in media element.")); } } - Ok(MediaElement { width: width, height: height, uris: uris }) + Ok(media) } } @@ -56,6 +58,7 @@ impl TryFrom for MediaElement { mod tests { use super::*; use data_forms::DataForm; + use std::error::Error as StdError; #[test] fn test_simple() { @@ -86,20 +89,36 @@ mod tests { #[test] fn test_invalid_width_height() { let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(elem).unwrap(); - assert!(media.width.is_none()); + let error = MediaElement::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "cannot parse integer from empty string"); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(elem).unwrap(); - assert!(media.width.is_none()); + let error = MediaElement::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(elem).unwrap(); - assert!(media.height.is_none()); + let error = MediaElement::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "cannot parse integer from empty string"); let elem: Element = "".parse().unwrap(); - let media = MediaElement::try_from(elem).unwrap(); - assert!(media.height.is_none()); + let error = MediaElement::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "invalid digit found in string"); } #[test] @@ -121,7 +140,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Attribute type on uri is mandatory."); + assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); From 947c49330f62177996821bc3050fdb76b9a7675c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 22:43:21 +0100 Subject: [PATCH 0256/1020] receipts: Parse 'id' using get_attr!(), and make it optional. --- src/receipts.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/receipts.rs b/src/receipts.rs index f7c1d50d2af1352210c98d6c3317742ef04b7e47..72b357a1de277ef15308ed3704a6c0a58fe17cb7 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -15,7 +15,7 @@ use ns; #[derive(Debug, Clone)] pub enum Receipt { Request, - Received(String), + Received(Option), } impl TryFrom for Receipt { @@ -26,9 +26,17 @@ impl TryFrom for Receipt { return Err(Error::ParseError("Unknown child in receipt element.")); } if elem.is("request", ns::RECEIPTS) { + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in request element.")); + } Ok(Receipt::Request) } else if elem.is("received", ns::RECEIPTS) { - let id = elem.attr("id").unwrap_or("").to_owned(); + for (attr, _) in elem.attrs() { + if attr != "id" { + return Err(Error::ParseError("Unknown attribute in received element.")); + } + } + let id = get_attr!(elem, "id", optional); Ok(Receipt::Received(id)) } else { Err(Error::ParseError("This is not a receipt element.")) @@ -40,13 +48,11 @@ impl Into for Receipt { fn into(self) -> Element { match self { Receipt::Request => Element::builder("request") - .ns(ns::RECEIPTS) - .build(), + .ns(ns::RECEIPTS), Receipt::Received(id) => Element::builder("received") .ns(ns::RECEIPTS) - .attr("id", id) - .build(), - } + .attr("id", id), + }.build() } } @@ -72,7 +78,7 @@ mod tests { let elem: Element = receipt.into(); assert!(elem.is("request", ns::RECEIPTS)); - let receipt = Receipt::Received("coucou".to_owned()); + let receipt = Receipt::Received(Some(String::from("coucou"))); let elem: Element = receipt.into(); assert!(elem.is("received", ns::RECEIPTS)); assert_eq!(elem.attr("id"), Some("coucou")); From 9bd1e7f295e7f3937d244e9724cb60a28602fc63 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 23:00:38 +0100 Subject: [PATCH 0257/1020] jingle_s5b: Remove the clones. --- src/jingle_s5b.rs | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index fa5ee667e6bb3b1b7c1d939c944808ac2f2f0167..42c188f8152b5a2ab5a7735c270a6f0bc5ea2636 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -67,12 +67,12 @@ impl Into for Candidate { fn into(self) -> Element { Element::builder("candidate") .ns(ns::JINGLE_S5B) - .attr("cid", self.cid.clone()) - .attr("host", self.host.clone()) - .attr("jid", self.jid.clone()) + .attr("cid", self.cid) + .attr("host", self.host) + .attr("jid", self.jid) .attr("port", match self.port { Some(port) => Some(format!("{}", port)), None => None }) .attr("priority", format!("{}", self.priority)) - .attr("type", self.type_.clone()) + .attr("type", self.type_) .build() } } @@ -143,22 +143,16 @@ impl TryFrom for Transport { payload = Some(if child.is("candidate", ns::JINGLE_S5B) { let mut candidates = match payload { Some(TransportPayload::Candidates(candidates)) => candidates, - Some(_) => return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")), + Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")), None => vec!(), }; - let cid = get_attr!(child, "cid", required); - let host = get_attr!(child, "host", required); - let jid = get_attr!(child, "jid", required); - let port = get_attr!(child, "port", optional); - let priority = get_attr!(child, "priority", required); - let type_ = get_attr!(child, "type", default); candidates.push(Candidate { - cid: cid, - host: host, - jid: jid, - port: port, - priority: priority, - type_: type_, + cid: get_attr!(child, "cid", required), + host: get_attr!(child, "host", required), + jid: get_attr!(child, "jid", required), + port: get_attr!(child, "port", optional), + priority: get_attr!(child, "priority", required), + type_: get_attr!(child, "type", default), }); TransportPayload::Candidates(candidates) } else if child.is("activated", ns::JINGLE_S5B) { @@ -204,13 +198,13 @@ impl Into for Transport { fn into(self) -> Element { Element::builder("transport") .ns(ns::JINGLE_S5B) - .attr("sid", self.sid.clone()) - .attr("dstaddr", self.dstaddr.clone()) - .attr("mode", self.mode.clone()) + .attr("sid", self.sid) + .attr("dstaddr", self.dstaddr) + .attr("mode", self.mode) .append(match self.payload { - TransportPayload::Candidates(candidates) => { - candidates.iter() - .map(|candidate| -> Element { candidate.clone().into() }) + TransportPayload::Candidates(mut candidates) => { + candidates.drain(..) + .map(|candidate| candidate.into()) .collect::>() }, TransportPayload::Activated(cid) => { From 17d69596064bb79d77d3b4a75319f0bf48840c3e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 23:38:44 +0100 Subject: [PATCH 0258/1020] jingle: Remove unused clones. --- src/jingle.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 7f925bf80af06a9d52469c3f6480615a5596b52c..06582200f93449d14a9f3681cb7bf3cabd792601 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -381,16 +381,16 @@ impl TryFrom for Jingle { for child in root.children().cloned() { if child.is("content", ns::JINGLE) { - let content = Content::try_from(child.clone())?; + let content = Content::try_from(child)?; jingle.contents.push(content); } else if child.is("reason", ns::JINGLE) { if jingle.reason.is_some() { return Err(Error::ParseError("Jingle must not have more than one reason.")); } - let reason = ReasonElement::try_from(child.clone())?; + let reason = ReasonElement::try_from(child)?; jingle.reason = Some(reason); } else { - jingle.other.push(child.clone()); + jingle.other.push(child); } } From 898baddd3f97f0dbe0ecef6fe7df51c2b9f1d653 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 23:47:27 +0100 Subject: [PATCH 0259/1020] disco: Split Into for Identity and Feature. --- src/disco.rs | 63 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index acd2d4872f78411fe40f86b22d18cab1ec7144a9..46d25dec88161063f9ab0ad17653f22f93856760 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; -use minidom::Element; +use minidom::{Element, IntoElements, ElementEmitter}; use error::Error; use ns; @@ -18,6 +18,21 @@ pub struct Feature { pub var: String, } +impl Into for Feature { + fn into(self) -> Element { + Element::builder("feature") + .ns(ns::DISCO_INFO) + .attr("var", self.var) + .build() + } +} + +impl IntoElements for Feature { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone)] pub struct Identity { pub category: String, // TODO: use an enum here. @@ -26,6 +41,24 @@ pub struct Identity { pub name: Option, } +impl Into for Identity { + fn into(self) -> Element { + Element::builder("identity") + .ns(ns::DISCO_INFO) + .attr("category", self.category) + .attr("type", self.type_) + .attr("xml:lang", self.xml_lang) + .attr("name", self.name) + .build() + } +} + +impl IntoElements for Identity { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone)] pub struct Disco { pub node: Option, @@ -111,31 +144,15 @@ impl TryFrom for Disco { impl Into for Disco { fn into(self) -> Element { - let mut root = Element::builder("query") - .ns(ns::DISCO_INFO) - .attr("node", self.node.clone()) - .build(); - for identity in self.identities { - let identity_element = Element::builder("identity") - .ns(ns::DISCO_INFO) - .attr("category", identity.category.clone()) - .attr("type", identity.type_.clone()) - .attr("xml:lang", identity.xml_lang.clone()) - .attr("name", identity.name.clone()) - .build(); - root.append_child(identity_element); - } - for feature in self.features { - let feature_element = Element::builder("feature") - .ns(ns::DISCO_INFO) - .attr("var", feature.var.clone()) - .build(); - root.append_child(feature_element); - } for _ in self.extensions { panic!("Not yet implemented!"); } - root + Element::builder("query") + .ns(ns::DISCO_INFO) + .attr("node", self.node) + .append(self.identities) + .append(self.features) + .build() } } From 3678d9f0d3dac11b03413f581a76a012b19ce257 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 23:56:35 +0100 Subject: [PATCH 0260/1020] stanza_error: Simplify attribute handling. --- src/stanza_error.rs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 4f30f3a44171529b4066c3c931d942ae710de766..0a3275792ad1c3a0ce727408e2963ecef47e7594 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use error::Error; use jid::Jid; @@ -39,15 +39,15 @@ impl FromStr for ErrorType { } } -impl From for String { - fn from(type_: ErrorType) -> String { - String::from(match type_ { +impl IntoAttributeValue for ErrorType { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { ErrorType::Auth => "auth", ErrorType::Cancel => "cancel", ErrorType::Continue => "continue", ErrorType::Modify => "modify", ErrorType::Wait => "wait", - }) + })) } } @@ -189,11 +189,7 @@ impl TryFrom for StanzaError { other = Some(child.clone()); } } - - if defined_condition.is_none() { - return Err(Error::ParseError("Error must have a defined-condition.")); - } - let defined_condition = defined_condition.unwrap(); + let defined_condition = defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; Ok(StanzaError { type_: type_, @@ -209,16 +205,13 @@ impl Into for StanzaError { fn into(self) -> Element { let mut root = Element::builder("error") .ns(ns::JABBER_CLIENT) - .attr("type", String::from(self.type_.clone())) - .attr("by", match self.by { - Some(ref by) => Some(String::from(by.clone())), - None => None, - }) - .append(Element::builder(self.defined_condition.clone()) + .attr("type", self.type_) + .attr("by", self.by.and_then(|by| Some(String::from(by)))) + .append(Element::builder(self.defined_condition) .ns(ns::XMPP_STANZAS) .build()) .build(); - for (lang, text) in self.texts.clone() { + for (lang, text) in self.texts { let elem = Element::builder("text") .ns(ns::XMPP_STANZAS) .attr("xml:lang", lang) @@ -226,8 +219,8 @@ impl Into for StanzaError { .build(); root.append_child(elem); } - if let Some(ref other) = self.other { - root.append_child(other.clone()); + if let Some(other) = self.other { + root.append_child(other); } root } From 9bb65ea8fb85887368ed2c74aebd1c1547eeec83 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 24 May 2017 23:59:45 +0100 Subject: [PATCH 0261/1020] jingle_ibb: Simplify parsing and remove clones. --- src/jingle_ibb.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index d89df221f80ce5a79d810d0ae334c03b999e053e..6f789d3e969b61a021565ebc8db3d3e29f1421ec 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -25,21 +25,17 @@ impl TryFrom for Transport { type Error = Error; fn try_from(elem: Element) -> Result { - if elem.is("transport", ns::JINGLE_IBB) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in JingleIBB element.")); - } - let block_size = get_attr!(elem, "block-size", required); - let sid = get_attr!(elem, "sid", required); - let stanza = get_attr!(elem, "stanza", default); - Ok(Transport { - block_size: block_size, - sid: sid, - stanza: stanza - }) - } else { - Err(Error::ParseError("This is not an JingleIBB element.")) + if !elem.is("transport", ns::JINGLE_IBB) { + return Err(Error::ParseError("This is not an JingleIBB element.")) } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in JingleIBB element.")); + } + Ok(Transport { + block_size: get_attr!(elem, "block-size", required), + sid: get_attr!(elem, "sid", required), + stanza: get_attr!(elem, "stanza", default), + }) } } @@ -48,8 +44,8 @@ impl Into for Transport { Element::builder("transport") .ns(ns::JINGLE_IBB) .attr("block-size", format!("{}", self.block_size)) - .attr("sid", self.sid.clone()) - .attr("stanza", self.stanza.clone()) + .attr("sid", self.sid) + .attr("stanza", self.stanza) .build() } } From b172a6e05ca7d7fcffb9accb2a6919d816f1dea1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 00:04:37 +0100 Subject: [PATCH 0262/1020] ecaps2: Remove the last clone. --- src/ecaps2.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index ac9df22edc6f3e3478963578b41b689037d4dcaa..57577ca51c4e2145046207217261b18f4c85fcad 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -48,11 +48,11 @@ impl TryFrom for ECaps2 { } impl Into for ECaps2 { - fn into(self) -> Element { + fn into(mut self) -> Element { Element::builder("c") .ns(ns::ECAPS2) - .append(self.hashes.iter() - .map(|hash| hash.clone().into()) + .append(self.hashes.drain(..) + .map(|hash| hash.into()) .collect::>()) .build() } From 070227ea03bd8e983d1a8b46956083d1b584a1c8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 00:30:00 +0100 Subject: [PATCH 0263/1020] eme: Remove two clones. --- src/eme.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index edf6cd359ba443022f60d9227ed0e032728116a9..fe9738613ecafe0d5b7e7e13b7a280643bf3955e 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -28,11 +28,9 @@ impl TryFrom for ExplicitMessageEncryption { for _ in elem.children() { return Err(Error::ParseError("Unknown child in encryption element.")); } - let namespace = get_attr!(elem, "namespace", required); - let name = get_attr!(elem, "name", optional); Ok(ExplicitMessageEncryption { - namespace: namespace, - name: name, + namespace: get_attr!(elem, "namespace", required), + name: get_attr!(elem, "name", optional), }) } } @@ -41,8 +39,8 @@ impl Into for ExplicitMessageEncryption { fn into(self) -> Element { Element::builder("encryption") .ns(ns::EME) - .attr("namespace", self.namespace.clone()) - .attr("name", self.name.clone()) + .attr("namespace", self.namespace) + .attr("name", self.name) .build() } } From a6b3152adde69226e3dfc020b51f3a037431f996 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 00:30:29 +0100 Subject: [PATCH 0264/1020] hashes: Make the hash a Vec, to avoid base64 issues. --- src/ecaps2.rs | 32 ++++++++++++++++++-------------- src/hashes.rs | 10 ++++++---- src/jingle_ft.rs | 5 +++-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 57577ca51c4e2145046207217261b18f4c85fcad..03f8a40b73d6ea6d8540182aac9d1a7b3dacc77a 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -18,7 +18,6 @@ use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; use blake2::Blake2b; use digest::{Digest, VariableOutput}; -use base64; #[derive(Debug, Clone)] pub struct ECaps2 { @@ -118,6 +117,12 @@ pub fn compute_disco(disco: &Disco) -> Vec { final_string } +fn get_hash_vec(hash: &[u8]) -> Vec { + let mut vec = Vec::with_capacity(hash.len()); + vec.extend_from_slice(hash); + vec +} + pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { Ok(Hash { hash: match algo { @@ -125,39 +130,39 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { let mut hasher = Sha256::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash.as_slice()) + get_hash_vec(hash.as_slice()) }, Algo::Sha_512 => { let mut hasher = Sha512::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash.as_slice()) + get_hash_vec(hash.as_slice()) }, Algo::Sha3_256 => { let mut hasher = Sha3_256::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash.as_slice()) + get_hash_vec(hash.as_slice()) }, Algo::Sha3_512 => { let mut hasher = Sha3_512::default(); hasher.input(data); let hash = hasher.result(); - base64::encode(&hash.as_slice()) + get_hash_vec(hash.as_slice()) }, Algo::Blake2b_256 => { let mut hasher = Blake2b::default(); hasher.input(data); let mut buf: [u8; 32] = [0; 32]; let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(hash) + get_hash_vec(hash) }, Algo::Blake2b_512 => { let mut hasher = Blake2b::default(); hasher.input(data); let mut buf: [u8; 64] = [0; 64]; let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(hash) + get_hash_vec(hash) }, Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), @@ -178,9 +183,9 @@ mod tests { let ecaps2 = ECaps2::try_from(elem).unwrap(); assert_eq!(ecaps2.hashes.len(), 2); assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256); - assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); + assert_eq!(ecaps2.hashes[0].hash, base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap()); assert_eq!(ecaps2.hashes[1].algo, Algo::Sha3_256); - assert_eq!(ecaps2.hashes[1].hash, "+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8="); + assert_eq!(ecaps2.hashes[1].hash, base64::decode("+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=").unwrap()); } #[test] @@ -266,9 +271,9 @@ mod tests { assert_eq!(ecaps2, expected); let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); - assert_eq!(sha_256.hash, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); + assert_eq!(sha_256.hash, base64::decode("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=").unwrap()); let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); - assert_eq!(sha3_256.hash, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); + assert_eq!(sha3_256.hash, base64::decode("79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q=").unwrap()); } #[test] @@ -438,9 +443,9 @@ mod tests { assert_eq!(ecaps2, expected); let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); - assert_eq!(sha_256.hash, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); + assert_eq!(sha_256.hash, base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap()); let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); - assert_eq!(sha3_256.hash, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); + assert_eq!(sha3_256.hash, base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap()); } #[test] @@ -452,7 +457,6 @@ mod tests { 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, ); - let known_hash = base64::encode(&known_hash); assert_eq!(hash.hash, known_hash); } } diff --git a/src/hashes.rs b/src/hashes.rs index af9c3a053c81626161b09b2fe0b011249b9899b4..3c9a05eba5cdee8c7ff910f6438e93adc85b995e 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -13,6 +13,8 @@ use error::Error; use ns; +use base64; + #[allow(non_camel_case_types)] #[derive(Debug, Clone, PartialEq)] pub enum Algo { @@ -63,7 +65,7 @@ impl IntoAttributeValue for Algo { #[derive(Debug, Clone, PartialEq)] pub struct Hash { pub algo: Algo, - pub hash: String, + pub hash: Vec, } impl TryFrom for Hash { @@ -79,7 +81,7 @@ impl TryFrom for Hash { let algo = get_attr!(elem, "algo", required); let hash = match elem.text().as_ref() { "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), - text => text.to_owned(), + text => base64::decode(text)?, }; Ok(Hash { algo: algo, @@ -93,7 +95,7 @@ impl Into for Hash { Element::builder("hash") .ns(ns::HASHES) .attr("algo", self.algo) - .append(self.hash.clone()) + .append(base64::encode(&self.hash)) .build() } } @@ -107,7 +109,7 @@ mod tests { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); let hash = Hash::try_from(elem).unwrap(); assert_eq!(hash.algo, Algo::Sha_256); - assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); + assert_eq!(hash.hash, base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap()); } #[test] diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 57cf626cf6ce235ea06be2b761dd0e5ca8677ae3..1a4550cf599f9ea6cbf3d091191fa5045808620b 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -236,6 +236,7 @@ impl Into for Description { mod tests { use super::*; use hashes::Algo; + use base64; #[test] fn test_description() { @@ -260,7 +261,7 @@ mod tests { assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); - assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); + assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()); } #[test] @@ -282,6 +283,6 @@ mod tests { assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); - assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); + assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()); } } From b4e47e9a78136be5404e5e307a28d9c0d43cc90b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 00:41:13 +0100 Subject: [PATCH 0265/1020] mam: Improve serialisation. --- src/mam.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index 22ec0576fa4f1d45da3eb2fcf1858269a9c8b83a..c8cd2ac81cdab8fedfc33f9b0fab9ac3b83810b5 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -188,18 +188,13 @@ impl TryFrom for Prefs { impl Into for Query { fn into(self) -> Element { - let mut elem = Element::builder("query") - .ns(ns::MAM) - .attr("queryid", self.queryid.clone()) - .attr("node", self.node.clone()) - .build(); - //if let Some(form) = self.form { - // elem.append_child(form.into()); - //} - if let Some(set) = self.set { - elem.append_child(set.into()); - } - elem + Element::builder("query") + .ns(ns::MAM) + .attr("queryid", self.queryid) + .attr("node", self.node) + //.append(self.form.map(|form| -> Element { form.into() })) + .append(self.set.map(|set| -> Element { set.into() })) + .build() } } @@ -236,7 +231,7 @@ impl Into for Prefs { let mut always = Element::builder("always") .ns(ns::RSM) .build(); - for jid in self.always.clone() { + for jid in self.always { always.append_child(Element::builder("jid") .ns(ns::RSM) .append(String::from(jid)) @@ -248,7 +243,7 @@ impl Into for Prefs { let mut never = Element::builder("never") .ns(ns::RSM) .build(); - for jid in self.never.clone() { + for jid in self.never { never.append_child(Element::builder("jid") .ns(ns::RSM) .append(String::from(jid)) From f08c81382c185b52f737f7b0e9e855485a742917 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 01:00:17 +0100 Subject: [PATCH 0266/1020] data_forms, media_element: Implement forgotten serialisation. --- src/data_forms.rs | 85 +++++++++++++++++++++++++++++++++++++++++++- src/media_element.rs | 35 +++++++++++++++++- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 21d2ab11db6276b488936edaf54dc4fb4501d054..4aba85287ebc26476545fd7dd2dde4c2a4e2271b 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use error::Error; use ns; @@ -55,12 +55,48 @@ impl FromStr for FieldType { } } +impl IntoAttributeValue for FieldType { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + FieldType::Boolean => "boolean", + FieldType::Fixed => "fixed", + FieldType::Hidden => "hidden", + FieldType::JidMulti => "jid-multi", + FieldType::JidSingle => "jid-single", + FieldType::ListMulti => "list-multi", + FieldType::ListSingle => "list-single", + FieldType::TextMulti => "text-multi", + FieldType::TextPrivate => "text-private", + FieldType::TextSingle => "text-single", + })) + } +} + #[derive(Debug, Clone)] pub struct Option_ { pub label: Option, pub value: String, } +impl From for Element { + fn from(option: Option_) -> Element { + Element::builder("option") + .ns(ns::DATA_FORMS) + .attr("label", option.label) + .append(Element::builder("value") + .ns(ns::DATA_FORMS) + .append(option.value) + .build()) + .build() + } +} + +impl IntoElements for Option_ { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone)] pub struct Field { pub var: String, @@ -72,6 +108,29 @@ pub struct Field { pub media: Vec, } +impl From for Element { + fn from(field: Field) -> Element { + Element::builder("field") + .ns(ns::DATA_FORMS) + .attr("var", field.var) + .attr("type", field.type_) + .attr("label", field.label) + .append(if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None }) + .append(field.options) + .append(field.values.iter().map(|value| { + Element::builder("value").ns(ns::DATA_FORMS).append(value).build() + }).collect::>()) + .append(field.media) + .build() + } +} + +impl IntoElements for Field { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone, PartialEq)] pub enum DataFormType { Cancel, @@ -95,6 +154,17 @@ impl FromStr for DataFormType { } } +impl IntoAttributeValue for DataFormType { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + DataFormType::Cancel => "cancel", + DataFormType::Form => "form", + DataFormType::Result_ => "result", + DataFormType::Submit => "submit", + })) + } +} + #[derive(Debug, Clone)] pub struct DataForm { pub type_: DataFormType, @@ -226,6 +296,19 @@ impl TryFrom for DataForm { } } +impl From for Element { + fn from(form: DataForm) -> Element { + Element::builder("x") + .ns(ns::DATA_FORMS) + .attr("type", form.type_) + .append(form.form_type) + .append(form.title) + .append(form.instructions) + .append(form.fields) + .build() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/media_element.rs b/src/media_element.rs index 931cab6f1d59dab90aed45034ed4d9e1e741d0ec..b1c36a64638fe7ee28a8de7ab376e4007a1f3e55 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; -use minidom::Element; +use minidom::{Element, IntoElements, ElementEmitter}; use error::Error; @@ -18,6 +18,22 @@ pub struct URI { pub uri: String, } +impl From for Element { + fn from(uri: URI) -> Element { + Element::builder("uri") + .ns(ns::MEDIA_ELEMENT) + .attr("type", uri.type_) + .append(uri.uri) + .build() + } +} + +impl IntoElements for URI { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[derive(Debug, Clone)] pub struct MediaElement { pub width: Option, @@ -54,6 +70,23 @@ impl TryFrom for MediaElement { } } +impl From for Element { + fn from(media: MediaElement) -> Element { + Element::builder("media") + .ns(ns::MEDIA_ELEMENT) + .attr("width", media.width) + .attr("height", media.height) + .append(media.uris) + .build() + } +} + +impl IntoElements for MediaElement { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[cfg(test)] mod tests { use super::*; From 56b7785942f61d8dd2a57140dc3e121ebf372765 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 01:04:51 +0100 Subject: [PATCH 0267/1020] delay: Remove clones. --- src/delay.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 08b406c5c7d04b58bf53eeeb73c1cd63a48b4875..19b1a495cdb6e492ce2c979af9eb4295f15593a2 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -48,9 +48,9 @@ impl Into for Delay { fn into(self) -> Element { Element::builder("delay") .ns(ns::DELAY) - .attr("from", self.from.clone().and_then(|value| Some(String::from(value)))) - .attr("stamp", self.stamp.clone()) - .append(self.data.clone()) + .attr("from", self.from.and_then(|value| Some(String::from(value)))) + .attr("stamp", self.stamp) + .append(self.data) .build() } } From 764a7190e97e631041472da8039f16661f274536 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 01:14:36 +0100 Subject: [PATCH 0268/1020] stanza_error: Simplify with IntoElements. --- src/jingle.rs | 20 ++++++++++---------- src/stanza_error.rs | 14 ++++++-------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 06582200f93449d14a9f3681cb7bf3cabd792601..c5041b09adc035c065eb75ec41e26ea5392bca24 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -99,12 +99,12 @@ impl FromStr for Creator { } } -impl From for String { - fn from(creator: Creator) -> String { - String::from(match creator { +impl IntoAttributeValue for Creator { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { Creator::Initiator => "initiator", Creator::Responder => "responder", - }) + })) } } @@ -137,14 +137,14 @@ impl FromStr for Senders { } } -impl From for String { - fn from(senders: Senders) -> String { - String::from(match senders { +impl IntoAttributeValue for Senders { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { Senders::Both => "both", Senders::Initiator => "initiator", Senders::None_ => "none", Senders::Responder => "responder", - }) + })) } } @@ -202,10 +202,10 @@ impl Into for Content { fn into(self) -> Element { Element::builder("content") .ns(ns::JINGLE) - .attr("creator", String::from(self.creator)) + .attr("creator", self.creator) .attr("disposition", self.disposition) .attr("name", self.name) - .attr("senders", String::from(self.senders)) + .attr("senders", self.senders) .append(self.description) .append(self.transport) .append(self.security) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 0a3275792ad1c3a0ce727408e2963ecef47e7594..3841ad5b49fd6343f4bfefa99294576c4da57ebe 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; -use minidom::{Element, IntoAttributeValue}; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use error::Error; use jid::Jid; @@ -110,9 +110,9 @@ impl FromStr for DefinedCondition { } } -impl From for String { - fn from(defined_condition: DefinedCondition) -> String { - String::from(match defined_condition { +impl IntoElements for DefinedCondition { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(Element::builder(match self { DefinedCondition::BadRequest => "bad-request", DefinedCondition::Conflict => "conflict", DefinedCondition::FeatureNotImplemented => "feature-not-implemented", @@ -135,7 +135,7 @@ impl From for String { DefinedCondition::SubscriptionRequired => "subscription-required", DefinedCondition::UndefinedCondition => "undefined-condition", DefinedCondition::UnexpectedRequest => "unexpected-request", - }) + }).ns(ns::XMPP_STANZAS).build()); } } @@ -207,9 +207,7 @@ impl Into for StanzaError { .ns(ns::JABBER_CLIENT) .attr("type", self.type_) .attr("by", self.by.and_then(|by| Some(String::from(by)))) - .append(Element::builder(self.defined_condition) - .ns(ns::XMPP_STANZAS) - .build()) + .append(self.defined_condition) .build(); for (lang, text) in self.texts { let elem = Element::builder("text") From dfdfd8cf712c207e8659e9bee07db36b49bca94c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 May 2017 02:34:03 +0100 Subject: [PATCH 0269/1020] Add a legacy caps parser and serialiser. --- Cargo.toml | 1 + src/caps.rs | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 + src/ns.rs | 3 + 4 files changed, 304 insertions(+) create mode 100644 src/caps.rs diff --git a/Cargo.toml b/Cargo.toml index a0d95aa30586e6afa8aa98a760d6069b5770399e..59a8a89b3c1fe930088991a08044100a6e4c9e84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ minidom = "0.4.1" jid = "0.2.0" base64 = "0.5.0" digest = "0.5.0" +sha-1 = "0.3.0" sha2 = "0.5.0" sha3 = "0.5.0" blake2 = "0.5.0" diff --git a/src/caps.rs b/src/caps.rs new file mode 100644 index 0000000000000000000000000000000000000000..d17cb5018976a18c36279b949f319b31f6df7a4c --- /dev/null +++ b/src/caps.rs @@ -0,0 +1,296 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; + +use disco::{Feature, Identity, Disco}; +use data_forms::DataForm; +use hashes::{Hash, Algo}; + +use minidom::Element; +use error::Error; +use ns; +use base64; + +use sha_1::Sha1; +use sha2::{Sha256, Sha512}; +use sha3::{Sha3_256, Sha3_512}; +use blake2::Blake2b; +use digest::{Digest, VariableOutput}; + +#[derive(Debug, Clone)] +pub struct Caps { + pub ext: Option, + pub node: String, + pub hash: Hash, +} + +impl TryFrom for Caps { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("c", ns::CAPS) { + return Err(Error::ParseError("This is not a caps element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in caps element.")); + } + let hash = get_attr!(elem, "hash", required); + let ver: String = get_attr!(elem, "ver", required); + let hash = Hash { + algo: hash, + hash: base64::decode(&ver)?, + }; + Ok(Caps { + ext: get_attr!(elem, "ext", optional), + node: get_attr!(elem, "node", required), + hash: hash, + }) + } +} + +impl From for Element { + fn from(caps: Caps) -> Element { + Element::builder("c") + .ns(ns::CAPS) + .attr("ext", caps.ext) + .attr("hash", caps.hash.algo) + .attr("node", caps.node) + .attr("ver", base64::encode(&caps.hash.hash)) + .build() + } +} + +fn compute_item(field: &str) -> Vec { + let mut bytes = field.as_bytes().to_vec(); + bytes.push(b'<'); + bytes +} + +fn compute_items Vec>(things: &[T], encode: F) -> Vec { + let mut string: Vec = vec!(); + let mut accumulator: Vec> = vec!(); + for thing in things { + let bytes = encode(thing); + accumulator.push(bytes); + } + // This works using the expected i;octet collation. + accumulator.sort(); + for mut bytes in accumulator { + string.append(&mut bytes); + } + string +} + +fn compute_features(features: &[Feature]) -> Vec { + compute_items(features, |feature| compute_item(&feature.var)) +} + +fn compute_identities(identities: &[Identity]) -> Vec { + compute_items(identities, |identity| { + let string = format!("{}/{}/{}/{}", identity.category, identity.type_, identity.xml_lang, match identity.name { Some(ref name) => name.clone(), None => String::new() }); + let bytes = string.as_bytes(); + let mut vec = Vec::with_capacity(bytes.len()); + vec.extend_from_slice(bytes); + vec.push(b'<'); + vec + }) +} + +fn compute_extensions(extensions: &[DataForm]) -> Vec { + compute_items(extensions, |extension| { + let mut bytes = vec!(); + // TODO: maybe handle the error case? + if let Some(ref form_type) = extension.form_type { + bytes.extend_from_slice(form_type.as_bytes()); + } + bytes.push(b'<'); + for field in extension.fields.clone() { + if field.var == "FORM_TYPE" { + continue; + } + bytes.append(&mut compute_item(&field.var)); + bytes.append(&mut compute_items(&field.values, + |value| compute_item(value))); + } + bytes + }) +} + +pub fn compute_disco(disco: &Disco) -> Vec { + let identities_string = compute_identities(&disco.identities); + let features_string = compute_features(&disco.features); + let extensions_string = compute_extensions(&disco.extensions); + + let mut final_string = vec!(); + final_string.extend(identities_string); + final_string.extend(features_string); + final_string.extend(extensions_string); + final_string +} + +fn get_hash_vec(hash: &[u8]) -> Vec { + let mut vec = Vec::with_capacity(hash.len()); + vec.extend_from_slice(hash); + vec +} + +pub fn hash_caps(data: &[u8], algo: Algo) -> Result { + Ok(Hash { + hash: match algo { + Algo::Sha_1 => { + let mut hasher = Sha1::default(); + hasher.input(data); + let hash = hasher.result(); + get_hash_vec(hash.as_slice()) + }, + Algo::Sha_256 => { + let mut hasher = Sha256::default(); + hasher.input(data); + let hash = hasher.result(); + get_hash_vec(hash.as_slice()) + }, + Algo::Sha_512 => { + let mut hasher = Sha512::default(); + hasher.input(data); + let hash = hasher.result(); + get_hash_vec(hash.as_slice()) + }, + Algo::Sha3_256 => { + let mut hasher = Sha3_256::default(); + hasher.input(data); + let hash = hasher.result(); + get_hash_vec(hash.as_slice()) + }, + Algo::Sha3_512 => { + let mut hasher = Sha3_512::default(); + hasher.input(data); + let hash = hasher.result(); + get_hash_vec(hash.as_slice()) + }, + Algo::Blake2b_256 => { + let mut hasher = Blake2b::default(); + hasher.input(data); + let mut buf: [u8; 32] = [0; 32]; + let hash = hasher.variable_result(&mut buf).unwrap(); + get_hash_vec(hash) + }, + Algo::Blake2b_512 => { + let mut hasher = Blake2b::default(); + hasher.input(data); + let mut buf: [u8; 64] = [0; 64]; + let hash = hasher.variable_result(&mut buf).unwrap(); + get_hash_vec(hash) + }, + Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), + }, + algo: algo, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use caps; + use base64; + + #[test] + fn test_parse() { + let elem: Element = "".parse().unwrap(); + let caps = Caps::try_from(elem).unwrap(); + assert_eq!(caps.node, String::from("coucou")); + assert_eq!(caps.hash.algo, Algo::Sha_256); + assert_eq!(caps.hash.hash, base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap()); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=".parse().unwrap(); + let error = Caps::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in caps element."); + } + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let disco = Disco::try_from(elem).unwrap(); + let caps = caps::compute_disco(&disco); + assert_eq!(caps.len(), 50); + } + + #[test] + fn test_xep_5_2() { + let elem: Element = r#" + + + + + + + +"#.parse().unwrap(); + + let data = b"client/pc//Exodus 0.9.1 + + + + + + + + + urn:xmpp:dataforms:softwareinfo + + + ipv4 + ipv6 + + + Mac + + + 10.5.1 + + + Psi + + + 0.11 + + + +"#.parse().unwrap(); + let data = b"client/pc/el/\xce\xa8 0.11 Date: Sat, 27 May 2017 12:20:19 +0100 Subject: [PATCH 0270/1020] idle: Add the chrono dependency to actually parse dates. --- Cargo.toml | 1 + src/error.rs | 8 ++++++ src/idle.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++----- src/lib.rs | 1 + 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 59a8a89b3c1fe930088991a08044100a6e4c9e84..d8b08268132ac7f972aa4488855be7f673e6d81a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ sha-1 = "0.3.0" sha2 = "0.5.0" sha3 = "0.5.0" blake2 = "0.5.0" +chrono = "0.3.1" diff --git a/src/error.rs b/src/error.rs index 2f7b254f40da4948bb7812cb7f9b6d9fdc5f7192..899e25f088612459d4bb276277e239e3b772622e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,6 +12,7 @@ use std::string; use base64; use minidom; use jid; +use chrono; #[derive(Debug)] pub enum Error { @@ -22,6 +23,7 @@ pub enum Error { ParseIntError(num::ParseIntError), ParseStringError(string::ParseError), JidParseError(jid::JidParseError), + ChronoParseError(chrono::ParseError), } impl From for Error { @@ -59,3 +61,9 @@ impl From for Error { Error::JidParseError(err) } } + +impl From for Error { + fn from(err: chrono::ParseError) -> Error { + Error::ChronoParseError(err) + } +} diff --git a/src/idle.rs b/src/idle.rs index 2800a0e1854b05c1db54970a8ca982eb21f81da2..ba13bd8954d660d0c8740aec392475d3c271eb62 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -7,16 +7,15 @@ use std::convert::TryFrom; use minidom::Element; +use chrono::prelude::*; use error::Error; use ns; -type Date = String; - #[derive(Debug, Clone)] pub struct Idle { - pub since: Date, + pub since: DateTime, } impl TryFrom for Idle { @@ -29,7 +28,7 @@ impl TryFrom for Idle { for _ in elem.children() { return Err(Error::ParseError("Unknown child in idle element.")); } - let since = get_attr!(elem, "since", required); + let since = get_attr!(elem, "since", required, since, DateTime::parse_from_rfc3339(since)?); Ok(Idle { since: since }) } } @@ -38,7 +37,7 @@ impl Into for Idle { fn into(self) -> Element { Element::builder("idle") .ns(ns::IDLE) - .attr("since", self.since.clone()) + .attr("since", self.since.to_rfc3339()) .build() } } @@ -46,6 +45,7 @@ impl Into for Idle { #[cfg(test)] mod tests { use super::*; + use std::error::Error as StdError; #[test] fn test_simple() { @@ -75,10 +75,67 @@ mod tests { assert_eq!(message, "Required attribute 'since' missing."); } + #[test] + fn test_invalid_date() { + // There is no thirteenth month. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input is out of range"); + + // Timezone ≥24:00 aren’t allowed. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input is out of range"); + + // Timezone without the : separator aren’t allowed. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // No seconds, error message could be improved. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // TODO: maybe we’ll want to support this one, as per XEP-0082 §4. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // No timezone. + let elem: Element = "".parse().unwrap(); + let error = Idle::try_from(elem).unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "premature end of input"); + } + #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let idle = Idle { since: Date::from("2017-05-21T20:19:55+01:00") }; + let idle = Idle { since: DateTime::parse_from_rfc3339("2017-05-21T20:19:55+01:00").unwrap() }; let elem2 = idle.into(); assert_eq!(elem, elem2); } diff --git a/src/lib.rs b/src/lib.rs index 363dfec3ebe37ca15bf5a36b6128b7a905fad1c6..50ed56e67d624c853a8c32ed5c32b33e3c0fcbee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ extern crate sha_1; extern crate sha2; extern crate sha3; extern crate blake2; +extern crate chrono; macro_rules! get_attr { ($elem:ident, $attr:tt, $type:tt) => ( From 357568813df887d74e23614261813ba49eab0070 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 12:21:32 +0100 Subject: [PATCH 0271/1020] presence: Wire up legacy caps. --- src/presence.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index 6b4b1ac4e587d4b70ca89fe7da13b925d57858eb..4d0391f1471026d2e08453bd008443032bcaf9bf 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -17,6 +17,7 @@ use error::Error; use ns; use stanza_error::StanzaError; +use caps::Caps; use delay::Delay; use idle::Idle; use ecaps2::ECaps2; @@ -66,6 +67,7 @@ pub type Priority = i8; #[derive(Debug, Clone)] pub enum PresencePayload { StanzaError(StanzaError), + Caps(Caps), Delay(Delay), Idle(Idle), ECaps2(ECaps2), @@ -80,6 +82,9 @@ impl TryFrom for PresencePayload { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { ("error", ns::JABBER_CLIENT) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), + // XEP-0115 + ("c", ns::CAPS) => PresencePayload::Caps(Caps::try_from(elem)?), + // XEP-0203 ("delay", ns::DELAY) => PresencePayload::Delay(Delay::try_from(elem)?), @@ -98,6 +103,7 @@ impl Into for PresencePayload { fn into(self) -> Element { match self { PresencePayload::StanzaError(stanza_error) => stanza_error.into(), + PresencePayload::Caps(caps) => caps.into(), PresencePayload::Delay(delay) => delay.into(), PresencePayload::Idle(idle) => idle.into(), PresencePayload::ECaps2(ecaps2) => ecaps2.into(), From 42235c42fb9dcf123482bc6bf2fe53e19b001ea4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 12:22:11 +0100 Subject: [PATCH 0272/1020] hashes: Implement From for String. --- src/hashes.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index 3c9a05eba5cdee8c7ff910f6438e93adc85b995e..f311788862e15bbce75d6c3ee9100ca0c1f75361 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -47,9 +47,9 @@ impl FromStr for Algo { } } -impl IntoAttributeValue for Algo { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { +impl From for String { + fn from(algo: Algo) -> String { + String::from(match algo { Algo::Sha_1 => "sha-1", Algo::Sha_256 => "sha-256", Algo::Sha_512 => "sha-512", @@ -57,8 +57,14 @@ impl IntoAttributeValue for Algo { Algo::Sha3_512 => "sha3-512", Algo::Blake2b_256 => "blake2b-256", Algo::Blake2b_512 => "blake2b-512", - Algo::Unknown(text) => return Some(text), - })) + Algo::Unknown(text) => return text, + }) + } +} + +impl IntoAttributeValue for Algo { + fn into_attribute_value(self) -> Option { + Some(String::from(self)) } } From bdaced76037cb8d02139948a2b3a39da89959b8f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 12:22:50 +0100 Subject: [PATCH 0273/1020] caps, ecaps2: Add a function to create a Disco query from a hash. --- src/caps.rs | 12 +++++++++++- src/ecaps2.rs | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index d17cb5018976a18c36279b949f319b31f6df7a4c..4b50424116762f9cd2d9edc8090a348129f9ec29 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -91,7 +91,8 @@ fn compute_features(features: &[Feature]) -> Vec { fn compute_identities(identities: &[Identity]) -> Vec { compute_items(identities, |identity| { - let string = format!("{}/{}/{}/{}", identity.category, identity.type_, identity.xml_lang, match identity.name { Some(ref name) => name.clone(), None => String::new() }); + let empty = String::new(); + let string = format!("{}/{}/{}/{}", identity.category, identity.type_, identity.xml_lang, match identity.name { Some(ref name) => name, None => &empty }); let bytes = string.as_bytes(); let mut vec = Vec::with_capacity(bytes.len()); vec.extend_from_slice(bytes); @@ -191,6 +192,15 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { }) } +pub fn query_caps(caps: Caps) -> Disco { + Disco { + node: Some(format!("{}#{}", caps.node, base64::encode(&caps.hash.hash))), + identities: vec!(), + features: vec!(), + extensions: vec!(), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 03f8a40b73d6ea6d8540182aac9d1a7b3dacc77a..09f1516ec57e5c2093249d37b5032307dec0665f 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -13,6 +13,7 @@ use hashes::{Hash, Algo}; use minidom::Element; use error::Error; use ns; +use base64; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; @@ -171,11 +172,19 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { }) } +pub fn query_ecaps2(hash: Hash) -> Disco { + Disco { + node: Some(format!("{}#{}.{}", ns::ECAPS2, String::from(hash.algo), base64::encode(&hash.hash))), + identities: vec!(), + features: vec!(), + extensions: vec!(), + } +} + #[cfg(test)] mod tests { use super::*; use ecaps2; - use base64; #[test] fn test_parse() { From d1a7d222f053ea081b2ec2fb747cb58c40314b30 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 12:29:21 +0100 Subject: [PATCH 0274/1020] delay: Use chrono to parse the stamp. --- src/delay.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 19b1a495cdb6e492ce2c979af9eb4295f15593a2..65349040c03faa2f0de16349f02ae7999755612e 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -7,6 +7,7 @@ use std::convert::TryFrom; use minidom::Element; +use chrono::prelude::*; use error::Error; use jid::Jid; @@ -16,7 +17,7 @@ use ns; #[derive(Debug, Clone)] pub struct Delay { pub from: Option, - pub stamp: String, + pub stamp: DateTime, pub data: Option, } @@ -31,7 +32,7 @@ impl TryFrom for Delay { return Err(Error::ParseError("Unknown child in delay element.")); } let from = get_attr!(elem, "from", optional); - let stamp = get_attr!(elem, "stamp", required); + let stamp = get_attr!(elem, "stamp", required, stamp, DateTime::parse_from_rfc3339(stamp)?); let data = match elem.text().as_ref() { "" => None, text => Some(text.to_owned()), @@ -49,7 +50,7 @@ impl Into for Delay { Element::builder("delay") .ns(ns::DELAY) .attr("from", self.from.and_then(|value| Some(String::from(value)))) - .attr("stamp", self.stamp) + .attr("stamp", self.stamp.to_rfc3339()) .append(self.data) .build() } @@ -65,7 +66,14 @@ mod tests { let elem: Element = "".parse().unwrap(); let delay = Delay::try_from(elem).unwrap(); assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap())); - assert_eq!(delay.stamp, "2002-09-10T23:08:25Z"); + assert_eq!(delay.stamp.year(), 2002); + assert_eq!(delay.stamp.month(), 9); + assert_eq!(delay.stamp.day(), 10); + assert_eq!(delay.stamp.hour(), 23); + assert_eq!(delay.stamp.minute(), 08); + assert_eq!(delay.stamp.second(), 25); + assert_eq!(delay.stamp.nanosecond(), 0); + assert_eq!(delay.stamp.timezone(), FixedOffset::east(0)); assert_eq!(delay.data, None); } @@ -93,10 +101,10 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let delay = Delay { from: None, - stamp: "2002-09-10T23:08:25Z".to_owned(), + stamp: DateTime::parse_from_rfc3339("2002-09-10T23:08:25Z").unwrap(), data: None, }; let elem2 = delay.into(); @@ -105,10 +113,10 @@ mod tests { #[test] fn test_serialise_data() { - let elem: Element = "Reason".parse().unwrap(); + let elem: Element = "Reason".parse().unwrap(); let delay = Delay { from: Some(Jid::from_str("juliet@example.org").unwrap()), - stamp: "2002-09-10T23:08:25Z".to_owned(), + stamp: DateTime::parse_from_rfc3339("2002-09-10T23:08:25Z").unwrap(), data: Some(String::from("Reason")), }; let elem2 = delay.into(); From 4cca174f68c2cb364ea900de6584b660a42f5bcb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 20:45:00 +0100 Subject: [PATCH 0275/1020] Implement the Hash trait on Jid. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 04f8c2e18a3da897b665823e0f40664d0e203491..b78001b05053436b49041500f83232c578f57286 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,7 @@ pub enum JidParseError { /// - A node/name, `node`, which is the optional part before the @. /// - A domain, `domain`, which is the mandatory part after the @ but before the /. /// - A resource, `resource`, which is the optional part after the /. -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct Jid { /// The node part of the Jabber ID, if it exists, else None. pub node: Option, From 8e1d5e7983ea580def4ac749e96faffd19f85015 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 22:10:00 +0100 Subject: [PATCH 0276/1020] disco: Make xml:lang a proper Option and rename it to lang. --- src/caps.rs | 5 +++-- src/disco.rs | 8 ++++---- src/ecaps2.rs | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index 4b50424116762f9cd2d9edc8090a348129f9ec29..b7753a195ed15fad27bd54ea4c60ae425e79da09 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -91,8 +91,9 @@ fn compute_features(features: &[Feature]) -> Vec { fn compute_identities(identities: &[Identity]) -> Vec { compute_items(identities, |identity| { - let empty = String::new(); - let string = format!("{}/{}/{}/{}", identity.category, identity.type_, identity.xml_lang, match identity.name { Some(ref name) => name, None => &empty }); + let lang = identity.lang.clone().unwrap_or_default(); + let name = identity.name.clone().unwrap_or_default(); + let string = format!("{}/{}/{}/{}", identity.category, identity.type_, lang, name); let bytes = string.as_bytes(); let mut vec = Vec::with_capacity(bytes.len()); vec.extend_from_slice(bytes); diff --git a/src/disco.rs b/src/disco.rs index 46d25dec88161063f9ab0ad17653f22f93856760..486f81e99bcdced741b5e655611257364b5828fd 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -37,7 +37,7 @@ impl IntoElements for Feature { pub struct Identity { pub category: String, // TODO: use an enum here. pub type_: String, // TODO: use an enum here. - pub xml_lang: String, + pub lang: Option, pub name: Option, } @@ -47,7 +47,7 @@ impl Into for Identity { .ns(ns::DISCO_INFO) .attr("category", self.category) .attr("type", self.type_) - .attr("xml:lang", self.xml_lang) + .attr("xml:lang", self.lang) .attr("name", self.name) .build() } @@ -98,12 +98,12 @@ impl TryFrom for Disco { return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) } - let lang = get_attr!(child, "xml:lang", default); + let lang = get_attr!(child, "xml:lang", optional); let name = get_attr!(child, "name", optional); identities.push(Identity { category: category, type_: type_, - xml_lang: lang, + lang: lang, name: name, }); } else if child.is("x", ns::DATA_FORMS) { diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 09f1516ec57e5c2093249d37b5032307dec0665f..7d9279bb2d73d1ae88a04b1e7add7559b8398009 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -88,7 +88,7 @@ fn compute_identities(identities: &[Identity]) -> Vec { compute_items(identities, 0x1c, |identity| { let mut bytes = compute_item(&identity.category); bytes.append(&mut compute_item(&identity.type_)); - bytes.append(&mut compute_item(&identity.xml_lang)); + bytes.append(&mut compute_item(&identity.lang.clone().unwrap_or_default())); bytes.append(&mut compute_item(&identity.name.clone().unwrap_or_default())); bytes.push(0x1e); bytes From abaf16079c9a10210785ff338b78b1f84229c91a Mon Sep 17 00:00:00 2001 From: lumi Date: Sat, 27 May 2017 23:18:46 +0200 Subject: [PATCH 0277/1020] add linkmauve to authors, bump version to 0.2.1 --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8ac64e9c1d78967e206d4e44778be5521d4c19a3..5a18dc9978757d36483b399b98f8ef6fc00fff7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jid" -version = "0.2.0" -authors = ["lumi "] +version = "0.2.1" +authors = ["lumi ", "Emmanuel Gil Peyrot "] description = "A crate which provides a Jid struct for Jabber IDs." homepage = "https://gitlab.com/lumi/jid-rs" repository = "https://gitlab.com/lumi/jid-rs" From ec31c2b4a3abbef3934c2d44247643e50d432c02 Mon Sep 17 00:00:00 2001 From: lumi Date: Sat, 27 May 2017 21:37:37 +0000 Subject: [PATCH 0278/1020] make CI work, hopefully --- .gitlab-ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bd8cf4313765d2f38bea2ff4edc4a22ba61b9866..0f54ef57dff651c4da0e8d1054489abc4653019e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,9 @@ image: "scorpil/rust:nightly" +before_script: +- apt-get update -yqq +- apt-get install -yqq --no-install-recommends build-essential + test:cargo: script: - rustc --version && cargo --version From d63c7b991d6285ae7f9d4c4498ca6c6b3c8cfdff Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 22:33:20 +0100 Subject: [PATCH 0279/1020] implement IntoAttributeValue for usize --- src/convert.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/convert.rs b/src/convert.rs index 3e3ed4162c0e3e8a3676fdf26720fa9938bb17f4..93631ad8b28cf068d66f783b75e18f61b93c1844 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -87,6 +87,12 @@ pub trait IntoAttributeValue { fn into_attribute_value(self) -> Option; } +impl IntoAttributeValue for usize { + fn into_attribute_value(self) -> Option { + Some(format!("{}", self)) + } +} + impl IntoAttributeValue for String { fn into_attribute_value(self) -> Option { Some(self) From 638866fa4c9a7d0bd863cb00690cea68407fbab4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 22:56:17 +0100 Subject: [PATCH 0280/1020] implement IntoAttributeValue for u32, u16 and u8 --- src/convert.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/convert.rs b/src/convert.rs index 93631ad8b28cf068d66f783b75e18f61b93c1844..980f68f72ced8458a42975ae0989c081952d9e19 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -93,6 +93,24 @@ impl IntoAttributeValue for usize { } } +impl IntoAttributeValue for u32 { + fn into_attribute_value(self) -> Option { + Some(format!("{}", self)) + } +} + +impl IntoAttributeValue for u16 { + fn into_attribute_value(self) -> Option { + Some(format!("{}", self)) + } +} + +impl IntoAttributeValue for u8 { + fn into_attribute_value(self) -> Option { + Some(format!("{}", self)) + } +} + impl IntoAttributeValue for String { fn into_attribute_value(self) -> Option { Some(self) From 4166751828ec2b4dd81109ff2903f86e7946769b Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 28 May 2017 00:02:26 +0200 Subject: [PATCH 0281/1020] bump version to 0.4.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bc53cd5a57a77ba225dfa2641dbc48b157e8eae1..39ba344161f302afe9efac7853b3a9d608e3a251 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.4.1" +version = "0.4.2" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] description = "A small, simple DOM implementation on top of xml-rs." homepage = "https://gitlab.com/lumi/minidom-rs" From 6794b347149e661f681db96b2ff5f045d5876121 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 May 2017 23:18:15 +0100 Subject: [PATCH 0282/1020] Cargo.toml: Update minidom to 0.4.3 to use its new IntoAttributeValue implementation on numbers. --- Cargo.toml | 2 +- src/ibb.rs | 4 ++-- src/jingle_ft.rs | 7 ++----- src/jingle_ibb.rs | 2 +- src/jingle_s5b.rs | 4 ++-- src/rsm.rs | 2 +- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d8b08268132ac7f972aa4488855be7f673e6d81a..700d7f1516d839ad031cc82e080017a3095a89a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.4.1" +minidom = "0.4.3" jid = "0.2.0" base64 = "0.5.0" digest = "0.5.0" diff --git a/src/ibb.rs b/src/ibb.rs index 71bf69511aa1bcd4143340dd53b6bff328fad5c9..18cb14e812dc7df3a9058563b690c8ecbd311ae5 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -113,7 +113,7 @@ impl Into for IBB { IBB::Open { block_size, sid, stanza } => { Element::builder("open") .ns(ns::IBB) - .attr("block-size", format!("{}", block_size)) + .attr("block-size", block_size) .attr("sid", sid) .attr("stanza", stanza) .build() @@ -121,7 +121,7 @@ impl Into for IBB { IBB::Data { seq, sid, data } => { Element::builder("data") .ns(ns::IBB) - .attr("seq", format!("{}", seq)) + .attr("seq", seq) .attr("sid", sid) .append(base64::encode(&data)) .build() diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 1a4550cf599f9ea6cbf3d091191fa5045808620b..1d37b6a9dda3576e0f93ca3cfe4a75bf56baadac 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -24,11 +24,8 @@ impl IntoElements for Range { fn into_elements(self, emitter: &mut ElementEmitter) { let mut elem = Element::builder("range") .ns(ns::JINGLE_FT) - .attr("offset", format!("{}", self.offset)) - .attr("length", match self.length { - Some(length) => Some(format!("{}", length)), - None => None - }) + .attr("offset", self.offset) + .attr("length", self.length) .build(); for hash in self.hashes { elem.append_child(hash.into()); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 6f789d3e969b61a021565ebc8db3d3e29f1421ec..afbf11c3308167ea02b4f0d8e589517434654894 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -43,7 +43,7 @@ impl Into for Transport { fn into(self) -> Element { Element::builder("transport") .ns(ns::JINGLE_IBB) - .attr("block-size", format!("{}", self.block_size)) + .attr("block-size", self.block_size) .attr("sid", self.sid) .attr("stanza", self.stanza) .build() diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 42c188f8152b5a2ab5a7735c270a6f0bc5ea2636..316a32102827f5e27c84621ca2926977028431ba 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -70,8 +70,8 @@ impl Into for Candidate { .attr("cid", self.cid) .attr("host", self.host) .attr("jid", self.jid) - .attr("port", match self.port { Some(port) => Some(format!("{}", port)), None => None }) - .attr("priority", format!("{}", self.priority)) + .attr("port", self.port) + .attr("priority", self.priority) .attr("type", self.type_) .build() } diff --git a/src/rsm.rs b/src/rsm.rs index 21d86ee790ad189768e48f087d4b13636c0e1397..e8d7109d8465dc7867077cfa0375bb5aa0ce2d05 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -103,7 +103,7 @@ impl Into for Set { if self.first.is_some() { elem.append_child(Element::builder("first") .ns(ns::RSM) - .attr("index", self.first_index.map(|index| format!("{}", index))) + .attr("index", self.first_index) .append(self.first).build()); } if self.index.is_some() { From d6a9e6e9eae67069342a9e98083aafae87cf391c Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 28 May 2017 00:25:57 +0200 Subject: [PATCH 0283/1020] implement ToAttributeValue on integral types, bump version --- Cargo.toml | 2 +- src/convert.rs | 45 ++++++++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 39ba344161f302afe9efac7853b3a9d608e3a251..0ddb0252e38126f9e33a170a48d5a52b17f88ca0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.4.2" +version = "0.4.3" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] description = "A small, simple DOM implementation on top of xml-rs." homepage = "https://gitlab.com/lumi/minidom-rs" diff --git a/src/convert.rs b/src/convert.rs index 980f68f72ced8458a42975ae0989c081952d9e19..126688b7a5f997a14b996677fe750b2878368649 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -87,29 +87,23 @@ pub trait IntoAttributeValue { fn into_attribute_value(self) -> Option; } -impl IntoAttributeValue for usize { - fn into_attribute_value(self) -> Option { - Some(format!("{}", self)) - } -} - -impl IntoAttributeValue for u32 { - fn into_attribute_value(self) -> Option { - Some(format!("{}", self)) +macro_rules! impl_into_attribute_value { + ($t:ty) => { + impl IntoAttributeValue for $t { + fn into_attribute_value(self) -> Option { + Some(format!("{}", self)) + } + } } } -impl IntoAttributeValue for u16 { - fn into_attribute_value(self) -> Option { - Some(format!("{}", self)) +macro_rules! impl_into_attribute_values { + ($($t:ty),*) => { + $(impl_into_attribute_value!($t);)* } } -impl IntoAttributeValue for u8 { - fn into_attribute_value(self) -> Option { - Some(format!("{}", self)) - } -} +impl_into_attribute_values!(usize, u64, u32, u16, u8, isize, i64, i32, i16, i8); impl IntoAttributeValue for String { fn into_attribute_value(self) -> Option { @@ -134,3 +128,20 @@ impl IntoAttributeValue for Option { self.and_then(|t| t.into_attribute_value()) } } + +#[cfg(test)] +mod tests { + use super::IntoAttributeValue; + + #[test] + fn test_into_attribute_value_on_ints() { + assert_eq!(16u8.into_attribute_value().unwrap() , "16"); + assert_eq!(17u16.into_attribute_value().unwrap() , "17"); + assert_eq!(18u32.into_attribute_value().unwrap() , "18"); + assert_eq!(19u64.into_attribute_value().unwrap() , "19"); + assert_eq!( 16i8.into_attribute_value().unwrap() , "16"); + assert_eq!((-17i16).into_attribute_value().unwrap(), "-17"); + assert_eq!( 18i32.into_attribute_value().unwrap(), "18"); + assert_eq!((-19i64).into_attribute_value().unwrap(), "-19"); + } +} From ee34bc1a8667b184efb3f57b42407205da436219 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 01:47:12 +0100 Subject: [PATCH 0284/1020] jingle_s5b: Remove unused .to_owned() during serialisation. --- src/jingle_s5b.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 316a32102827f5e27c84621ca2926977028431ba..e244a8c05d51ec77b28e6b15c6dff4382873bdb6 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -221,7 +221,7 @@ impl Into for Transport { TransportPayload::CandidateUsed(ref cid) => { vec!(Element::builder("candidate-used") .ns(ns::JINGLE_S5B) - .attr("cid", cid.to_owned()) + .attr("cid", cid) .build()) }, TransportPayload::ProxyError => { From 07fbc0adf1b4e09af6f534f49c939eee4344ff35 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 01:47:32 +0100 Subject: [PATCH 0285/1020] ChangeLog: Add version 0.4.0. --- ChangeLog | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ChangeLog b/ChangeLog index e8c42e2a450e16e1ee37f80a0ac2d2d6015cb07c..375c06fcb35bd58614f282ff681cc7e2eb50046d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +Version 0.4.0: +2017-05-28 Emmanuel Gil Peyrot + * Incompatible changes: + - Receipts now make the id optional, as per the specification. + - Hashes now expose their raw binary value, instead of staying + base64-encoded. + - Parse dates (XEP-0082) in delayed delivery (XEP-0203) and + last user interaction (XEP-0319), using the chrono crate. + * Improvements: + - Removal of most of the remaining clones, the only ones left + are due to minidom not exposing a draining iterator over the + children. + - Finish to parse all of the attributes using get_attr!(). + - More attribute checks. + - Split more parsers into one parser per element. + - Rely on minidom 0.4.3 to serialise more standard types + automatically. + - Implement forgotten serialisation for data forms (XEP-0004). + - Implement legacy capabilities (XEP-0115) for compatibility + with older software. + Version 0.3.0: 2017-05-23 Emmanuel Gil Peyrot * Big changes: From fcfe1888e2893ee81da804061a184210f9efac6c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 01:48:03 +0100 Subject: [PATCH 0286/1020] Release version 0.4.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 700d7f1516d839ad031cc82e080017a3095a89a6..84fe4addfdc40ae5956099519135e7634e51e8d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.3.0" +version = "0.4.0" authors = ["Emmanuel Gil Peyrot "] description = "Collection of parsers and serialisers for XMPP extensions" homepage = "https://hg.linkmauve.fr/xmpp-parsers" From aae435c4d95d131aecfd9f78fa621f272131bb7b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 16:30:43 +0100 Subject: [PATCH 0288/1020] Add a roster parser/serialiser. --- ChangeLog | 6 ++ src/lib.rs | 3 + src/ns.rs | 3 + src/roster.rs | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 298 insertions(+) create mode 100644 src/roster.rs diff --git a/ChangeLog b/ChangeLog index 375c06fcb35bd58614f282ff681cc7e2eb50046d..63f21648c286ded9afd6544e967e512fa94914ca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Version 0.?.?: +2017-??-?? Emmanuel Gil Peyrot + * New parsers/serialisers: + - Implementation of the roster management protocol defined in + RFC 6121 §2. + Version 0.4.0: 2017-05-28 Emmanuel Gil Peyrot * Incompatible changes: diff --git a/src/lib.rs b/src/lib.rs index 50ed56e67d624c853a8c32ed5c32b33e3c0fcbee..c6f11031aaebd1b5ac05a32f3a2296c61f0b91a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,9 @@ pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; +/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence +pub mod roster; + /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/ns.rs b/src/ns.rs index 99865ae56c929772e5140ff82830de313af969a1..4f563e620f8e99c4fcd2b0269aa091f1c6d355a7 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -9,6 +9,9 @@ pub const JABBER_CLIENT: &'static str = "jabber:client"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const XMPP_STANZAS: &'static str = "urn:ietf:params:xml:ns:xmpp-stanzas"; +/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence +pub const ROSTER: &'static str = "jabber:iq:roster"; + /// XEP-0004: Data Forms pub const DATA_FORMS: &'static str = "jabber:x:data"; diff --git a/src/roster.rs b/src/roster.rs new file mode 100644 index 0000000000000000000000000000000000000000..aaca6d8ef2582a1a489fbaf9ee3f257c86be078b --- /dev/null +++ b/src/roster.rs @@ -0,0 +1,286 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; +use std::str::FromStr; + +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use jid::Jid; + +use error::Error; +use ns; + +type Group = String; + +#[derive(Debug, Clone, PartialEq)] +pub enum Subscription { + None, + From, + To, + Both, + Remove, +} + +impl FromStr for Subscription { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "none" => Subscription::None, + "from" => Subscription::From, + "to" => Subscription::To, + "both" => Subscription::Both, + "remove" => Subscription::Remove, + + _ => return Err(Error::ParseError("Unknown value for attribute 'subscription'.")), + }) + } +} + +impl IntoAttributeValue for Subscription { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Subscription::None => "none", + Subscription::From => "from", + Subscription::To => "to", + Subscription::Both => "both", + Subscription::Remove => "remove", + })) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Item { + pub jid: Jid, + pub name: Option, + pub subscription: Option, + pub groups: Vec, +} + +impl TryFrom for Item { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("item", ns::ROSTER) { + return Err(Error::ParseError("This is not a roster item element.")); + } + + let mut item = Item { + jid: get_attr!(elem, "jid", required), + name: get_attr!(elem, "name", optional), + subscription: get_attr!(elem, "subscription", optional), + groups: vec!(), + }; + for child in elem.children() { + if !child.is("group", ns::ROSTER) { + return Err(Error::ParseError("Unknown element in roster item element.")); + } + for _ in child.children() { + return Err(Error::ParseError("Roster item group can’t have children.")); + } + item.groups.push(child.text()); + } + Ok(item) + } +} + +impl Into for Item { + fn into(self) -> Element { + Element::builder("item") + .ns(ns::ROSTER) + .attr("jid", String::from(self.jid)) + .attr("name", self.name) + .attr("subscription", self.subscription) + .append(self.groups) + .build() + } +} + +impl IntoElements for Item { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone)] +pub struct Roster { + pub ver: Option, + pub items: Vec, +} + +impl TryFrom for Roster { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::ROSTER) { + return Err(Error::ParseError("This is not a roster element.")); + } + for (attr, _) in elem.attrs() { + if attr != "ver" { + return Err(Error::ParseError("Unknown attribute in roster element.")); + } + } + + let mut roster = Roster { + ver: get_attr!(elem, "ver", optional), + items: vec!(), + }; + for child in elem.children() { + if !child.is("item", ns::ROSTER) { + return Err(Error::ParseError("Unknown element in roster element.")); + } + let item = Item::try_from(child.clone())?; + roster.items.push(item); + } + Ok(roster) + } +} + +impl Into for Roster { + fn into(self) -> Element { + Element::builder("query") + .ns(ns::ROSTER) + .attr("ver", self.ver) + .append(self.items) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get() { + let elem: Element = "".parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert!(roster.ver.is_none()); + assert!(roster.items.is_empty()); + } + + #[test] + fn test_result() { + let elem: Element = "".parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert_eq!(roster.ver, Some(String::from("ver7"))); + assert_eq!(roster.items.len(), 2); + + let elem: Element = "".parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert_eq!(roster.ver, Some(String::from("ver9"))); + assert!(roster.items.is_empty()); + + let elem: Element = r#" + + + Friends + + + + +"#.parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert_eq!(roster.ver, Some(String::from("ver11"))); + assert_eq!(roster.items.len(), 3); + assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap()); + assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); + assert_eq!(roster.items[0].subscription, Some(Subscription::Both)); + assert_eq!(roster.items[0].groups, vec!(String::from("Friends"))); + } + + #[test] + fn test_set() { + let elem: Element = "".parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert!(roster.ver.is_none()); + assert_eq!(roster.items.len(), 1); + + let elem: Element = r#" + + + Servants + + + +"#.parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert!(roster.ver.is_none()); + assert_eq!(roster.items.len(), 1); + assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); + assert_eq!(roster.items[0].name, Some(String::from("Nurse"))); + assert_eq!(roster.items[0].groups.len(), 1); + assert_eq!(roster.items[0].groups[0], String::from("Servants")); + + let elem: Element = r#" + + + +"#.parse().unwrap(); + let roster = Roster::try_from(elem).unwrap(); + assert!(roster.ver.is_none()); + assert_eq!(roster.items.len(), 1); + assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); + assert!(roster.items[0].name.is_none()); + assert!(roster.items[0].groups.is_empty()); + assert_eq!(roster.items[0].subscription, Some(Subscription::Remove)); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = Roster::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown element in roster element."); + + let elem: Element = "".parse().unwrap(); + let error = Roster::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in roster element."); + } + + #[test] + fn test_invalid_item() { + let elem: Element = "".parse().unwrap(); + let error = Roster::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'jid' missing."); + + /* + let elem: Element = "".parse().unwrap(); + let error = Roster::try_from(elem).unwrap_err(); + let error = match error { + Error::JidParseError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "Invalid JID, I guess?"); + */ + + let elem: Element = "".parse().unwrap(); + let error = Roster::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown element in roster item element."); + } +} From 32bfa84551e6f5577a0e0bc9a046507ed1e7112a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 16:33:43 +0100 Subject: [PATCH 0289/1020] =?UTF-8?q?presence:=20Rename=20Available=20to?= =?UTF-8?q?=20None,=20since=20that=E2=80=99s=20what=20it=20is.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presence.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 4d0391f1471026d2e08453bd008443032bcaf9bf..1d4ff35ed0c66bde1ffc9ad909bb567d839e8a79 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -117,7 +117,7 @@ impl Into for PresencePayload { pub enum PresenceType { /// This value is not an acceptable 'type' attribute, it is only used /// internally to signal the absence of 'type'. - Available, + None, Error, Probe, Subscribe, @@ -129,7 +129,7 @@ pub enum PresenceType { impl Default for PresenceType { fn default() -> PresenceType { - PresenceType::Available + PresenceType::None } } @@ -154,7 +154,7 @@ impl FromStr for PresenceType { impl IntoAttributeValue for PresenceType { fn into_attribute_value(self) -> Option { Some(match self { - PresenceType::Available => return None, + PresenceType::None => return None, PresenceType::Error => "error", PresenceType::Probe => "probe", @@ -283,7 +283,7 @@ mod tests { assert_eq!(presence.from, None); assert_eq!(presence.to, None); assert_eq!(presence.id, None); - assert_eq!(presence.type_, PresenceType::Available); + assert_eq!(presence.type_, PresenceType::None); assert!(presence.payloads.is_empty()); } From 9eb8f39a38ba23238e4bb60395d748beb0037193 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 17:10:12 +0100 Subject: [PATCH 0290/1020] presence: Make Show handle the None case, and rename PresenceType to Type. --- src/presence.rs | 111 +++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 1d4ff35ed0c66bde1ffc9ad909bb567d839e8a79..b359eb5e9b0eea6ac75da016d05484548554340a 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; -use minidom::{Element, IntoAttributeValue}; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use jid::Jid; @@ -24,12 +24,19 @@ use ecaps2::ECaps2; #[derive(Debug, Clone, PartialEq)] pub enum Show { + None, Away, Chat, Dnd, Xa, } +impl Default for Show { + fn default() -> Show { + Show::None + } +} + impl FromStr for Show { type Err = Error; @@ -45,16 +52,21 @@ impl FromStr for Show { } } -impl Into for Show { - fn into(self) -> Element { - Element::builder("show") - .append(match self { - Show::Away => "away", - Show::Chat => "chat", - Show::Dnd => "dnd", - Show::Xa => "xa", - }) - .build() +impl IntoElements for Show { + fn into_elements(self, emitter: &mut ElementEmitter) { + if self == Show::None { + return; + } + emitter.append_child( + Element::builder("show") + .append(match self { + Show::None => unreachable!(), + Show::Away => Some("away"), + Show::Chat => Some("chat"), + Show::Dnd => Some("dnd"), + Show::Xa => Some("xa"), + }) + .build()) } } @@ -114,7 +126,7 @@ impl Into for PresencePayload { } #[derive(Debug, Clone, PartialEq)] -pub enum PresenceType { +pub enum Type { /// This value is not an acceptable 'type' attribute, it is only used /// internally to signal the absence of 'type'. None, @@ -127,42 +139,42 @@ pub enum PresenceType { Unsubscribed, } -impl Default for PresenceType { - fn default() -> PresenceType { - PresenceType::None +impl Default for Type { + fn default() -> Type { + Type::None } } -impl FromStr for PresenceType { +impl FromStr for Type { type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { Ok(match s { - "error" => PresenceType::Error, - "probe" => PresenceType::Probe, - "subscribe" => PresenceType::Subscribe, - "subscribed" => PresenceType::Subscribed, - "unavailable" => PresenceType::Unavailable, - "unsubscribe" => PresenceType::Unsubscribe, - "unsubscribed" => PresenceType::Unsubscribed, + "error" => Type::Error, + "probe" => Type::Probe, + "subscribe" => Type::Subscribe, + "subscribed" => Type::Subscribed, + "unavailable" => Type::Unavailable, + "unsubscribe" => Type::Unsubscribe, + "unsubscribed" => Type::Unsubscribed, _ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")), }) } } -impl IntoAttributeValue for PresenceType { +impl IntoAttributeValue for Type { fn into_attribute_value(self) -> Option { Some(match self { - PresenceType::None => return None, - - PresenceType::Error => "error", - PresenceType::Probe => "probe", - PresenceType::Subscribe => "subscribe", - PresenceType::Subscribed => "subscribed", - PresenceType::Unavailable => "unavailable", - PresenceType::Unsubscribe => "unsubscribe", - PresenceType::Unsubscribed => "unsubscribed", + Type::None => return None, + + Type::Error => "error", + Type::Probe => "probe", + Type::Subscribe => "subscribe", + Type::Subscribed => "subscribed", + Type::Unavailable => "unavailable", + Type::Unsubscribe => "unsubscribe", + Type::Unsubscribed => "unsubscribed", }.to_owned()) } } @@ -172,8 +184,8 @@ pub struct Presence { pub from: Option, pub to: Option, pub id: Option, - pub type_: PresenceType, - pub show: Option, + pub type_: Type, + pub show: Show, pub statuses: BTreeMap, pub priority: Priority, pub payloads: Vec, @@ -186,20 +198,21 @@ impl TryFrom for Presence { if !root.is("presence", ns::JABBER_CLIENT) { return Err(Error::ParseError("This is not a presence element.")); } + let mut show = None; let mut priority = None; let mut presence = Presence { from: get_attr!(root, "from", optional), to: get_attr!(root, "to", optional), id: get_attr!(root, "id", optional), type_: get_attr!(root, "type", default), - show: None, + show: Show::None, statuses: BTreeMap::new(), priority: 0i8, payloads: vec!(), }; for elem in root.children() { if elem.is("show", ns::JABBER_CLIENT) { - if presence.show.is_some() { + if show.is_some() { return Err(Error::ParseError("More than one show element in a presence.")); } for _ in elem.children() { @@ -208,7 +221,7 @@ impl TryFrom for Presence { for _ in elem.attrs() { return Err(Error::ParseError("Unknown attribute in show element.")); } - presence.show = Some(Show::from_str(elem.text().as_ref())?); + show = Some(Show::from_str(elem.text().as_ref())?); } else if elem.is("status", ns::JABBER_CLIENT) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); @@ -237,6 +250,9 @@ impl TryFrom for Presence { presence.payloads.push(elem.clone()); } } + if let Some(show) = show { + presence.show = show; + } if let Some(priority) = priority { presence.priority = priority; } @@ -252,10 +268,7 @@ impl Into for Presence { .attr("to", self.to.and_then(|value| Some(String::from(value)))) .attr("id", self.id) .attr("type", self.type_) - .append(match self.show { - Some(show) => Some({ let elem: Element = show.into(); elem }), - None => None - }) + .append(self.show) .append(self.statuses.iter().map(|(lang, status)| { Element::builder("status") .attr("xml:lang", match lang.as_ref() { @@ -283,7 +296,7 @@ mod tests { assert_eq!(presence.from, None); assert_eq!(presence.to, None); assert_eq!(presence.id, None); - assert_eq!(presence.type_, PresenceType::None); + assert_eq!(presence.type_, Type::None); assert!(presence.payloads.is_empty()); } @@ -294,8 +307,8 @@ mod tests { from: None, to: None, id: None, - type_: PresenceType::Unavailable, - show: None, + type_: Type::Unavailable, + show: Show::None, statuses: BTreeMap::new(), priority: 0i8, payloads: vec!(), @@ -309,7 +322,7 @@ mod tests { let elem: Element = "chat".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); - assert_eq!(presence.show, Some(Show::Chat)); + assert_eq!(presence.show, Show::Chat); } #[test] @@ -432,8 +445,8 @@ mod tests { from: None, to: None, id: None, - type_: PresenceType::Unavailable, - show: None, + type_: Type::Unavailable, + show: Show::None, statuses: statuses, priority: 0i8, payloads: vec!(), From 073e208f060697e423828a3a872dd27a2ba2032c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 May 2017 17:12:46 +0100 Subject: [PATCH 0291/1020] iq: Wire up Roster. --- src/iq.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 7ab3309fc74b52cb6c7ed1263146afa76b59da14..76a97b1f217408f2973030d93241ed3120b62d2e 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -17,6 +17,7 @@ use error::Error; use ns; use stanza_error::StanzaError; +use roster::Roster; use disco::Disco; use ibb::IBB; use jingle::Jingle; @@ -26,6 +27,7 @@ use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum IqPayload { + Roster(Roster), Disco(Disco), IBB(IBB), Jingle(Jingle), @@ -42,6 +44,9 @@ impl TryFrom for IqPayload { fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + // RFC-6121 + ("query", ns::ROSTER) => IqPayload::Roster(Roster::try_from(elem)?), + // XEP-0030 ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?), @@ -165,6 +170,7 @@ impl TryFrom for Iq { impl Into for IqPayload { fn into(self) -> Element { match self { + IqPayload::Roster(roster) => roster.into(), IqPayload::Disco(disco) => disco.into(), IqPayload::IBB(ibb) => ibb.into(), IqPayload::Jingle(jingle) => jingle.into(), From 7395e4b88ffcef26d448f78e36e1e6964bcda26f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 29 May 2017 03:40:34 +0100 Subject: [PATCH 0292/1020] roster: Make an empty name be None instead. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFC 6121 §2.4.1: “Implementation Note: Including an empty 'name' attribute is equivalent to including no 'name' attribute; both actions set the name to the empty string.” --- src/roster.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/roster.rs b/src/roster.rs index aaca6d8ef2582a1a489fbaf9ee3f257c86be078b..51e8eb131eb113d99778aa71e591b7229cb24bdd 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -70,7 +70,7 @@ impl TryFrom for Item { let mut item = Item { jid: get_attr!(elem, "jid", required), - name: get_attr!(elem, "name", optional), + name: get_attr!(elem, "name", optional).and_then(|name| if name == "" { None } else { Some(name) }), subscription: get_attr!(elem, "subscription", optional), groups: vec!(), }; From 08ba1640733b6e14a17ef73ea668f5ff3a10b28e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 29 May 2017 03:42:11 +0100 Subject: [PATCH 0293/1020] roster: Add a test for empty name == no name. --- src/roster.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/roster.rs b/src/roster.rs index 51e8eb131eb113d99778aa71e591b7229cb24bdd..e8dd8ad5609299662148cbb3e78797b693b254df 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -168,6 +168,10 @@ mod tests { assert_eq!(roster.ver, Some(String::from("ver7"))); assert_eq!(roster.items.len(), 2); + let elem2: Element = "".parse().unwrap(); + let roster2 = Roster::try_from(elem2).unwrap(); + assert_eq!(roster.items, roster2.items); + let elem: Element = "".parse().unwrap(); let roster = Roster::try_from(elem).unwrap(); assert_eq!(roster.ver, Some(String::from("ver9"))); From 33994c10425fe0609766a6c2190dbbc787133c57 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 29 May 2017 05:14:49 +0100 Subject: [PATCH 0294/1020] hashes: Implement Eq and Hash. --- src/hashes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index f311788862e15bbce75d6c3ee9100ca0c1f75361..07f2822c479fbe9feb8e06ec93308caaad00920f 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -16,7 +16,7 @@ use ns; use base64; #[allow(non_camel_case_types)] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Algo { Sha_1, Sha_256, @@ -68,7 +68,7 @@ impl IntoAttributeValue for Algo { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Hash { pub algo: Algo, pub hash: Vec, From c4c65281976e68af54a03e885c739c737735321d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 30 May 2017 22:02:56 +0100 Subject: [PATCH 0295/1020] Muc parser --- Cargo.toml | 5 ++- src/lib.rs | 4 +++ src/muc.rs | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 4 +++ src/presence.rs | 7 +++++ 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/muc.rs diff --git a/Cargo.toml b/Cargo.toml index 84fe4addfdc40ae5956099519135e7634e51e8d7..1a93c909c8a33752dadc64aa1a629cd1ffaa7b92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "xmpp-parsers" version = "0.4.0" -authors = ["Emmanuel Gil Peyrot "] +authors = [ + "Emmanuel Gil Peyrot ", + "Maxime “pep” Buquet ", +] description = "Collection of parsers and serialisers for XMPP extensions" homepage = "https://hg.linkmauve.fr/xmpp-parsers" repository = "https://hg.linkmauve.fr/xmpp-parsers" diff --git a/src/lib.rs b/src/lib.rs index c6f11031aaebd1b5ac05a32f3a2296c61f0b91a0..b5ee57ef1fedd8c683f8580eed44c67de7fb6a16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ //! back before being sent over the wire. // Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -71,6 +72,9 @@ pub mod data_forms; /// XEP-0030: Service Discovery pub mod disco; +/// XEP-0045: Multi-User Chat +pub mod muc; + /// XEP-0047: In-Band Bytestreams pub mod ibb; diff --git a/src/muc.rs b/src/muc.rs new file mode 100644 index 0000000000000000000000000000000000000000..12923fc01d27783dc1ad6f9695523761893f4588 --- /dev/null +++ b/src/muc.rs @@ -0,0 +1,82 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; + +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct Muc; + +impl TryFrom for Muc { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("x", ns::MUC) { + return Err(Error::ParseError("This is not an x element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in x element.")); + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in x element.")); + } + Ok(Muc) + } +} + +impl Into for Muc { + fn into(self) -> Element { + Element::builder("x") + .ns(ns::MUC) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + Muc::try_from(elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = Muc::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in x element."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let muc = Muc; + let elem2 = muc.into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = Muc::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in x element."); + } +} diff --git a/src/ns.rs b/src/ns.rs index 4f563e620f8e99c4fcd2b0269aa091f1c6d355a7..d7706617e29e1746e338c3ec7c37159a1b1a10f6 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,4 +1,5 @@ // Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -18,6 +19,9 @@ pub const DATA_FORMS: &'static str = "jabber:x:data"; /// XEP-0030: Service Discovery pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; +/// XEP-0045: Multi-User Chat +pub const MUC: &'static str = "http://jabber.org/protocol/muc"; + /// XEP-0047: In-Band Bytestreams pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; diff --git a/src/presence.rs b/src/presence.rs index b359eb5e9b0eea6ac75da016d05484548554340a..d04a3d2e3a227b956adc440e0cfde59a1e73b83e 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -1,4 +1,5 @@ // Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -17,6 +18,7 @@ use error::Error; use ns; use stanza_error::StanzaError; +use muc::Muc; use caps::Caps; use delay::Delay; use idle::Idle; @@ -79,6 +81,7 @@ pub type Priority = i8; #[derive(Debug, Clone)] pub enum PresencePayload { StanzaError(StanzaError), + Muc(Muc), Caps(Caps), Delay(Delay), Idle(Idle), @@ -94,6 +97,9 @@ impl TryFrom for PresencePayload { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { ("error", ns::JABBER_CLIENT) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), + // XEP-0045 + ("x", ns::MUC) => PresencePayload::Muc(Muc::try_from(elem)?), + // XEP-0115 ("c", ns::CAPS) => PresencePayload::Caps(Caps::try_from(elem)?), @@ -115,6 +121,7 @@ impl Into for PresencePayload { fn into(self) -> Element { match self { PresencePayload::StanzaError(stanza_error) => stanza_error.into(), + PresencePayload::Muc(muc) => muc.into(), PresencePayload::Caps(caps) => caps.into(), PresencePayload::Delay(delay) => delay.into(), PresencePayload::Idle(idle) => idle.into(), From bd19341f69a7482176dcede95af4c9e3e6763efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 31 May 2017 02:54:47 +0100 Subject: [PATCH 0296/1020] Muc and parser --- src/muc.rs | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/ns.rs | 2 + 2 files changed, 271 insertions(+), 5 deletions(-) diff --git a/src/muc.rs b/src/muc.rs index 12923fc01d27783dc1ad6f9695523761893f4588..c8248b7500df034a926f847a7c83bdc2572d82b2 100644 --- a/src/muc.rs +++ b/src/muc.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; -use minidom::Element; +use minidom::{Element, IntoElements, ElementEmitter}; use error::Error; @@ -40,18 +40,189 @@ impl Into for Muc { } } +#[derive(Debug, Clone, PartialEq)] +pub enum Status { + // 100 + NonAnonymousRoom, + + // 101 + AffiliationChange, + + // 102 + ConfigShowsUnavailableMembers, + + // 103 + ConfigHidesUnavailableMembers, + + // 104 + ConfigNonPrivacyRelated, + + // 110 + SelfPresence, + + // 170 + ConfigRoomLoggingEnabled, + + // 171 + ConfigRoomLoggingDisabled, + + // 172 + ConfigRoomNonAnonymous, + + // 173 + ConfigRoomSemiAnonymous, + + // 201 + RoomHasBeenCreated, + + // 210 + AssignedNick, + + // 301 + Banned, + + // 303 + NewNick, + + // 307 + Kicked, + + // 321 + RemovalFromRoom, + + // 322 + ConfigMembersOnly, + + // 332 + ServiceShutdown, +} + +impl TryFrom for Status { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("status", ns::MUC_USER) { + return Err(Error::ParseError("This is not a status element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in status element.")); + } + for (attr, _) in elem.attrs() { + if attr != "code" { + return Err(Error::ParseError("Unknown attribute in status element.")); + } + } + let code = get_attr!(elem, "code", required); + + Ok(match code { + 100 => Status::NonAnonymousRoom, + 101 => Status::AffiliationChange, + 102 => Status::ConfigShowsUnavailableMembers, + 103 => Status::ConfigHidesUnavailableMembers, + 104 => Status::ConfigNonPrivacyRelated, + 110 => Status::SelfPresence, + 170 => Status::ConfigRoomLoggingEnabled, + 171 => Status::ConfigRoomLoggingDisabled, + 172 => Status::ConfigRoomNonAnonymous, + 173 => Status::ConfigRoomSemiAnonymous, + 201 => Status::RoomHasBeenCreated, + 210 => Status::AssignedNick, + 301 => Status::Banned, + 303 => Status::NewNick, + 307 => Status::Kicked, + 321 => Status::RemovalFromRoom, + 322 => Status::ConfigMembersOnly, + 332 => Status::ServiceShutdown, + _ => return Err(Error::ParseError("Invalid status code.")), + }) + } +} + +impl Into for Status { + fn into(self) -> Element { + Element::builder("status") + .ns(ns::MUC_USER) + .attr("code", match self { + Status::NonAnonymousRoom => 100, + Status::AffiliationChange => 101, + Status::ConfigShowsUnavailableMembers => 102, + Status::ConfigHidesUnavailableMembers => 103, + Status::ConfigNonPrivacyRelated => 104, + Status::SelfPresence => 110, + Status::ConfigRoomLoggingEnabled => 170, + Status::ConfigRoomLoggingDisabled => 171, + Status::ConfigRoomNonAnonymous => 172, + Status::ConfigRoomSemiAnonymous => 173, + Status::RoomHasBeenCreated => 201, + Status::AssignedNick => 210, + Status::Banned => 301, + Status::NewNick => 303, + Status::Kicked => 307, + Status::RemovalFromRoom => 321, + Status::ConfigMembersOnly => 322, + Status::ServiceShutdown => 332, + }) + .build() + } +} + +impl IntoElements for Status { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone)] +pub struct MucUser { + status: Vec, +} + +impl TryFrom for MucUser { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("x", ns::MUC_USER) { + return Err(Error::ParseError("This is not an x element.")); + } + let mut status = vec!(); + for child in elem.children() { + if child.is("status", ns::MUC_USER) { + status.push(Status::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown child in x element.")); + } + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in x element.")); + } + Ok(MucUser { + status: status, + }) + } +} + +impl Into for MucUser { + fn into(self) -> Element { + Element::builder("x") + .ns(ns::MUC_USER) + .append(self.status) + .build() + } +} + #[cfg(test)] mod tests { use super::*; + use std::error::Error as StdError; #[test] - fn test_simple() { + fn test_muc_simple() { let elem: Element = "".parse().unwrap(); Muc::try_from(elem).unwrap(); } #[test] - fn test_invalid_child() { + fn test_muc_invalid_child() { let elem: Element = "".parse().unwrap(); let error = Muc::try_from(elem).unwrap_err(); let message = match error { @@ -62,7 +233,7 @@ mod tests { } #[test] - fn test_serialise() { + fn test_muc_serialise() { let elem: Element = "".parse().unwrap(); let muc = Muc; let elem2 = muc.into(); @@ -70,7 +241,7 @@ mod tests { } #[test] - fn test_invalid_attribute() { + fn test_muc_invalid_attribute() { let elem: Element = "".parse().unwrap(); let error = Muc::try_from(elem).unwrap_err(); let message = match error { @@ -79,4 +250,97 @@ mod tests { }; assert_eq!(message, "Unknown attribute in x element."); } + + #[test] + fn test_muc_user_simple() { + let elem: Element = "".parse().unwrap(); + MucUser::try_from(elem).unwrap(); + } + + #[test] + fn test_muc_user_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = MucUser::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in x element."); + } + + #[test] + fn test_muc_user_serialise() { + let elem: Element = "".parse().unwrap(); + let muc = MucUser { status: vec!() }; + let elem2 = muc.into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_muc_user_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = MucUser::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in x element."); + } + + #[test] + fn test_status_simple() { + let elem: Element = "".parse().unwrap(); + Status::try_from(elem).unwrap(); + } + + #[test] + fn test_status_invalid() { + let elem: Element = "".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'code' missing."); + } + + #[test] + fn test_status_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in status element."); + } + + #[test] + fn test_status_simple_code() { + let elem: Element = "".parse().unwrap(); + let status = Status::try_from(elem).unwrap(); + assert_eq!(status, Status::Kicked); + } + + #[test] + fn test_status_invalid_code() { + let elem: Element = "".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Invalid status code."); + } + + #[test] + fn test_status_invalid_code2() { + let elem: Element = "".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "invalid digit found in string"); + } } diff --git a/src/ns.rs b/src/ns.rs index d7706617e29e1746e338c3ec7c37159a1b1a10f6..149025ac5402cd5cef9514a860c7a95345bdb4e9 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -21,6 +21,8 @@ pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; /// XEP-0045: Multi-User Chat pub const MUC: &'static str = "http://jabber.org/protocol/muc"; +/// XEP-0045: Multi-User Chat +pub const MUC_USER: &'static str = "http://jabber.org/protocol/muc#user"; /// XEP-0047: In-Band Bytestreams pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; From 212d9e7e7e80d48eb1f2fa39f5503b5782fd542a Mon Sep 17 00:00:00 2001 From: Astro Date: Fri, 2 Jun 2017 00:42:57 +0200 Subject: [PATCH 0297/1020] this ain't work --- .gitignore | 2 + Cargo.toml | 9 +++ src/lib.rs | 112 ++++++++++++++++++++++++++++++++++ src/xmpp_codec.rs | 149 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 272 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/xmpp_codec.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a9d37c560c6ab8d4afbf47eda643e8c42e857716 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..4e643380825af52a1c9bf9460b5608f7446b2d03 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "tokio-xmpp" +version = "0.1.0" +authors = ["Astro "] + +[dependencies] +futures = "0.1.6" +tokio-core = "0.1.1" +RustyXML = "0.1.1" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..e9162b02c91e1c8af9754f70e0514c185c925b3e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,112 @@ +extern crate futures; +extern crate tokio_core; +extern crate xml; + +use std::net::SocketAddr; +use std::net::ToSocketAddrs; +use std::sync::Arc; +use std::io::ErrorKind; +use futures::{Future, BoxFuture, Sink, Poll}; +use futures::stream::{Stream, iter}; +use futures::future::result; +use tokio_core::reactor::Handle; +use tokio_core::io::Io; +use tokio_core::net::TcpStream; + +mod xmpp_codec; +use xmpp_codec::*; + + +// type FullClient = sasl::Client> + +type Event = (); +type Error = std::io::Error; + +struct TCPStream { + source: Box>, + sink: Arc>>>, +} + +impl TCPStream { + pub fn connect(addr: &SocketAddr, handle: &Handle) -> BoxFuture, std::io::Error> { + TcpStream::connect(addr, handle) + .and_then(|stream| { + let (sink, source) = stream.framed(XMPPCodec::new()) + // .framed(UTF8Codec::new()) + .split(); + + sink.send(Packet::StreamStart) + .and_then(|sink| result(Ok((Arc::new(Box::new(sink)), source)))) + }) + .and_then(|(sink, source)| { + let sink1 = sink.clone(); + let source = source + .map(|items| iter(items.into_iter().map(Ok))) + .flatten() + .filter_map(move |pkt| Self::process_packet(pkt, &sink1)) + // .for_each(|ev| { + // match ev { + // Packet::Stanza + // _ => (), + // } + // Ok(println!("xmpp: {:?}", ev)) + // }) + // .boxed(); + ; + result(Ok(Arc::new(TCPStream { + source: Box::new(source), + sink: sink, + }))) + }).boxed() + //.map_err(|e| std::io::Error::new(ErrorKind::Other, e)); + } + + fn process_packet(pkt: Packet, sink: &Arc) -> Option + where S: Sink { + + println!("pkt: {:?}", pkt); + None + } +} + +struct ClientStream { + inner: TCPStream, +} + +impl ClientStream { + pub fn connect(jid: &str, password: &str, handle: &Handle) -> Box> { + let addr = "[2a01:4f8:a0:33d0::5]:5222" + .to_socket_addrs().unwrap() + .next().unwrap(); + let stream = + TCPStream::connect(&addr, handle) + .and_then(|stream| { + Ok(ClientStream { + inner: stream + }) + }); + Box::new(stream) + } +} + +#[cfg(test)] +mod tests { + use tokio_core::reactor::Core; + + #[test] + fn it_works() { + let mut core = Core::new().unwrap(); + let client = super::ClientStream::connect( + "astro@spaceboyz.net", + "...", + &core.handle() + ).and_then(|stream| { + stream.inner.source.boxed().for_each(|item| { + Ok(println!("stream item: {:?}", item)) + }) + }).boxed(); + core.run(client).unwrap(); + } + + // TODO: test truncated utf8 +} diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs new file mode 100644 index 0000000000000000000000000000000000000000..847339315fce725d93bebb51b0c194fe86810e1a --- /dev/null +++ b/src/xmpp_codec.rs @@ -0,0 +1,149 @@ +use std; +use std::str::from_utf8; +use std::io::{Error, ErrorKind}; +use std::collections::HashMap; +use tokio_core::io::{Codec, EasyBuf}; +use xml; + +const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/"; +const NS_STREAMS: &'static str = "http://etherx.jabber.org/streams"; +const NS_CLIENT: &'static str = "jabber:client"; + +struct XMPPRoot { + builder: xml::ElementBuilder, + pub attributes: HashMap<(String, Option), String>, +} + +impl XMPPRoot { + fn new(root: xml::StartTag) -> Self { + let mut builder = xml::ElementBuilder::new(); + let mut attributes = HashMap::new(); + println!("root attributes: {:?}", root.attributes); + for (name_ns, value) in root.attributes { + match name_ns { + (ref name, None) if name == "xmlns" => + builder.set_default_ns(value), + (ref prefix, Some(ref ns)) if ns == NS_XMLNS => + builder.define_prefix(prefix.to_owned(), value), + _ => { + attributes.insert(name_ns, value); + }, + } + } + + XMPPRoot { + builder: builder, + attributes: attributes, + } + } + + fn handle_event(&mut self, event: Result) + -> Option> { + self.builder.handle_event(event) + } +} + +#[derive(Debug)] +pub enum Packet { + Error(Box), + StreamStart, + Stanza(xml::Element), + StreamEnd, +} + +pub struct XMPPCodec { + parser: xml::Parser, + root: Option, +} + +impl XMPPCodec { + pub fn new() -> Self { + XMPPCodec { + parser: xml::Parser::new(), + root: None, + } + } +} + +impl Codec for XMPPCodec { + type In = Vec; + type Out = Packet; + + fn decode(&mut self, buf: &mut EasyBuf) -> Result, Error> { + match from_utf8(buf.as_slice()) { + Ok(s) => + self.parser.feed_str(s), + Err(e) => + return Err(Error::new(ErrorKind::InvalidInput, e)), + } + + let mut new_root = None; + let mut results = Vec::new(); + for event in &mut self.parser { + match &mut self.root { + &mut None => { + // Expecting + match event { + Ok(xml::Event::ElementStart(start_tag)) => { + new_root = Some(XMPPRoot::new(start_tag)); + results.push(Packet::StreamStart); + }, + Err(e) => + results.push(Packet::Error(Box::new(e))), + _ => + (), + } + } + + &mut Some(ref mut root) => { + match root.handle_event(event) { + None => (), + Some(Ok(stanza)) => { + println!("stanza: {}", stanza); + results.push(Packet::Stanza(stanza)); + }, + Some(Err(e)) => + results.push(Packet::Error(Box::new(e))), + }; + }, + } + + match new_root.take() { + None => (), + Some(root) => self.root = Some(root), + } + } + + if results.len() == 0 { + Ok(None) + } else { + Ok(Some(results)) + } + } + + fn encode(&mut self, msg: Self::Out, buf: &mut Vec) -> Result<(), Error> { + match msg { + Packet::StreamStart => { + let mut write = |s: &str| { + buf.extend_from_slice(s.as_bytes()); + }; + + write("\n"); + write("\n"); + + Ok(()) + }, + // TODO: Implement all + _ => Ok(()) + } + } + + fn decode_eof(&mut self, _buf: &mut EasyBuf) -> Result { + Ok(vec!()) + } +} From 005013f37ca8af711431e76852842b2bb32c83a3 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 3 Jun 2017 01:58:31 +0200 Subject: [PATCH 0298/1020] this kinda works --- Cargo.toml | 6 +- src/lib.rs | 147 ++++++++++++++++++++++++++-------------------- src/xmpp_codec.rs | 5 +- 3 files changed, 89 insertions(+), 69 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e643380825af52a1c9bf9460b5608f7446b2d03..2a22e0bdfce87bb334039930997ae76d54fe982b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,6 @@ version = "0.1.0" authors = ["Astro "] [dependencies] -futures = "0.1.6" -tokio-core = "0.1.1" -RustyXML = "0.1.1" +futures = "*" +tokio-core = "*" +RustyXML = "*" diff --git a/src/lib.rs b/src/lib.rs index e9162b02c91e1c8af9754f70e0514c185c925b3e..f8e6453e0d2b74008d72865223f4712c16cc9c2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#[macro_use] extern crate futures; extern crate tokio_core; extern crate xml; @@ -6,12 +7,12 @@ use std::net::SocketAddr; use std::net::ToSocketAddrs; use std::sync::Arc; use std::io::ErrorKind; -use futures::{Future, BoxFuture, Sink, Poll}; +use futures::{Future, BoxFuture, Sink, Poll, Async}; use futures::stream::{Stream, iter}; use futures::future::result; use tokio_core::reactor::Handle; use tokio_core::io::Io; -use tokio_core::net::TcpStream; +use tokio_core::net::{TcpStream, TcpStreamNew}; mod xmpp_codec; use xmpp_codec::*; @@ -19,92 +20,108 @@ use xmpp_codec::*; // type FullClient = sasl::Client> -type Event = (); -type Error = std::io::Error; +#[derive(Debug)] +pub struct TcpClient { + state: TcpClientState, +} -struct TCPStream { - source: Box>, - sink: Arc>>>, +enum TcpClientState { + Connecting(TcpStreamNew), + SendStart(futures::sink::Send>), + RecvStart(Option>), + Established, + Invalid, } -impl TCPStream { - pub fn connect(addr: &SocketAddr, handle: &Handle) -> BoxFuture, std::io::Error> { - TcpStream::connect(addr, handle) - .and_then(|stream| { - let (sink, source) = stream.framed(XMPPCodec::new()) - // .framed(UTF8Codec::new()) - .split(); - - sink.send(Packet::StreamStart) - .and_then(|sink| result(Ok((Arc::new(Box::new(sink)), source)))) - }) - .and_then(|(sink, source)| { - let sink1 = sink.clone(); - let source = source - .map(|items| iter(items.into_iter().map(Ok))) - .flatten() - .filter_map(move |pkt| Self::process_packet(pkt, &sink1)) - // .for_each(|ev| { - // match ev { - // Packet::Stanza - // _ => (), - // } - // Ok(println!("xmpp: {:?}", ev)) - // }) - // .boxed(); - ; - result(Ok(Arc::new(TCPStream { - source: Box::new(source), - sink: sink, - }))) - }).boxed() - //.map_err(|e| std::io::Error::new(ErrorKind::Other, e)); +impl std::fmt::Debug for TcpClientState { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + let s = match *self { + TcpClientState::Connecting(_) => "Connecting", + TcpClientState::SendStart(_) => "SendStart", + TcpClientState::RecvStart(_) => "RecvStart", + TcpClientState::Established => "Established", + TcpClientState::Invalid => "Invalid", + }; + write!(fmt, "{}", s) } +} - fn process_packet(pkt: Packet, sink: &Arc) -> Option - where S: Sink { - - println!("pkt: {:?}", pkt); - None +impl TcpClient { + pub fn connect(addr: &SocketAddr, handle: &Handle) -> Self { + let tcp_stream_new = TcpStream::connect(addr, handle); + TcpClient { + state: TcpClientState::Connecting(tcp_stream_new), + } } } -struct ClientStream { - inner: TCPStream, -} +impl Future for TcpClient { + type Item = XMPPStream; + type Error = std::io::Error; -impl ClientStream { - pub fn connect(jid: &str, password: &str, handle: &Handle) -> Box> { - let addr = "[2a01:4f8:a0:33d0::5]:5222" - .to_socket_addrs().unwrap() - .next().unwrap(); - let stream = - TCPStream::connect(&addr, handle) - .and_then(|stream| { - Ok(ClientStream { - inner: stream - }) - }); - Box::new(stream) + fn poll(&mut self) -> Poll { + let (new_state, result) = match self.state { + TcpClientState::Connecting(ref mut tcp_stream_new) => { + let tcp_stream = try_ready!(tcp_stream_new.poll()); + let xmpp_stream = tcp_stream.framed(XMPPCodec::new()); + let send = xmpp_stream.send(Packet::StreamStart); + let new_state = TcpClientState::SendStart(send); + (new_state, Ok(Async::NotReady)) + }, + TcpClientState::SendStart(ref mut send) => { + let xmpp_stream = try_ready!(send.poll()); + let new_state = TcpClientState::RecvStart(Some(xmpp_stream)); + (new_state, Ok(Async::NotReady)) + }, + TcpClientState::RecvStart(ref mut opt_xmpp_stream) => { + let mut xmpp_stream = opt_xmpp_stream.take().unwrap(); + match xmpp_stream.poll() { + Ok(Async::Ready(Some(events))) => println!("Recv start: {:?}", events), + Ok(Async::Ready(_)) => return Err(std::io::Error::from(ErrorKind::InvalidData)), + Ok(Async::NotReady) => { + *opt_xmpp_stream = Some(xmpp_stream); + return Ok(Async::NotReady); + }, + Err(e) => return Err(e) + }; + let new_state = TcpClientState::Established; + (new_state, Ok(Async::Ready(xmpp_stream))) + }, + TcpClientState::Established | TcpClientState::Invalid => + unreachable!(), + }; + + println!("Next state: {:?}", new_state); + self.state = new_state; + match result { + // by polling again, we register new future + Ok(Async::NotReady) => self.poll(), + result => result + } } } #[cfg(test)] mod tests { use tokio_core::reactor::Core; + use futures::{Future, Stream}; #[test] fn it_works() { + use std::net::ToSocketAddrs; + let addr = "[2a01:4f8:a0:33d0::5]:5222" + .to_socket_addrs().unwrap() + .next().unwrap(); + let mut core = Core::new().unwrap(); - let client = super::ClientStream::connect( - "astro@spaceboyz.net", - "...", + let client = super::TcpClient::connect( + &addr, &core.handle() ).and_then(|stream| { - stream.inner.source.boxed().for_each(|item| { + stream.for_each(|item| { Ok(println!("stream item: {:?}", item)) }) - }).boxed(); + }); core.run(client).unwrap(); } diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 847339315fce725d93bebb51b0c194fe86810e1a..e76dce59eb5e8521b6e277b26bdcdc4917398234 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -2,7 +2,7 @@ use std; use std::str::from_utf8; use std::io::{Error, ErrorKind}; use std::collections::HashMap; -use tokio_core::io::{Codec, EasyBuf}; +use tokio_core::io::{Codec, EasyBuf, Framed}; use xml; const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/"; @@ -51,6 +51,8 @@ pub enum Packet { StreamEnd, } +pub type XMPPStream = Framed; + pub struct XMPPCodec { parser: xml::Parser, root: Option, @@ -70,6 +72,7 @@ impl Codec for XMPPCodec { type Out = Packet; fn decode(&mut self, buf: &mut EasyBuf) -> Result, Error> { + println!("XMPPCodec.decode {:?}", buf.len()); match from_utf8(buf.as_slice()) { Ok(s) => self.parser.feed_str(s), From aece3798c11e564add80b2cdcd8f235fa907cc25 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 3 Jun 2017 02:17:12 +0200 Subject: [PATCH 0299/1020] XMPPCodec Input shall be just one Packet --- src/lib.rs | 2 +- src/xmpp_codec.rs | 34 +++++++++++++++++++--------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f8e6453e0d2b74008d72865223f4712c16cc9c2c..f57e7b8fc6e887869092189bc0cce0a5c25bfd80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,7 @@ impl Future for TcpClient { TcpClientState::RecvStart(ref mut opt_xmpp_stream) => { let mut xmpp_stream = opt_xmpp_stream.take().unwrap(); match xmpp_stream.poll() { - Ok(Async::Ready(Some(events))) => println!("Recv start: {:?}", events), + Ok(Async::Ready(Some(Packet::StreamStart))) => println!("Recv start!"), Ok(Async::Ready(_)) => return Err(std::io::Error::from(ErrorKind::InvalidData)), Ok(Async::NotReady) => { *opt_xmpp_stream = Some(xmpp_stream); diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index e76dce59eb5e8521b6e277b26bdcdc4917398234..ec718146dcab60b96d8681b25f05c27bc295654c 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -68,12 +68,14 @@ impl XMPPCodec { } impl Codec for XMPPCodec { - type In = Vec; + type In = Packet; type Out = Packet; fn decode(&mut self, buf: &mut EasyBuf) -> Result, Error> { println!("XMPPCodec.decode {:?}", buf.len()); - match from_utf8(buf.as_slice()) { + let buf_len = buf.len(); + let chunk = buf.drain_to(buf_len); + match from_utf8(chunk.as_slice()) { Ok(s) => self.parser.feed_str(s), Err(e) => @@ -81,7 +83,7 @@ impl Codec for XMPPCodec { } let mut new_root = None; - let mut results = Vec::new(); + let mut result = None; for event in &mut self.parser { match &mut self.root { &mut None => { @@ -89,10 +91,13 @@ impl Codec for XMPPCodec { match event { Ok(xml::Event::ElementStart(start_tag)) => { new_root = Some(XMPPRoot::new(start_tag)); - results.push(Packet::StreamStart); + result = Some(Packet::StreamStart); + break + }, + Err(e) => { + result = Some(Packet::Error(Box::new(e))); + break }, - Err(e) => - results.push(Packet::Error(Box::new(e))), _ => (), } @@ -103,10 +108,13 @@ impl Codec for XMPPCodec { None => (), Some(Ok(stanza)) => { println!("stanza: {}", stanza); - results.push(Packet::Stanza(stanza)); + result = Some(Packet::Stanza(stanza)); + break }, - Some(Err(e)) => - results.push(Packet::Error(Box::new(e))), + Some(Err(e)) => { + result = Some(Packet::Error(Box::new(e))); + break + } }; }, } @@ -117,11 +125,7 @@ impl Codec for XMPPCodec { } } - if results.len() == 0 { - Ok(None) - } else { - Ok(Some(results)) - } + Ok(result) } fn encode(&mut self, msg: Self::Out, buf: &mut Vec) -> Result<(), Error> { @@ -147,6 +151,6 @@ impl Codec for XMPPCodec { } fn decode_eof(&mut self, _buf: &mut EasyBuf) -> Result { - Ok(vec!()) + Err(Error::from(ErrorKind::UnexpectedEof)) } } From 0a34c6c71f9b3e508f9117b3a5d1b925b2e1d0e8 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 3 Jun 2017 02:26:44 +0200 Subject: [PATCH 0300/1020] fix stanza events --- src/lib.rs | 9 +++++++-- src/xmpp_codec.rs | 8 ++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f57e7b8fc6e887869092189bc0cce0a5c25bfd80..043c81e7c3b2c36b5608c9fdb8488207c6821c49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,6 +105,7 @@ impl Future for TcpClient { mod tests { use tokio_core::reactor::Core; use futures::{Future, Stream}; + use xmpp_codec::Packet; #[test] fn it_works() { @@ -118,8 +119,12 @@ mod tests { &addr, &core.handle() ).and_then(|stream| { - stream.for_each(|item| { - Ok(println!("stream item: {:?}", item)) + stream.for_each(|event| { + match event { + Packet::Stanza(el) => println!("<< {}", el), + _ => println!("!! {:?}", event), + } + Ok(()) }) }); core.run(client).unwrap(); diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index ec718146dcab60b96d8681b25f05c27bc295654c..dc7aeb7e031b06077481ac95a003003a956e406b 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -85,12 +85,12 @@ impl Codec for XMPPCodec { let mut new_root = None; let mut result = None; for event in &mut self.parser { - match &mut self.root { - &mut None => { + match self.root { + None => { // Expecting match event { Ok(xml::Event::ElementStart(start_tag)) => { - new_root = Some(XMPPRoot::new(start_tag)); + self.root = Some(XMPPRoot::new(start_tag)); result = Some(Packet::StreamStart); break }, @@ -103,7 +103,7 @@ impl Codec for XMPPCodec { } } - &mut Some(ref mut root) => { + Some(ref mut root) => { match root.handle_event(event) { None => (), Some(Ok(stanza)) => { From 288930bcd44351d9f9617f8a213a5ebd3e2e8a81 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 4 Jun 2017 01:37:46 +0200 Subject: [PATCH 0301/1020] reorg --- examples/echo_bot.rs | 29 ++++++++++ src/lib.rs | 128 ++----------------------------------------- src/tcp.rs | 97 ++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 123 deletions(-) create mode 100644 examples/echo_bot.rs create mode 100644 src/tcp.rs diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs new file mode 100644 index 0000000000000000000000000000000000000000..f1e4aff2c1bbc61c0e480fc4af4e322ad3272ef7 --- /dev/null +++ b/examples/echo_bot.rs @@ -0,0 +1,29 @@ +extern crate futures; +extern crate tokio_core; +extern crate tokio_xmpp; + +use tokio_core::reactor::Core; +use futures::{Future, Stream}; +use tokio_xmpp::{Packet, TcpClient}; + +fn main() { + use std::net::ToSocketAddrs; + let addr = "[2a01:4f8:a0:33d0::5]:5222" + .to_socket_addrs().unwrap() + .next().unwrap(); + + let mut core = Core::new().unwrap(); + let client = TcpClient::connect( + &addr, + &core.handle() + ).and_then(|stream| { + stream.for_each(|event| { + match event { + Packet::Stanza(el) => println!("<< {}", el), + _ => println!("!! {:?}", event), + } + Ok(()) + }) + }); + core.run(client).unwrap(); +} diff --git a/src/lib.rs b/src/lib.rs index 043c81e7c3b2c36b5608c9fdb8488207c6821c49..02a3295ac4cc2d4acb24755bbe48b892a83b8d05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,133 +2,15 @@ extern crate futures; extern crate tokio_core; extern crate xml; +extern crate rustls; +extern crate tokio_rustls; -use std::net::SocketAddr; -use std::net::ToSocketAddrs; -use std::sync::Arc; -use std::io::ErrorKind; -use futures::{Future, BoxFuture, Sink, Poll, Async}; -use futures::stream::{Stream, iter}; -use futures::future::result; -use tokio_core::reactor::Handle; -use tokio_core::io::Io; -use tokio_core::net::{TcpStream, TcpStreamNew}; mod xmpp_codec; -use xmpp_codec::*; +pub use xmpp_codec::*; +mod tcp; +pub use tcp::*; // type FullClient = sasl::Client> -#[derive(Debug)] -pub struct TcpClient { - state: TcpClientState, -} - -enum TcpClientState { - Connecting(TcpStreamNew), - SendStart(futures::sink::Send>), - RecvStart(Option>), - Established, - Invalid, -} - -impl std::fmt::Debug for TcpClientState { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - let s = match *self { - TcpClientState::Connecting(_) => "Connecting", - TcpClientState::SendStart(_) => "SendStart", - TcpClientState::RecvStart(_) => "RecvStart", - TcpClientState::Established => "Established", - TcpClientState::Invalid => "Invalid", - }; - write!(fmt, "{}", s) - } -} - -impl TcpClient { - pub fn connect(addr: &SocketAddr, handle: &Handle) -> Self { - let tcp_stream_new = TcpStream::connect(addr, handle); - TcpClient { - state: TcpClientState::Connecting(tcp_stream_new), - } - } -} - -impl Future for TcpClient { - type Item = XMPPStream; - type Error = std::io::Error; - - fn poll(&mut self) -> Poll { - let (new_state, result) = match self.state { - TcpClientState::Connecting(ref mut tcp_stream_new) => { - let tcp_stream = try_ready!(tcp_stream_new.poll()); - let xmpp_stream = tcp_stream.framed(XMPPCodec::new()); - let send = xmpp_stream.send(Packet::StreamStart); - let new_state = TcpClientState::SendStart(send); - (new_state, Ok(Async::NotReady)) - }, - TcpClientState::SendStart(ref mut send) => { - let xmpp_stream = try_ready!(send.poll()); - let new_state = TcpClientState::RecvStart(Some(xmpp_stream)); - (new_state, Ok(Async::NotReady)) - }, - TcpClientState::RecvStart(ref mut opt_xmpp_stream) => { - let mut xmpp_stream = opt_xmpp_stream.take().unwrap(); - match xmpp_stream.poll() { - Ok(Async::Ready(Some(Packet::StreamStart))) => println!("Recv start!"), - Ok(Async::Ready(_)) => return Err(std::io::Error::from(ErrorKind::InvalidData)), - Ok(Async::NotReady) => { - *opt_xmpp_stream = Some(xmpp_stream); - return Ok(Async::NotReady); - }, - Err(e) => return Err(e) - }; - let new_state = TcpClientState::Established; - (new_state, Ok(Async::Ready(xmpp_stream))) - }, - TcpClientState::Established | TcpClientState::Invalid => - unreachable!(), - }; - - println!("Next state: {:?}", new_state); - self.state = new_state; - match result { - // by polling again, we register new future - Ok(Async::NotReady) => self.poll(), - result => result - } - } -} - -#[cfg(test)] -mod tests { - use tokio_core::reactor::Core; - use futures::{Future, Stream}; - use xmpp_codec::Packet; - - #[test] - fn it_works() { - use std::net::ToSocketAddrs; - let addr = "[2a01:4f8:a0:33d0::5]:5222" - .to_socket_addrs().unwrap() - .next().unwrap(); - - let mut core = Core::new().unwrap(); - let client = super::TcpClient::connect( - &addr, - &core.handle() - ).and_then(|stream| { - stream.for_each(|event| { - match event { - Packet::Stanza(el) => println!("<< {}", el), - _ => println!("!! {:?}", event), - } - Ok(()) - }) - }); - core.run(client).unwrap(); - } - - // TODO: test truncated utf8 -} diff --git a/src/tcp.rs b/src/tcp.rs new file mode 100644 index 0000000000000000000000000000000000000000..fc0d6654e0cfcf1dd9cc23dfb46c0edf6dd238d2 --- /dev/null +++ b/src/tcp.rs @@ -0,0 +1,97 @@ +use std::fmt; +use std::net::SocketAddr; +use std::net::ToSocketAddrs; +use std::sync::Arc; +use std::io::{Error, ErrorKind}; +use futures::{Future, BoxFuture, Sink, Poll, Async}; +use futures::stream::{Stream, iter}; +use futures::sink; +use tokio_core::reactor::Handle; +use tokio_core::io::Io; +use tokio_core::net::{TcpStream, TcpStreamNew}; +use rustls::ClientConfig; +use tokio_rustls::ClientConfigExt; + +use super::{XMPPStream, XMPPCodec, Packet}; + + +#[derive(Debug)] +pub struct TcpClient { + state: TcpClientState, +} + +enum TcpClientState { + Connecting(TcpStreamNew), + SendStart(sink::Send>), + RecvStart(Option>), + Established, + Invalid, +} + +impl fmt::Debug for TcpClientState { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let s = match *self { + TcpClientState::Connecting(_) => "Connecting", + TcpClientState::SendStart(_) => "SendStart", + TcpClientState::RecvStart(_) => "RecvStart", + TcpClientState::Established => "Established", + TcpClientState::Invalid => "Invalid", + }; + write!(fmt, "{}", s) + } +} + +impl TcpClient { + pub fn connect(addr: &SocketAddr, handle: &Handle) -> Self { + let tcp_stream_new = TcpStream::connect(addr, handle); + TcpClient { + state: TcpClientState::Connecting(tcp_stream_new), + } + } +} + +impl Future for TcpClient { + type Item = XMPPStream; + type Error = Error; + + fn poll(&mut self) -> Poll { + let (new_state, result) = match self.state { + TcpClientState::Connecting(ref mut tcp_stream_new) => { + let tcp_stream = try_ready!(tcp_stream_new.poll()); + let xmpp_stream = tcp_stream.framed(XMPPCodec::new()); + let send = xmpp_stream.send(Packet::StreamStart); + let new_state = TcpClientState::SendStart(send); + (new_state, Ok(Async::NotReady)) + }, + TcpClientState::SendStart(ref mut send) => { + let xmpp_stream = try_ready!(send.poll()); + let new_state = TcpClientState::RecvStart(Some(xmpp_stream)); + (new_state, Ok(Async::NotReady)) + }, + TcpClientState::RecvStart(ref mut opt_xmpp_stream) => { + let mut xmpp_stream = opt_xmpp_stream.take().unwrap(); + match xmpp_stream.poll() { + Ok(Async::Ready(Some(Packet::StreamStart))) => println!("Recv start!"), + Ok(Async::Ready(_)) => return Err(Error::from(ErrorKind::InvalidData)), + Ok(Async::NotReady) => { + *opt_xmpp_stream = Some(xmpp_stream); + return Ok(Async::NotReady); + }, + Err(e) => return Err(e) + }; + let new_state = TcpClientState::Established; + (new_state, Ok(Async::Ready(xmpp_stream))) + }, + TcpClientState::Established | TcpClientState::Invalid => + unreachable!(), + }; + + println!("Next state: {:?}", new_state); + self.state = new_state; + match result { + // by polling again, we register new future + Ok(Async::NotReady) => self.poll(), + result => result + } + } +} From 482bf779551b48d82a3c54f5f8a40ba93401eab2 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 4 Jun 2017 02:05:08 +0200 Subject: [PATCH 0302/1020] tidy up --- Cargo.toml | 2 ++ src/lib.rs | 4 ++-- src/tcp.rs | 19 +++++++---------- src/xmpp_codec.rs | 53 ++++++++++++++++++++++------------------------- 4 files changed, 36 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2a22e0bdfce87bb334039930997ae76d54fe982b..23d59db4b893beba008b8dbd1552479ac60c4432 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,6 @@ authors = ["Astro "] [dependencies] futures = "*" tokio-core = "*" +tokio-io = "*" +bytes = "*" RustyXML = "*" diff --git a/src/lib.rs b/src/lib.rs index 02a3295ac4cc2d4acb24755bbe48b892a83b8d05..20ded2ef7b9f65e20020acba0b31e279bec4ea2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ #[macro_use] extern crate futures; extern crate tokio_core; +extern crate tokio_io; +extern crate bytes; extern crate xml; -extern crate rustls; -extern crate tokio_rustls; mod xmpp_codec; diff --git a/src/tcp.rs b/src/tcp.rs index fc0d6654e0cfcf1dd9cc23dfb46c0edf6dd238d2..01b9db0f102438758326221d04b4d04e049bcd17 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -1,16 +1,12 @@ use std::fmt; use std::net::SocketAddr; -use std::net::ToSocketAddrs; -use std::sync::Arc; use std::io::{Error, ErrorKind}; -use futures::{Future, BoxFuture, Sink, Poll, Async}; -use futures::stream::{Stream, iter}; +use futures::{Future, Sink, Poll, Async}; +use futures::stream::Stream; use futures::sink; use tokio_core::reactor::Handle; -use tokio_core::io::Io; +use tokio_io::AsyncRead; use tokio_core::net::{TcpStream, TcpStreamNew}; -use rustls::ClientConfig; -use tokio_rustls::ClientConfigExt; use super::{XMPPStream, XMPPCodec, Packet}; @@ -25,7 +21,6 @@ enum TcpClientState { SendStart(sink::Send>), RecvStart(Option>), Established, - Invalid, } impl fmt::Debug for TcpClientState { @@ -35,9 +30,9 @@ impl fmt::Debug for TcpClientState { TcpClientState::SendStart(_) => "SendStart", TcpClientState::RecvStart(_) => "RecvStart", TcpClientState::Established => "Established", - TcpClientState::Invalid => "Invalid", }; - write!(fmt, "{}", s) + try!(write!(fmt, "{}", s)); + Ok(()) } } @@ -58,7 +53,7 @@ impl Future for TcpClient { let (new_state, result) = match self.state { TcpClientState::Connecting(ref mut tcp_stream_new) => { let tcp_stream = try_ready!(tcp_stream_new.poll()); - let xmpp_stream = tcp_stream.framed(XMPPCodec::new()); + let xmpp_stream = AsyncRead::framed(tcp_stream, XMPPCodec::new()); let send = xmpp_stream.send(Packet::StreamStart); let new_state = TcpClientState::SendStart(send); (new_state, Ok(Async::NotReady)) @@ -82,7 +77,7 @@ impl Future for TcpClient { let new_state = TcpClientState::Established; (new_state, Ok(Async::Ready(xmpp_stream))) }, - TcpClientState::Established | TcpClientState::Invalid => + TcpClientState::Established => unreachable!(), }; diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index dc7aeb7e031b06077481ac95a003003a956e406b..28c0dfbd21d98ae1460e6e3f9207954d6a8f68ea 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -1,9 +1,11 @@ use std; +use std::fmt::Write; use std::str::from_utf8; use std::io::{Error, ErrorKind}; use std::collections::HashMap; -use tokio_core::io::{Codec, EasyBuf, Framed}; +use tokio_io::codec::{Framed, Encoder, Decoder}; use xml; +use bytes::*; const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/"; const NS_STREAMS: &'static str = "http://etherx.jabber.org/streams"; @@ -67,22 +69,20 @@ impl XMPPCodec { } } -impl Codec for XMPPCodec { - type In = Packet; - type Out = Packet; +impl Decoder for XMPPCodec { + type Item = Packet; + type Error = Error; - fn decode(&mut self, buf: &mut EasyBuf) -> Result, Error> { + fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { println!("XMPPCodec.decode {:?}", buf.len()); - let buf_len = buf.len(); - let chunk = buf.drain_to(buf_len); - match from_utf8(chunk.as_slice()) { + match from_utf8(buf.take().as_ref()) { Ok(s) => self.parser.feed_str(s), Err(e) => return Err(Error::new(ErrorKind::InvalidInput, e)), } - let mut new_root = None; + let mut new_root: Option = None; let mut result = None; for event in &mut self.parser { match self.root { @@ -128,29 +128,26 @@ impl Codec for XMPPCodec { Ok(result) } - fn encode(&mut self, msg: Self::Out, buf: &mut Vec) -> Result<(), Error> { - match msg { + fn decode_eof(&mut self, buf: &mut BytesMut) -> Result, Error> { + self.decode(buf) + } +} + +impl Encoder for XMPPCodec { + type Item = Packet; + type Error = Error; + + fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { + match item { Packet::StreamStart => { - let mut write = |s: &str| { - buf.extend_from_slice(s.as_bytes()); - }; - - write("\n"); - write("\n"); - - Ok(()) + write!(dst, + "\n +\n", + NS_CLIENT, NS_STREAMS) + .map_err(|_| Error::from(ErrorKind::WriteZero)) }, // TODO: Implement all _ => Ok(()) } } - - fn decode_eof(&mut self, _buf: &mut EasyBuf) -> Result { - Err(Error::from(ErrorKind::UnexpectedEof)) - } } From a618acd6d6fcc9f97490902fc401c2f157270fa0 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 5 Jun 2017 00:42:35 +0200 Subject: [PATCH 0303/1020] starttls works --- Cargo.toml | 2 + examples/echo_bot.rs | 21 ++++++- src/lib.rs | 4 ++ src/starttls.rs | 142 +++++++++++++++++++++++++++++++++++++++++++ src/tcp.rs | 3 +- src/xmpp_codec.rs | 10 +++ 6 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 src/starttls.rs diff --git a/Cargo.toml b/Cargo.toml index 23d59db4b893beba008b8dbd1552479ac60c4432..39de218681051fa316f44d867e007f7f5fad8306 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,5 @@ tokio-core = "*" tokio-io = "*" bytes = "*" RustyXML = "*" +rustls = "*" +tokio-rustls = "*" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index f1e4aff2c1bbc61c0e480fc4af4e322ad3272ef7..6933e1a2f227a357b282e62ffbbaf20b61948768 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,10 +1,15 @@ extern crate futures; extern crate tokio_core; extern crate tokio_xmpp; +extern crate rustls; +use std::sync::Arc; +use std::io::BufReader; +use std::fs::File; use tokio_core::reactor::Core; use futures::{Future, Stream}; -use tokio_xmpp::{Packet, TcpClient}; +use tokio_xmpp::{Packet, TcpClient, StartTlsClient}; +use rustls::ClientConfig; fn main() { use std::net::ToSocketAddrs; @@ -12,10 +17,16 @@ fn main() { .to_socket_addrs().unwrap() .next().unwrap(); + let mut config = ClientConfig::new(); + let mut certfile = BufReader::new(File::open("/usr/share/ca-certificates/CAcert/root.crt").unwrap()); + config.root_store.add_pem_file(&mut certfile).unwrap(); + let arc_config = Arc::new(config); + let mut core = Core::new().unwrap(); let client = TcpClient::connect( &addr, &core.handle() + ).and_then(|stream| StartTlsClient::from_stream(stream, arc_config) ).and_then(|stream| { stream.for_each(|event| { match event { @@ -25,5 +36,11 @@ fn main() { Ok(()) }) }); - core.run(client).unwrap(); + match core.run(client) { + Ok(_) => (), + Err(e) => { + println!("Fatal: {}", e); + () + } + } } diff --git a/src/lib.rs b/src/lib.rs index 20ded2ef7b9f65e20020acba0b31e279bec4ea2b..bcf666512f471a7c91730a5363928b5dca974dc5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,16 @@ extern crate tokio_core; extern crate tokio_io; extern crate bytes; extern crate xml; +extern crate rustls; +extern crate tokio_rustls; mod xmpp_codec; pub use xmpp_codec::*; mod tcp; pub use tcp::*; +mod starttls; +pub use starttls::*; // type FullClient = sasl::Client> diff --git a/src/starttls.rs b/src/starttls.rs new file mode 100644 index 0000000000000000000000000000000000000000..b14982aba596815b73bf26191d30a9446fcf0e98 --- /dev/null +++ b/src/starttls.rs @@ -0,0 +1,142 @@ +use std::mem::replace; +use std::io::{Error, ErrorKind}; +use std::sync::Arc; +use futures::{Future, Sink, Poll, Async}; +use futures::stream::Stream; +use futures::sink; +use tokio_core::net::TcpStream; +use rustls::*; +use tokio_rustls::*; +use xml; + +use super::{XMPPStream, XMPPCodec, Packet}; + + +const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; +const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; + +pub struct StartTlsClient { + state: StartTlsClientState, + arc_config: Arc, +} + +enum StartTlsClientState { + Invalid, + AwaitFeatures(XMPPStream), + SendStartTls(sink::Send>), + AwaitProceed(XMPPStream), + StartingTls(ConnectAsync), +} + +impl StartTlsClient { + /// Waits for + pub fn from_stream(xmpp_stream: XMPPStream, arc_config: Arc) -> Self { + StartTlsClient { + state: StartTlsClientState::AwaitFeatures(xmpp_stream), + arc_config: arc_config, + } + } +} + +// TODO: eval , check ns +impl Future for StartTlsClient { + type Item = XMPPStream>; + type Error = Error; + + fn poll(&mut self) -> Poll { + let old_state = replace(&mut self.state, StartTlsClientState::Invalid); + let mut retry = false; + + let (new_state, result) = match old_state { + StartTlsClientState::AwaitFeatures(mut xmpp_stream) => + match xmpp_stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.name == "features" + && stanza.ns == Some(NS_XMPP_STREAM.to_owned()) + => + { + println!("Got features: {}", stanza); + match stanza.get_child("starttls", Some(NS_XMPP_TLS)) { + None => + (StartTlsClientState::Invalid, Err(Error::from(ErrorKind::InvalidData))), + Some(_) => { + let nonza = xml::Element::new( + "starttls".to_owned(), Some(NS_XMPP_TLS.to_owned()), + vec![] + ); + println!("send {}", nonza); + let packet = Packet::Stanza(nonza); + let send = xmpp_stream.send(packet); + let new_state = StartTlsClientState::SendStartTls(send); + retry = true; + (new_state, Ok(Async::NotReady)) + }, + } + }, + Ok(Async::Ready(value)) => { + println!("StartTlsClient ignore {:?}", value); + (StartTlsClientState::AwaitFeatures(xmpp_stream), Ok(Async::NotReady)) + }, + Ok(_) => + (StartTlsClientState::AwaitFeatures(xmpp_stream), Ok(Async::NotReady)), + Err(e) => + (StartTlsClientState::AwaitFeatures(xmpp_stream), Err(e)), + }, + StartTlsClientState::SendStartTls(mut send) => + match send.poll() { + Ok(Async::Ready(xmpp_stream)) => { + println!("starttls sent"); + let new_state = StartTlsClientState::AwaitProceed(xmpp_stream); + retry = true; + (new_state, Ok(Async::NotReady)) + }, + Ok(Async::NotReady) => + (StartTlsClientState::SendStartTls(send), Ok(Async::NotReady)), + Err(e) => + (StartTlsClientState::SendStartTls(send), Err(e)), + }, + StartTlsClientState::AwaitProceed(mut xmpp_stream) => + match xmpp_stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.name == "proceed" => + { + println!("* proceed *"); + let stream = xmpp_stream.into_inner(); + let connect = self.arc_config.connect_async("spaceboyz.net", stream); + let new_state = StartTlsClientState::StartingTls(connect); + retry = true; + (new_state, Ok(Async::NotReady)) + }, + Ok(Async::Ready(value)) => { + println!("StartTlsClient ignore {:?}", value); + (StartTlsClientState::AwaitFeatures(xmpp_stream), Ok(Async::NotReady)) + }, + Ok(_) => + (StartTlsClientState::AwaitProceed(xmpp_stream), Ok(Async::NotReady)), + Err(e) => + (StartTlsClientState::AwaitProceed(xmpp_stream), Err(e)), + }, + StartTlsClientState::StartingTls(mut connect) => + match connect.poll() { + Ok(Async::Ready(tls_stream)) => { + println!("Got a TLS stream!"); + let xmpp_stream = XMPPCodec::frame_stream(tls_stream); + (StartTlsClientState::Invalid, Ok(Async::Ready(xmpp_stream))) + }, + Ok(Async::NotReady) => + (StartTlsClientState::StartingTls(connect), Ok(Async::NotReady)), + Err(e) => + (StartTlsClientState::StartingTls(connect), Err(e)), + }, + StartTlsClientState::Invalid => + unreachable!(), + }; + + self.state = new_state; + if retry { + self.poll() + } else { + result + } + } +} diff --git a/src/tcp.rs b/src/tcp.rs index 01b9db0f102438758326221d04b4d04e049bcd17..29608ea6f2abcf47a358a42e1235eee573888a79 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -5,7 +5,6 @@ use futures::{Future, Sink, Poll, Async}; use futures::stream::Stream; use futures::sink; use tokio_core::reactor::Handle; -use tokio_io::AsyncRead; use tokio_core::net::{TcpStream, TcpStreamNew}; use super::{XMPPStream, XMPPCodec, Packet}; @@ -53,7 +52,7 @@ impl Future for TcpClient { let (new_state, result) = match self.state { TcpClientState::Connecting(ref mut tcp_stream_new) => { let tcp_stream = try_ready!(tcp_stream_new.poll()); - let xmpp_stream = AsyncRead::framed(tcp_stream, XMPPCodec::new()); + let xmpp_stream = XMPPCodec::frame_stream(tcp_stream); let send = xmpp_stream.send(Packet::StreamStart); let new_state = TcpClientState::SendStart(send); (new_state, Ok(Async::NotReady)) diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 28c0dfbd21d98ae1460e6e3f9207954d6a8f68ea..38c593e46cd736aeb2976819dba501eaa96c376a 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -3,6 +3,7 @@ use std::fmt::Write; use std::str::from_utf8; use std::io::{Error, ErrorKind}; use std::collections::HashMap; +use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::{Framed, Encoder, Decoder}; use xml; use bytes::*; @@ -67,6 +68,12 @@ impl XMPPCodec { root: None, } } + + pub fn frame_stream(stream: S) -> Framed + where S: AsyncRead + AsyncWrite + { + AsyncRead::framed(stream, XMPPCodec::new()) + } } impl Decoder for XMPPCodec { @@ -146,6 +153,9 @@ impl Encoder for XMPPCodec { NS_CLIENT, NS_STREAMS) .map_err(|_| Error::from(ErrorKind::WriteZero)) }, + Packet::Stanza(stanza) => + write!(dst, "{}", stanza) + .map_err(|_| Error::from(ErrorKind::InvalidInput)), // TODO: Implement all _ => Ok(()) } From 98e7a2fbf4781f1b92c854a919ea075afe83899f Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 5 Jun 2017 00:45:16 +0200 Subject: [PATCH 0304/1020] starttls: parameterize TcpStream --- src/starttls.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/starttls.rs b/src/starttls.rs index b14982aba596815b73bf26191d30a9446fcf0e98..59946da38922cd3d60b941551f73fe85e6d5c5f1 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use futures::{Future, Sink, Poll, Async}; use futures::stream::Stream; use futures::sink; -use tokio_core::net::TcpStream; +use tokio_io::{AsyncRead, AsyncWrite}; use rustls::*; use tokio_rustls::*; use xml; @@ -15,22 +15,22 @@ use super::{XMPPStream, XMPPCodec, Packet}; const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; -pub struct StartTlsClient { - state: StartTlsClientState, +pub struct StartTlsClient { + state: StartTlsClientState, arc_config: Arc, } -enum StartTlsClientState { +enum StartTlsClientState { Invalid, - AwaitFeatures(XMPPStream), - SendStartTls(sink::Send>), - AwaitProceed(XMPPStream), - StartingTls(ConnectAsync), + AwaitFeatures(XMPPStream), + SendStartTls(sink::Send>), + AwaitProceed(XMPPStream), + StartingTls(ConnectAsync), } -impl StartTlsClient { +impl StartTlsClient { /// Waits for - pub fn from_stream(xmpp_stream: XMPPStream, arc_config: Arc) -> Self { + pub fn from_stream(xmpp_stream: XMPPStream, arc_config: Arc) -> Self { StartTlsClient { state: StartTlsClientState::AwaitFeatures(xmpp_stream), arc_config: arc_config, @@ -39,8 +39,8 @@ impl StartTlsClient { } // TODO: eval , check ns -impl Future for StartTlsClient { - type Item = XMPPStream>; +impl Future for StartTlsClient { + type Item = XMPPStream>; type Error = Error; fn poll(&mut self) -> Poll { From c32a38874c00e1befdbbb2cd92e6f3501410255f Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 5 Jun 2017 02:50:22 +0200 Subject: [PATCH 0305/1020] refactor into stream_start + xmpp_stream --- examples/echo_bot.rs | 10 ++++- src/lib.rs | 5 ++- src/starttls.rs | 79 ++++++++++++++------------------- src/stream_start.rs | 102 +++++++++++++++++++++++++++++++++++++++++++ src/tcp.rs | 52 ++++------------------ src/xmpp_codec.rs | 46 +++++++++---------- src/xmpp_stream.rs | 62 ++++++++++++++++++++++++++ 7 files changed, 239 insertions(+), 117 deletions(-) create mode 100644 src/stream_start.rs create mode 100644 src/xmpp_stream.rs diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 6933e1a2f227a357b282e62ffbbaf20b61948768..0cdd6b07e5c9dc8a4f460d22510daac81577cefc 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -8,7 +8,8 @@ use std::io::BufReader; use std::fs::File; use tokio_core::reactor::Core; use futures::{Future, Stream}; -use tokio_xmpp::{Packet, TcpClient, StartTlsClient}; +use tokio_xmpp::TcpClient; +use tokio_xmpp::xmpp_codec::Packet; use rustls::ClientConfig; fn main() { @@ -26,8 +27,13 @@ fn main() { let client = TcpClient::connect( &addr, &core.handle() - ).and_then(|stream| StartTlsClient::from_stream(stream, arc_config) ).and_then(|stream| { + if stream.can_starttls() { + stream.starttls(arc_config) + } else { + panic!("No STARTTLS") + } + }).and_then(|stream| { stream.for_each(|event| { match event { Packet::Stanza(el) => println!("<< {}", el), diff --git a/src/lib.rs b/src/lib.rs index bcf666512f471a7c91730a5363928b5dca974dc5..9764daeae2d7f1418f58ee181e2ca36730136f20 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,8 +8,9 @@ extern crate rustls; extern crate tokio_rustls; -mod xmpp_codec; -pub use xmpp_codec::*; +pub mod xmpp_codec; +pub mod xmpp_stream; +mod stream_start; mod tcp; pub use tcp::*; mod starttls; diff --git a/src/starttls.rs b/src/starttls.rs index 59946da38922cd3d60b941551f73fe85e6d5c5f1..b0fdc92f16c6807d8159e4e8baac7e92d8310719 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -1,5 +1,5 @@ use std::mem::replace; -use std::io::{Error, ErrorKind}; +use std::io::Error; use std::sync::Arc; use futures::{Future, Sink, Poll, Async}; use futures::stream::Stream; @@ -9,36 +9,44 @@ use rustls::*; use tokio_rustls::*; use xml; -use super::{XMPPStream, XMPPCodec, Packet}; +use xmpp_codec::*; +use xmpp_stream::*; +use stream_start::StreamStart; -const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; -const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; +pub const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; -pub struct StartTlsClient { +pub struct StartTlsClient { state: StartTlsClientState, arc_config: Arc, } -enum StartTlsClientState { +enum StartTlsClientState { Invalid, - AwaitFeatures(XMPPStream), SendStartTls(sink::Send>), AwaitProceed(XMPPStream), StartingTls(ConnectAsync), + Start(StreamStart>), } -impl StartTlsClient { +impl StartTlsClient { /// Waits for pub fn from_stream(xmpp_stream: XMPPStream, arc_config: Arc) -> Self { + let nonza = xml::Element::new( + "starttls".to_owned(), Some(NS_XMPP_TLS.to_owned()), + vec![] + ); + println!("send {}", nonza); + let packet = Packet::Stanza(nonza); + let send = xmpp_stream.send(packet); + StartTlsClient { - state: StartTlsClientState::AwaitFeatures(xmpp_stream), + state: StartTlsClientState::SendStartTls(send), arc_config: arc_config, } } } -// TODO: eval , check ns impl Future for StartTlsClient { type Item = XMPPStream>; type Error = Error; @@ -48,40 +56,6 @@ impl Future for StartTlsClient { let mut retry = false; let (new_state, result) = match old_state { - StartTlsClientState::AwaitFeatures(mut xmpp_stream) => - match xmpp_stream.poll() { - Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.name == "features" - && stanza.ns == Some(NS_XMPP_STREAM.to_owned()) - => - { - println!("Got features: {}", stanza); - match stanza.get_child("starttls", Some(NS_XMPP_TLS)) { - None => - (StartTlsClientState::Invalid, Err(Error::from(ErrorKind::InvalidData))), - Some(_) => { - let nonza = xml::Element::new( - "starttls".to_owned(), Some(NS_XMPP_TLS.to_owned()), - vec![] - ); - println!("send {}", nonza); - let packet = Packet::Stanza(nonza); - let send = xmpp_stream.send(packet); - let new_state = StartTlsClientState::SendStartTls(send); - retry = true; - (new_state, Ok(Async::NotReady)) - }, - } - }, - Ok(Async::Ready(value)) => { - println!("StartTlsClient ignore {:?}", value); - (StartTlsClientState::AwaitFeatures(xmpp_stream), Ok(Async::NotReady)) - }, - Ok(_) => - (StartTlsClientState::AwaitFeatures(xmpp_stream), Ok(Async::NotReady)), - Err(e) => - (StartTlsClientState::AwaitFeatures(xmpp_stream), Err(e)), - }, StartTlsClientState::SendStartTls(mut send) => match send.poll() { Ok(Async::Ready(xmpp_stream)) => { @@ -109,7 +83,7 @@ impl Future for StartTlsClient { }, Ok(Async::Ready(value)) => { println!("StartTlsClient ignore {:?}", value); - (StartTlsClientState::AwaitFeatures(xmpp_stream), Ok(Async::NotReady)) + (StartTlsClientState::AwaitProceed(xmpp_stream), Ok(Async::NotReady)) }, Ok(_) => (StartTlsClientState::AwaitProceed(xmpp_stream), Ok(Async::NotReady)), @@ -120,14 +94,25 @@ impl Future for StartTlsClient { match connect.poll() { Ok(Async::Ready(tls_stream)) => { println!("Got a TLS stream!"); - let xmpp_stream = XMPPCodec::frame_stream(tls_stream); - (StartTlsClientState::Invalid, Ok(Async::Ready(xmpp_stream))) + let start = XMPPStream::from_stream(tls_stream, "spaceboyz.net".to_owned()); + let new_state = StartTlsClientState::Start(start); + retry = true; + (new_state, Ok(Async::NotReady)) }, Ok(Async::NotReady) => (StartTlsClientState::StartingTls(connect), Ok(Async::NotReady)), Err(e) => (StartTlsClientState::StartingTls(connect), Err(e)), }, + StartTlsClientState::Start(mut start) => + match start.poll() { + Ok(Async::Ready(xmpp_stream)) => + (StartTlsClientState::Invalid, Ok(Async::Ready(xmpp_stream))), + Ok(Async::NotReady) => + (StartTlsClientState::Start(start), Ok(Async::NotReady)), + Err(e) => + (StartTlsClientState::Invalid, Err(e)), + }, StartTlsClientState::Invalid => unreachable!(), }; diff --git a/src/stream_start.rs b/src/stream_start.rs new file mode 100644 index 0000000000000000000000000000000000000000..480178eb58588e4959c6b60389c0328c47eec290 --- /dev/null +++ b/src/stream_start.rs @@ -0,0 +1,102 @@ +use std::mem::replace; +use std::io::{Error, ErrorKind}; +use std::collections::HashMap; +use futures::*; +use tokio_io::{AsyncRead, AsyncWrite}; +use tokio_io::codec::Framed; + +use xmpp_codec::*; +use xmpp_stream::*; + +const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; + +pub struct StreamStart { + state: StreamStartState, +} + +enum StreamStartState { + SendStart(sink::Send>), + RecvStart(Framed), + RecvFeatures(Framed, HashMap), + Invalid, +} + +impl StreamStart { + pub fn from_stream(stream: Framed, to: String) -> Self { + let attrs = [("to".to_owned(), to), + ("version".to_owned(), "1.0".to_owned()), + ("xmlns".to_owned(), "jabber:client".to_owned()), + ("xmlns:stream".to_owned(), NS_XMPP_STREAM.to_owned()), + ].iter().cloned().collect(); + let send = stream.send(Packet::StreamStart(attrs)); + + StreamStart { + state: StreamStartState::SendStart(send), + } + } +} + +impl Future for StreamStart { + type Item = XMPPStream; + type Error = Error; + + fn poll(&mut self) -> Poll { + let old_state = replace(&mut self.state, StreamStartState::Invalid); + let mut retry = false; + + let (new_state, result) = match old_state { + StreamStartState::SendStart(mut send) => + match send.poll() { + Ok(Async::Ready(stream)) => { + retry = true; + (StreamStartState::RecvStart(stream), Ok(Async::NotReady)) + }, + Ok(Async::NotReady) => + (StreamStartState::SendStart(send), Ok(Async::NotReady)), + Err(e) => + (StreamStartState::Invalid, Err(e)), + }, + StreamStartState::RecvStart(mut stream) => + match stream.poll() { + Ok(Async::Ready(Some(Packet::StreamStart(stream_attrs)))) => { + retry = true; + // TODO: skip RecvFeatures for version < 1.0 + (StreamStartState::RecvFeatures(stream, stream_attrs), Ok(Async::NotReady)) + }, + Ok(Async::Ready(_)) => + return Err(Error::from(ErrorKind::InvalidData)), + Ok(Async::NotReady) => + (StreamStartState::RecvStart(stream), Ok(Async::NotReady)), + Err(e) => + return Err(e), + }, + StreamStartState::RecvFeatures(mut stream, stream_attrs) => + match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => + if stanza.name == "features" + && stanza.ns == Some(NS_XMPP_STREAM.to_owned()) { + (StreamStartState::Invalid, Ok(Async::Ready(XMPPStream { stream, stream_attrs, stream_features: stanza }))) + } else { + (StreamStartState::RecvFeatures(stream, stream_attrs), Ok(Async::NotReady)) + }, + Ok(Async::Ready(item)) => { + println!("StreamStart skip {:?}", item); + (StreamStartState::RecvFeatures(stream, stream_attrs), Ok(Async::NotReady)) + }, + Ok(Async::NotReady) => + (StreamStartState::RecvFeatures(stream, stream_attrs), Ok(Async::NotReady)), + Err(e) => + return Err(e), + }, + StreamStartState::Invalid => + unreachable!(), + }; + + self.state = new_state; + if retry { + self.poll() + } else { + result + } + } +} diff --git a/src/tcp.rs b/src/tcp.rs index 29608ea6f2abcf47a358a42e1235eee573888a79..57fb0b1fe28d34368d56c268b0814e26a0af3796 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -1,40 +1,22 @@ -use std::fmt; use std::net::SocketAddr; -use std::io::{Error, ErrorKind}; -use futures::{Future, Sink, Poll, Async}; -use futures::stream::Stream; -use futures::sink; +use std::io::Error; +use futures::{Future, Poll, Async}; use tokio_core::reactor::Handle; use tokio_core::net::{TcpStream, TcpStreamNew}; -use super::{XMPPStream, XMPPCodec, Packet}; +use xmpp_stream::*; +use stream_start::StreamStart; - -#[derive(Debug)] pub struct TcpClient { state: TcpClientState, } enum TcpClientState { Connecting(TcpStreamNew), - SendStart(sink::Send>), - RecvStart(Option>), + Start(StreamStart), Established, } -impl fmt::Debug for TcpClientState { - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let s = match *self { - TcpClientState::Connecting(_) => "Connecting", - TcpClientState::SendStart(_) => "SendStart", - TcpClientState::RecvStart(_) => "RecvStart", - TcpClientState::Established => "Established", - }; - try!(write!(fmt, "{}", s)); - Ok(()) - } -} - impl TcpClient { pub fn connect(addr: &SocketAddr, handle: &Handle) -> Self { let tcp_stream_new = TcpStream::connect(addr, handle); @@ -52,27 +34,12 @@ impl Future for TcpClient { let (new_state, result) = match self.state { TcpClientState::Connecting(ref mut tcp_stream_new) => { let tcp_stream = try_ready!(tcp_stream_new.poll()); - let xmpp_stream = XMPPCodec::frame_stream(tcp_stream); - let send = xmpp_stream.send(Packet::StreamStart); - let new_state = TcpClientState::SendStart(send); - (new_state, Ok(Async::NotReady)) - }, - TcpClientState::SendStart(ref mut send) => { - let xmpp_stream = try_ready!(send.poll()); - let new_state = TcpClientState::RecvStart(Some(xmpp_stream)); + let start = XMPPStream::from_stream(tcp_stream, "spaceboyz.net".to_owned()); + let new_state = TcpClientState::Start(start); (new_state, Ok(Async::NotReady)) }, - TcpClientState::RecvStart(ref mut opt_xmpp_stream) => { - let mut xmpp_stream = opt_xmpp_stream.take().unwrap(); - match xmpp_stream.poll() { - Ok(Async::Ready(Some(Packet::StreamStart))) => println!("Recv start!"), - Ok(Async::Ready(_)) => return Err(Error::from(ErrorKind::InvalidData)), - Ok(Async::NotReady) => { - *opt_xmpp_stream = Some(xmpp_stream); - return Ok(Async::NotReady); - }, - Err(e) => return Err(e) - }; + TcpClientState::Start(ref mut start) => { + let xmpp_stream = try_ready!(start.poll()); let new_state = TcpClientState::Established; (new_state, Ok(Async::Ready(xmpp_stream))) }, @@ -80,7 +47,6 @@ impl Future for TcpClient { unreachable!(), }; - println!("Next state: {:?}", new_state); self.state = new_state; match result { // by polling again, we register new future diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 38c593e46cd736aeb2976819dba501eaa96c376a..8595dfca21e47aaff03557e1d9770e5f4f0b726a 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -3,18 +3,17 @@ use std::fmt::Write; use std::str::from_utf8; use std::io::{Error, ErrorKind}; use std::collections::HashMap; -use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_io::codec::{Framed, Encoder, Decoder}; +use tokio_io::codec::{Encoder, Decoder}; use xml; use bytes::*; const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/"; -const NS_STREAMS: &'static str = "http://etherx.jabber.org/streams"; -const NS_CLIENT: &'static str = "jabber:client"; + +pub type Attributes = HashMap<(String, Option), String>; struct XMPPRoot { builder: xml::ElementBuilder, - pub attributes: HashMap<(String, Option), String>, + pub attributes: Attributes, } impl XMPPRoot { @@ -49,13 +48,11 @@ impl XMPPRoot { #[derive(Debug)] pub enum Packet { Error(Box), - StreamStart, + StreamStart(HashMap), Stanza(xml::Element), StreamEnd, } -pub type XMPPStream = Framed; - pub struct XMPPCodec { parser: xml::Parser, root: Option, @@ -68,12 +65,6 @@ impl XMPPCodec { root: None, } } - - pub fn frame_stream(stream: S) -> Framed - where S: AsyncRead + AsyncWrite - { - AsyncRead::framed(stream, XMPPCodec::new()) - } } impl Decoder for XMPPCodec { @@ -97,8 +88,12 @@ impl Decoder for XMPPCodec { // Expecting match event { Ok(xml::Event::ElementStart(start_tag)) => { + let mut attrs: HashMap = HashMap::new(); + for (&(ref name, _), value) in &start_tag.attributes { + attrs.insert(name.to_owned(), value.to_owned()); + } + result = Some(Packet::StreamStart(attrs)); self.root = Some(XMPPRoot::new(start_tag)); - result = Some(Packet::StreamStart); break }, Err(e) => { @@ -146,18 +141,23 @@ impl Encoder for XMPPCodec { fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { match item { - Packet::StreamStart => { - write!(dst, - "\n -\n", - NS_CLIENT, NS_STREAMS) - .map_err(|_| Error::from(ErrorKind::WriteZero)) + Packet::StreamStart(start_attrs) => { + let mut buf = String::new(); + write!(buf, "\n").unwrap(); + + println!("Encode start to {}", buf); + write!(dst, "{}", buf) }, Packet::Stanza(stanza) => - write!(dst, "{}", stanza) - .map_err(|_| Error::from(ErrorKind::InvalidInput)), + write!(dst, "{}", stanza), // TODO: Implement all _ => Ok(()) } + .map_err(|_| Error::from(ErrorKind::InvalidInput)) } } diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs new file mode 100644 index 0000000000000000000000000000000000000000..c4080bb7a36bd428b6d7e04af287088be482a7dc --- /dev/null +++ b/src/xmpp_stream.rs @@ -0,0 +1,62 @@ +use std::sync::Arc; +use std::collections::HashMap; +use futures::*; +use tokio_io::{AsyncRead, AsyncWrite}; +use tokio_io::codec::Framed; +use rustls::ClientConfig; +use xml; + +use xmpp_codec::*; +use stream_start::*; +use starttls::{NS_XMPP_TLS, StartTlsClient}; + +pub const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; + +pub struct XMPPStream { + pub stream: Framed, + pub stream_attrs: HashMap, + pub stream_features: xml::Element, +} + +impl XMPPStream { + pub fn from_stream(stream: S, to: String) -> StreamStart { + let xmpp_stream = AsyncRead::framed(stream, XMPPCodec::new()); + StreamStart::from_stream(xmpp_stream, to) + } + + pub fn into_inner(self) -> S { + self.stream.into_inner() + } + + pub fn can_starttls(&self) -> bool { + self.stream_features + .get_child("starttls", Some(NS_XMPP_TLS)) + .is_some() + } + + pub fn starttls(self, arc_config: Arc) -> StartTlsClient { + StartTlsClient::from_stream(self, arc_config) + } +} + +impl Sink for XMPPStream { + type SinkItem = as Sink>::SinkItem; + type SinkError = as Sink>::SinkError; + + fn start_send(&mut self, item: Self::SinkItem) -> StartSend { + self.stream.start_send(item) + } + + fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { + self.stream.poll_complete() + } +} + +impl Stream for XMPPStream { + type Item = as Stream>::Item; + type Error = as Stream>::Error; + + fn poll(&mut self) -> Poll, Self::Error> { + self.stream.poll() + } +} From f8de49569f0433f25963ac2bfc5e235d6447853a Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 6 Jun 2017 01:29:20 +0200 Subject: [PATCH 0306/1020] add client_auth using sasl --- Cargo.toml | 2 + examples/echo_bot.rs | 5 +- src/client_auth.rs | 160 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 ++ src/xmpp_stream.rs | 12 ++++ 5 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 src/client_auth.rs diff --git a/Cargo.toml b/Cargo.toml index 39de218681051fa316f44d867e007f7f5fad8306..5da50ccad8d58aff1c6af27d9a15c0a591d791e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,5 @@ bytes = "*" RustyXML = "*" rustls = "*" tokio-rustls = "*" +sasl = "*" +rustc-serialize = "*" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 0cdd6b07e5c9dc8a4f460d22510daac81577cefc..416e98d599646043772105717b7a045a6da8e8c8 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -33,6 +33,9 @@ fn main() { } else { panic!("No STARTTLS") } + }).map_err(|e| format!("{}", e) + ).and_then(|stream| { + stream.auth("astrobot", "").expect("auth") }).and_then(|stream| { stream.for_each(|event| { match event { @@ -40,7 +43,7 @@ fn main() { _ => println!("!! {:?}", event), } Ok(()) - }) + }).map_err(|e| format!("{}", e)) }); match core.run(client) { Ok(_) => (), diff --git a/src/client_auth.rs b/src/client_auth.rs new file mode 100644 index 0000000000000000000000000000000000000000..ecb0b78f1b84d0dd1835a8be0064f0cb449ae002 --- /dev/null +++ b/src/client_auth.rs @@ -0,0 +1,160 @@ +use std::mem::replace; +use futures::*; +use futures::sink; +use tokio_io::{AsyncRead, AsyncWrite}; +use xml; +use sasl::common::Credentials; +use sasl::common::scram::*; +use sasl::client::Mechanism; +use sasl::client::mechanisms::*; +use serialize::base64::{self, ToBase64, FromBase64}; + +use xmpp_codec::*; +use xmpp_stream::*; + +const NS_XMPP_SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; + +pub struct ClientAuth { + state: ClientAuthState, + mechanism: Box, +} + +enum ClientAuthState { + WaitSend(sink::Send>), + WaitRecv(XMPPStream), + Invalid, +} + +impl ClientAuth { + pub fn new(stream: XMPPStream, creds: Credentials) -> Result { + let mechs: Vec> = vec![ + Box::new(Scram::::from_credentials(creds.clone()).unwrap()), + Box::new(Scram::::from_credentials(creds.clone()).unwrap()), + Box::new(Plain::from_credentials(creds).unwrap()), + Box::new(Anonymous::new()), + ]; + + println!("stream_features: {}", stream.stream_features); + let mech_names: Vec = + match stream.stream_features.get_child("mechanisms", Some(NS_XMPP_SASL)) { + None => + return Err("No auth mechanisms".to_owned()), + Some(mechs) => + mechs.get_children("mechanism", Some(NS_XMPP_SASL)) + .map(|mech_el| mech_el.content_str()) + .collect(), + }; + println!("Offered mechanisms: {:?}", mech_names); + + for mut mech in mechs { + let name = mech.name().to_owned(); + if mech_names.iter().any(|name1| *name1 == name) { + println!("Selected mechanism: {:?}", name); + let initial = try!(mech.initial()); + let mut this = ClientAuth { + state: ClientAuthState::Invalid, + mechanism: mech, + }; + this.send( + stream, + "auth", &[("mechanism".to_owned(), name)], + &initial + ); + return Ok(this); + } + } + + Err("No supported SASL mechanism available".to_owned()) + } + + fn send(&mut self, stream: XMPPStream, nonza_name: &str, attrs: &[(String, String)], content: &[u8]) { + let mut nonza = xml::Element::new( + nonza_name.to_owned(), + Some(NS_XMPP_SASL.to_owned()), + attrs.iter() + .map(|&(ref name, ref value)| (name.clone(), None, value.clone())) + .collect() + ); + nonza.text(content.to_base64(base64::URL_SAFE)); + + println!("send {}", nonza); + let send = stream.send(Packet::Stanza(nonza)); + + self.state = ClientAuthState::WaitSend(send); + } +} + +impl Future for ClientAuth { + type Item = XMPPStream; + type Error = String; + + fn poll(&mut self) -> Poll { + let state = replace(&mut self.state, ClientAuthState::Invalid); + + match state { + ClientAuthState::WaitSend(mut send) => + match send.poll() { + Ok(Async::Ready(stream)) => { + println!("send done"); + self.state = ClientAuthState::WaitRecv(stream); + self.poll() + }, + Ok(Async::NotReady) => { + self.state = ClientAuthState::WaitSend(send); + Ok(Async::NotReady) + }, + Err(e) => + Err(format!("{}", e)), + }, + ClientAuthState::WaitRecv(mut stream) => + match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.name == "challenge" + && stanza.ns == Some(NS_XMPP_SASL.to_owned()) => + { + let content = try!( + stanza.content_str() + .from_base64() + .map_err(|e| format!("{}", e)) + ); + let response = try!(self.mechanism.response(&content)); + self.send(stream, "response", &[], &response); + self.poll() + }, + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.name == "success" + && stanza.ns == Some(NS_XMPP_SASL.to_owned()) => + Ok(Async::Ready(stream)), + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.name == "failure" + && stanza.ns == Some(NS_XMPP_SASL.to_owned()) => + { + let mut e = None; + for child in &stanza.children { + match child { + &xml::Xml::ElementNode(ref child) => { + e = Some(child.name.clone()); + break + }, + _ => (), + } + } + let e = e.unwrap_or_else(|| "Authentication failure".to_owned()); + Err(e) + }, + Ok(Async::Ready(event)) => { + println!("ClientAuth ignore {:?}", event); + Ok(Async::NotReady) + }, + Ok(_) => { + self.state = ClientAuthState::WaitRecv(stream); + Ok(Async::NotReady) + }, + Err(e) => + Err(format!("{}", e)), + }, + ClientAuthState::Invalid => + unreachable!(), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 9764daeae2d7f1418f58ee181e2ca36730136f20..ceb6842433ab5645986f1f34b65208e3d22aceea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ extern crate bytes; extern crate xml; extern crate rustls; extern crate tokio_rustls; +extern crate sasl; +extern crate rustc_serialize as serialize; pub mod xmpp_codec; @@ -15,6 +17,8 @@ mod tcp; pub use tcp::*; mod starttls; pub use starttls::*; +mod client_auth; +pub use client_auth::*; // type FullClient = sasl::Client> diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index c4080bb7a36bd428b6d7e04af287088be482a7dc..e480ffdb1df8b7def06028a23ab3913e03e56626 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -1,3 +1,4 @@ +use std::default::Default; use std::sync::Arc; use std::collections::HashMap; use futures::*; @@ -5,10 +6,12 @@ use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; use rustls::ClientConfig; use xml; +use sasl::common::Credentials; use xmpp_codec::*; use stream_start::*; use starttls::{NS_XMPP_TLS, StartTlsClient}; +use client_auth::ClientAuth; pub const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; @@ -37,8 +40,16 @@ impl XMPPStream { pub fn starttls(self, arc_config: Arc) -> StartTlsClient { StartTlsClient::from_stream(self, arc_config) } + + pub fn auth(self, username: &str, password: &str) -> Result, String> { + let creds = Credentials::default() + .with_username(username) + .with_password(password); + ClientAuth::new(self, creds) + } } +/// Proxy to self.stream impl Sink for XMPPStream { type SinkItem = as Sink>::SinkItem; type SinkError = as Sink>::SinkError; @@ -52,6 +63,7 @@ impl Sink for XMPPStream { } } +/// Proxy to self.stream impl Stream for XMPPStream { type Item = as Stream>::Item; type Error = as Stream>::Error; From 52c60229e33a82b85ba345e564c3855c08c2ccb5 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 6 Jun 2017 01:38:48 +0200 Subject: [PATCH 0307/1020] client_auth: add stream restart --- src/client_auth.rs | 19 ++++++++++++++++++- src/xmpp_stream.rs | 7 +++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/client_auth.rs b/src/client_auth.rs index ecb0b78f1b84d0dd1835a8be0064f0cb449ae002..5ae57b76c9f9d2b2926292febad2c461b0a8c1a9 100644 --- a/src/client_auth.rs +++ b/src/client_auth.rs @@ -11,6 +11,7 @@ use serialize::base64::{self, ToBase64, FromBase64}; use xmpp_codec::*; use xmpp_stream::*; +use stream_start::*; const NS_XMPP_SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; @@ -22,6 +23,7 @@ pub struct ClientAuth { enum ClientAuthState { WaitSend(sink::Send>), WaitRecv(XMPPStream), + Start(StreamStart), Invalid, } @@ -124,7 +126,11 @@ impl Future for ClientAuth { Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) if stanza.name == "success" && stanza.ns == Some(NS_XMPP_SASL.to_owned()) => - Ok(Async::Ready(stream)), + { + let start = stream.restart(); + self.state = ClientAuthState::Start(start); + self.poll() + }, Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) if stanza.name == "failure" && stanza.ns == Some(NS_XMPP_SASL.to_owned()) => @@ -153,6 +159,17 @@ impl Future for ClientAuth { Err(e) => Err(format!("{}", e)), }, + ClientAuthState::Start(mut start) => + match start.poll() { + Ok(Async::Ready(stream)) => + Ok(Async::Ready(stream)), + Ok(Async::NotReady) => { + self.state = ClientAuthState::Start(start); + Ok(Async::NotReady) + }, + Err(e) => + Err(format!("{}", e)), + }, ClientAuthState::Invalid => unreachable!(), } diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index e480ffdb1df8b7def06028a23ab3913e03e56626..56c778c4777f2744add2f36cf972e3fa3b3ead72 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -31,6 +31,13 @@ impl XMPPStream { self.stream.into_inner() } + pub fn restart(self) -> StreamStart { + let to = self.stream_attrs.get("from") + .map(|s| s.to_owned()) + .unwrap_or_else(|| "".to_owned()); + Self::from_stream(self.into_inner(), to.clone()) + } + pub fn can_starttls(&self) -> bool { self.stream_features .get_child("starttls", Some(NS_XMPP_TLS)) From 0bae1ce3366dc0f1e1dc1a8956df8775bcee5fb1 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 6 Jun 2017 02:03:38 +0200 Subject: [PATCH 0308/1020] switch from rustls to native-tls --- Cargo.toml | 4 ++-- examples/echo_bot.rs | 16 +++------------- src/lib.rs | 4 ++-- src/starttls.rs | 36 ++++++++++++++++++++---------------- src/xmpp_stream.rs | 6 ++---- 5 files changed, 29 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5da50ccad8d58aff1c6af27d9a15c0a591d791e7..82c0ea9b74c7de518789bbd72e4c590ed4e02169 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ tokio-core = "*" tokio-io = "*" bytes = "*" RustyXML = "*" -rustls = "*" -tokio-rustls = "*" +native-tls = "*" +tokio-tls = "*" sasl = "*" rustc-serialize = "*" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 416e98d599646043772105717b7a045a6da8e8c8..9dfe1556b86690402aeeca1d0201a659b36c575d 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,16 +1,11 @@ extern crate futures; extern crate tokio_core; extern crate tokio_xmpp; -extern crate rustls; -use std::sync::Arc; -use std::io::BufReader; -use std::fs::File; use tokio_core::reactor::Core; use futures::{Future, Stream}; use tokio_xmpp::TcpClient; use tokio_xmpp::xmpp_codec::Packet; -use rustls::ClientConfig; fn main() { use std::net::ToSocketAddrs; @@ -18,23 +13,18 @@ fn main() { .to_socket_addrs().unwrap() .next().unwrap(); - let mut config = ClientConfig::new(); - let mut certfile = BufReader::new(File::open("/usr/share/ca-certificates/CAcert/root.crt").unwrap()); - config.root_store.add_pem_file(&mut certfile).unwrap(); - let arc_config = Arc::new(config); - let mut core = Core::new().unwrap(); let client = TcpClient::connect( &addr, &core.handle() + ).map_err(|e| format!("{}", e) ).and_then(|stream| { if stream.can_starttls() { - stream.starttls(arc_config) + stream.starttls() } else { panic!("No STARTTLS") } - }).map_err(|e| format!("{}", e) - ).and_then(|stream| { + }).and_then(|stream| { stream.auth("astrobot", "").expect("auth") }).and_then(|stream| { stream.for_each(|event| { diff --git a/src/lib.rs b/src/lib.rs index ceb6842433ab5645986f1f34b65208e3d22aceea..6a402c366b22db62ceab465256310623dda657ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,8 @@ extern crate tokio_core; extern crate tokio_io; extern crate bytes; extern crate xml; -extern crate rustls; -extern crate tokio_rustls; +extern crate native_tls; +extern crate tokio_tls; extern crate sasl; extern crate rustc_serialize as serialize; diff --git a/src/starttls.rs b/src/starttls.rs index b0fdc92f16c6807d8159e4e8baac7e92d8310719..c893f0597bdd45c4d667366021f7f29fd9e926a9 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -1,12 +1,10 @@ use std::mem::replace; -use std::io::Error; -use std::sync::Arc; use futures::{Future, Sink, Poll, Async}; use futures::stream::Stream; use futures::sink; use tokio_io::{AsyncRead, AsyncWrite}; -use rustls::*; -use tokio_rustls::*; +use tokio_tls::*; +use native_tls::TlsConnector; use xml; use xmpp_codec::*; @@ -18,7 +16,7 @@ pub const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; pub struct StartTlsClient { state: StartTlsClientState, - arc_config: Arc, + domain: String, } enum StartTlsClientState { @@ -26,12 +24,16 @@ enum StartTlsClientState { SendStartTls(sink::Send>), AwaitProceed(XMPPStream), StartingTls(ConnectAsync), - Start(StreamStart>), + Start(StreamStart>), } impl StartTlsClient { /// Waits for - pub fn from_stream(xmpp_stream: XMPPStream, arc_config: Arc) -> Self { + pub fn from_stream(xmpp_stream: XMPPStream) -> Self { + let domain = xmpp_stream.stream_attrs.get("from") + .map(|s| s.to_owned()) + .unwrap_or_else(|| String::new()); + let nonza = xml::Element::new( "starttls".to_owned(), Some(NS_XMPP_TLS.to_owned()), vec![] @@ -42,14 +44,14 @@ impl StartTlsClient { StartTlsClient { state: StartTlsClientState::SendStartTls(send), - arc_config: arc_config, + domain, } } } impl Future for StartTlsClient { - type Item = XMPPStream>; - type Error = Error; + type Item = XMPPStream>; + type Error = String; fn poll(&mut self) -> Poll { let old_state = replace(&mut self.state, StartTlsClientState::Invalid); @@ -67,7 +69,7 @@ impl Future for StartTlsClient { Ok(Async::NotReady) => (StartTlsClientState::SendStartTls(send), Ok(Async::NotReady)), Err(e) => - (StartTlsClientState::SendStartTls(send), Err(e)), + (StartTlsClientState::SendStartTls(send), Err(format!("{}", e))), }, StartTlsClientState::AwaitProceed(mut xmpp_stream) => match xmpp_stream.poll() { @@ -76,7 +78,9 @@ impl Future for StartTlsClient { { println!("* proceed *"); let stream = xmpp_stream.into_inner(); - let connect = self.arc_config.connect_async("spaceboyz.net", stream); + let connect = TlsConnector::builder().unwrap() + .build().unwrap() + .connect_async(&self.domain, stream); let new_state = StartTlsClientState::StartingTls(connect); retry = true; (new_state, Ok(Async::NotReady)) @@ -88,13 +92,13 @@ impl Future for StartTlsClient { Ok(_) => (StartTlsClientState::AwaitProceed(xmpp_stream), Ok(Async::NotReady)), Err(e) => - (StartTlsClientState::AwaitProceed(xmpp_stream), Err(e)), + (StartTlsClientState::AwaitProceed(xmpp_stream), Err(format!("{}", e))), }, StartTlsClientState::StartingTls(mut connect) => match connect.poll() { Ok(Async::Ready(tls_stream)) => { println!("Got a TLS stream!"); - let start = XMPPStream::from_stream(tls_stream, "spaceboyz.net".to_owned()); + let start = XMPPStream::from_stream(tls_stream, self.domain.clone()); let new_state = StartTlsClientState::Start(start); retry = true; (new_state, Ok(Async::NotReady)) @@ -102,7 +106,7 @@ impl Future for StartTlsClient { Ok(Async::NotReady) => (StartTlsClientState::StartingTls(connect), Ok(Async::NotReady)), Err(e) => - (StartTlsClientState::StartingTls(connect), Err(e)), + (StartTlsClientState::StartingTls(connect), Err(format!("{}", e))), }, StartTlsClientState::Start(mut start) => match start.poll() { @@ -111,7 +115,7 @@ impl Future for StartTlsClient { Ok(Async::NotReady) => (StartTlsClientState::Start(start), Ok(Async::NotReady)), Err(e) => - (StartTlsClientState::Invalid, Err(e)), + (StartTlsClientState::Invalid, Err(format!("{}", e))), }, StartTlsClientState::Invalid => unreachable!(), diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index 56c778c4777f2744add2f36cf972e3fa3b3ead72..a8793db1f59f045dfb201643519c1c28bf5c347d 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -1,10 +1,8 @@ use std::default::Default; -use std::sync::Arc; use std::collections::HashMap; use futures::*; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; -use rustls::ClientConfig; use xml; use sasl::common::Credentials; @@ -44,8 +42,8 @@ impl XMPPStream { .is_some() } - pub fn starttls(self, arc_config: Arc) -> StartTlsClient { - StartTlsClient::from_stream(self, arc_config) + pub fn starttls(self) -> StartTlsClient { + StartTlsClient::from_stream(self) } pub fn auth(self, username: &str, password: &str) -> Result, String> { From 88a3f507f69a0151be8df5e00e22ae1dbff600e7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 8 Jun 2017 22:46:27 +0200 Subject: [PATCH 0309/1020] =?UTF-8?q?idle,=20delay:=20Don=E2=80=99t=20impo?= =?UTF-8?q?rt=20*=20from=20chrono::prelude.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/delay.rs | 5 +++-- src/idle.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 65349040c03faa2f0de16349f02ae7999755612e..d744751da9ec01e743ae9358d0a3bf1319a6666b 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use minidom::Element; -use chrono::prelude::*; +use chrono::{DateTime, FixedOffset}; use error::Error; use jid::Jid; @@ -58,8 +58,9 @@ impl Into for Delay { #[cfg(test)] mod tests { - use std::str::FromStr; use super::*; + use std::str::FromStr; + use chrono::{Datelike, Timelike}; #[test] fn test_simple() { diff --git a/src/idle.rs b/src/idle.rs index ba13bd8954d660d0c8740aec392475d3c271eb62..82009668bfbeed21044f8a7ef0b61f47222d2fd3 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use minidom::Element; -use chrono::prelude::*; +use chrono::{DateTime, FixedOffset}; use error::Error; From 9cec9fce9b8d203cd5b4c6edb7b5a0b78b8e129a Mon Sep 17 00:00:00 2001 From: Bastien Orivel Date: Wed, 7 Jun 2017 22:40:53 +0200 Subject: [PATCH 0310/1020] Replace xml-rs by quick_xml quick_xml is way faster than xml-rs Here is an example with a quick atom parser: With xml-rs: test parse_factorio_atom ... bench: 3,295,678 ns/iter (+/- 165,851) With quick_xml: test parse_factorio_atom ... bench: 203,215 ns/iter (+/- 13,485) Unfortunately I had to break the API for this change to happen. * Element::from_reader now takes `R: BufRead` instead of `R: Read` * Element::write_to now takes `W: io::Write` instead of `EventWriter` This migration also allow us to have a write_to function which assumes we're already in a given namespace (see `write_to_in_namespace`). --- Cargo.toml | 6 +- src/element.rs | 223 +++++++++++++++++++++++++++---------------------- src/error.rs | 22 ++--- src/lib.rs | 2 +- src/tests.rs | 24 ++++-- 5 files changed, 153 insertions(+), 124 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0ddb0252e38126f9e33a170a48d5a52b17f88ca0..ebb09d1893cad3bff8654b0da2c514d214fde03e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "minidom" -version = "0.4.3" +version = "0.5.0" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] -description = "A small, simple DOM implementation on top of xml-rs." +description = "A small, simple DOM implementation on top of quick-xml" homepage = "https://gitlab.com/lumi/minidom-rs" repository = "https://gitlab.com/lumi/minidom-rs" documentation = "https://docs.rs/minidom" @@ -14,5 +14,5 @@ license = "MIT" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -xml-rs = "0.4.1" +quick-xml = "0.7.3" error-chain = "0.10.0" diff --git a/src/element.rs b/src/element.rs index 3c5c9d522076cf593bedb57345a32fa29b1aad0c..ee2485083823876146d84774dcbde32770a38943 100644 --- a/src/element.rs +++ b/src/element.rs @@ -1,18 +1,16 @@ //! Provides an `Element` type, which represents DOM nodes, and a builder to create them with. -use std::io::prelude::*; -use std::io::Cursor; -use std::collections::BTreeMap; -use std::collections::btree_map; +use std::io:: Write; +use std::collections::{btree_map, BTreeMap}; -use std::fmt; +use std::str; use error::{Error, ErrorKind, Result}; -use xml::reader::{XmlEvent as ReaderEvent, EventReader}; -use xml::writer::{XmlEvent as WriterEvent, EventWriter, EmitterConfig}; -use xml::name::Name; -use xml::namespace::NS_NO_PREFIX; +use quick_xml::reader::Reader as EventReader; +use quick_xml::events::{Event, BytesStart}; + +use std::io::BufRead; use std::str::FromStr; @@ -69,9 +67,18 @@ impl Node { Node::Text(ref s) => Some(s), } } + + fn write_to_inner(&self, writer: &mut W, last_namespace: &mut Option) -> Result<()>{ + match *self { + Node::Element(ref elmt) => elmt.write_to_inner(writer, last_namespace)?, + Node::Text(ref s) => write!(writer, "{}", s)?, + } + + Ok(()) + } } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, Debug)] /// A struct representing a DOM Element. pub struct Element { name: String, @@ -82,26 +89,18 @@ pub struct Element { impl<'a> From<&'a Element> for String { fn from(elem: &'a Element) -> String { - let mut out = Vec::new(); - let config = EmitterConfig::new() - .write_document_declaration(false); - elem.write_to(&mut EventWriter::new_with_config(&mut out, config)).unwrap(); - String::from_utf8(out).unwrap() + let mut writer = Vec::new(); + elem.write_to(&mut writer).unwrap(); + String::from_utf8(writer).unwrap() } } -impl fmt::Debug for Element { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", String::from(self))?; - Ok(()) - } -} impl FromStr for Element { type Err = Error; fn from_str(s: &str) -> Result { - let mut reader = EventReader::new(Cursor::new(s)); + let mut reader = EventReader::from_str(s); Element::from_reader(&mut reader) } } @@ -246,106 +245,105 @@ impl Element { } /// Parse a document from an `EventReader`. - pub fn from_reader(reader: &mut EventReader) -> Result { + pub fn from_reader(reader: &mut EventReader) -> Result { + let mut buf = Vec::new(); + let root: Element; + loop { - let e = reader.next()?; + let e = reader.read_event(&mut buf)?; match e { - ReaderEvent::StartElement { name, attributes, namespace } => { - let attributes = attributes.into_iter() - .map(|o| { - (match o.name.prefix { - Some(prefix) => format!("{}:{}", prefix, o.name.local_name), - None => o.name.local_name - }, - o.value) - }) - .collect(); - let ns = if let Some(ref prefix) = name.prefix { - namespace.get(prefix) - } - else { - namespace.get(NS_NO_PREFIX) - }.map(|s| s.to_owned()); - - let mut root = Element::new(name.local_name, ns, attributes, Vec::new()); - root.from_reader_inner(reader)?; - return Ok(root); + Event::Empty(ref e) | Event::Start(ref e) => { + root = build_element(e)?; // FIXME: could be break build_element(e)? when break value is stable + break; }, - ReaderEvent::EndDocument => { + Event::Eof => { bail!(ErrorKind::EndOfDocument); }, _ => () // TODO: may need more errors } - } - } + }; + + let mut stack = vec![root]; - #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] - fn from_reader_inner(&mut self, reader: &mut EventReader) -> Result<()> { loop { - let e = reader.next()?; - match e { - ReaderEvent::StartElement { name, attributes, namespace } => { - let attributes = attributes.into_iter() - .map(|o| { - (match o.name.prefix { - Some(prefix) => format!("{}:{}", prefix, o.name.local_name), - None => o.name.local_name - }, - o.value) - }) - .collect(); - let ns = if let Some(ref prefix) = name.prefix { - namespace.get(prefix) - } - else { - namespace.get(NS_NO_PREFIX) - }.map(|s| s.to_owned()); - let elem = Element::new(name.local_name, ns, attributes, Vec::with_capacity(1)); - let elem_ref = self.append_child(elem); - elem_ref.from_reader_inner(reader)?; + match reader.read_event(&mut buf)? { + Event::Empty(ref e) => { + let elem = build_element(e)?; + // Since there is no Event::End after, directly append it to the current node + stack.last_mut().unwrap().append_child(elem); }, - ReaderEvent::EndElement { .. } => { - // TODO: may want to check whether we're closing the correct element - return Ok(()); + Event::Start(ref e) => { + let elem = build_element(e)?; + stack.push(elem); }, - ReaderEvent::Characters(s) | ReaderEvent::CData(s) => { - self.append_text_node(s); + Event::End(ref e) => { + if stack.len() <= 1 { + break; + } + let elem = stack.pop().unwrap(); + if let Some(to) = stack.last_mut() { + if elem.name().as_bytes() != e.name() { + bail!(ErrorKind::InvalidElementClosed); + } + to.append_child(elem); + } }, - ReaderEvent::EndDocument => { - bail!(ErrorKind::EndOfDocument); + Event::Text(s) | Event::CData(s) => { + let text = s.unescape_and_decode(reader)?; + if text != "" { + let mut current_elem = stack.last_mut().unwrap(); + current_elem.append_text_node(text); + } + }, + Event::Eof => { + break; }, _ => (), // TODO: may need to implement more } } + Ok(stack.pop().unwrap()) } - /// Output a document to an `EventWriter`. - pub fn write_to(&self, writer: &mut EventWriter) -> Result<()> { - let name = if let Some(ref ns) = self.namespace { - Name::qualified(&self.name, ns, None) - } - else { - Name::local(&self.name) - }; - let mut start = WriterEvent::start_element(name); + /// Output a document to a `Writer`. + pub fn write_to(&self, writer: &mut W) -> Result<()> { + let mut last_namespace = None; + write!(writer, "")?; + self.write_to_inner(writer, &mut last_namespace) + } + + /// Output a document to a `Writer` assuming you're already in the provided namespace + pub fn write_to_in_namespace(&self, writer: &mut W, namespace: &str) -> Result<()> { + write!(writer, "")?; + self.write_to_inner(writer, &mut Some(namespace.to_owned())) + } + + fn write_to_inner(&self, writer: &mut W, last_namespace: &mut Option) -> Result<()> { + write!(writer, "<")?; + write!(writer, "{}", self.name)?; + if let Some(ref ns) = self.namespace { - start = start.default_ns(ns.clone()); + if *last_namespace != self.namespace { + write!(writer, " xmlns=\"{}\"", ns)?; + *last_namespace = Some(ns.clone()); + } + } + + for (key, value) in &self.attributes { + write!(writer, " {}=\"{}\"", key, value)?; } - for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently - start = start.attr(Name::local(attr.0), attr.1); + + if self.children.is_empty() { + write!(writer, " />")?; + return Ok(()) } - writer.write(start)?; + + write!(writer, ">")?; + for child in &self.children { - match *child { - Node::Element(ref e) => { - e.write_to(writer)?; - }, - Node::Text(ref s) => { - writer.write(WriterEvent::characters(s))?; - }, - } + child.write_to_inner(writer, last_namespace)?; } - writer.write(WriterEvent::end_element())?; + + write!(writer, "", self.name)?; Ok(()) } @@ -354,7 +352,7 @@ impl Element { /// # Examples /// /// ```rust - /// use minidom::{Element, Node}; + /// use minidom::Element; /// /// let elem: Element = "abc".parse().unwrap(); /// @@ -592,6 +590,31 @@ impl Element { } } +fn build_element(event: &BytesStart) -> Result { + let mut attributes = event.attributes() + .map(|o| { + let o = o?; + let key = str::from_utf8(o.key)?.to_owned(); + let value = str::from_utf8(o.value)?.to_owned(); + Ok((key, value)) + } + ) + .collect::>>()?; + let mut ns_key = None; + for (key, _) in &attributes { + if key == "xmlns" || key.starts_with("xmlns:") { + ns_key = Some(key.clone()); + } + } + + let ns = match ns_key { + None => None, + Some(key) => attributes.remove(&key), + }; + let name = str::from_utf8(event.name())?.to_owned(); + Ok(Element::new(name, ns, attributes, Vec::new())) +} + /// An iterator over references to child elements of an `Element`. pub struct Children<'a> { iter: slice::Iter<'a, Node>, diff --git a/src/error.rs b/src/error.rs index 4e76c6a9a1be50012fe4f115f05c325e16dcda76..0b8eb23911911c327fb13c14b356685a9373f92e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,30 +1,30 @@ //! Provides an error type for this crate. -use std::io; - use std::convert::From; -use xml::writer::Error as WriterError; -use xml::reader::Error as ReaderError; - error_chain! { foreign_links { - XmlWriterError(WriterError) - /// An error with writing an XML event, from xml::writer::EventWriter. + XmlError(::quick_xml::errors::Error) + /// An error from quick_xml. ; - XmlReaderError(ReaderError) - /// An error with reading an XML event, from xml::reader::EventReader. + Utf8Error(::std::str::Utf8Error) + /// An UTF-8 conversion error. ; - IoError(io::Error) + IoError(::std::io::Error) /// An I/O error, from std::io. ; } errors { - /// En error which is returned when the end of the document was reached prematurely. + /// An error which is returned when the end of the document was reached prematurely. EndOfDocument { description("the end of the document has been reached prematurely") display("the end of the document has been reached prematurely") } + /// An error which is returned when an element is closed when it shouldn't be + InvalidElementClosed { + description("The XML is invalid, an element was wrongly closed") + display("the XML is invalid, an element was wrongly closed") + } } } diff --git a/src/lib.rs b/src/lib.rs index d51de436e8250786a1b24288daf74c08d14899e5..2e00b9fd17be4ddb88e0ec7566d8863d223fe487 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,7 +64,7 @@ //! minidom = "*" //! ``` -extern crate xml; +extern crate quick_xml; #[macro_use] extern crate error_chain; pub mod error; diff --git a/src/tests.rs b/src/tests.rs index f58bb6ebb1804f095bb928ece8fb459da3dc6029..bc71ca22c41730f403e52c1506c38dec8be14d5a 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,9 +1,6 @@ -use std::io::Cursor; - use std::iter::Iterator; -use xml::reader::EventReader; -use xml::writer::EventWriter; +use quick_xml::reader::Reader; use element::Element; @@ -32,19 +29,18 @@ fn build_test_tree() -> Element { #[test] fn reader_works() { - let mut reader = EventReader::new(Cursor::new(TEST_STRING)); + let mut reader = Reader::from_str(TEST_STRING); assert_eq!(Element::from_reader(&mut reader).unwrap(), build_test_tree()); } #[test] fn writer_works() { let root = build_test_tree(); - let mut out = Vec::new(); + let mut writer = Vec::new(); { - let mut writer = EventWriter::new(&mut out); root.write_to(&mut writer).unwrap(); } - assert_eq!(String::from_utf8(out).unwrap(), TEST_STRING); + assert_eq!(String::from_utf8(writer).unwrap(), TEST_STRING); } #[test] @@ -110,8 +106,18 @@ fn two_elements_with_same_arguments_different_order_are_equal() { #[test] fn namespace_attributes_works() { - let mut reader = EventReader::new(Cursor::new(TEST_STRING)); + let mut reader = Reader::from_str(TEST_STRING); let root = Element::from_reader(&mut reader).unwrap(); assert_eq!("en", root.attr("xml:lang").unwrap()); assert_eq!("fr", root.get_child("child", "child_ns").unwrap().attr("xml:lang").unwrap()); } + +#[test] +fn wrongly_closed_elements_error() { + let elem1 = "".parse::(); + assert!(elem1.is_err()); + let elem1 = "".parse::(); + assert!(elem1.is_err()); + let elem1 = "".parse::(); + assert!(elem1.is_ok()); +} From 8b50dadb92a876b7ecb3760dcd12587ed7ced698 Mon Sep 17 00:00:00 2001 From: lumi Date: Sat, 10 Jun 2017 17:44:28 +0200 Subject: [PATCH 0311/1020] added changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..5063d8fe24f6667b2020de57e223412c9482cb69 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +Version 0.5.0, released 2017-06-10: + * Big changes + - Eijebong made parsing a lot faster by switching the crate from xml-rs to quick_xml. ( https://gitlab.com/lumi/minidom-rs/merge_requests/11 ) From 334f2f78f80d02759befd653b2b3df47c088d76a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 11 Jun 2017 14:42:11 +0100 Subject: [PATCH 0312/1020] data_forms: Implement IntoElements. --- src/data_forms.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/data_forms.rs b/src/data_forms.rs index 4aba85287ebc26476545fd7dd2dde4c2a4e2271b..70c2d3aa127a0923edb82d68f7eb73566304946a 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -309,6 +309,12 @@ impl From for Element { } } +impl IntoElements for DataForm { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + #[cfg(test)] mod tests { use super::*; From 6c1c9d0851c439274c771f5eb2acb7a012f7515e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 11 Jun 2017 14:48:31 +0100 Subject: [PATCH 0313/1020] Add a PubSub event parser and serialiser. --- src/lib.rs | 3 + src/ns.rs | 9 + src/pubsub/event.rs | 461 ++++++++++++++++++++++++++++++++++++++++++++ src/pubsub/mod.rs | 9 + 4 files changed, 482 insertions(+) create mode 100644 src/pubsub/event.rs create mode 100644 src/pubsub/mod.rs diff --git a/src/lib.rs b/src/lib.rs index b5ee57ef1fedd8c683f8580eed44c67de7fb6a16..46a2b8ef9c0c2cac2bc338c561eabc3178da839a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,6 +81,9 @@ pub mod ibb; /// XEP-0059: Result Set Management pub mod rsm; +/// XEP-0060: Publish-Subscribe +pub mod pubsub; + /// XEP-0085: Chat State Notifications pub mod chatstates; diff --git a/src/ns.rs b/src/ns.rs index 149025ac5402cd5cef9514a860c7a95345bdb4e9..88541e0e75e7c7af1e70b5b9d992508dcbf26ce4 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -30,6 +30,15 @@ pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; /// XEP-0059: Result Set Management pub const RSM: &'static str = "http://jabber.org/protocol/rsm"; +/// XEP-0060: Publish-Subscribe +pub const PUBSUB: &'static str = "http://jabber.org/protocol/pubsub"; +/// XEP-0060: Publish-Subscribe +pub const PUBSUB_ERRORS: &'static str = "http://jabber.org/protocol/pubsub#errors"; +/// XEP-0060: Publish-Subscribe +pub const PUBSUB_EVENT: &'static str = "http://jabber.org/protocol/pubsub#event"; +/// XEP-0060: Publish-Subscribe +pub const PUBSUB_OWNER: &'static str = "http://jabber.org/protocol/pubsub#owner"; + /// XEP-0085: Chat State Notifications pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs new file mode 100644 index 0000000000000000000000000000000000000000..47d9a4699cd8033cb3a7ad979bbdc1460f295469 --- /dev/null +++ b/src/pubsub/event.rs @@ -0,0 +1,461 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; +use std::str::FromStr; + +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use jid::Jid; +use chrono::{DateTime, FixedOffset}; + +use error::Error; + +use ns; + +use data_forms::DataForm; + +#[derive(Debug, Clone)] +pub struct Item { + payload: Option, + id: Option, + node: Option, + publisher: Option, +} + +impl From for Element { + fn from(item: Item) -> Element { + Element::builder("item") + .ns(ns::PUBSUB_EVENT) + .attr("id", item.id) + .attr("node", item.node) + .attr("publisher", item.publisher.and_then(|publisher| Some(String::from(publisher)))) + .append(item.payload) + .build() + } +} + +impl IntoElements for Item { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Subscription { + None, + Pending, + Subscribed, + Unconfigured, +} + +impl Default for Subscription { + fn default() -> Subscription { + Subscription::None + } +} + +impl FromStr for Subscription { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "none" => Subscription::None, + "pending" => Subscription::Pending, + "subscribed" => Subscription::Subscribed, + "unconfigured" => Subscription::Unconfigured, + + _ => return Err(Error::ParseError("Invalid 'subscription' attribute.")), + }) + } +} + +impl IntoAttributeValue for Subscription { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Subscription::None => return None, + Subscription::Pending => "pending", + Subscription::Subscribed => "subscribed", + Subscription::Unconfigured => "unconfigured", + })) + } +} + +#[derive(Debug, Clone)] +pub enum PubSubEvent { + /* + Collection { + }, + */ + Configuration { + node: String, + form: Option, + }, + Delete { + node: String, + redirect: Option, + }, + EmptyItems { + node: String, + }, + PublishedItems { + node: String, + items: Vec, + }, + RetractedItems { + node: String, + items: Vec, + }, + Purge { + node: String, + }, + Subscription { + node: String, + expiry: Option>, + jid: Option, + subid: Option, + subscription: Option, + }, +} + +fn parse_items(elem: Element, node: String) -> Result { + let mut is_retract = None; + let mut items = vec!(); + let mut retracts = vec!(); + for child in elem.children() { + if child.is("item", ns::PUBSUB_EVENT) { + match is_retract { + None => is_retract = Some(false), + Some(false) => (), + Some(true) => return Err(Error::ParseError("Mix of item and retract in items element.")), + } + let mut payloads = child.children().cloned().collect::>(); + let payload = payloads.pop(); + if !payloads.is_empty() { + return Err(Error::ParseError("More than a single payload in item element.")); + } + let item = Item { + payload, + id: get_attr!(child, "id", optional), + node: get_attr!(child, "node", optional), + publisher: get_attr!(child, "publisher", optional), + }; + items.push(item); + } else if child.is("retract", ns::PUBSUB_EVENT) { + match is_retract { + None => is_retract = Some(true), + Some(true) => (), + Some(false) => return Err(Error::ParseError("Mix of item and retract in items element.")), + } + for _ in child.children() { + return Err(Error::ParseError("Unknown child in retract element.")); + } + for (attr, _) in child.attrs() { + if attr != "id" { + return Err(Error::ParseError("Unknown attribute in retract element.")); + } + } + let id = get_attr!(child, "id", required); + retracts.push(id); + } else { + return Err(Error::ParseError("Invalid child in items element.")); + } + } + Ok(match is_retract { + None => PubSubEvent::EmptyItems { node }, + Some(false) => PubSubEvent::PublishedItems { node, items }, + Some(true) => PubSubEvent::RetractedItems { node, items: retracts }, + }) +} + +impl TryFrom for PubSubEvent { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("event", ns::PUBSUB_EVENT) { + return Err(Error::ParseError("This is not an event element.")); + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in event element.")); + } + let mut payload = None; + for child in elem.children() { + /* + for (attr, _) in child.attrs() { + if attr != "node" { + return Err(Error::ParseError("Unknown attribute in items element.")); + } + } + */ + let node = get_attr!(child, "node", required); + if child.is("configuration", ns::PUBSUB_EVENT) { + let mut payloads = child.children().cloned().collect::>(); + let item = payloads.pop(); + if !payloads.is_empty() { + return Err(Error::ParseError("More than a single payload in configuration element.")); + } + let form = match item { + None => None, + Some(payload) => Some(DataForm::try_from(payload)?), + }; + payload = Some(PubSubEvent::Configuration { node, form }); + } else if child.is("delete", ns::PUBSUB_EVENT) { + let mut redirect = None; + for item in child.children() { + if item.is("redirect", ns::PUBSUB_EVENT) { + if redirect.is_some() { + return Err(Error::ParseError("More than one redirect in delete element.")); + } + let uri = get_attr!(item, "uri", required); + redirect = Some(uri); + } else { + return Err(Error::ParseError("Unknown child in delete element.")); + } + } + payload = Some(PubSubEvent::Delete { node, redirect }); + } else if child.is("items", ns::PUBSUB_EVENT) { + payload = Some(parse_items(child.clone(), node)?); + } else if child.is("purge", ns::PUBSUB_EVENT) { + for _ in child.children() { + return Err(Error::ParseError("Unknown child in purge element.")); + } + payload = Some(PubSubEvent::Purge { node }); + } else if child.is("subscription", ns::PUBSUB_EVENT) { + for _ in child.children() { + return Err(Error::ParseError("Unknown child in purge element.")); + } + payload = Some(PubSubEvent::Subscription { + node: node, + expiry: get_attr!(child, "expiry", optional), + jid: get_attr!(child, "jid", optional), + subid: get_attr!(child, "subid", optional), + subscription: get_attr!(child, "subscription", optional), + }); + } else { + return Err(Error::ParseError("Unknown child in event element.")); + } + } + Ok(payload.ok_or(Error::ParseError("No payload in event element."))?) + } +} + +impl From for Element { + fn from(event: PubSubEvent) -> Element { + let payload = match event { + PubSubEvent::Configuration { node, form } => { + Element::builder("configuration") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(form) + .build() + }, + PubSubEvent::Delete { node, redirect } => { + Element::builder("purge") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(redirect.and_then(|redirect| { + Some(Element::builder("redirect") + .ns(ns::PUBSUB_EVENT) + .attr("uri", redirect) + .build()) + })) + .build() + }, + PubSubEvent::EmptyItems { node } => { + Element::builder("items") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .build() + }, + PubSubEvent::PublishedItems { node, items } => { + Element::builder("items") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(items) + .build() + }, + PubSubEvent::RetractedItems { node, items } => { + Element::builder("items") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(items) + .build() + }, + PubSubEvent::Purge { node } => { + Element::builder("purge") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .build() + }, + PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => { + Element::builder("subscription") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .attr("expiry", expiry.and_then(|expiry| Some(expiry.to_rfc3339()))) + .attr("jid", jid.and_then(|jid| Some(String::from(jid)))) + .attr("subid", subid) + .attr("subscription", subscription) + .build() + }, + }; + Element::builder("event") + .ns(ns::PUBSUB_EVENT) + .append(payload) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::EmptyItems { node } => assert_eq!(node, String::from("coucou")), + _ => panic!(), + } + } + + #[test] + fn test_simple_items() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::PublishedItems { node, items } => { + assert_eq!(node, String::from("coucou")); + assert_eq!(items[0].id, Some(String::from("test"))); + assert_eq!(items[0].node, Some(String::from("huh?"))); + assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap())); + assert_eq!(items[0].payload, None); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_pep() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::PublishedItems { node, items } => { + assert_eq!(node, String::from("something")); + assert_eq!(items[0].id, None); + assert_eq!(items[0].node, None); + assert_eq!(items[0].publisher, None); + match items[0].payload { + Some(ref elem) => assert!(elem.is("foreign", "example:namespace")), + _ => panic!(), + } + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_retract() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::RetractedItems { node, items } => { + assert_eq!(node, String::from("something")); + assert_eq!(items[0], String::from("coucou")); + assert_eq!(items[1], String::from("test")); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_delete() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::Delete { node, redirect } => { + assert_eq!(node, String::from("coucou")); + assert_eq!(redirect, Some(String::from("hello"))); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_purge() { + let elem: Element = "".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::Purge { node } => { + assert_eq!(node, String::from("coucou")); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_configure() { + let elem: Element = "http://jabber.org/protocol/pubsub#node_config".parse().unwrap(); + let event = PubSubEvent::try_from(elem).unwrap(); + match event { + PubSubEvent::Configuration { node, form: _ } => { + assert_eq!(node, String::from("coucou")); + //assert_eq!(form.type_, Result_); + }, + _ => panic!(), + } + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = PubSubEvent::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in event element."); + } + + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = PubSubEvent::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in event element."); + } + + #[test] + fn test_ex221_subscription() { + let elem: Element = r#" + + + +"#.parse().unwrap(); + let event = PubSubEvent::try_from(elem.clone()).unwrap(); + match event.clone() { + PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => { + assert_eq!(node, String::from("princely_musings")); + assert_eq!(subid, Some(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3"))); + assert_eq!(subscription, Some(Subscription::Subscribed)); + assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap())); + assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap())); + }, + _ => panic!(), + } + + let elem2: Element = event.into(); + assert_eq!(elem, elem2); + } +} diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..9eb939442e863cabae522697569d5da6ac324f00 --- /dev/null +++ b/src/pubsub/mod.rs @@ -0,0 +1,9 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +pub mod event; + +pub use self::event::PubSubEvent; From 60521298d60de10b444d943551ed3b199444c9b4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 11 Jun 2017 14:57:30 +0100 Subject: [PATCH 0314/1020] Cargo.toml: Bump base64, improve performances and reduce unsafe code. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1a93c909c8a33752dadc64aa1a629cd1ffaa7b92..51a4a37b66df2fdcf9f9627078a6ccc55158f23e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MPL-2.0" [dependencies] minidom = "0.4.3" jid = "0.2.0" -base64 = "0.5.0" +base64 = "0.6.0" digest = "0.5.0" sha-1 = "0.3.0" sha2 = "0.5.0" From 7202a6e1906aa84c0cb9705522cf3480f8ac5046 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 11 Jun 2017 14:58:05 +0100 Subject: [PATCH 0315/1020] ChangeLog: Add release notes for version 0.5.0. --- ChangeLog | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 63f21648c286ded9afd6544e967e512fa94914ca..d219bf0bcc742eebae08be95e8a41ecf8a3efee7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,16 @@ -Version 0.?.?: -2017-??-?? Emmanuel Gil Peyrot +Version 0.5.0: +2017-06-11 Emmanuel Gil Peyrot * New parsers/serialisers: - Implementation of the roster management protocol defined in RFC 6121 §2. + - Implementation of PubSub events (except collections). + - Early implementation of MUC. + * Breaking changes: + - Rename presence enums to make them easier to use. + * Improvements: + - Make hashes comparable and hashable. + - Make data forms embeddable easily into minidom + Element::builder. Version 0.4.0: 2017-05-28 Emmanuel Gil Peyrot From 019848c8647f3c043fd8ac52d322b5ff9a6a013c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 11 Jun 2017 14:58:34 +0100 Subject: [PATCH 0316/1020] Release version 0.5.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 51a4a37b66df2fdcf9f9627078a6ccc55158f23e..68d32e5d16e530ced1da8127c69fd5f10304c230 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.4.0" +version = "0.5.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 072cba6a3ed6389886fcd87ff9ec71eb8f9ab524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 12 Jun 2017 17:40:39 +0100 Subject: [PATCH 0318/1020] Add get_ functions that return new truncated structs from the current one --- src/lib.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index b78001b05053436b49041500f83232c578f57286..323c43f640f34aa1583421842f1603974af01a5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -190,6 +190,28 @@ impl Jid { } } + /// Returns a new Jabber ID from the current one with only node and domain. + /// + /// This is of the form `node`@`domain`. + /// + /// # Examples + /// + /// ``` + /// use jid::Jid; + /// + /// let jid = Jid::full("node", "domain", "resource").get_bare(); + /// + /// assert_eq!(jid.node, Some("node".to_owned())); + /// assert_eq!(jid.domain, "domain".to_owned()); + /// assert_eq!(jid.resource, None); + pub fn get_bare(self) -> Jid { + Jid { + node: self.node.clone(), + domain: self.domain.clone(), + resource: None, + } + } + /// Constructs a Jabber ID containing only a `domain`. /// /// This is of the form `domain`. @@ -214,6 +236,28 @@ impl Jid { } } + /// Returns a new Jabber ID from the current one with only domain. + /// + /// This is of the form `domain`. + /// + /// # Examples + /// + /// ``` + /// use jid::Jid; + /// + /// let jid = Jid::full("node", "domain", "resource").get_domain(); + /// + /// assert_eq!(jid.node, None); + /// assert_eq!(jid.domain, "domain".to_owned()); + /// assert_eq!(jid.resource, None); + pub fn get_domain(self) -> Jid { + Jid { + node: None, + domain: self.domain.clone(), + resource: None, + } + } + /// Constructs a Jabber ID containing the `domain` and `resource` components. /// /// This is of the form `domain`/`resource`. From 10ab104ea011a503ecd0eb1e62ad74c4449eea4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 12 Jun 2017 17:46:06 +0100 Subject: [PATCH 0319/1020] Better without clones --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 323c43f640f34aa1583421842f1603974af01a5b..56de906a15504a42f10304743a7fb4d7e06e2c25 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -206,8 +206,8 @@ impl Jid { /// assert_eq!(jid.resource, None); pub fn get_bare(self) -> Jid { Jid { - node: self.node.clone(), - domain: self.domain.clone(), + node: self.node, + domain: self.domain, resource: None, } } @@ -253,7 +253,7 @@ impl Jid { pub fn get_domain(self) -> Jid { Jid { node: None, - domain: self.domain.clone(), + domain: self.domain, resource: None, } } From b69ecb31aa69669a25fb5655ee258ee374bf11c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 12 Jun 2017 18:05:19 +0100 Subject: [PATCH 0320/1020] Renaming functions --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 56de906a15504a42f10304743a7fb4d7e06e2c25..00af16f840aaee132f79fdcc18d438d12cebdeaf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -199,12 +199,12 @@ impl Jid { /// ``` /// use jid::Jid; /// - /// let jid = Jid::full("node", "domain", "resource").get_bare(); + /// let jid = Jid::full("node", "domain", "resource").into_bare_jid(); /// /// assert_eq!(jid.node, Some("node".to_owned())); /// assert_eq!(jid.domain, "domain".to_owned()); /// assert_eq!(jid.resource, None); - pub fn get_bare(self) -> Jid { + pub fn into_bare_jid(self) -> Jid { Jid { node: self.node, domain: self.domain, @@ -245,12 +245,12 @@ impl Jid { /// ``` /// use jid::Jid; /// - /// let jid = Jid::full("node", "domain", "resource").get_domain(); + /// let jid = Jid::full("node", "domain", "resource").into_domain_jid(); /// /// assert_eq!(jid.node, None); /// assert_eq!(jid.domain, "domain".to_owned()); /// assert_eq!(jid.resource, None); - pub fn get_domain(self) -> Jid { + pub fn into_domain_jid(self) -> Jid { Jid { node: None, domain: self.domain, From 2f59ca4b56c392e05b47091ff3c2005ff85fcb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 12 Jun 2017 18:20:42 +0100 Subject: [PATCH 0321/1020] Fix doctests --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 00af16f840aaee132f79fdcc18d438d12cebdeaf..5ced40038ffc54efd895bbc4946596eaac8041c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,6 +204,7 @@ impl Jid { /// assert_eq!(jid.node, Some("node".to_owned())); /// assert_eq!(jid.domain, "domain".to_owned()); /// assert_eq!(jid.resource, None); + /// ``` pub fn into_bare_jid(self) -> Jid { Jid { node: self.node, @@ -250,6 +251,7 @@ impl Jid { /// assert_eq!(jid.node, None); /// assert_eq!(jid.domain, "domain".to_owned()); /// assert_eq!(jid.resource, None); + /// ``` pub fn into_domain_jid(self) -> Jid { Jid { node: None, From 1f11796057f867d361c952dc32da7db403b72ff2 Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 12 Jun 2017 22:28:14 +0200 Subject: [PATCH 0322/1020] set version to 0.2.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5a18dc9978757d36483b399b98f8ef6fc00fff7c..715fc12844732dd8cf389a2934562545edb51e38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.2.1" +version = "0.2.2" authors = ["lumi ", "Emmanuel Gil Peyrot "] description = "A crate which provides a Jid struct for Jabber IDs." homepage = "https://gitlab.com/lumi/jid-rs" From e00cfa2c2e8bcdba946c400f7817fdfcbe60b144 Mon Sep 17 00:00:00 2001 From: Bastien Orivel Date: Mon, 12 Jun 2017 23:11:37 +0200 Subject: [PATCH 0323/1020] Speedup jid parsing name control ns/iter variable ns/iter diff ns/iter diff % speedup big_jids 638 456 -182 -28.53% x 1.40 small_jids 92 91 -1 -1.09% x 1.01 --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5ced40038ffc54efd895bbc4946596eaac8041c3..3f39cbcfc0378a5589e1ad0e48e2a2d4909b33f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,7 @@ impl FromStr for Jid { fn from_str(s: &str) -> Result { // TODO: very naive, may need to do it differently let iter = s.chars(); - let mut buf = String::new(); + let mut buf = String::with_capacity(s.len()); let mut state = ParserState::Node; let mut node = None; let mut domain = None; From 9955c1131bc6a283b769652a817c5c577477c1d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 12 Jun 2017 22:49:27 +0100 Subject: [PATCH 0324/1020] ns: Remove now useless 'static lifetime. --- src/ns.rs | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/ns.rs b/src/ns.rs index 88541e0e75e7c7af1e70b5b9d992508dcbf26ce4..af76dd9050c2e2c0a22fd9704ed6aab4cd2b52e5 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -6,108 +6,108 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core -pub const JABBER_CLIENT: &'static str = "jabber:client"; +pub const JABBER_CLIENT: &str = "jabber:client"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core -pub const XMPP_STANZAS: &'static str = "urn:ietf:params:xml:ns:xmpp-stanzas"; +pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence -pub const ROSTER: &'static str = "jabber:iq:roster"; +pub const ROSTER: &str = "jabber:iq:roster"; /// XEP-0004: Data Forms -pub const DATA_FORMS: &'static str = "jabber:x:data"; +pub const DATA_FORMS: &str = "jabber:x:data"; /// XEP-0030: Service Discovery -pub const DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; +pub const DISCO_INFO: &str = "http://jabber.org/protocol/disco#info"; /// XEP-0045: Multi-User Chat -pub const MUC: &'static str = "http://jabber.org/protocol/muc"; +pub const MUC: &str = "http://jabber.org/protocol/muc"; /// XEP-0045: Multi-User Chat -pub const MUC_USER: &'static str = "http://jabber.org/protocol/muc#user"; +pub const MUC_USER: &str = "http://jabber.org/protocol/muc#user"; /// XEP-0047: In-Band Bytestreams -pub const IBB: &'static str = "http://jabber.org/protocol/ibb"; +pub const IBB: &str = "http://jabber.org/protocol/ibb"; /// XEP-0059: Result Set Management -pub const RSM: &'static str = "http://jabber.org/protocol/rsm"; +pub const RSM: &str = "http://jabber.org/protocol/rsm"; /// XEP-0060: Publish-Subscribe -pub const PUBSUB: &'static str = "http://jabber.org/protocol/pubsub"; +pub const PUBSUB: &str = "http://jabber.org/protocol/pubsub"; /// XEP-0060: Publish-Subscribe -pub const PUBSUB_ERRORS: &'static str = "http://jabber.org/protocol/pubsub#errors"; +pub const PUBSUB_ERRORS: &str = "http://jabber.org/protocol/pubsub#errors"; /// XEP-0060: Publish-Subscribe -pub const PUBSUB_EVENT: &'static str = "http://jabber.org/protocol/pubsub#event"; +pub const PUBSUB_EVENT: &str = "http://jabber.org/protocol/pubsub#event"; /// XEP-0060: Publish-Subscribe -pub const PUBSUB_OWNER: &'static str = "http://jabber.org/protocol/pubsub#owner"; +pub const PUBSUB_OWNER: &str = "http://jabber.org/protocol/pubsub#owner"; /// XEP-0085: Chat State Notifications -pub const CHATSTATES: &'static str = "http://jabber.org/protocol/chatstates"; +pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; /// XEP-0115: Entity Capabilities -pub const CAPS: &'static str = "http://jabber.org/protocol/caps"; +pub const CAPS: &str = "http://jabber.org/protocol/caps"; /// XEP-0166: Jingle -pub const JINGLE: &'static str = "urn:xmpp:jingle:1"; +pub const JINGLE: &str = "urn:xmpp:jingle:1"; /// XEP-0184: Message Delivery Receipts -pub const RECEIPTS: &'static str = "urn:xmpp:receipts"; +pub const RECEIPTS: &str = "urn:xmpp:receipts"; /// XEP-0199: XMPP Ping -pub const PING: &'static str = "urn:xmpp:ping"; +pub const PING: &str = "urn:xmpp:ping"; /// XEP-0203: Delayed Delivery -pub const DELAY: &'static str = "urn:xmpp:delay"; +pub const DELAY: &str = "urn:xmpp:delay"; /// XEP-0221: Data Forms Media Element -pub const MEDIA_ELEMENT: &'static str = "urn:xmpp:media-element"; +pub const MEDIA_ELEMENT: &str = "urn:xmpp:media-element"; /// XEP-0224: Attention -pub const ATTENTION: &'static str = "urn:xmpp:attention:0"; +pub const ATTENTION: &str = "urn:xmpp:attention:0"; /// XEP-0234: Jingle File Transfer -pub const JINGLE_FT: &'static str = "urn:xmpp:jingle:apps:file-transfer:5"; +pub const JINGLE_FT: &str = "urn:xmpp:jingle:apps:file-transfer:5"; /// XEP-0234: Jingle File Transfer -pub const JINGLE_FT_ERROR: &'static str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; +pub const JINGLE_FT_ERROR: &str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; /// XEP-0260: Jingle SOCKS5 Bytestreams Transport Method -pub const JINGLE_S5B: &'static str = "urn:xmpp:jingle:transports:s5b:1"; +pub const JINGLE_S5B: &str = "urn:xmpp:jingle:transports:s5b:1"; /// XEP-0261: Jingle In-Band Bytestreams Transport Method -pub const JINGLE_IBB: &'static str = "urn:xmpp:jingle:transports:ibb:1"; +pub const JINGLE_IBB: &str = "urn:xmpp:jingle:transports:ibb:1"; /// XEP-0297: Stanza Forwarding -pub const FORWARD: &'static str = "urn:xmpp:forward:0"; +pub const FORWARD: &str = "urn:xmpp:forward:0"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASHES: &'static str = "urn:xmpp:hashes:2"; +pub const HASHES: &str = "urn:xmpp:hashes:2"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA_256: &'static str = "urn:xmpp:hash-function-text-names:sha-256"; +pub const HASH_ALGO_SHA_256: &str = "urn:xmpp:hash-function-text-names:sha-256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA_512: &'static str = "urn:xmpp:hash-function-text-names:sha-512"; +pub const HASH_ALGO_SHA_512: &str = "urn:xmpp:hash-function-text-names:sha-512"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA3_256: &'static str = "urn:xmpp:hash-function-text-names:sha3-256"; +pub const HASH_ALGO_SHA3_256: &str = "urn:xmpp:hash-function-text-names:sha3-256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_SHA3_512: &'static str = "urn:xmpp:hash-function-text-names:sha3-512"; +pub const HASH_ALGO_SHA3_512: &str = "urn:xmpp:hash-function-text-names:sha3-512"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_BLAKE2B_256: &'static str = "urn:xmpp:hash-function-text-names:id-blake2b256"; +pub const HASH_ALGO_BLAKE2B_256: &str = "urn:xmpp:hash-function-text-names:id-blake2b256"; /// XEP-0300: Use of Cryptographic Hash Functions in XMPP -pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-names:id-blake2b512"; +pub const HASH_ALGO_BLAKE2B_512: &str = "urn:xmpp:hash-function-text-names:id-blake2b512"; /// XEP-0308: Last Message Correction -pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; +pub const MESSAGE_CORRECT: &str = "urn:xmpp:message-correct:0"; /// XEP-0313: Message Archive Management -pub const MAM: &'static str = "urn:xmpp:mam:2"; +pub const MAM: &str = "urn:xmpp:mam:2"; /// XEP-0319: Last User Interaction in Presence -pub const IDLE: &'static str = "urn:xmpp:idle:1"; +pub const IDLE: &str = "urn:xmpp:idle:1"; /// XEP-0359: Unique and Stable Stanza IDs -pub const SID: &'static str = "urn:xmpp:sid:0"; +pub const SID: &str = "urn:xmpp:sid:0"; /// XEP-0380: Explicit Message Encryption -pub const EME: &'static str = "urn:xmpp:eme:0"; +pub const EME: &str = "urn:xmpp:eme:0"; /// XEP-0390: Entity Capabilities 2.0 -pub const ECAPS2: &'static str = "urn:xmpp:caps"; +pub const ECAPS2: &str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 -pub const ECAPS2_OPTIMIZE: &'static str = "urn:xmpp:caps:optimize"; +pub const ECAPS2_OPTIMIZE: &str = "urn:xmpp:caps:optimize"; From 1d9790a1d8a761f2305f49783ebe9e364bb3c32e Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 13 Jun 2017 23:52:41 +0200 Subject: [PATCH 0325/1020] stream_start: fix style --- src/stream_start.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream_start.rs b/src/stream_start.rs index 480178eb58588e4959c6b60389c0328c47eec290..3e5bc8b2f9eb971932f6f2806a1e13763d5e2bd9 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -75,7 +75,7 @@ impl Future for StreamStart { Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => if stanza.name == "features" && stanza.ns == Some(NS_XMPP_STREAM.to_owned()) { - (StreamStartState::Invalid, Ok(Async::Ready(XMPPStream { stream, stream_attrs, stream_features: stanza }))) + (StreamStartState::Invalid, Ok(Async::Ready(XMPPStream::new(stream, stream_attrs, stanza)))) } else { (StreamStartState::RecvFeatures(stream, stream_attrs), Ok(Async::NotReady)) }, From 0f297d2d2d3a19587d41e0948313930608bf29d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 00:50:57 +0100 Subject: [PATCH 0326/1020] Add a generate_attribute! macro, and use it for the common case. --- src/data_forms.rs | 102 +++++---------------------- src/ibb.rs | 37 ++-------- src/jingle.rs | 165 +++++++++----------------------------------- src/jingle_s5b.rs | 82 +++------------------- src/lib.rs | 60 ++++++++++++++++ src/mam.rs | 35 ++-------- src/message.rs | 43 ++---------- src/pubsub/event.rs | 45 ++---------- src/roster.rs | 43 ++---------- src/stanza_error.rs | 45 +++--------- 10 files changed, 158 insertions(+), 499 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 70c2d3aa127a0923edb82d68f7eb73566304946a..0899cc5ca88df9b01cab812eefe54bf9f3e7e0ad 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -14,19 +14,18 @@ use ns; use media_element::MediaElement; -#[derive(Debug, Clone, PartialEq)] -pub enum FieldType { - Boolean, - Fixed, - Hidden, - JidMulti, - JidSingle, - ListMulti, - ListSingle, - TextMulti, - TextPrivate, - TextSingle, -} +generate_attribute!(FieldType, "type", { + Boolean => "boolean", + Fixed => "fixed", + Hidden => "hidden", + JidMulti => "jid-multi", + JidSingle => "jid-single", + ListMulti => "list-multi", + ListSingle => "list-single", + TextMulti => "text-multi", + TextPrivate => "text-private", + TextSingle => "text-single", +}); impl Default for FieldType { fn default() -> FieldType { @@ -34,44 +33,6 @@ impl Default for FieldType { } } -impl FromStr for FieldType { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "boolean" => FieldType::Boolean, - "fixed" => FieldType::Fixed, - "hidden" => FieldType::Hidden, - "jid-multi" => FieldType::JidMulti, - "jid-single" => FieldType::JidSingle, - "list-multi" => FieldType::ListMulti, - "list-single" => FieldType::ListSingle, - "text-multi" => FieldType::TextMulti, - "text-private" => FieldType::TextPrivate, - "text-single" => FieldType::TextSingle, - - _ => return Err(Error::ParseError("Invalid 'type' attribute in field element.")), - }) - } -} - -impl IntoAttributeValue for FieldType { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - FieldType::Boolean => "boolean", - FieldType::Fixed => "fixed", - FieldType::Hidden => "hidden", - FieldType::JidMulti => "jid-multi", - FieldType::JidSingle => "jid-single", - FieldType::ListMulti => "list-multi", - FieldType::ListSingle => "list-single", - FieldType::TextMulti => "text-multi", - FieldType::TextPrivate => "text-private", - FieldType::TextSingle => "text-single", - })) - } -} - #[derive(Debug, Clone)] pub struct Option_ { pub label: Option, @@ -131,39 +92,12 @@ impl IntoElements for Field { } } -#[derive(Debug, Clone, PartialEq)] -pub enum DataFormType { - Cancel, - Form, - Result_, - Submit, -} - -impl FromStr for DataFormType { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "cancel" => DataFormType::Cancel, - "form" => DataFormType::Form, - "result" => DataFormType::Result_, - "submit" => DataFormType::Submit, - - _ => return Err(Error::ParseError("Unknown data form type.")), - }) - } -} - -impl IntoAttributeValue for DataFormType { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - DataFormType::Cancel => "cancel", - DataFormType::Form => "form", - DataFormType::Result_ => "result", - DataFormType::Submit => "submit", - })) - } -} +generate_attribute!(Subscription, "subscription", { + Cancel => "cancel", + Form => "form", + Result_ => "result", + Submit => "submit", +}); #[derive(Debug, Clone)] pub struct DataForm { diff --git a/src/ibb.rs b/src/ibb.rs index 18cb14e812dc7df3a9058563b690c8ecbd311ae5..5463caae809975377e084151a4ac661a63f1a8ba 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -14,39 +14,10 @@ use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum Stanza { - Iq, - Message, -} - -impl Default for Stanza { - fn default() -> Stanza { - Stanza::Iq - } -} - -impl FromStr for Stanza { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "iq" => Stanza::Iq, - "message" => Stanza::Message, - - _ => return Err(Error::ParseError("Invalid 'stanza' attribute.")), - }) - } -} - -impl IntoAttributeValue for Stanza { - fn into_attribute_value(self) -> Option { - match self { - Stanza::Iq => None, - Stanza::Message => Some(String::from("message")), - } - } -} +generate_attribute!(Stanza, "stanza", { + Iq => "iq", + Message => "message", +}, Default = Iq); #[derive(Debug, Clone)] pub enum IBB { diff --git a/src/jingle.rs b/src/jingle.rs index c5041b09adc035c065eb75ec41e26ea5392bca24..e5fab352b53c491151c0045476fd4e534efda515 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -13,108 +13,35 @@ use jid::Jid; use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum Action { - ContentAccept, - ContentAdd, - ContentModify, - ContentReject, - ContentRemove, - DescriptionInfo, - SecurityInfo, - SessionAccept, - SessionInfo, - SessionInitiate, - SessionTerminate, - TransportAccept, - TransportInfo, - TransportReject, - TransportReplace, -} - -impl FromStr for Action { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "content-accept" => Action::ContentAccept, - "content-add" => Action::ContentAdd, - "content-modify" => Action::ContentModify, - "content-reject" => Action::ContentReject, - "content-remove" => Action::ContentRemove, - "description-info" => Action::DescriptionInfo, - "security-info" => Action::SecurityInfo, - "session-accept" => Action::SessionAccept, - "session-info" => Action::SessionInfo, - "session-initiate" => Action::SessionInitiate, - "session-terminate" => Action::SessionTerminate, - "transport-accept" => Action::TransportAccept, - "transport-info" => Action::TransportInfo, - "transport-reject" => Action::TransportReject, - "transport-replace" => Action::TransportReplace, - - _ => return Err(Error::ParseError("Unknown action.")), - }) - } -} - -impl IntoAttributeValue for Action { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Action::ContentAccept => "content-accept", - Action::ContentAdd => "content-add", - Action::ContentModify => "content-modify", - Action::ContentReject => "content-reject", - Action::ContentRemove => "content-remove", - Action::DescriptionInfo => "description-info", - Action::SecurityInfo => "security-info", - Action::SessionAccept => "session-accept", - Action::SessionInfo => "session-info", - Action::SessionInitiate => "session-initiate", - Action::SessionTerminate => "session-terminate", - Action::TransportAccept => "transport-accept", - Action::TransportInfo => "transport-info", - Action::TransportReject => "transport-reject", - Action::TransportReplace => "transport-replace", - })) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Creator { - Initiator, - Responder, -} - -impl FromStr for Creator { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "initiator" => Creator::Initiator, - "responder" => Creator::Responder, - - _ => return Err(Error::ParseError("Unknown creator.")), - }) - } -} - -impl IntoAttributeValue for Creator { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Creator::Initiator => "initiator", - Creator::Responder => "responder", - })) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Senders { - Both, - Initiator, - None_, - Responder, -} +generate_attribute!(Action, "action", { + ContentAccept => "content-accept", + ContentAdd => "content-add", + ContentModify => "content-modify", + ContentReject => "content-reject", + ContentRemove => "content-remove", + DescriptionInfo => "description-info", + SecurityInfo => "security-info", + SessionAccept => "session-accept", + SessionInfo => "session-info", + SessionInitiate => "session-initiate", + SessionTerminate => "session-terminate", + TransportAccept => "transport-accept", + TransportInfo => "transport-info", + TransportReject => "transport-reject", + TransportReplace => "transport-replace", +}); + +generate_attribute!(Creator, "creator", { + Initiator => "initiator", + Responder => "responder", +}); + +generate_attribute!(Senders, "senders", { + Both => "both", + Initiator => "initiator", + None => "none", + Responder => "responder", +}); impl Default for Senders { fn default() -> Senders { @@ -122,32 +49,6 @@ impl Default for Senders { } } -impl FromStr for Senders { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "both" => Senders::Both, - "initiator" => Senders::Initiator, - "none" => Senders::None_, - "responder" => Senders::Responder, - - _ => return Err(Error::ParseError("Unknown senders.")), - }) - } -} - -impl IntoAttributeValue for Senders { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Senders::Both => "both", - Senders::Initiator => "initiator", - Senders::None_ => "none", - Senders::Responder => "responder", - })) - } -} - #[derive(Debug, Clone)] pub struct Content { pub creator: Creator, @@ -448,7 +349,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown action."); + assert_eq!(message, "Unknown value for 'action' attribute."); } #[test] @@ -493,7 +394,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown creator."); + assert_eq!(message, "Unknown value for 'creator' attribute."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); @@ -501,7 +402,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown senders."); + assert_eq!(message, "Unknown value for 'senders' attribute."); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); @@ -509,7 +410,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown senders."); + assert_eq!(message, "Unknown value for 'senders' attribute."); } #[test] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index e244a8c05d51ec77b28e6b15c6dff4382873bdb6..089d16056d4530195a344c992fdc4a6b39804362 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -13,45 +13,12 @@ use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum Type { - Assisted, - Direct, - Proxy, - Tunnel, -} - -impl Default for Type { - fn default() -> Type { - Type::Direct - } -} - -impl FromStr for Type { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "assisted" => Type::Assisted, - "direct" => Type::Direct, - "proxy" => Type::Proxy, - "tunnel" => Type::Tunnel, - - _ => return Err(Error::ParseError("Invalid 'type' attribute in candidate element.")), - }) - } -} - -impl IntoAttributeValue for Type { - fn into_attribute_value(self) -> Option { - Some(match self { - Type::Assisted => String::from("assisted"), - Type::Direct => return None, - Type::Proxy => String::from("proxy"), - Type::Tunnel => String::from("tunnel"), - }) - } -} +generate_attribute!(Type, "type", { + Assisted => "assisted", + Direct => "direct", + Proxy => "proxy", + Tunnel => "tunnel", +}, Default = Direct); #[derive(Debug, Clone)] pub struct Candidate { @@ -77,39 +44,10 @@ impl Into for Candidate { } } -#[derive(Debug, Clone, PartialEq)] -pub enum Mode { - Tcp, - Udp, -} - -impl Default for Mode { - fn default() -> Mode { - Mode::Tcp - } -} - -impl FromStr for Mode { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "tcp" => Mode::Tcp, - "udp" => Mode::Udp, - - _ => return Err(Error::ParseError("Invalid 'mode' attribute.")), - }) - } -} - -impl IntoAttributeValue for Mode { - fn into_attribute_value(self) -> Option { - match self { - Mode::Tcp => None, - Mode::Udp => Some(String::from("udp")), - } - } -} +generate_attribute!(Mode, "mode", { + Tcp => "tcp", + Udp => "udp", +}, Default = Tcp); #[derive(Debug, Clone)] pub enum TransportPayload { diff --git a/src/lib.rs b/src/lib.rs index 46a2b8ef9c0c2cac2bc338c561eabc3178da839a..4fc420b82108ff51a0d759f799b8b6bf4275c183 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,66 @@ macro_rules! get_attr { ); } +macro_rules! generate_attribute { + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => ( + generate_attribute!($elem, $name, {$($a => $b),+}); + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => ( + generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default); + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => ( + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $($a),+ + } + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + Ok(match s { + $($b => $elem::$a),+, + _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + $($elem::$a => $b),+ + })) + } + } + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => ( + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $($a),+ + } + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + Ok(match s { + $($b => $elem::$a),+, + _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl IntoAttributeValue for $elem { + #[allow(unreachable_patterns)] + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + $elem::$default => return None, + $($elem::$a => $b),+ + })) + } + } + impl Default for $elem { + fn default() -> $elem { + $elem::$default + } + } + ); +} + /// Error type returned by every parser on failure. pub mod error; /// XML namespace definitions used through XMPP. diff --git a/src/mam.rs b/src/mam.rs index c8cd2ac81cdab8fedfc33f9b0fab9ac3b83810b5..920cf8d01a76ae44d95839ad658405e99372dcd6 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -39,36 +39,11 @@ pub struct Fin { pub set: Set, } -#[derive(Debug, Clone)] -pub enum DefaultPrefs { - Always, - Never, - Roster, -} - -impl FromStr for DefaultPrefs { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "always" => DefaultPrefs::Always, - "never" => DefaultPrefs::Never, - "roster" => DefaultPrefs::Roster, - - _ => return Err(Error::ParseError("Invalid 'default' attribute.")), - }) - } -} - -impl IntoAttributeValue for DefaultPrefs { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - DefaultPrefs::Always => "always", - DefaultPrefs::Never => "never", - DefaultPrefs::Roster => "roster", - })) - } -} +generate_attribute!(DefaultPrefs, "default", { + Always => "always", + Never => "never", + Roster => "roster", +}); #[derive(Debug, Clone)] pub struct Prefs { diff --git a/src/message.rs b/src/message.rs index 13caed356d623307feedd049e2c79a7ca46c2f5f..4efabbdba1c6ff71c8325bb768d68fdf0ec3349a 100644 --- a/src/message.rs +++ b/src/message.rs @@ -102,14 +102,13 @@ impl Into for MessagePayload { } } -#[derive(Debug, Clone, PartialEq)] -pub enum MessageType { - Chat, - Error, - Groupchat, - Headline, - Normal, -} +generate_attribute!(MessageType, "type", { + Chat => "chat", + Error => "error", + Groupchat => "groupchat", + Headline => "headline", + Normal => "normal", +}); impl Default for MessageType { fn default() -> MessageType { @@ -117,34 +116,6 @@ impl Default for MessageType { } } -impl FromStr for MessageType { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "chat" => MessageType::Chat, - "error" => MessageType::Error, - "groupchat" => MessageType::Groupchat, - "headline" => MessageType::Headline, - "normal" => MessageType::Normal, - - _ => return Err(Error::ParseError("Invalid 'type' attribute on message element.")), - }) - } -} - -impl IntoAttributeValue for MessageType { - fn into_attribute_value(self) -> Option { - Some(match self { - MessageType::Chat => "chat", - MessageType::Error => "error", - MessageType::Groupchat => "groupchat", - MessageType::Headline => "headline", - MessageType::Normal => "normal", - }.to_owned()) - } -} - type Lang = String; type Body = String; type Subject = String; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 47d9a4699cd8033cb3a7ad979bbdc1460f295469..07e465c89a59dc3c7bdc3af520b1f787751dd588 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -43,45 +43,12 @@ impl IntoElements for Item { } } -#[derive(Debug, Clone, PartialEq)] -pub enum Subscription { - None, - Pending, - Subscribed, - Unconfigured, -} - -impl Default for Subscription { - fn default() -> Subscription { - Subscription::None - } -} - -impl FromStr for Subscription { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "none" => Subscription::None, - "pending" => Subscription::Pending, - "subscribed" => Subscription::Subscribed, - "unconfigured" => Subscription::Unconfigured, - - _ => return Err(Error::ParseError("Invalid 'subscription' attribute.")), - }) - } -} - -impl IntoAttributeValue for Subscription { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Subscription::None => return None, - Subscription::Pending => "pending", - Subscription::Subscribed => "subscribed", - Subscription::Unconfigured => "unconfigured", - })) - } -} +generate_attribute!(Subscription, "subscription", { + None => "none", + Pending => "pending", + Subscribed => "subscribed", + Unconfigured => "unconfigured", +}, Default = None); #[derive(Debug, Clone)] pub enum PubSubEvent { diff --git a/src/roster.rs b/src/roster.rs index e8dd8ad5609299662148cbb3e78797b693b254df..f38817d0794290d833ed4dcd64cee1cd75d286d1 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -15,42 +15,13 @@ use ns; type Group = String; -#[derive(Debug, Clone, PartialEq)] -pub enum Subscription { - None, - From, - To, - Both, - Remove, -} - -impl FromStr for Subscription { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "none" => Subscription::None, - "from" => Subscription::From, - "to" => Subscription::To, - "both" => Subscription::Both, - "remove" => Subscription::Remove, - - _ => return Err(Error::ParseError("Unknown value for attribute 'subscription'.")), - }) - } -} - -impl IntoAttributeValue for Subscription { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Subscription::None => "none", - Subscription::From => "from", - Subscription::To => "to", - Subscription::Both => "both", - Subscription::Remove => "remove", - })) - } -} +generate_attribute!(Subscription, "subscription", { + None => "none", + From => "from", + To => "to", + Both => "both", + Remove => "remove", +}); #[derive(Debug, Clone, PartialEq)] pub struct Item { diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 3841ad5b49fd6343f4bfefa99294576c4da57ebe..badd1efab7606026dde90871142add5083bb35a7 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -14,42 +14,13 @@ use error::Error; use jid::Jid; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum ErrorType { - Auth, - Cancel, - Continue, - Modify, - Wait, -} - -impl FromStr for ErrorType { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "auth" => ErrorType::Auth, - "cancel" => ErrorType::Cancel, - "continue" => ErrorType::Continue, - "modify" => ErrorType::Modify, - "wait" => ErrorType::Wait, - - _ => return Err(Error::ParseError("Unknown error type.")), - }) - } -} - -impl IntoAttributeValue for ErrorType { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - ErrorType::Auth => "auth", - ErrorType::Cancel => "cancel", - ErrorType::Continue => "continue", - ErrorType::Modify => "modify", - ErrorType::Wait => "wait", - })) - } -} +generate_attribute!(ErrorType, "type", { + Auth => "auth", + Cancel => "cancel", + Continue => "continue", + Modify => "modify", + Wait => "wait", +}); #[derive(Debug, Clone, PartialEq)] pub enum DefinedCondition { @@ -252,7 +223,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown error type."); + assert_eq!(message, "Unknown value for 'type' attribute."); } #[test] From ebc7582250d8a3e4a4a30576ad32c7bb3b47bbb2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 00:53:18 +0100 Subject: [PATCH 0327/1020] Move the Default implementation into more generate_attribute!. --- src/data_forms.rs | 8 +------- src/jingle.rs | 8 +------- src/message.rs | 8 +------- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 0899cc5ca88df9b01cab812eefe54bf9f3e7e0ad..9905f4b400ba4e0d6f33f36afdaee5a90cd07d76 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -25,13 +25,7 @@ generate_attribute!(FieldType, "type", { TextMulti => "text-multi", TextPrivate => "text-private", TextSingle => "text-single", -}); - -impl Default for FieldType { - fn default() -> FieldType { - FieldType::TextSingle - } -} +}, Default = TextSingle); #[derive(Debug, Clone)] pub struct Option_ { diff --git a/src/jingle.rs b/src/jingle.rs index e5fab352b53c491151c0045476fd4e534efda515..d189684e8efa31d6b4ac89083ad3df72acc8c94a 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -41,13 +41,7 @@ generate_attribute!(Senders, "senders", { Initiator => "initiator", None => "none", Responder => "responder", -}); - -impl Default for Senders { - fn default() -> Senders { - Senders::Both - } -} +}, Default = Both); #[derive(Debug, Clone)] pub struct Content { diff --git a/src/message.rs b/src/message.rs index 4efabbdba1c6ff71c8325bb768d68fdf0ec3349a..79038c1249b92736fee62a21123a732eb2b98a36 100644 --- a/src/message.rs +++ b/src/message.rs @@ -108,13 +108,7 @@ generate_attribute!(MessageType, "type", { Groupchat => "groupchat", Headline => "headline", Normal => "normal", -}); - -impl Default for MessageType { - fn default() -> MessageType { - MessageType::Normal - } -} +}, Default = Normal); type Lang = String; type Body = String; From df423e50478e022f3342e2f7e6c661e5fac661c6 Mon Sep 17 00:00:00 2001 From: Astro Date: Wed, 14 Jun 2017 01:55:56 +0200 Subject: [PATCH 0328/1020] use jid --- Cargo.toml | 1 + examples/echo_bot.rs | 10 +++++++++- src/lib.rs | 1 + src/starttls.rs | 15 +++++++-------- src/stream_start.rs | 10 +++++++--- src/tcp.rs | 7 +++++-- src/xmpp_stream.rs | 20 +++++++++++++------- 7 files changed, 43 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 82c0ea9b74c7de518789bbd72e4c590ed4e02169..12e818a70061ce8dffe43d691054bcd8a9589708 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ native-tls = "*" tokio-tls = "*" sasl = "*" rustc-serialize = "*" +jid = "*" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 9dfe1556b86690402aeeca1d0201a659b36c575d..4f5f044730e978bb5251bae94156eab20abc1db6 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,13 +1,19 @@ extern crate futures; extern crate tokio_core; extern crate tokio_xmpp; +extern crate jid; +use std::str::FromStr; use tokio_core::reactor::Core; use futures::{Future, Stream}; use tokio_xmpp::TcpClient; use tokio_xmpp::xmpp_codec::Packet; +use jid::Jid; fn main() { + let jid = Jid::from_str("astrobot@example.net").expect("JID"); + let password = "".to_owned(); + use std::net::ToSocketAddrs; let addr = "[2a01:4f8:a0:33d0::5]:5222" .to_socket_addrs().unwrap() @@ -15,6 +21,7 @@ fn main() { let mut core = Core::new().unwrap(); let client = TcpClient::connect( + jid.clone(), &addr, &core.handle() ).map_err(|e| format!("{}", e) @@ -25,7 +32,8 @@ fn main() { panic!("No STARTTLS") } }).and_then(|stream| { - stream.auth("astrobot", "").expect("auth") + let username = jid.node.as_ref().unwrap().to_owned(); + stream.auth(username, password).expect("auth") }).and_then(|stream| { stream.for_each(|event| { match event { diff --git a/src/lib.rs b/src/lib.rs index 6a402c366b22db62ceab465256310623dda657ea..e04778ac5134c090a8f38a3fc42b79cee879cd44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ extern crate native_tls; extern crate tokio_tls; extern crate sasl; extern crate rustc_serialize as serialize; +extern crate jid; pub mod xmpp_codec; diff --git a/src/starttls.rs b/src/starttls.rs index c893f0597bdd45c4d667366021f7f29fd9e926a9..2e7da7bc92bdbb8db945aaa105f4d7eedd1fb09d 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -6,6 +6,7 @@ use tokio_io::{AsyncRead, AsyncWrite}; use tokio_tls::*; use native_tls::TlsConnector; use xml; +use jid::Jid; use xmpp_codec::*; use xmpp_stream::*; @@ -16,7 +17,7 @@ pub const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; pub struct StartTlsClient { state: StartTlsClientState, - domain: String, + jid: Jid, } enum StartTlsClientState { @@ -30,9 +31,7 @@ enum StartTlsClientState { impl StartTlsClient { /// Waits for pub fn from_stream(xmpp_stream: XMPPStream) -> Self { - let domain = xmpp_stream.stream_attrs.get("from") - .map(|s| s.to_owned()) - .unwrap_or_else(|| String::new()); + let jid = xmpp_stream.jid.clone(); let nonza = xml::Element::new( "starttls".to_owned(), Some(NS_XMPP_TLS.to_owned()), @@ -44,7 +43,7 @@ impl StartTlsClient { StartTlsClient { state: StartTlsClientState::SendStartTls(send), - domain, + jid, } } } @@ -77,10 +76,10 @@ impl Future for StartTlsClient { if stanza.name == "proceed" => { println!("* proceed *"); - let stream = xmpp_stream.into_inner(); + let stream = xmpp_stream.stream.into_inner(); let connect = TlsConnector::builder().unwrap() .build().unwrap() - .connect_async(&self.domain, stream); + .connect_async(&self.jid.domain, stream); let new_state = StartTlsClientState::StartingTls(connect); retry = true; (new_state, Ok(Async::NotReady)) @@ -98,7 +97,7 @@ impl Future for StartTlsClient { match connect.poll() { Ok(Async::Ready(tls_stream)) => { println!("Got a TLS stream!"); - let start = XMPPStream::from_stream(tls_stream, self.domain.clone()); + let start = XMPPStream::from_stream(tls_stream, self.jid.clone()); let new_state = StartTlsClientState::Start(start); retry = true; (new_state, Ok(Async::NotReady)) diff --git a/src/stream_start.rs b/src/stream_start.rs index 3e5bc8b2f9eb971932f6f2806a1e13763d5e2bd9..11b074e6c7cb119c186b6211ab4961cecf936a5e 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use futures::*; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; +use jid::Jid; use xmpp_codec::*; use xmpp_stream::*; @@ -12,6 +13,7 @@ const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; pub struct StreamStart { state: StreamStartState, + jid: Jid, } enum StreamStartState { @@ -22,8 +24,8 @@ enum StreamStartState { } impl StreamStart { - pub fn from_stream(stream: Framed, to: String) -> Self { - let attrs = [("to".to_owned(), to), + pub fn from_stream(stream: Framed, jid: Jid) -> Self { + let attrs = [("to".to_owned(), jid.domain.clone()), ("version".to_owned(), "1.0".to_owned()), ("xmlns".to_owned(), "jabber:client".to_owned()), ("xmlns:stream".to_owned(), NS_XMPP_STREAM.to_owned()), @@ -32,6 +34,7 @@ impl StreamStart { StreamStart { state: StreamStartState::SendStart(send), + jid, } } } @@ -75,7 +78,8 @@ impl Future for StreamStart { Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => if stanza.name == "features" && stanza.ns == Some(NS_XMPP_STREAM.to_owned()) { - (StreamStartState::Invalid, Ok(Async::Ready(XMPPStream::new(stream, stream_attrs, stanza)))) + let stream = XMPPStream::new(self.jid.clone(), stream, stream_attrs, stanza); + (StreamStartState::Invalid, Ok(Async::Ready(stream))) } else { (StreamStartState::RecvFeatures(stream, stream_attrs), Ok(Async::NotReady)) }, diff --git a/src/tcp.rs b/src/tcp.rs index 57fb0b1fe28d34368d56c268b0814e26a0af3796..4510a39e2917e6ffc2c53aabc885f88025b058e8 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -3,12 +3,14 @@ use std::io::Error; use futures::{Future, Poll, Async}; use tokio_core::reactor::Handle; use tokio_core::net::{TcpStream, TcpStreamNew}; +use jid::Jid; use xmpp_stream::*; use stream_start::StreamStart; pub struct TcpClient { state: TcpClientState, + jid: Jid, } enum TcpClientState { @@ -18,10 +20,11 @@ enum TcpClientState { } impl TcpClient { - pub fn connect(addr: &SocketAddr, handle: &Handle) -> Self { + pub fn connect(jid: Jid, addr: &SocketAddr, handle: &Handle) -> Self { let tcp_stream_new = TcpStream::connect(addr, handle); TcpClient { state: TcpClientState::Connecting(tcp_stream_new), + jid, } } } @@ -34,7 +37,7 @@ impl Future for TcpClient { let (new_state, result) = match self.state { TcpClientState::Connecting(ref mut tcp_stream_new) => { let tcp_stream = try_ready!(tcp_stream_new.poll()); - let start = XMPPStream::from_stream(tcp_stream, "spaceboyz.net".to_owned()); + let start = XMPPStream::from_stream(tcp_stream, self.jid.clone()); let new_state = TcpClientState::Start(start); (new_state, Ok(Async::NotReady)) }, diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index a8793db1f59f045dfb201643519c1c28bf5c347d..2b8f0981d202e65c9c2dcb828f4def30797ab95d 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -5,6 +5,7 @@ use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; use xml; use sasl::common::Credentials; +use jid::Jid; use xmpp_codec::*; use stream_start::*; @@ -14,15 +15,23 @@ use client_auth::ClientAuth; pub const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; pub struct XMPPStream { + pub jid: Jid, pub stream: Framed, pub stream_attrs: HashMap, pub stream_features: xml::Element, } impl XMPPStream { - pub fn from_stream(stream: S, to: String) -> StreamStart { + pub fn new(jid: Jid, + stream: Framed, + stream_attrs: HashMap, + stream_features: xml::Element) -> Self { + XMPPStream { jid, stream, stream_attrs, stream_features } + } + + pub fn from_stream(stream: S, jid: Jid) -> StreamStart { let xmpp_stream = AsyncRead::framed(stream, XMPPCodec::new()); - StreamStart::from_stream(xmpp_stream, to) + StreamStart::from_stream(xmpp_stream, jid) } pub fn into_inner(self) -> S { @@ -30,10 +39,7 @@ impl XMPPStream { } pub fn restart(self) -> StreamStart { - let to = self.stream_attrs.get("from") - .map(|s| s.to_owned()) - .unwrap_or_else(|| "".to_owned()); - Self::from_stream(self.into_inner(), to.clone()) + Self::from_stream(self.stream.into_inner(), self.jid) } pub fn can_starttls(&self) -> bool { @@ -46,7 +52,7 @@ impl XMPPStream { StartTlsClient::from_stream(self) } - pub fn auth(self, username: &str, password: &str) -> Result, String> { + pub fn auth(self, username: String, password: String) -> Result, String> { let creds = Credentials::default() .with_username(username) .with_password(password); From 393402032c371f15333b095ea7526e20d3a6efc7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 00:59:37 +0100 Subject: [PATCH 0329/1020] jingle: Add a comment. --- src/jingle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index d189684e8efa31d6b4ac89083ad3df72acc8c94a..84600f43c7fd0dcd6bbc0ce5d83d136a3fba329d 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -46,7 +46,7 @@ generate_attribute!(Senders, "senders", { #[derive(Debug, Clone)] pub struct Content { pub creator: Creator, - pub disposition: String, + pub disposition: String, // TODO: the list of values is defined, use an enum! pub name: String, pub senders: Senders, pub description: Option, From 216f6f838987f27d06ec5a687da89b7d394fb1e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 01:57:02 +0100 Subject: [PATCH 0330/1020] Fix build and compilation. --- src/data_forms.rs | 4 ++-- src/ibb.rs | 2 +- src/jingle_ibb.rs | 2 +- src/message.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 9905f4b400ba4e0d6f33f36afdaee5a90cd07d76..b5cd004b6e691e07c5670576ebbca3b420e5e736 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -86,7 +86,7 @@ impl IntoElements for Field { } } -generate_attribute!(Subscription, "subscription", { +generate_attribute!(DataFormType, "type", { Cancel => "cancel", Form => "form", Result_ => "result", @@ -272,7 +272,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown data form type."); + assert_eq!(message, "Unknown value for 'type' attribute."); } #[test] diff --git a/src/ibb.rs b/src/ibb.rs index 5463caae809975377e084151a4ac661a63f1a8ba..ad9873778afcfc4cb362fecb38fdd59ab0e9991d 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -181,6 +181,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Invalid 'stanza' attribute."); + assert_eq!(message, "Unknown value for 'stanza' attribute."); } } diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index afbf11c3308167ea02b4f0d8e589517434654894..25dbaa377ce1eb3966725a053479cbb3cc568eac 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -107,6 +107,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Invalid 'stanza' attribute."); + assert_eq!(message, "Unknown value for 'stanza' attribute."); } } diff --git a/src/message.rs b/src/message.rs index 79038c1249b92736fee62a21123a732eb2b98a36..8025a2a12ffca4d3085f7f01ea22bb21ffc7dad0 100644 --- a/src/message.rs +++ b/src/message.rs @@ -236,7 +236,7 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let message = Message { from: None, to: None, From 48d340120c53a99f1d833491571a5d896b95bc10 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 02:28:42 +0100 Subject: [PATCH 0331/1020] jingle: Specialise Sid to get an increased type safety. --- src/jingle.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index 84600f43c7fd0dcd6bbc0ce5d83d136a3fba329d..ca9858ae85018c13bb4c3aafaa40719e5e464bfa 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -245,12 +245,29 @@ impl IntoElements for ReasonElement { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Sid(String); + +impl FromStr for Sid { + type Err = Error; + fn from_str(s: &str) -> Result { + // TODO: implement the NMTOKEN restrictions: https://www.w3.org/TR/2000/WD-xml-2e-20000814#NT-Nmtoken + Ok(Sid(String::from(s))) + } +} + +impl IntoAttributeValue for Sid { + fn into_attribute_value(self) -> Option { + return Some(self.0); + } +} + #[derive(Debug, Clone)] pub struct Jingle { pub action: Action, pub initiator: Option, pub responder: Option, - pub sid: String, + pub sid: Sid, pub contents: Vec, pub reason: Option, pub other: Vec, From e7f0c45da56ff2931bded25ef3ec8bbf427823bf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 14 Jun 2017 09:19:06 +0100 Subject: [PATCH 0332/1020] jingle: Fix sid test. --- src/jingle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle.rs b/src/jingle.rs index ca9858ae85018c13bb4c3aafaa40719e5e464bfa..fad59aa3df5ffb38a960ca0ff66e3bc9ba065e43 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -333,7 +333,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.action, Action::SessionInitiate); - assert_eq!(jingle.sid, "coucou"); + assert_eq!(jingle.sid, Sid(String::from("coucou"))); } #[test] From 6f69f2d7d9be3a65f8c4e8f6e8faa7fbc23381a8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 16 Jun 2017 20:37:48 +0100 Subject: [PATCH 0333/1020] jingle_ft: Parse into a proper DateTime. --- src/jingle_ft.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 1d37b6a9dda3576e0f93ca3cfe4a75bf56baadac..a70b012b200b15dbec53db1bc0e44c2ca2d650a7 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -9,6 +9,7 @@ use std::convert::TryFrom; use hashes::Hash; use minidom::{Element, IntoElements, ElementEmitter}; +use chrono::{DateTime, FixedOffset}; use error::Error; use ns; @@ -36,7 +37,7 @@ impl IntoElements for Range { #[derive(Debug, Clone)] pub struct File { - pub date: Option, + pub date: Option>, pub media_type: Option, pub name: Option, pub desc: Option, @@ -110,7 +111,7 @@ impl TryFrom for Description { if date.is_some() { return Err(Error::ParseError("File must not have more than one date.")); } - date = Some(file_payload.text()); + date = Some(file_payload.text().parse()?); } else if file_payload.is("media-type", ns::JINGLE_FT) { if media_type.is_some() { return Err(Error::ParseError("File must not have more than one media-type.")); @@ -179,7 +180,7 @@ impl Into for File { if let Some(date) = self.date { root.append_child(Element::builder("date") .ns(ns::JINGLE_FT) - .append(date) + .append(date.to_rfc3339()) .build()); } if let Some(media_type) = self.media_type { @@ -242,7 +243,7 @@ mod tests { text/plain test.txt - 2015-07-26T21:46:00 + 2015-07-26T21:46:00+01:00 6144 w0mcJylzCn+AfvuGdqkty2+KP48= @@ -254,7 +255,7 @@ mod tests { assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.desc, None); - assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); + assert_eq!(desc.file.date, Some(DateTime::parse_from_rfc3339("2015-07-26T21:46:00+01:00").unwrap())); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); From 17798190cf6fd4fd57273a91f906d36666f6ee65 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 17 Jun 2017 03:36:12 +0100 Subject: [PATCH 0334/1020] error: Implement fmt::Display and error::Error. --- src/error.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/error.rs b/src/error.rs index 899e25f088612459d4bb276277e239e3b772622e..f8d7372b8710384c93b9a56162f693e600f34b01 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,6 +8,8 @@ use std::convert::From; use std::io; use std::num; use std::string; +use std::fmt; +use std::error; use base64; use minidom; @@ -26,6 +28,36 @@ pub enum Error { ChronoParseError(chrono::ParseError), } +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::ParseError(s) => write!(fmt, "{}", s), + Error::IoError(ref e) => write!(fmt, "{}", e), + Error::XMLError(ref e) => write!(fmt, "{}", e), + Error::Base64Error(ref e) => write!(fmt, "{}", e), + Error::ParseIntError(ref e) => write!(fmt, "{}", e), + Error::ParseStringError(ref e) => write!(fmt, "{}", e), + Error::JidParseError(_) => write!(fmt, "JID parse error"), + Error::ChronoParseError(ref e) => write!(fmt, "{}", e), + } + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::ParseError(s) => s, + Error::IoError(ref e) => e.description(), + Error::XMLError(ref e) => e.description(), + Error::Base64Error(ref e) => e.description(), + Error::ParseIntError(ref e) => e.description(), + Error::ParseStringError(ref e) => e.description(), + Error::JidParseError(_) => "JID parse error", + Error::ChronoParseError(ref e) => e.description(), + } + } +} + impl From for Error { fn from(err: io::Error) -> Error { Error::IoError(err) From 973a5ca659848118f6aae9148a77b1a58e303492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 17 Jun 2017 01:23:38 +0100 Subject: [PATCH 0335/1020] Force channel binding to None --- src/tcp.rs | 10 +++++----- src/xmpp_stream.rs | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/tcp.rs b/src/tcp.rs index 4510a39e2917e6ffc2c53aabc885f88025b058e8..2f207ec513a00998dea4e03745fb28a6c7172a5e 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -51,10 +51,10 @@ impl Future for TcpClient { }; self.state = new_state; - match result { - // by polling again, we register new future - Ok(Async::NotReady) => self.poll(), - result => result - } + match result { + // by polling again, we register new future + Ok(Async::NotReady) => self.poll(), + result => result + } } } diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index 2b8f0981d202e65c9c2dcb828f4def30797ab95d..c84777f024cee98eb2e049b68b44a2216d0a0d68 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -4,7 +4,7 @@ use futures::*; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; use xml; -use sasl::common::Credentials; +use sasl::common::{Credentials, ChannelBinding}; use jid::Jid; use xmpp_codec::*; @@ -55,7 +55,8 @@ impl XMPPStream { pub fn auth(self, username: String, password: String) -> Result, String> { let creds = Credentials::default() .with_username(username) - .with_password(password); + .with_password(password) + .with_channel_binding(ChannelBinding::None); ClientAuth::new(self, creds) } } From 014633d119e523bae098da7ca688d27da1b8b905 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 19 Jun 2017 02:16:47 +0200 Subject: [PATCH 0336/1020] add client_bind --- examples/echo_bot.rs | 3 + src/client_bind.rs | 138 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/xmpp_stream.rs | 5 ++ 4 files changed, 148 insertions(+) create mode 100644 src/client_bind.rs diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 4f5f044730e978bb5251bae94156eab20abc1db6..df33cf0a5985262447bc428f4095fef397e4a2d4 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -35,6 +35,9 @@ fn main() { let username = jid.node.as_ref().unwrap().to_owned(); stream.auth(username, password).expect("auth") }).and_then(|stream| { + stream.bind() + }).and_then(|stream| { + println!("Bound to {}", stream.jid); stream.for_each(|event| { match event { Packet::Stanza(el) => println!("<< {}", el), diff --git a/src/client_bind.rs b/src/client_bind.rs new file mode 100644 index 0000000000000000000000000000000000000000..24238f63bc50b2d5f1a6452fbadb0f0d76e1e0f3 --- /dev/null +++ b/src/client_bind.rs @@ -0,0 +1,138 @@ +use std::mem::replace; +use std::error::Error; +use std::str::FromStr; +use futures::*; +use futures::sink; +use tokio_io::{AsyncRead, AsyncWrite}; +use xml; +use jid::Jid; + +use xmpp_codec::*; +use xmpp_stream::*; + +const NS_XMPP_BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind"; +const BIND_REQ_ID: &str = "resource-bind"; + +pub enum ClientBind { + Unsupported(XMPPStream), + WaitSend(sink::Send>), + WaitRecv(XMPPStream), + Invalid, +} + +impl ClientBind { + /// Consumes and returns the stream to express that you cannot use + /// the stream for anything else until the resource binding + /// req/resp are done. + pub fn new(stream: XMPPStream) -> Self { + match stream.stream_features.get_child("bind", Some(NS_XMPP_BIND)) { + None => + // No resource binding available, + // return the (probably // usable) stream immediately + ClientBind::Unsupported(stream), + Some(_) => { + println!("Bind is supported!"); + + let iq = make_bind_request(stream.jid.resource.as_ref()); + println!("Send {}", iq); + let send = stream.send(Packet::Stanza(iq)); + ClientBind::WaitSend(send) + }, + } + } +} + +fn make_bind_request(resource: Option<&String>) -> xml::Element { + let mut iq = xml::Element::new( + "iq".to_owned(), + None, + vec![("type".to_owned(), None, "set".to_owned()), + ("id".to_owned(), None, BIND_REQ_ID.to_owned())] + ); + { + let bind_el = iq.tag( + xml::Element::new( + "bind".to_owned(), + Some(NS_XMPP_BIND.to_owned()), + vec![] + )); + resource.map(|resource| { + let resource_el = bind_el.tag( + xml::Element::new( + "resource".to_owned(), + Some(NS_XMPP_BIND.to_owned()), + vec![] + )); + resource_el.text(resource.clone()); + }); + } + iq +} + +impl Future for ClientBind { + type Item = XMPPStream; + type Error = String; + + fn poll(&mut self) -> Poll { + let state = replace(self, ClientBind::Invalid); + + match state { + ClientBind::Unsupported(stream) => + Ok(Async::Ready(stream)), + ClientBind::WaitSend(mut send) => { + match send.poll() { + Ok(Async::Ready(stream)) => { + replace(self, ClientBind::WaitRecv(stream)); + self.poll() + }, + Ok(Async::NotReady) => { + replace(self, ClientBind::WaitSend(send)); + Ok(Async::NotReady) + }, + Err(e) => + Err(e.description().to_owned()), + } + }, + ClientBind::WaitRecv(mut stream) => { + match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(ref iq)))) + if iq.name == "iq" + && iq.get_attribute("id", None) == Some(BIND_REQ_ID) => { + match iq.get_attribute("type", None) { + Some("result") => { + get_bind_response_jid(&iq) + .map(|jid| stream.jid = jid); + Ok(Async::Ready(stream)) + }, + _ => + Err("resource bind response".to_owned()), + } + }, + Ok(Async::Ready(_)) => { + replace(self, ClientBind::WaitRecv(stream)); + self.poll() + }, + Ok(_) => { + replace(self, ClientBind::WaitRecv(stream)); + Ok(Async::NotReady) + }, + Err(e) => + Err(e.description().to_owned()), + } + }, + ClientBind::Invalid => + unreachable!(), + } + } +} + +fn get_bind_response_jid(iq: &xml::Element) -> Option { + iq.get_child("bind", Some(NS_XMPP_BIND)) + .and_then(|bind_el| + bind_el.get_child("jid", Some(NS_XMPP_BIND)) + ) + .and_then(|jid_el| + Jid::from_str(&jid_el.content_str()) + .ok() + ) +} diff --git a/src/lib.rs b/src/lib.rs index e04778ac5134c090a8f38a3fc42b79cee879cd44..12b710e171ca4de85274f96fb2f5d618bca82986 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,8 @@ mod starttls; pub use starttls::*; mod client_auth; pub use client_auth::*; +mod client_bind; +pub use client_bind::*; // type FullClient = sasl::Client> diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index c84777f024cee98eb2e049b68b44a2216d0a0d68..6eb2b24d7c7bd98d8d45727797771c3c425654e2 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -11,6 +11,7 @@ use xmpp_codec::*; use stream_start::*; use starttls::{NS_XMPP_TLS, StartTlsClient}; use client_auth::ClientAuth; +use client_bind::ClientBind; pub const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; @@ -59,6 +60,10 @@ impl XMPPStream { .with_channel_binding(ChannelBinding::None); ClientAuth::new(self, creds) } + + pub fn bind(self) -> ClientBind { + ClientBind::new(self) + } } /// Proxy to self.stream From 2e0dff5153e4d3f2edfb31572bbcdd123092b37f Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 19 Jun 2017 02:17:06 +0200 Subject: [PATCH 0337/1020] xmpp_codec: add Packet::Text --- src/xmpp_codec.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 8595dfca21e47aaff03557e1d9770e5f4f0b726a..b780a4800508371a233138467c34c5e8420e8c71 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -50,6 +50,7 @@ pub enum Packet { Error(Box), StreamStart(HashMap), Stanza(xml::Element), + Text(String), StreamEnd, } @@ -155,6 +156,8 @@ impl Encoder for XMPPCodec { }, Packet::Stanza(stanza) => write!(dst, "{}", stanza), + Packet::Text(text) => + write!(dst, "{}", xml::escape(&text)), // TODO: Implement all _ => Ok(()) } From 8b7a49d03a7e4f0f940af0de515e35146c5a36c9 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 19 Jun 2017 02:33:01 +0200 Subject: [PATCH 0338/1020] client_bind: tighten match this branch is specific to Async::NotReady --- src/client_bind.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client_bind.rs b/src/client_bind.rs index 24238f63bc50b2d5f1a6452fbadb0f0d76e1e0f3..ac85a9c9b10e31ba7257b284189110703da25486 100644 --- a/src/client_bind.rs +++ b/src/client_bind.rs @@ -112,7 +112,7 @@ impl Future for ClientBind { replace(self, ClientBind::WaitRecv(stream)); self.poll() }, - Ok(_) => { + Ok(Async::NotReady) => { replace(self, ClientBind::WaitRecv(stream)); Ok(Async::NotReady) }, From 000f848dd0f91dcd728b0992876e7ab461254302 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 19 Jun 2017 02:34:16 +0200 Subject: [PATCH 0339/1020] consolidate debug output --- src/client_auth.rs | 7 ++----- src/client_bind.rs | 3 --- src/starttls.rs | 5 +---- src/xmpp_codec.rs | 27 +++++++++++++++++---------- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/client_auth.rs b/src/client_auth.rs index 5ae57b76c9f9d2b2926292febad2c461b0a8c1a9..c6aa79d67975e09a4aedc2f76b9a3fe5a5ac3f06 100644 --- a/src/client_auth.rs +++ b/src/client_auth.rs @@ -36,7 +36,6 @@ impl ClientAuth { Box::new(Anonymous::new()), ]; - println!("stream_features: {}", stream.stream_features); let mech_names: Vec = match stream.stream_features.get_child("mechanisms", Some(NS_XMPP_SASL)) { None => @@ -46,12 +45,12 @@ impl ClientAuth { .map(|mech_el| mech_el.content_str()) .collect(), }; - println!("Offered mechanisms: {:?}", mech_names); + println!("SASL mechanisms offered: {:?}", mech_names); for mut mech in mechs { let name = mech.name().to_owned(); if mech_names.iter().any(|name1| *name1 == name) { - println!("Selected mechanism: {:?}", name); + println!("SASL mechanism selected: {:?}", name); let initial = try!(mech.initial()); let mut this = ClientAuth { state: ClientAuthState::Invalid, @@ -79,7 +78,6 @@ impl ClientAuth { ); nonza.text(content.to_base64(base64::URL_SAFE)); - println!("send {}", nonza); let send = stream.send(Packet::Stanza(nonza)); self.state = ClientAuthState::WaitSend(send); @@ -97,7 +95,6 @@ impl Future for ClientAuth { ClientAuthState::WaitSend(mut send) => match send.poll() { Ok(Async::Ready(stream)) => { - println!("send done"); self.state = ClientAuthState::WaitRecv(stream); self.poll() }, diff --git a/src/client_bind.rs b/src/client_bind.rs index ac85a9c9b10e31ba7257b284189110703da25486..45f68e6f6f5a8ccf16b8c99dd01dd581c2f3990e 100644 --- a/src/client_bind.rs +++ b/src/client_bind.rs @@ -31,10 +31,7 @@ impl ClientBind { // return the (probably // usable) stream immediately ClientBind::Unsupported(stream), Some(_) => { - println!("Bind is supported!"); - let iq = make_bind_request(stream.jid.resource.as_ref()); - println!("Send {}", iq); let send = stream.send(Packet::Stanza(iq)); ClientBind::WaitSend(send) }, diff --git a/src/starttls.rs b/src/starttls.rs index 2e7da7bc92bdbb8db945aaa105f4d7eedd1fb09d..97a65c511509f0765ce69b209f96f0bb5547dc2c 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -37,7 +37,6 @@ impl StartTlsClient { "starttls".to_owned(), Some(NS_XMPP_TLS.to_owned()), vec![] ); - println!("send {}", nonza); let packet = Packet::Stanza(nonza); let send = xmpp_stream.send(packet); @@ -60,7 +59,6 @@ impl Future for StartTlsClient { StartTlsClientState::SendStartTls(mut send) => match send.poll() { Ok(Async::Ready(xmpp_stream)) => { - println!("starttls sent"); let new_state = StartTlsClientState::AwaitProceed(xmpp_stream); retry = true; (new_state, Ok(Async::NotReady)) @@ -75,7 +73,6 @@ impl Future for StartTlsClient { Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) if stanza.name == "proceed" => { - println!("* proceed *"); let stream = xmpp_stream.stream.into_inner(); let connect = TlsConnector::builder().unwrap() .build().unwrap() @@ -96,7 +93,7 @@ impl Future for StartTlsClient { StartTlsClientState::StartingTls(mut connect) => match connect.poll() { Ok(Async::Ready(tls_stream)) => { - println!("Got a TLS stream!"); + println!("TLS stream established"); let start = XMPPStream::from_stream(tls_stream, self.jid.clone()); let new_state = StartTlsClientState::Start(start); retry = true; diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index b780a4800508371a233138467c34c5e8420e8c71..2551cc44fa13e8310ca066fe32e8397e668d465a 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -20,7 +20,6 @@ impl XMPPRoot { fn new(root: xml::StartTag) -> Self { let mut builder = xml::ElementBuilder::new(); let mut attributes = HashMap::new(); - println!("root attributes: {:?}", root.attributes); for (name_ns, value) in root.attributes { match name_ns { (ref name, None) if name == "xmlns" => @@ -73,10 +72,13 @@ impl Decoder for XMPPCodec { type Error = Error; fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { - println!("XMPPCodec.decode {:?}", buf.len()); match from_utf8(buf.take().as_ref()) { - Ok(s) => - self.parser.feed_str(s), + Ok(s) => { + if s.len() > 0 { + println!("<< {}", s); + self.parser.feed_str(s); + } + }, Err(e) => return Err(Error::new(ErrorKind::InvalidInput, e)), } @@ -110,7 +112,7 @@ impl Decoder for XMPPCodec { match root.handle_event(event) { None => (), Some(Ok(stanza)) => { - println!("stanza: {}", stanza); + // Emit the stanza result = Some(Packet::Stanza(stanza)); break }, @@ -151,13 +153,18 @@ impl Encoder for XMPPCodec { } write!(buf, ">\n").unwrap(); - println!("Encode start to {}", buf); + print!(">> {}", buf); write!(dst, "{}", buf) }, - Packet::Stanza(stanza) => - write!(dst, "{}", stanza), - Packet::Text(text) => - write!(dst, "{}", xml::escape(&text)), + Packet::Stanza(stanza) => { + println!(">> {}", stanza); + write!(dst, "{}", stanza) + }, + Packet::Text(text) => { + let escaped = xml::escape(&text); + println!(">> {}", escaped); + write!(dst, "{}", escaped) + }, // TODO: Implement all _ => Ok(()) } From 547902fe8f0172e9446944cdb4a9e1668d369449 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 19 Jun 2017 02:35:21 +0200 Subject: [PATCH 0340/1020] echo_bot: send presence --- examples/echo_bot.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index df33cf0a5985262447bc428f4095fef397e4a2d4..e9761c9338365fb8829e0277fe71b611b567cd77 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -2,13 +2,14 @@ extern crate futures; extern crate tokio_core; extern crate tokio_xmpp; extern crate jid; +extern crate xml; use std::str::FromStr; use tokio_core::reactor::Core; -use futures::{Future, Stream}; +use futures::{Future, Stream, Sink}; +use jid::Jid; use tokio_xmpp::TcpClient; use tokio_xmpp::xmpp_codec::Packet; -use jid::Jid; fn main() { let jid = Jid::from_str("astrobot@example.net").expect("JID"); @@ -38,6 +39,11 @@ fn main() { stream.bind() }).and_then(|stream| { println!("Bound to {}", stream.jid); + + let presence = xml::Element::new("presence".to_owned(), None, vec![]); + stream.send(Packet::Stanza(presence)) + .map_err(|e| format!("{}", e)) + }).and_then(|stream| { stream.for_each(|event| { match event { Packet::Stanza(el) => println!("<< {}", el), From 1e2672ba5022c3ebe60b56471014f2ffe41a27fc Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 20 Jun 2017 21:26:51 +0200 Subject: [PATCH 0341/1020] impl stream for client --- examples/echo_bot.rs | 124 ++++++++++++------ src/{client_auth.rs => client/auth.rs} | 0 src/{client_bind.rs => client/bind.rs} | 0 src/client/mod.rs | 167 +++++++++++++++++++++++++ src/lib.rs | 6 +- src/starttls.rs | 1 + src/xmpp_stream.rs | 26 ---- 7 files changed, 254 insertions(+), 70 deletions(-) rename src/{client_auth.rs => client/auth.rs} (100%) rename src/{client_bind.rs => client/bind.rs} (100%) create mode 100644 src/client/mod.rs diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index e9761c9338365fb8829e0277fe71b611b567cd77..9f39dfe4e513f45f33ee88432fd6ef939fb369bb 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -6,53 +6,85 @@ extern crate xml; use std::str::FromStr; use tokio_core::reactor::Core; -use futures::{Future, Stream, Sink}; -use jid::Jid; -use tokio_xmpp::TcpClient; +use futures::{Future, Stream, Sink, future}; +use tokio_xmpp::{Client, ClientEvent}; use tokio_xmpp::xmpp_codec::Packet; fn main() { - let jid = Jid::from_str("astrobot@example.net").expect("JID"); - let password = "".to_owned(); + let mut core = Core::new().unwrap(); + let client = Client::new("astrobot@example.org", "", &core.handle()).unwrap(); + // let client = TcpClient::connect( + // jid.clone(), + // &addr, + // &core.handle() + // ).map_err(|e| format!("{}", e) + // ).and_then(|stream| { + // if stream.can_starttls() { + // stream.starttls() + // } else { + // panic!("No STARTTLS") + // } + // }).and_then(|stream| { + // let username = jid.node.as_ref().unwrap().to_owned(); + // stream.auth(username, password).expect("auth") + // }).and_then(|stream| { + // stream.bind() + // }).and_then(|stream| { + // println!("Bound to {}", stream.jid); - use std::net::ToSocketAddrs; - let addr = "[2a01:4f8:a0:33d0::5]:5222" - .to_socket_addrs().unwrap() - .next().unwrap(); + // let presence = xml::Element::new("presence".to_owned(), None, vec![]); + // stream.send(Packet::Stanza(presence)) + // .map_err(|e| format!("{}", e)) + // }).and_then(|stream| { + // let main_loop = |stream| { + // stream.into_future() + // .and_then(|(event, stream)| { + // stream.send(Packet::Stanza(unreachable!())) + // }).and_then(main_loop) + // }; + // main_loop(stream) + // }).and_then(|(event, stream)| { + // let (mut sink, stream) = stream.split(); + // stream.for_each(move |event| { + // match event { + // Packet::Stanza(ref message) + // if message.name == "message" => { + // let ty = message.get_attribute("type", None); + // let body = message.get_child("body", Some("jabber:client")) + // .map(|body_el| body_el.content_str()); + // match ty { + // None | Some("normal") | Some("chat") + // if body.is_some() => { + // let from = message.get_attribute("from", None).unwrap(); + // println!("Got message from {}: {:?}", from, body); + // let reply = make_reply(from, body.unwrap()); + // sink.send(Packet::Stanza(reply)) + // .and_then(|_| Ok(())) + // }, + // _ => future::ok(()), + // } + // }, + // _ => future::ok(()), + // } + // }).map_err(|e| format!("{}", e)) + // }); - let mut core = Core::new().unwrap(); - let client = TcpClient::connect( - jid.clone(), - &addr, - &core.handle() - ).map_err(|e| format!("{}", e) - ).and_then(|stream| { - if stream.can_starttls() { - stream.starttls() - } else { - panic!("No STARTTLS") + let done = client.for_each(|event| { + match event { + ClientEvent::Online => { + println!("Online!"); + }, + ClientEvent::Stanza(stanza) => { + }, + _ => { + println!("Event: {:?}", event); + }, } - }).and_then(|stream| { - let username = jid.node.as_ref().unwrap().to_owned(); - stream.auth(username, password).expect("auth") - }).and_then(|stream| { - stream.bind() - }).and_then(|stream| { - println!("Bound to {}", stream.jid); - - let presence = xml::Element::new("presence".to_owned(), None, vec![]); - stream.send(Packet::Stanza(presence)) - .map_err(|e| format!("{}", e)) - }).and_then(|stream| { - stream.for_each(|event| { - match event { - Packet::Stanza(el) => println!("<< {}", el), - _ => println!("!! {:?}", event), - } - Ok(()) - }).map_err(|e| format!("{}", e)) + + Ok(()) }); - match core.run(client) { + + match core.run(done) { Ok(_) => (), Err(e) => { println!("Fatal: {}", e); @@ -60,3 +92,15 @@ fn main() { } } } + +fn make_reply(to: &str, body: String) -> xml::Element { + let mut message = xml::Element::new( + "message".to_owned(), + None, + vec![("type".to_owned(), None, "chat".to_owned()), + ("to".to_owned(), None, to.to_owned())] + ); + message.tag(xml::Element::new("body".to_owned(), None, vec![])) + .text(body); + message +} diff --git a/src/client_auth.rs b/src/client/auth.rs similarity index 100% rename from src/client_auth.rs rename to src/client/auth.rs diff --git a/src/client_bind.rs b/src/client/bind.rs similarity index 100% rename from src/client_bind.rs rename to src/client/bind.rs diff --git a/src/client/mod.rs b/src/client/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..624bf7249df1f664e6024df9797fb557cc9559d4 --- /dev/null +++ b/src/client/mod.rs @@ -0,0 +1,167 @@ +use std::mem::replace; +use std::str::FromStr; +use std::error::Error; +use tokio_core::reactor::{Core, Handle}; +use tokio_core::net::TcpStream; +use tokio_io::{AsyncRead, AsyncWrite}; +use tokio_tls::TlsStream; +use futures::*; +use jid::{Jid, JidParseError}; +use xml; +use sasl::common::{Credentials, ChannelBinding}; + +use super::xmpp_codec::Packet; +use super::xmpp_stream; +use super::tcp::TcpClient; +use super::starttls::{NS_XMPP_TLS, StartTlsClient}; + +mod auth; +use self::auth::*; +mod bind; +use self::bind::*; + +pub struct Client { + pub jid: Jid, + password: String, + state: ClientState, +} + +type XMPPStream = xmpp_stream::XMPPStream>; + +enum ClientState { + Invalid, + Disconnected, + Connecting(Box>), + Connected(XMPPStream), + // Sending, + // Drain, +} + +impl Client { + pub fn new(jid: &str, password: &str, handle: &Handle) -> Result { + let jid = try!(Jid::from_str(jid)); + let password = password.to_owned(); + let connect = Self::make_connect(jid.clone(), password.clone(), handle); + Ok(Client { + jid, password, + state: ClientState::Connecting(connect), + }) + } + + fn make_connect(jid: Jid, password: String, handle: &Handle) -> Box> { + use std::net::ToSocketAddrs; + let addr = "89.238.79.220:5222" + .to_socket_addrs().unwrap() + .next().unwrap(); + let username = jid.node.as_ref().unwrap().to_owned(); + let password = password; + Box::new( + TcpClient::connect( + jid, + &addr, + handle + ).map_err(|e| format!("{}", e) + ).and_then(|stream| { + if Self::can_starttls(&stream) { + Self::starttls(stream) + } else { + panic!("No STARTTLS") + } + }).and_then(move |stream| { + Self::auth(stream, username, password).expect("auth") + }).and_then(|stream| { + Self::bind(stream) + }).and_then(|stream| { + println!("Bound to {}", stream.jid); + + let presence = xml::Element::new("presence".to_owned(), None, vec![]); + stream.send(Packet::Stanza(presence)) + .map_err(|e| format!("{}", e)) + }) + ) + } + + fn can_starttls(stream: &xmpp_stream::XMPPStream) -> bool { + stream.stream_features + .get_child("starttls", Some(NS_XMPP_TLS)) + .is_some() + } + + fn starttls(stream: xmpp_stream::XMPPStream) -> StartTlsClient { + StartTlsClient::from_stream(stream) + } + + fn auth(stream: xmpp_stream::XMPPStream, username: String, password: String) -> Result, String> { + let creds = Credentials::default() + .with_username(username) + .with_password(password) + .with_channel_binding(ChannelBinding::None); + ClientAuth::new(stream, creds) + } + + fn bind(stream: xmpp_stream::XMPPStream) -> ClientBind { + ClientBind::new(stream) + } +} + +#[derive(Debug)] +pub enum ClientEvent { + Online, + Disconnected, + Stanza(xml::Element), +} + +impl Stream for Client { + type Item = ClientEvent; + type Error = String; + + fn poll(&mut self) -> Poll, Self::Error> { + println!("stream.poll"); + let state = replace(&mut self.state, ClientState::Invalid); + + match state { + ClientState::Invalid => + Err("invalid client state".to_owned()), + ClientState::Disconnected => + Ok(Async::NotReady), + ClientState::Connecting(mut connect) => { + match connect.poll() { + Ok(Async::Ready(stream)) => { + println!("connected"); + self.state = ClientState::Connected(stream); + self.poll() + }, + Ok(Async::NotReady) => { + self.state = ClientState::Connecting(connect); + Ok(Async::NotReady) + }, + Err(e) => + Err(e), + } + }, + ClientState::Connected(mut stream) => { + match stream.poll() { + Ok(Async::NotReady) => { + self.state = ClientState::Connected(stream); + Ok(Async::NotReady) + }, + Ok(Async::Ready(None)) => { + // EOF + self.state = ClientState::Disconnected; + Ok(Async::Ready(Some(ClientEvent::Disconnected))) + }, + Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { + self.state = ClientState::Connected(stream); + Ok(Async::Ready(Some(ClientEvent::Stanza(stanza)))) + }, + Ok(Async::Ready(_)) => { + self.state = ClientState::Connected(stream); + Ok(Async::NotReady) + }, + Err(e) => + Err(e.description().to_owned()), + } + }, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 12b710e171ca4de85274f96fb2f5d618bca82986..49cab6e9de522064cb9862a20678080b95aae8f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,10 +18,8 @@ mod tcp; pub use tcp::*; mod starttls; pub use starttls::*; -mod client_auth; -pub use client_auth::*; -mod client_bind; -pub use client_bind::*; +mod client; +pub use client::{Client, ClientEvent}; // type FullClient = sasl::Client> diff --git a/src/starttls.rs b/src/starttls.rs index 97a65c511509f0765ce69b209f96f0bb5547dc2c..79ee731c236e444cd50a541dfb3225ad9ab8dee3 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -15,6 +15,7 @@ use stream_start::StreamStart; pub const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; + pub struct StartTlsClient { state: StartTlsClientState, jid: Jid, diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index 6eb2b24d7c7bd98d8d45727797771c3c425654e2..01d4aceb5b3617b9bdb98c3cd56943b1fc8b7326 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -4,14 +4,10 @@ use futures::*; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; use xml; -use sasl::common::{Credentials, ChannelBinding}; use jid::Jid; use xmpp_codec::*; use stream_start::*; -use starttls::{NS_XMPP_TLS, StartTlsClient}; -use client_auth::ClientAuth; -use client_bind::ClientBind; pub const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; @@ -42,28 +38,6 @@ impl XMPPStream { pub fn restart(self) -> StreamStart { Self::from_stream(self.stream.into_inner(), self.jid) } - - pub fn can_starttls(&self) -> bool { - self.stream_features - .get_child("starttls", Some(NS_XMPP_TLS)) - .is_some() - } - - pub fn starttls(self) -> StartTlsClient { - StartTlsClient::from_stream(self) - } - - pub fn auth(self, username: String, password: String) -> Result, String> { - let creds = Credentials::default() - .with_username(username) - .with_password(password) - .with_channel_binding(ChannelBinding::None); - ClientAuth::new(self, creds) - } - - pub fn bind(self) -> ClientBind { - ClientBind::new(self) - } } /// Proxy to self.stream From eca588ad6f89079a3af05b99b41b193188f03d0a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 22 Jun 2017 01:41:10 +0100 Subject: [PATCH 0342/1020] Cargo.toml: Update the chrono dependency to 0.4.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 68d32e5d16e530ced1da8127c69fd5f10304c230..224ace7dca438fdbb43ce747cac262917e9795c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,4 @@ sha-1 = "0.3.0" sha2 = "0.5.0" sha3 = "0.5.0" blake2 = "0.5.0" -chrono = "0.3.1" +chrono = "0.4.0" From 13e85078487476a6732954248a94a69cda8fb29a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 19:55:55 +0100 Subject: [PATCH 0343/1020] chatstates: Improve parsing. --- src/chatstates.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index 54f0aefe7e93d343be9c46e8df6fc2d83e4f7f2e..47df01e0cd63330b65a1517c0c40fa3e40f97b33 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -25,25 +25,23 @@ impl TryFrom for ChatState { type Error = Error; fn try_from(elem: Element) -> Result { + if elem.ns() != Some(ns::CHATSTATES) { + return Err(Error::ParseError("This is not a chatstate element.")); + } for _ in elem.children() { return Err(Error::ParseError("Unknown child in chatstate element.")); } for _ in elem.attrs() { return Err(Error::ParseError("Unknown attribute in chatstate element.")); } - if elem.is("active", ns::CHATSTATES) { - Ok(ChatState::Active) - } else if elem.is("composing", ns::CHATSTATES) { - Ok(ChatState::Composing) - } else if elem.is("gone", ns::CHATSTATES) { - Ok(ChatState::Gone) - } else if elem.is("inactive", ns::CHATSTATES) { - Ok(ChatState::Inactive) - } else if elem.is("paused", ns::CHATSTATES) { - Ok(ChatState::Paused) - } else { - Err(Error::ParseError("This is not a chatstate element.")) - } + Ok(match elem.name() { + "active" => ChatState::Active, + "composing" => ChatState::Composing, + "gone" => ChatState::Gone, + "inactive" => ChatState::Inactive, + "paused" => ChatState::Paused, + _ => return Err(Error::ParseError("This is not a chatstate element.")), + }) } } From a48565e171ccb60aa6a6bac880b0096f1dd1349e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 20:13:43 +0100 Subject: [PATCH 0344/1020] data_forms: Fix FORM_TYPE, and <instructions/> serialisation. --- src/data_forms.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index b5cd004b6e691e07c5670576ebbca3b420e5e736..7e187ec194b60a1269e1fa78d5d72c467386d1cb 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -229,9 +229,8 @@ impl From<DataForm> for Element { Element::builder("x") .ns(ns::DATA_FORMS) .attr("type", form.type_) - .append(form.form_type) - .append(form.title) - .append(form.instructions) + .append(if form.title.is_some() { Some(Element::builder("title").ns(ns::DATA_FORMS).append(form.title)) } else { None }) + .append(if form.instructions.is_some() { Some(Element::builder("instructions").ns(ns::DATA_FORMS).append(form.instructions)) } else { None }) .append(form.fields) .build() } From f428ee070d17bff0778f09fee7110efbac1ddd6d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> Date: Sun, 25 Jun 2017 21:03:48 +0100 Subject: [PATCH 0345/1020] Add an IBR parser. --- src/ibr.rs | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 3 + 3 files changed, 207 insertions(+) create mode 100644 src/ibr.rs diff --git a/src/ibr.rs b/src/ibr.rs new file mode 100644 index 0000000000000000000000000000000000000000..183d371638f203b948f522411a690a85192005e1 --- /dev/null +++ b/src/ibr.rs @@ -0,0 +1,201 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::collections::HashMap; +use std::convert::TryFrom; + +use minidom::{Element, IntoElements, ElementEmitter}; + +use error::Error; + +use data_forms::DataForm; + +use ns; + +#[derive(Debug, Clone)] +pub struct Query { + pub fields: HashMap<String, String>, + pub registered: bool, + pub remove: bool, + pub form: Option<DataForm>, + // Not yet implemented. + //pub oob: Option<Oob>, +} + +impl TryFrom<Element> for Query { + type Error = Error; + + fn try_from(elem: Element) -> Result<Query, Error> { + if !elem.is("query", ns::REGISTER) { + return Err(Error::ParseError("This is not an ibr element.")); + } + let mut query = Query { + registered: false, + fields: HashMap::new(), + remove: false, + form: None, + }; + for child in elem.children() { + let namespace = child.ns().unwrap(); + if namespace == ns::REGISTER { + let name = child.name(); + let fields = vec!["address", "city", "date", "email", "first", "instructions", + "key", "last", "misc", "name", "nick", "password", "phone", + "state", "text", "url", "username", "zip"]; + if fields.binary_search(&name).is_ok() { + query.fields.insert(name.to_owned(), child.text()); + } else if name == "registered" { + query.registered = true; + } else if name == "remove" { + query.remove = true; + } else { + return Err(Error::ParseError("Wrong field in ibr element.")); + } + } else if child.is("x", ns::DATA_FORMS) { + query.form = Some(DataForm::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown child in ibr element.")); + } + } + Ok(query) + } +} + +impl Into<Element> for Query { + fn into(self) -> Element { + Element::builder("query") + .ns(ns::REGISTER) + .append(if self.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None }) + .append(self.fields.iter().map(|(name, value)| { + Element::builder(name.clone()).ns(ns::REGISTER).append(value.clone()) + }).collect::<Vec<_>>()) + .append(if self.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None }) + .append(self.form) + .build() + } +} + +impl IntoElements for Query { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "<query xmlns='jabber:iq:register'/>".parse().unwrap(); + Query::try_from(elem).unwrap(); + } + + #[test] + fn test_ex2() { + let elem: Element = r#" +<query xmlns='jabber:iq:register'> + <instructions> + Choose a username and password for use with this service. + Please also provide your email address. + </instructions> + <username/> + <password/> + <email/> +</query> +"#.parse().unwrap(); + let query = Query::try_from(elem).unwrap(); + assert_eq!(query.registered, false); + assert_eq!(query.fields["instructions"], "\n Choose a username and password for use with this service.\n Please also provide your email address.\n "); + assert_eq!(query.fields["username"], ""); + assert_eq!(query.fields["password"], ""); + assert_eq!(query.fields["email"], ""); + assert_eq!(query.fields.contains_key("name"), false); + + // FIXME: HashMap doesn’t keep the order right. + //let elem2 = query.into(); + //assert_eq!(elem, elem2); + } + + #[test] + fn test_ex9() { + let elem: Element = r#" +<query xmlns='jabber:iq:register'> + <instructions> + Use the enclosed form to register. If your Jabber client does not + support Data Forms, visit http://www.shakespeare.lit/contests.php + </instructions> + <x xmlns='jabber:x:data' type='form'> + <title>Contest Registration + + Please provide the following information + to sign up for our special contests! + + + jabber:iq:register + + + + + + + + + + + + + + + + +"#.parse().unwrap(); + let elem1 = elem.clone(); + let query = Query::try_from(elem).unwrap(); + assert_eq!(query.registered, false); + assert!(!query.fields["instructions"].is_empty()); + let form = query.form.clone().unwrap(); + assert!(!form.instructions.unwrap().is_empty()); + assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("first"))).is_ok()); + assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok()); + assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err()); + let elem2 = query.into(); + assert_eq!(elem1, elem2); + } + + #[test] + fn test_ex10() { + let elem: Element = r#" + + + + jabber:iq:register + + + Juliet + + + Capulet + + + juliet@capulet.com + + + F + + + +"#.parse().unwrap(); + let elem1 = elem.clone(); + let query = Query::try_from(elem).unwrap(); + assert_eq!(query.registered, false); + for _ in &query.fields { + panic!(); + } + let elem2 = query.into(); + assert_eq!(elem1, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index 4fc420b82108ff51a0d759f799b8b6bf4275c183..00b2592ce44f13d0e17cbdad0f299ab740e158b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,6 +144,9 @@ pub mod rsm; /// XEP-0060: Publish-Subscribe pub mod pubsub; +/// XEP-0077: In-Band Registration +pub mod ibr; + /// XEP-0085: Chat State Notifications pub mod chatstates; diff --git a/src/ns.rs b/src/ns.rs index af76dd9050c2e2c0a22fd9704ed6aab4cd2b52e5..fbc0048f952bb04d7691628a65692b818e845468 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -39,6 +39,9 @@ pub const PUBSUB_EVENT: &str = "http://jabber.org/protocol/pubsub#event"; /// XEP-0060: Publish-Subscribe pub const PUBSUB_OWNER: &str = "http://jabber.org/protocol/pubsub#owner"; +/// XEP-0077: In-Band Registration +pub const REGISTER: &str = "jabber:iq:register"; + /// XEP-0085: Chat State Notifications pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; From 3ea0c45337111b9664fe80ae4de760e157e9b3ac Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 21:38:58 +0100 Subject: [PATCH 0346/1020] jingle_s5b: Strengthen type safety for ids. --- src/jingle_s5b.rs | 54 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 089d16056d4530195a344c992fdc4a6b39804362..b585cf19149b28fc340338f7ca6d47680c218767 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -20,9 +20,46 @@ generate_attribute!(Type, "type", { Tunnel => "tunnel", }, Default = Direct); +generate_attribute!(Mode, "mode", { + Tcp => "tcp", + Udp => "udp", +}, Default = Tcp); + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CandidateId(String); + +impl FromStr for CandidateId { + type Err = Error; + fn from_str(s: &str) -> Result { + Ok(CandidateId(String::from(s))) + } +} + +impl IntoAttributeValue for CandidateId { + fn into_attribute_value(self) -> Option { + return Some(self.0); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StreamId(String); + +impl FromStr for StreamId { + type Err = Error; + fn from_str(s: &str) -> Result { + Ok(StreamId(String::from(s))) + } +} + +impl IntoAttributeValue for StreamId { + fn into_attribute_value(self) -> Option { + return Some(self.0); + } +} + #[derive(Debug, Clone)] pub struct Candidate { - pub cid: String, + pub cid: CandidateId, pub host: String, pub jid: String, pub port: Option, @@ -44,11 +81,6 @@ impl Into for Candidate { } } -generate_attribute!(Mode, "mode", { - Tcp => "tcp", - Udp => "udp", -}, Default = Tcp); - #[derive(Debug, Clone)] pub enum TransportPayload { Activated(String), @@ -61,7 +93,7 @@ pub enum TransportPayload { #[derive(Debug, Clone)] pub struct Transport { - pub sid: String, + pub sid: StreamId, pub dstaddr: Option, pub mode: Mode, pub payload: TransportPayload, @@ -181,7 +213,7 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let transport = Transport::try_from(elem).unwrap(); - assert_eq!(transport.sid, "coucou"); + assert_eq!(transport.sid, StreamId(String::from("coucou"))); assert_eq!(transport.dstaddr, None); assert_eq!(transport.mode, Mode::Tcp); match transport.payload { @@ -194,7 +226,7 @@ mod tests { fn test_serialise_activated() { let elem: Element = "".parse().unwrap(); let transport = Transport { - sid: String::from("coucou"), + sid: StreamId(String::from("coucou")), dstaddr: None, mode: Mode::Tcp, payload: TransportPayload::Activated(String::from("coucou")), @@ -207,11 +239,11 @@ mod tests { fn test_serialise_candidate() { let elem: Element = "".parse().unwrap(); let transport = Transport { - sid: String::from("coucou"), + sid: StreamId(String::from("coucou")), dstaddr: None, mode: Mode::Tcp, payload: TransportPayload::Candidates(vec!(Candidate { - cid: String::from("coucou"), + cid: CandidateId(String::from("coucou")), host: String::from("coucou"), jid: String::from("coucou@coucou"), port: None, From 95a19b4bb4ec648d58c82a9b17f68ee50409a0cb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 22:14:21 +0100 Subject: [PATCH 0347/1020] lib: Implement a generate_id! macro. --- src/lib.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 00b2592ce44f13d0e17cbdad0f299ab740e158b2..cb8b9d4a42755afdb996f960be57b3697e27dbfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,6 +109,25 @@ macro_rules! generate_attribute { ); } +macro_rules! generate_id { + ($elem:ident) => ( + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $elem(String); + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + // TODO: add a way to parse that differently when needed. + Ok($elem(String::from(s))) + } + } + impl IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + Some(self.0) + } + } + ); +} + /// Error type returned by every parser on failure. pub mod error; /// XML namespace definitions used through XMPP. From a219501fed1c4aba544f1364eab866341802203e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 22:14:51 +0100 Subject: [PATCH 0348/1020] jingle, jingle_ft: Use the new generate_id! macro to simplify Sid/Cid generation. --- src/jingle.rs | 21 +++------------------ src/jingle_s5b.rs | 32 ++------------------------------ 2 files changed, 5 insertions(+), 48 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index fad59aa3df5ffb38a960ca0ff66e3bc9ba065e43..224bb54332349cd299f4d057233434480576a84f 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -245,29 +245,14 @@ impl IntoElements for ReasonElement { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Sid(String); - -impl FromStr for Sid { - type Err = Error; - fn from_str(s: &str) -> Result { - // TODO: implement the NMTOKEN restrictions: https://www.w3.org/TR/2000/WD-xml-2e-20000814#NT-Nmtoken - Ok(Sid(String::from(s))) - } -} - -impl IntoAttributeValue for Sid { - fn into_attribute_value(self) -> Option { - return Some(self.0); - } -} +generate_id!(SessionId); #[derive(Debug, Clone)] pub struct Jingle { pub action: Action, pub initiator: Option, pub responder: Option, - pub sid: Sid, + pub sid: SessionId, pub contents: Vec, pub reason: Option, pub other: Vec, @@ -333,7 +318,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.action, Action::SessionInitiate); - assert_eq!(jingle.sid, Sid(String::from("coucou"))); + assert_eq!(jingle.sid, SessionId(String::from("coucou"))); } #[test] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index b585cf19149b28fc340338f7ca6d47680c218767..cc916a3ba99c71e3722440efd178f760e387a89e 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -25,37 +25,9 @@ generate_attribute!(Mode, "mode", { Udp => "udp", }, Default = Tcp); -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct CandidateId(String); +generate_id!(CandidateId); -impl FromStr for CandidateId { - type Err = Error; - fn from_str(s: &str) -> Result { - Ok(CandidateId(String::from(s))) - } -} - -impl IntoAttributeValue for CandidateId { - fn into_attribute_value(self) -> Option { - return Some(self.0); - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StreamId(String); - -impl FromStr for StreamId { - type Err = Error; - fn from_str(s: &str) -> Result { - Ok(StreamId(String::from(s))) - } -} - -impl IntoAttributeValue for StreamId { - fn into_attribute_value(self) -> Option { - return Some(self.0); - } -} +generate_id!(StreamId); #[derive(Debug, Clone)] pub struct Candidate { From f6b222f49ae482ff0ef0a4367d49cec463d50969 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 22:15:18 +0100 Subject: [PATCH 0349/1020] =?UTF-8?q?jingle=5Fibb:=20Strengthen=20sid?= =?UTF-8?q?=E2=80=99s=20type=20safety.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jingle_ibb.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 25dbaa377ce1eb3966725a053479cbb3cc568eac..72ebba550ea5ae6e9ac6d02faadd8a7d210624fa 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -5,8 +5,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::convert::TryFrom; +use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use error::Error; @@ -14,10 +15,12 @@ use ns; use ibb::Stanza; +generate_id!(StreamId); + #[derive(Debug, Clone)] pub struct Transport { pub block_size: u16, - pub sid: String, + pub sid: StreamId, pub stanza: Stanza, } @@ -60,7 +63,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.block_size, 3); - assert_eq!(transport.sid, "coucou"); + assert_eq!(transport.sid, StreamId(String::from("coucou"))); assert_eq!(transport.stanza, Stanza::Iq); } From 4338d9d6384135f68d7201b9733175cf9d518c1d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 22:20:24 +0100 Subject: [PATCH 0350/1020] jingle_s5b: Make jid a Jid and not a String. --- src/jingle_s5b.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index cc916a3ba99c71e3722440efd178f760e387a89e..57807be313ec817c1fbd9d7c8f44791c568d2e7f 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -8,6 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; +use jid::Jid; use error::Error; @@ -33,7 +34,7 @@ generate_id!(StreamId); pub struct Candidate { pub cid: CandidateId, pub host: String, - pub jid: String, + pub jid: Jid, pub port: Option, pub priority: u32, pub type_: Type, @@ -45,7 +46,7 @@ impl Into for Candidate { .ns(ns::JINGLE_S5B) .attr("cid", self.cid) .attr("host", self.host) - .attr("jid", self.jid) + .attr("jid", String::from(self.jid)) .attr("port", self.port) .attr("priority", self.priority) .attr("type", self.type_) @@ -217,7 +218,7 @@ mod tests { payload: TransportPayload::Candidates(vec!(Candidate { cid: CandidateId(String::from("coucou")), host: String::from("coucou"), - jid: String::from("coucou@coucou"), + jid: Jid::from_str("coucou@coucou").unwrap(), port: None, priority: 0u32, type_: Type::Direct, From 96fe2d200eed4dc75264775e6f626062dd198b96 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Jun 2017 23:27:38 +0100 Subject: [PATCH 0351/1020] roster: Fix group serialisation and add a test. --- src/roster.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/roster.rs b/src/roster.rs index f38817d0794290d833ed4dcd64cee1cd75d286d1..db939ad8135ae537040af90125947eacdca36e4e 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -65,7 +65,7 @@ impl Into for Item { .attr("jid", String::from(self.jid)) .attr("name", self.name) .attr("subscription", self.subscription) - .append(self.groups) + .append(self.groups.iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) .build() } } @@ -172,6 +172,29 @@ mod tests { assert_eq!(roster.items[0].groups, vec!(String::from("Friends"))); } + #[test] + fn test_multiple_groups() { + let elem: Element = r#" + + + A + B + + +"#.parse().unwrap(); + let elem1 = elem.clone(); + let roster = Roster::try_from(elem).unwrap(); + assert!(roster.ver.is_none()); + assert_eq!(roster.items.len(), 1); + assert_eq!(roster.items[0].jid, Jid::from_str("test@example.org").unwrap()); + assert_eq!(roster.items[0].name, None); + assert_eq!(roster.items[0].groups.len(), 2); + assert_eq!(roster.items[0].groups[0], String::from("A")); + assert_eq!(roster.items[0].groups[1], String::from("B")); + let elem2 = roster.into(); + assert_eq!(elem1, elem2); + } + #[test] fn test_set() { let elem: Element = "".parse().unwrap(); @@ -186,7 +209,6 @@ mod tests { Servants - "#.parse().unwrap(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); From ad1d4adcecc296cd40377b32d79f1937620e3bdb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 26 Jun 2017 19:47:26 +0100 Subject: [PATCH 0352/1020] message: Add a simpler way to create a Message. --- src/message.rs | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/message.rs b/src/message.rs index 8025a2a12ffca4d3085f7f01ea22bb21ffc7dad0..47c8db16eaebfbbb8256a7a45a2ce91a589bb4d0 100644 --- a/src/message.rs +++ b/src/message.rs @@ -127,6 +127,21 @@ pub struct Message { pub payloads: Vec, } +impl Message { + pub fn new(to: Option) -> Message { + Message { + from: None, + to: to, + id: None, + type_: MessageType::Chat, + bodies: BTreeMap::new(), + subjects: BTreeMap::new(), + thread: None, + payloads: vec!(), + } + } +} + impl TryFrom for Message { type Error = Error; @@ -237,16 +252,8 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let message = Message { - from: None, - to: None, - id: None, - type_: MessageType::Normal, - bodies: BTreeMap::new(), - subjects: BTreeMap::new(), - thread: None, - payloads: vec!(), - }; + let mut message = Message::new(None); + message.type_ = MessageType::Normal; let elem2 = message.into(); assert_eq!(elem, elem2); } @@ -265,18 +272,8 @@ mod tests { #[test] fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); - let mut bodies = BTreeMap::new(); - bodies.insert(String::from(""), String::from("Hello world!")); - let message = Message { - from: None, - to: Some(Jid::from_str("coucou@example.org").unwrap()), - id: None, - type_: MessageType::Chat, - bodies: bodies, - subjects: BTreeMap::new(), - thread: None, - payloads: vec!(), - }; + let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); + message.bodies.insert(String::from(""), String::from("Hello world!")); let elem2 = message.into(); assert_eq!(elem, elem2); } From 8ac460224060a888778e7f84671c448d1cc747b9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 26 Jun 2017 19:49:16 +0100 Subject: [PATCH 0353/1020] presence: Add a simpler way to create a Presence. --- src/presence.rs | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index d04a3d2e3a227b956adc440e0cfde59a1e73b83e..c85f25bec450a255fa2ce6adb2cec30ba29c1d36 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -198,6 +198,21 @@ pub struct Presence { pub payloads: Vec, } +impl Presence { + pub fn new(type_: Type) -> Presence { + Presence { + from: None, + to: None, + id: None, + type_: type_, + show: Show::None, + statuses: BTreeMap::new(), + priority: 0i8, + payloads: vec!(), + } + } +} + impl TryFrom for Presence { type Error = Error; @@ -310,16 +325,7 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let presence = Presence { - from: None, - to: None, - id: None, - type_: Type::Unavailable, - show: Show::None, - statuses: BTreeMap::new(), - priority: 0i8, - payloads: vec!(), - }; + let presence = Presence::new(Type::Unavailable); let elem2 = presence.into(); assert_eq!(elem, elem2); } @@ -446,18 +452,8 @@ mod tests { #[test] fn test_serialise_status() { let status = Status::from("Hello world!"); - let mut statuses = BTreeMap::new(); - statuses.insert(String::from(""), status); - let presence = Presence { - from: None, - to: None, - id: None, - type_: Type::Unavailable, - show: Show::None, - statuses: statuses, - priority: 0i8, - payloads: vec!(), - }; + let mut presence = Presence::new(Type::Unavailable); + presence.statuses.insert(String::from(""), status); let elem: Element = presence.into(); assert!(elem.is("presence", ns::JABBER_CLIENT)); assert!(elem.children().collect::>()[0].is("status", ns::JABBER_CLIENT)); From 27cf9a6298bbb6060f7926aac345a9671c13e6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 26 Jun 2017 23:07:12 +0100 Subject: [PATCH 0354/1020] muc::Muc and muc::MucUser --- src/muc.rs | 346 -------------------- src/muc/mod.rs | 11 + src/muc/muc.rs | 108 +++++++ src/muc/user.rs | 830 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 949 insertions(+), 346 deletions(-) delete mode 100644 src/muc.rs create mode 100644 src/muc/mod.rs create mode 100644 src/muc/muc.rs create mode 100644 src/muc/user.rs diff --git a/src/muc.rs b/src/muc.rs deleted file mode 100644 index c8248b7500df034a926f847a7c83bdc2572d82b2..0000000000000000000000000000000000000000 --- a/src/muc.rs +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright (c) 2017 Maxime “pep” Buquet -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -use std::convert::TryFrom; - -use minidom::{Element, IntoElements, ElementEmitter}; - -use error::Error; - -use ns; - -#[derive(Debug, Clone)] -pub struct Muc; - -impl TryFrom for Muc { - type Error = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("x", ns::MUC) { - return Err(Error::ParseError("This is not an x element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in x element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in x element.")); - } - Ok(Muc) - } -} - -impl Into for Muc { - fn into(self) -> Element { - Element::builder("x") - .ns(ns::MUC) - .build() - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Status { - // 100 - NonAnonymousRoom, - - // 101 - AffiliationChange, - - // 102 - ConfigShowsUnavailableMembers, - - // 103 - ConfigHidesUnavailableMembers, - - // 104 - ConfigNonPrivacyRelated, - - // 110 - SelfPresence, - - // 170 - ConfigRoomLoggingEnabled, - - // 171 - ConfigRoomLoggingDisabled, - - // 172 - ConfigRoomNonAnonymous, - - // 173 - ConfigRoomSemiAnonymous, - - // 201 - RoomHasBeenCreated, - - // 210 - AssignedNick, - - // 301 - Banned, - - // 303 - NewNick, - - // 307 - Kicked, - - // 321 - RemovalFromRoom, - - // 322 - ConfigMembersOnly, - - // 332 - ServiceShutdown, -} - -impl TryFrom for Status { - type Error = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("status", ns::MUC_USER) { - return Err(Error::ParseError("This is not a status element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in status element.")); - } - for (attr, _) in elem.attrs() { - if attr != "code" { - return Err(Error::ParseError("Unknown attribute in status element.")); - } - } - let code = get_attr!(elem, "code", required); - - Ok(match code { - 100 => Status::NonAnonymousRoom, - 101 => Status::AffiliationChange, - 102 => Status::ConfigShowsUnavailableMembers, - 103 => Status::ConfigHidesUnavailableMembers, - 104 => Status::ConfigNonPrivacyRelated, - 110 => Status::SelfPresence, - 170 => Status::ConfigRoomLoggingEnabled, - 171 => Status::ConfigRoomLoggingDisabled, - 172 => Status::ConfigRoomNonAnonymous, - 173 => Status::ConfigRoomSemiAnonymous, - 201 => Status::RoomHasBeenCreated, - 210 => Status::AssignedNick, - 301 => Status::Banned, - 303 => Status::NewNick, - 307 => Status::Kicked, - 321 => Status::RemovalFromRoom, - 322 => Status::ConfigMembersOnly, - 332 => Status::ServiceShutdown, - _ => return Err(Error::ParseError("Invalid status code.")), - }) - } -} - -impl Into for Status { - fn into(self) -> Element { - Element::builder("status") - .ns(ns::MUC_USER) - .attr("code", match self { - Status::NonAnonymousRoom => 100, - Status::AffiliationChange => 101, - Status::ConfigShowsUnavailableMembers => 102, - Status::ConfigHidesUnavailableMembers => 103, - Status::ConfigNonPrivacyRelated => 104, - Status::SelfPresence => 110, - Status::ConfigRoomLoggingEnabled => 170, - Status::ConfigRoomLoggingDisabled => 171, - Status::ConfigRoomNonAnonymous => 172, - Status::ConfigRoomSemiAnonymous => 173, - Status::RoomHasBeenCreated => 201, - Status::AssignedNick => 210, - Status::Banned => 301, - Status::NewNick => 303, - Status::Kicked => 307, - Status::RemovalFromRoom => 321, - Status::ConfigMembersOnly => 322, - Status::ServiceShutdown => 332, - }) - .build() - } -} - -impl IntoElements for Status { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - -#[derive(Debug, Clone)] -pub struct MucUser { - status: Vec, -} - -impl TryFrom for MucUser { - type Error = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("x", ns::MUC_USER) { - return Err(Error::ParseError("This is not an x element.")); - } - let mut status = vec!(); - for child in elem.children() { - if child.is("status", ns::MUC_USER) { - status.push(Status::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in x element.")); - } - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in x element.")); - } - Ok(MucUser { - status: status, - }) - } -} - -impl Into for MucUser { - fn into(self) -> Element { - Element::builder("x") - .ns(ns::MUC_USER) - .append(self.status) - .build() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::error::Error as StdError; - - #[test] - fn test_muc_simple() { - let elem: Element = "".parse().unwrap(); - Muc::try_from(elem).unwrap(); - } - - #[test] - fn test_muc_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = Muc::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in x element."); - } - - #[test] - fn test_muc_serialise() { - let elem: Element = "".parse().unwrap(); - let muc = Muc; - let elem2 = muc.into(); - assert_eq!(elem, elem2); - } - - #[test] - fn test_muc_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = Muc::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in x element."); - } - - #[test] - fn test_muc_user_simple() { - let elem: Element = "".parse().unwrap(); - MucUser::try_from(elem).unwrap(); - } - - #[test] - fn test_muc_user_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = MucUser::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in x element."); - } - - #[test] - fn test_muc_user_serialise() { - let elem: Element = "".parse().unwrap(); - let muc = MucUser { status: vec!() }; - let elem2 = muc.into(); - assert_eq!(elem, elem2); - } - - #[test] - fn test_muc_user_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = MucUser::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in x element."); - } - - #[test] - fn test_status_simple() { - let elem: Element = "".parse().unwrap(); - Status::try_from(elem).unwrap(); - } - - #[test] - fn test_status_invalid() { - let elem: Element = "".parse().unwrap(); - let error = Status::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Required attribute 'code' missing."); - } - - #[test] - fn test_status_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = Status::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in status element."); - } - - #[test] - fn test_status_simple_code() { - let elem: Element = "".parse().unwrap(); - let status = Status::try_from(elem).unwrap(); - assert_eq!(status, Status::Kicked); - } - - #[test] - fn test_status_invalid_code() { - let elem: Element = "".parse().unwrap(); - let error = Status::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Invalid status code."); - } - - #[test] - fn test_status_invalid_code2() { - let elem: Element = "".parse().unwrap(); - let error = Status::try_from(elem).unwrap_err(); - let error = match error { - Error::ParseIntError(error) => error, - _ => panic!(), - }; - assert_eq!(error.description(), "invalid digit found in string"); - } -} diff --git a/src/muc/mod.rs b/src/muc/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..80d61d88b2066769c909c56d899683a2058c682f --- /dev/null +++ b/src/muc/mod.rs @@ -0,0 +1,11 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +pub mod muc; +pub mod user; + +pub use self::muc::Muc; +pub use self::user::MucUser; diff --git a/src/muc/muc.rs b/src/muc/muc.rs new file mode 100644 index 0000000000000000000000000000000000000000..75667d877352ccd6e38404a1a16ef4e1431cf7db --- /dev/null +++ b/src/muc/muc.rs @@ -0,0 +1,108 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; + +use minidom::Element; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub struct Muc { + pub password: Option, +} + +impl TryFrom for Muc { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("x", ns::MUC) { + return Err(Error::ParseError("This is not an x element.")); + } + + let mut password = None; + for child in elem.children() { + if child.is("password", ns::MUC) { + password = Some(child.text()); + } else { + return Err(Error::ParseError("Unknown child in x element.")); + } + } + + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in x element.")); + } + + Ok(Muc { + password: password, + }) + } +} + +impl Into for Muc { + fn into(self) -> Element { + Element::builder("x") + .ns(ns::MUC) + .append(self.password) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_muc_simple() { + let elem: Element = "".parse().unwrap(); + Muc::try_from(elem).unwrap(); + } + + #[test] + fn test_muc_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = Muc::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in x element."); + } + + #[test] + fn test_muc_serialise() { + let elem: Element = "".parse().unwrap(); + let muc = Muc { + password: None, + }; + let elem2 = muc.into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_muc_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = Muc::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in x element."); + } + + #[test] + fn test_muc_simple_password() { + let elem: Element = " + + coucou + " + .parse().unwrap(); + let muc = Muc::try_from(elem).unwrap(); + assert_eq!(muc.password, Some("coucou".to_owned())); + } +} diff --git a/src/muc/user.rs b/src/muc/user.rs new file mode 100644 index 0000000000000000000000000000000000000000..8dae704a0a05e0eae428081f81396e747ef1b9c8 --- /dev/null +++ b/src/muc/user.rs @@ -0,0 +1,830 @@ +// Copyright (c) 2017 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; +use std::convert::TryInto; +use std::str::FromStr; + +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; + +use jid::Jid; + +use error::Error; + +use ns; + +#[derive(Debug, Clone, PartialEq)] +pub enum Status { + /// Status: 100 + NonAnonymousRoom, + + /// Status: 101 + AffiliationChange, + + /// Status: 102 + ConfigShowsUnavailableMembers, + + /// Status: 103 + ConfigHidesUnavailableMembers, + + /// Status: 104 + ConfigNonPrivacyRelated, + + /// Status: 110 + SelfPresence, + + /// Status: 170 + ConfigRoomLoggingEnabled, + + /// Status: 171 + ConfigRoomLoggingDisabled, + + /// Status: 172 + ConfigRoomNonAnonymous, + + /// Status: 173 + ConfigRoomSemiAnonymous, + + /// Status: 201 + RoomHasBeenCreated, + + /// Status: 210 + AssignedNick, + + /// Status: 301 + Banned, + + /// Status: 303 + NewNick, + + /// Status: 307 + Kicked, + + /// Status: 321 + RemovalFromRoom, + + /// Status: 322 + ConfigMembersOnly, + + /// Status: 332 + ServiceShutdown, +} + +impl TryFrom for Status { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("status", ns::MUC_USER) { + return Err(Error::ParseError("This is not a status element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in status element.")); + } + for (attr, _) in elem.attrs() { + if attr != "code" { + return Err(Error::ParseError("Unknown attribute in status element.")); + } + } + let code = get_attr!(elem, "code", required); + + Ok(match code { + 100 => Status::NonAnonymousRoom, + 101 => Status::AffiliationChange, + 102 => Status::ConfigShowsUnavailableMembers, + 103 => Status::ConfigHidesUnavailableMembers, + 104 => Status::ConfigNonPrivacyRelated, + 110 => Status::SelfPresence, + 170 => Status::ConfigRoomLoggingEnabled, + 171 => Status::ConfigRoomLoggingDisabled, + 172 => Status::ConfigRoomNonAnonymous, + 173 => Status::ConfigRoomSemiAnonymous, + 201 => Status::RoomHasBeenCreated, + 210 => Status::AssignedNick, + 301 => Status::Banned, + 303 => Status::NewNick, + 307 => Status::Kicked, + 321 => Status::RemovalFromRoom, + 322 => Status::ConfigMembersOnly, + 332 => Status::ServiceShutdown, + _ => return Err(Error::ParseError("Invalid status code.")), + }) + } +} + +impl Into for Status { + fn into(self) -> Element { + Element::builder("status") + .ns(ns::MUC_USER) + .attr("code", match self { + Status::NonAnonymousRoom => 100, + Status::AffiliationChange => 101, + Status::ConfigShowsUnavailableMembers => 102, + Status::ConfigHidesUnavailableMembers => 103, + Status::ConfigNonPrivacyRelated => 104, + Status::SelfPresence => 110, + Status::ConfigRoomLoggingEnabled => 170, + Status::ConfigRoomLoggingDisabled => 171, + Status::ConfigRoomNonAnonymous => 172, + Status::ConfigRoomSemiAnonymous => 173, + Status::RoomHasBeenCreated => 201, + Status::AssignedNick => 210, + Status::Banned => 301, + Status::NewNick => 303, + Status::Kicked => 307, + Status::RemovalFromRoom => 321, + Status::ConfigMembersOnly => 322, + Status::ServiceShutdown => 332, + }) + .build() + } +} + +impl IntoElements for Status { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +/// Optional element used in elements inside presence stanzas of type +/// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking +/// purposes. -- CHANGELOG 0.17 (2002-10-23) +/// Possesses a 'jid' and a 'nick' attribute, so that an action can be attributed either to a real +/// JID or to a roomnick. -- CHANGELOG 1.25 (2012-02-08) +#[derive(Debug, Clone, PartialEq)] +pub enum Actor { + Jid(Jid), + Nick(String), +} + +impl TryFrom for Actor { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("actor", ns::MUC_USER) { + return Err(Error::ParseError("This is not a actor element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in actor element.")); + } + for (attr, _) in elem.attrs() { + if attr != "jid" && attr != "nick" { + return Err(Error::ParseError("Unknown attribute in actor element.")); + } + } + let jid: Option = get_attr!(elem, "jid", optional); + let nick = get_attr!(elem, "nick", optional); + + match (jid, nick) { + (Some(_), Some(_)) + | (None, None) => + return Err(Error::ParseError("Either 'jid' or 'nick' attribute is required.")), + (Some(jid), _) => Ok(Actor::Jid(jid)), + (_, Some(nick)) => Ok(Actor::Nick(nick)), + } + } +} + +impl Into for Actor { + fn into(self) -> Element { + let elem = Element::builder("actor").ns(ns::MUC_USER); + + (match self { + Actor::Jid(jid) => elem.attr("jid", String::from(jid)), + Actor::Nick(nick) => elem.attr("nick", nick), + }).build() + } +} + +impl IntoElements for Actor { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Continue { + thread: Option, +} + +impl TryFrom for Continue { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("continue", ns::MUC_USER) { + return Err(Error::ParseError("This is not a continue element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in continue element.")); + } + for (attr, _) in elem.attrs() { + if attr != "thread" { + return Err(Error::ParseError("Unknown attribute in continue element.")); + } + } + Ok(Continue { thread: get_attr!(elem, "thread", optional) }) + } +} + +impl Into for Continue { + fn into(self) -> Element { + Element::builder("continue") + .ns(ns::MUC_USER) + .attr("thread", self.thread) + .build() + } +} + +impl IntoElements for Continue { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Reason(String); + +impl TryFrom for Reason { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("reason", ns::MUC_USER) { + return Err(Error::ParseError("This is not a reason element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in reason element.")); + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in reason element.")); + } + Ok(Reason(elem.text())) + } +} + +impl Into for Reason { + fn into(self) -> Element { + Element::builder("reason") + .ns(ns::MUC_USER) + .append(self.0) + .build() + } +} + +impl IntoElements for Reason { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_child(self.into()); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Affiliation { + Owner, + Admin, + Member, + Outcast, + None, +} + +impl FromStr for Affiliation { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "owner" => Affiliation::Owner, + "admin" => Affiliation::Admin, + "member" => Affiliation::Member, + "outcast" => Affiliation::Outcast, + "none" => Affiliation::None, + + _ => return Err(Error::ParseError("Unknown affiliation.")), + }) + } +} + +impl IntoAttributeValue for Affiliation { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Affiliation::Owner => "owner", + Affiliation::Admin => "admin", + Affiliation::Member => "member", + Affiliation::Outcast => "outcast", + Affiliation::None => "none", + })) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Role { + Moderator, + Participant, + Visitor, + None, +} + +impl FromStr for Role { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "moderator" => Role::Moderator, + "participant" => Role::Participant, + "visitor" => Role::Visitor, + "none" => Role::None, + + _ => return Err(Error::ParseError("Unknown role.")), + }) + } +} + +impl IntoAttributeValue for Role { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Role::Moderator => "moderator", + Role::Participant => "participant", + Role::Visitor => "visitor", + Role::None => "none", + })) + } +} + +#[derive(Debug, Clone)] +pub struct Item { + pub affiliation: Affiliation, + pub jid: Option, + pub nick: Option, + pub role: Role, + pub actor: Option, + pub continue_: Option, + pub reason: Option, +} + +impl TryFrom for Item { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("item", ns::MUC_USER) { + return Err(Error::ParseError("This is not a item element.")); + } + let mut actor: Option = None; + let mut continue_: Option = None; + let mut reason: Option = None; + for child in elem.children() { + if child.is("actor", ns::MUC_USER) { + actor = Some(child.clone().try_into()?); + } else if child.is("continue", ns::MUC_USER) { + continue_ = Some(child.clone().try_into()?); + } else if child.is("reason", ns::MUC_USER) { + reason = Some(child.clone().try_into()?); + } else { + return Err(Error::ParseError("Unknown child in item element.")); + } + } + for (attr, _) in elem.attrs() { + if attr != "affiliation" && attr != "jid" && + attr != "nick" && attr != "role" { + return Err(Error::ParseError("Unknown attribute in item element.")); + } + } + + let affiliation: Affiliation = get_attr!(elem, "affiliation", required); + let jid: Option = get_attr!(elem, "jid", optional); + let nick: Option = get_attr!(elem, "nick", optional); + let role: Role = get_attr!(elem, "role", required); + + Ok(Item{ + affiliation: affiliation, + jid: jid, + nick: nick, + role: role, + actor: actor, + continue_: continue_, + reason: reason, + }) + } +} + +impl Into for Item { + fn into(self) -> Element { + Element::builder("item") + .ns(ns::MUC_USER) + .attr("affiliation", self.affiliation) + .attr("jid", match self.jid { + Some(jid) => Some(String::from(jid)), + None => None, + }) + .attr("nick", self.nick) + .attr("role", self.role) + .append(self.actor) + .append(self.continue_) + .append(self.reason) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct MucUser { + pub status: Vec, + pub items: Vec, +} + +impl TryFrom for MucUser { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("x", ns::MUC_USER) { + return Err(Error::ParseError("This is not an x element.")); + } + let mut status = vec!(); + let mut items = vec!(); + for child in elem.children() { + if child.is("status", ns::MUC_USER) { + status.push(Status::try_from(child.clone())?); + } else if child.is("item", ns::MUC_USER) { + items.push(Item::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown child in x element.")); + } + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in x element.")); + } + Ok(MucUser { + status, + items, + }) + } +} + +impl Into for MucUser { + fn into(self) -> Element { + Element::builder("x") + .ns(ns::MUC_USER) + .append(self.status) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::error::Error as StdError; + + #[test] + fn test_simple() { + let elem: Element = " + + ".parse().unwrap(); + MucUser::try_from(elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = " + + + + ".parse().unwrap(); + let error = MucUser::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in x element."); + } + + #[test] + fn test_serialise() { + let elem: Element = " + + ".parse().unwrap(); + let muc = MucUser { status: vec!(), items: vec!() }; + let elem2 = muc.into(); + assert_eq!(elem, elem2); + } + + #[test] + fn test_invalid_attribute() { + let elem: Element = " + + ".parse().unwrap(); + let error = MucUser::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in x element."); + } + + #[test] + fn test_status_simple() { + let elem: Element = " + + ".parse().unwrap(); + Status::try_from(elem).unwrap(); + } + + #[test] + fn test_status_invalid() { + let elem: Element = " + + ".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'code' missing."); + } + + #[test] + fn test_status_invalid_child() { + let elem: Element = " + + + + ".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in status element."); + } + + #[test] + fn test_status_simple_code() { + let elem: Element = " + + ".parse().unwrap(); + let status = Status::try_from(elem).unwrap(); + assert_eq!(status, Status::Kicked); + } + + #[test] + fn test_status_invalid_code() { + let elem: Element = " + + ".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Invalid status code."); + } + + #[test] + fn test_status_invalid_code2() { + let elem: Element = " + + ".parse().unwrap(); + let error = Status::try_from(elem).unwrap_err(); + let error = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(error.description(), "invalid digit found in string"); + } + + #[test] + fn test_actor_required_attributes() { + let elem: Element = " + + ".parse().unwrap(); + let error = Actor::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Either 'jid' or 'nick' attribute is required."); + } + + #[test] + fn test_actor_required_attributes2() { + let elem: Element = " + + ".parse().unwrap(); + let error = Actor::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Either 'jid' or 'nick' attribute is required."); + } + + #[test] + fn test_actor_jid() { + let elem: Element = " + + ".parse().unwrap(); + let actor = Actor::try_from(elem).unwrap(); + let jid = match actor { + Actor::Jid(jid) => jid, + _ => panic!(), + }; + assert_eq!(jid, "foo@bar/baz".parse::().unwrap()); + } + + #[test] + fn test_actor_nick() { + let elem: Element = " + + ".parse().unwrap(); + let actor = Actor::try_from(elem).unwrap(); + let nick = match actor { + Actor::Nick(nick) => nick, + _ => panic!(), + }; + assert_eq!(nick, "baz".to_owned()); + } + + #[test] + fn test_continue_simple() { + let elem: Element = " + + ".parse().unwrap(); + Continue::try_from(elem).unwrap(); + } + + #[test] + fn test_continue_thread_attribute() { + let elem: Element = " + + ".parse().unwrap(); + let continue_ = Continue::try_from(elem).unwrap(); + assert_eq!(continue_, Continue { thread: Some("foo".to_owned()) }); + } + + #[test] + fn test_continue_invalid() { + let elem: Element = " + + + + ".parse().unwrap(); + let continue_ = Continue::try_from(elem).unwrap_err(); + let message = match continue_ { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in continue element.".to_owned()); + } + + #[test] + fn test_reason_simple() { + let elem: Element = " + Reason" + .parse().unwrap(); + let reason = Reason::try_from(elem).unwrap(); + assert_eq!(reason.0, "Reason".to_owned()); + } + + #[test] + fn test_reason_invalid_attribute() { + let elem: Element = " + + ".parse().unwrap(); + let error = Reason::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in reason element.".to_owned()); + } + + #[test] + fn test_reason_invalid() { + let elem: Element = " + + + + ".parse().unwrap(); + let error = Reason::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in reason element.".to_owned()); + } + + #[test] + fn test_item_invalid_attr(){ + let elem: Element = " + + ".parse().unwrap(); + let error = Item::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in item element.".to_owned()); + } + + #[test] + fn test_item_affiliation_role_attr(){ + let elem: Element = " + + ".parse().unwrap(); + Item::try_from(elem).unwrap(); + } + + #[test] + fn test_item_affiliation_role_invalid_attr(){ + let elem: Element = " + + ".parse().unwrap(); + let error = Item::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'role' missing.".to_owned()); + } + + #[test] + fn test_item_nick_attr(){ + let elem: Element = " + + ".parse().unwrap(); + let item = Item::try_from(elem).unwrap(); + match item { + Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())), + } + } + + #[test] + fn test_item_affiliation_role_invalid_attr2(){ + let elem: Element = " + + ".parse().unwrap(); + let error = Item::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'affiliation' missing.".to_owned()); + } + + #[test] + fn test_item_role_actor_child(){ + let elem: Element = " + + + + ".parse().unwrap(); + let item = Item::try_from(elem).unwrap(); + match item { + Item { actor, .. } => + assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))), + } + } + + #[test] + fn test_item_role_continue_child(){ + let elem: Element = " + + + + ".parse().unwrap(); + let item = Item::try_from(elem).unwrap(); + let continue_1 = Continue { thread: Some("foobar".to_owned()) }; + match item { + Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2, continue_1), + _ => panic!(), + } + } + + #[test] + fn test_item_role_reason_child(){ + let elem: Element = " + + foobar + + ".parse().unwrap(); + let item = Item::try_from(elem).unwrap(); + match item { + Item { reason, .. } => + assert_eq!(reason, Some(Reason("foobar".to_owned()))), + } + } +} From 98878042dd4ba1a630f85b329645527f59093b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 27 Jun 2017 00:14:40 +0100 Subject: [PATCH 0355/1020] Use the generate_attribute macro in muc::user --- src/muc/user.rs | 84 +++++++++---------------------------------------- 1 file changed, 14 insertions(+), 70 deletions(-) diff --git a/src/muc/user.rs b/src/muc/user.rs index 8dae704a0a05e0eae428081f81396e747ef1b9c8..833b5604a959b43f9455f0003331b001f1646574 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -278,76 +278,20 @@ impl IntoElements for Reason { } } -#[derive(Debug, Clone, PartialEq)] -pub enum Affiliation { - Owner, - Admin, - Member, - Outcast, - None, -} - -impl FromStr for Affiliation { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "owner" => Affiliation::Owner, - "admin" => Affiliation::Admin, - "member" => Affiliation::Member, - "outcast" => Affiliation::Outcast, - "none" => Affiliation::None, - - _ => return Err(Error::ParseError("Unknown affiliation.")), - }) - } -} - -impl IntoAttributeValue for Affiliation { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Affiliation::Owner => "owner", - Affiliation::Admin => "admin", - Affiliation::Member => "member", - Affiliation::Outcast => "outcast", - Affiliation::None => "none", - })) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Role { - Moderator, - Participant, - Visitor, - None, -} - -impl FromStr for Role { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "moderator" => Role::Moderator, - "participant" => Role::Participant, - "visitor" => Role::Visitor, - "none" => Role::None, - - _ => return Err(Error::ParseError("Unknown role.")), - }) - } -} - -impl IntoAttributeValue for Role { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - Role::Moderator => "moderator", - Role::Participant => "participant", - Role::Visitor => "visitor", - Role::None => "none", - })) - } -} +generate_attribute!(Affiliation, "affiliation", { + Owner => "owner", + Admin => "admin", + Member => "member", + Outcast => "outcast", + None => "none", +}, Default = None); + +generate_attribute!(Role, "role", { + Moderator => "moderator", + Participant => "participant", + Visitor => "visitor", + None => "none", +}, Default = None); #[derive(Debug, Clone)] pub struct Item { From 244c9b2710ce08e3886a9cd2a25ae0d938e60fe5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 27 Jun 2017 22:21:35 +0100 Subject: [PATCH 0356/1020] presence: Remove unused import in tests. --- src/presence.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/presence.rs b/src/presence.rs index c85f25bec450a255fa2ce6adb2cec30ba29c1d36..74458797a457e320a0e158790cca120f5f8b90c4 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -308,7 +308,6 @@ impl Into for Presence { #[cfg(test)] mod tests { - use std::collections::BTreeMap; use super::*; #[test] From 6ea175ad2ed16596e13a699826dffb49f7633f83 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 27 Jun 2017 22:25:13 +0100 Subject: [PATCH 0357/1020] Cargo.toml: Update the hashing libraries. --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 224ace7dca438fdbb43ce747cac262917e9795c3..ac9eae51923a2300b002672b78cf9ceca3e8157e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,9 @@ license = "MPL-2.0" minidom = "0.4.3" jid = "0.2.0" base64 = "0.6.0" -digest = "0.5.0" -sha-1 = "0.3.0" -sha2 = "0.5.0" -sha3 = "0.5.0" -blake2 = "0.5.0" +digest = "0.6.0" +sha-1 = "0.4.0" +sha2 = "0.6.0" +sha3 = "0.6.0" +blake2 = "0.6.0" chrono = "0.4.0" From 7ddb1b8301175c00c974842877ba57a833d4a151 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 27 Jun 2017 22:19:06 +0100 Subject: [PATCH 0358/1020] ChangeLog: Add 0.6.0 release notes. --- ChangeLog | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ChangeLog b/ChangeLog index d219bf0bcc742eebae08be95e8a41ecf8a3efee7..5edf6648790440e927d0026eb6ffbf044a801d6e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +Version 0.6.0: +2017-06-27 Emmanuel Gil Peyrot + * New parsers/serialisers: + - In-Band Registration (XEP-0077) was added. + - Multi-User Chat (XEP-0045) got expanded a lot, thanks pep.! + * Breaking changes: + - Added wrappers for Strings used as identifiers, to add type + safety. + - Use chrono’s DateTime for JingleFT’s date element. + - Use Jid for JingleS5B’s jid attribute. + * Improvements: + - Use more macros for common tasks. + - Add a constructor for Message and Presence. + - Implement std::fmt::Display and std::error::Error on our + error type. + - Fix DataForms serialisation. + - Fix roster group serialisation. + - Update libraries, notably chrono whose version 0.3.1 got + yanked. + Version 0.5.0: 2017-06-11 Emmanuel Gil Peyrot * New parsers/serialisers: From a2855112bac37ae4fd4f561d817345bbb317fcea Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 27 Jun 2017 22:25:33 +0100 Subject: [PATCH 0359/1020] Release version 0.6.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ac9eae51923a2300b002672b78cf9ceca3e8157e..608f2ff536685715ccc4a39e5d221499772a85ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.5.0" +version = "0.6.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 3c952e47d13dc9851f2518609791da85e5a05bb9 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 2 Jul 2017 01:25:22 +0200 Subject: [PATCH 0361/1020] impl Sink for Client + complete echo_bot --- examples/echo_bot.rs | 114 +++++++++++++++++++------------------------ src/client/mod.rs | 52 +++++++++++++++----- src/xmpp_stream.rs | 1 - 3 files changed, 89 insertions(+), 78 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 9f39dfe4e513f45f33ee88432fd6ef939fb369bb..03053dbdcb40b8028c2f4158bfaddf438335b805 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -4,84 +4,61 @@ extern crate tokio_xmpp; extern crate jid; extern crate xml; -use std::str::FromStr; use tokio_core::reactor::Core; use futures::{Future, Stream, Sink, future}; use tokio_xmpp::{Client, ClientEvent}; -use tokio_xmpp::xmpp_codec::Packet; fn main() { let mut core = Core::new().unwrap(); let client = Client::new("astrobot@example.org", "", &core.handle()).unwrap(); - // let client = TcpClient::connect( - // jid.clone(), - // &addr, - // &core.handle() - // ).map_err(|e| format!("{}", e) - // ).and_then(|stream| { - // if stream.can_starttls() { - // stream.starttls() - // } else { - // panic!("No STARTTLS") - // } - // }).and_then(|stream| { - // let username = jid.node.as_ref().unwrap().to_owned(); - // stream.auth(username, password).expect("auth") - // }).and_then(|stream| { - // stream.bind() - // }).and_then(|stream| { - // println!("Bound to {}", stream.jid); - // let presence = xml::Element::new("presence".to_owned(), None, vec![]); - // stream.send(Packet::Stanza(presence)) - // .map_err(|e| format!("{}", e)) - // }).and_then(|stream| { - // let main_loop = |stream| { - // stream.into_future() - // .and_then(|(event, stream)| { - // stream.send(Packet::Stanza(unreachable!())) - // }).and_then(main_loop) - // }; - // main_loop(stream) - // }).and_then(|(event, stream)| { - // let (mut sink, stream) = stream.split(); - // stream.for_each(move |event| { - // match event { - // Packet::Stanza(ref message) - // if message.name == "message" => { - // let ty = message.get_attribute("type", None); - // let body = message.get_child("body", Some("jabber:client")) - // .map(|body_el| body_el.content_str()); - // match ty { - // None | Some("normal") | Some("chat") - // if body.is_some() => { - // let from = message.get_attribute("from", None).unwrap(); - // println!("Got message from {}: {:?}", from, body); - // let reply = make_reply(from, body.unwrap()); - // sink.send(Packet::Stanza(reply)) - // .and_then(|_| Ok(())) - // }, - // _ => future::ok(()), - // } - // }, - // _ => future::ok(()), - // } - // }).map_err(|e| format!("{}", e)) - // }); - - let done = client.for_each(|event| { - match event { + let (sink, stream) = client.split(); + let mut sink = Some(sink); + let done = stream.for_each(move |event| { + let result: Box> = match event { ClientEvent::Online => { println!("Online!"); + + let presence = make_presence(); + sink = Some( + sink.take(). + expect("sink") + .send(presence) + .wait() + .expect("sink.send") + ); + Box::new( + future::ok(()) + ) }, - ClientEvent::Stanza(stanza) => { + ClientEvent::Stanza(ref stanza) + if stanza.name == "message" + && stanza.get_attribute("type", None) != Some("error") => + { + let from = stanza.get_attribute("from", None); + let body = stanza.get_child("body", Some("jabber:client")) + .map(|el| el.content_str()); + + match (from.as_ref(), body) { + (Some(from), Some(body)) => { + let reply = make_reply(from, body); + sink = Some( + sink.take(). + expect("sink") + .send(reply) + .wait() + .expect("sink.send") + ); + }, + _ => (), + }; + Box::new(future::ok(())) }, _ => { - println!("Event: {:?}", event); + Box::new(future::ok(())) }, - } - - Ok(()) + }; + result }); match core.run(done) { @@ -93,6 +70,15 @@ fn main() { } } +fn make_presence() -> xml::Element { + let mut presence = xml::Element::new("presence".to_owned(), None, vec![]); + presence.tag(xml::Element::new("status".to_owned(), None, vec![])) + .text("chat".to_owned()); + presence.tag(xml::Element::new("show".to_owned(), None, vec![])) + .text("Echoing messages".to_owned()); + presence +} + fn make_reply(to: &str, body: String) -> xml::Element { let mut message = xml::Element::new( "message".to_owned(), diff --git a/src/client/mod.rs b/src/client/mod.rs index 624bf7249df1f664e6024df9797fb557cc9559d4..6554a6bce2f9a57126eb725869466526ab28b5e0 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,7 +1,7 @@ use std::mem::replace; use std::str::FromStr; use std::error::Error; -use tokio_core::reactor::{Core, Handle}; +use tokio_core::reactor::Handle; use tokio_core::net::TcpStream; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_tls::TlsStream; @@ -22,7 +22,6 @@ use self::bind::*; pub struct Client { pub jid: Jid, - password: String, state: ClientState, } @@ -33,8 +32,6 @@ enum ClientState { Disconnected, Connecting(Box>), Connected(XMPPStream), - // Sending, - // Drain, } impl Client { @@ -43,7 +40,7 @@ impl Client { let password = password.to_owned(); let connect = Self::make_connect(jid.clone(), password.clone(), handle); Ok(Client { - jid, password, + jid, state: ClientState::Connecting(connect), }) } @@ -73,10 +70,7 @@ impl Client { Self::bind(stream) }).and_then(|stream| { println!("Bound to {}", stream.jid); - - let presence = xml::Element::new("presence".to_owned(), None, vec![]); - stream.send(Packet::Stanza(presence)) - .map_err(|e| format!("{}", e)) + Ok(stream) }) ) } @@ -116,20 +110,18 @@ impl Stream for Client { type Error = String; fn poll(&mut self) -> Poll, Self::Error> { - println!("stream.poll"); let state = replace(&mut self.state, ClientState::Invalid); match state { ClientState::Invalid => Err("invalid client state".to_owned()), ClientState::Disconnected => - Ok(Async::NotReady), + Ok(Async::Ready(None)), ClientState::Connecting(mut connect) => { match connect.poll() { Ok(Async::Ready(stream)) => { - println!("connected"); self.state = ClientState::Connected(stream); - self.poll() + Ok(Async::Ready(Some(ClientEvent::Online))) }, Ok(Async::NotReady) => { self.state = ClientState::Connecting(connect); @@ -165,3 +157,37 @@ impl Stream for Client { } } } + +impl Sink for Client { + type SinkItem = xml::Element; + type SinkError = String; + + fn start_send(&mut self, item: Self::SinkItem) -> StartSend { + match self.state { + ClientState::Connected(ref mut stream) => + match stream.start_send(Packet::Stanza(item)) { + Ok(AsyncSink::NotReady(Packet::Stanza(stanza))) => + Ok(AsyncSink::NotReady(stanza)), + Ok(AsyncSink::NotReady(_)) => + panic!("Client.start_send with stanza but got something else back"), + Ok(AsyncSink::Ready) => { + Ok(AsyncSink::Ready) + }, + Err(e) => + Err(e.description().to_owned()), + }, + _ => + Ok(AsyncSink::NotReady(item)), + } + } + + fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { + match &mut self.state { + &mut ClientState::Connected(ref mut stream) => + stream.poll_complete() + .map_err(|e| e.description().to_owned()), + _ => + Ok(Async::Ready(())), + } + } +} diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index 01d4aceb5b3617b9bdb98c3cd56943b1fc8b7326..a09b5a984c0b625dcd5342f2411536d53a47cb16 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -1,4 +1,3 @@ -use std::default::Default; use std::collections::HashMap; use futures::*; use tokio_io::{AsyncRead, AsyncWrite}; From 32031a239ef23a156ef4a5c10ea2274272d29462 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 2 Jul 2017 01:26:36 +0200 Subject: [PATCH 0362/1020] TODO --- src/client/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/mod.rs b/src/client/mod.rs index 6554a6bce2f9a57126eb725869466526ab28b5e0..e23b90fcf015ad21edbc4c32329006a7ea07b095 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -46,6 +46,7 @@ impl Client { } fn make_connect(jid: Jid, password: String, handle: &Handle) -> Box> { + // TODO: implement proper DNS SRV lookup use std::net::ToSocketAddrs; let addr = "89.238.79.220:5222" .to_socket_addrs().unwrap() From 444b78c63a481f236728bd996152a7e392174879 Mon Sep 17 00:00:00 2001 From: Johann Tuffe Date: Sat, 8 Jul 2017 12:18:15 +0800 Subject: [PATCH 0363/1020] unescape_and_decode_value for element attributes --- README.md | 2 +- src/element.rs | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index c0426ba8e5a48d5f3e957b9769672152d8bc9587..93582491b6513fc889957fd7c8b2a7d331329e36 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ minidom-rs What's this? ------------ -A minimal DOM library on top of xml-rs. +A minimal DOM library on top of quick-xml. What license is it under? ------------------------- diff --git a/src/element.rs b/src/element.rs index ee2485083823876146d84774dcbde32770a38943..854fcb0b3b35d0e111356664225ffadbcb57d931 100644 --- a/src/element.rs +++ b/src/element.rs @@ -253,7 +253,7 @@ impl Element { let e = reader.read_event(&mut buf)?; match e { Event::Empty(ref e) | Event::Start(ref e) => { - root = build_element(e)?; // FIXME: could be break build_element(e)? when break value is stable + root = build_element(&reader, e)?; // FIXME: could be break build_element(e)? when break value is stable break; }, Event::Eof => { @@ -268,12 +268,12 @@ impl Element { loop { match reader.read_event(&mut buf)? { Event::Empty(ref e) => { - let elem = build_element(e)?; + let elem = build_element(&reader, e)?; // Since there is no Event::End after, directly append it to the current node stack.last_mut().unwrap().append_child(elem); }, Event::Start(ref e) => { - let elem = build_element(e)?; + let elem = build_element(&reader, e)?; stack.push(elem); }, Event::End(ref e) => { @@ -590,29 +590,29 @@ impl Element { } } -fn build_element(event: &BytesStart) -> Result { +fn build_element(reader: &EventReader, event: &BytesStart) -> Result { let mut attributes = event.attributes() .map(|o| { let o = o?; let key = str::from_utf8(o.key)?.to_owned(); - let value = str::from_utf8(o.value)?.to_owned(); + let value = o.unescape_and_decode_value(reader)?; Ok((key, value)) } ) .collect::>>()?; - let mut ns_key = None; - for (key, _) in &attributes { - if key == "xmlns" || key.starts_with("xmlns:") { - ns_key = Some(key.clone()); - } + let mut ns_key = None; + for (key, _) in &attributes { + if key == "xmlns" || key.starts_with("xmlns:") { + ns_key = Some(key.clone()); } + } - let ns = match ns_key { - None => None, - Some(key) => attributes.remove(&key), - }; - let name = str::from_utf8(event.name())?.to_owned(); - Ok(Element::new(name, ns, attributes, Vec::new())) + let ns = match ns_key { + None => None, + Some(key) => attributes.remove(&key), + }; + let name = str::from_utf8(event.name())?.to_owned(); + Ok(Element::new(name, ns, attributes, Vec::new())) } /// An iterator over references to child elements of an `Element`. From d5768c4c787b51b5c3599cd285fc6be38b2ae8cb Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 13 Jul 2017 01:40:14 +0200 Subject: [PATCH 0364/1020] echo_bot: unify send() --- examples/echo_bot.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 03053dbdcb40b8028c2f4158bfaddf438335b805..4af8e8e2ea89dc6a8bd0703795fd83c9c1d48ac4 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -14,19 +14,22 @@ fn main() { let (sink, stream) = client.split(); let mut sink = Some(sink); - let done = stream.for_each(move |event| { + let mut send = move |stanza| { + sink = Some( + sink.take(). + expect("sink") + .send(stanza) + .wait() + .expect("sink.send") + ); + }; + let done = stream.for_each(|event| { let result: Box> = match event { ClientEvent::Online => { println!("Online!"); let presence = make_presence(); - sink = Some( - sink.take(). - expect("sink") - .send(presence) - .wait() - .expect("sink.send") - ); + send(presence); Box::new( future::ok(()) ) @@ -42,13 +45,7 @@ fn main() { match (from.as_ref(), body) { (Some(from), Some(body)) => { let reply = make_reply(from, body); - sink = Some( - sink.take(). - expect("sink") - .send(reply) - .wait() - .expect("sink.send") - ); + send(reply); }, _ => (), }; From e2a4f609fb796dcd5ac6ed5f6903d11c407d30aa Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 13 Jul 2017 01:43:27 +0200 Subject: [PATCH 0365/1020] rm stale code --- src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 49cab6e9de522064cb9862a20678080b95aae8f3..6f9cf09c786f1a876ced54613678eb378791cad0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,3 @@ mod starttls; pub use starttls::*; mod client; pub use client::{Client, ClientEvent}; - - -// type FullClient = sasl::Client> - From 7f667041d95e63a0769074d98e5d6931f5eeffaf Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 13 Jul 2017 01:47:05 +0200 Subject: [PATCH 0366/1020] more of a ClientEvent api --- examples/echo_bot.rs | 41 +++++++++++++++++++---------------------- src/client/event.rs | 31 +++++++++++++++++++++++++++++++ src/client/mod.rs | 9 ++------- 3 files changed, 52 insertions(+), 29 deletions(-) create mode 100644 src/client/event.rs diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 4af8e8e2ea89dc6a8bd0703795fd83c9c1d48ac4..f5923f016833b3a3d1ad4c1f1db1b77c23028ad3 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -6,7 +6,7 @@ extern crate xml; use tokio_core::reactor::Core; use futures::{Future, Stream, Sink, future}; -use tokio_xmpp::{Client, ClientEvent}; +use tokio_xmpp::Client; fn main() { let mut core = Core::new().unwrap(); @@ -24,8 +24,8 @@ fn main() { ); }; let done = stream.for_each(|event| { - let result: Box> = match event { - ClientEvent::Online => { + let result: Box> = + if event.is_online() { println!("Online!"); let presence = make_presence(); @@ -33,28 +33,25 @@ fn main() { Box::new( future::ok(()) ) - }, - ClientEvent::Stanza(ref stanza) - if stanza.name == "message" - && stanza.get_attribute("type", None) != Some("error") => - { - let from = stanza.get_attribute("from", None); - let body = stanza.get_child("body", Some("jabber:client")) - .map(|el| el.content_str()); + } else if let Some(stanza) = event.as_stanza() { + if stanza.name == "message" && + stanza.get_attribute("type", None) != Some("error") { + let from = stanza.get_attribute("from", None); + let body = stanza.get_child("body", Some("jabber:client")) + .map(|el| el.content_str()); - match (from.as_ref(), body) { - (Some(from), Some(body)) => { - let reply = make_reply(from, body); - send(reply); - }, - _ => (), - }; + match (from.as_ref(), body) { + (Some(from), Some(body)) => { + let reply = make_reply(from, body); + send(reply); + }, + _ => (), + }; + } Box::new(future::ok(())) - }, - _ => { + } else { Box::new(future::ok(())) - }, - }; + }; result }); diff --git a/src/client/event.rs b/src/client/event.rs new file mode 100644 index 0000000000000000000000000000000000000000..e54cff4121e0f60bdd0d24ea379d3ddce3c75f48 --- /dev/null +++ b/src/client/event.rs @@ -0,0 +1,31 @@ +use xml; + +#[derive(Debug)] +pub enum Event { + Online, + Disconnected, + Stanza(xml::Element), +} + +impl Event { + pub fn is_online(&self) -> bool { + match self { + &Event::Online => true, + _ => false, + } + } + + pub fn is_stanza(&self, name: &str) -> bool { + match self { + &Event::Stanza(ref stanza) => stanza.name == name, + _ => false, + } + } + + pub fn as_stanza(&self) -> Option<&xml::Element> { + match self { + &Event::Stanza(ref stanza) => Some(stanza), + _ => None, + } + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs index e23b90fcf015ad21edbc4c32329006a7ea07b095..3f962d9148ba6c16827cf7d53cb4f94705107317 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -19,6 +19,8 @@ mod auth; use self::auth::*; mod bind; use self::bind::*; +mod event; +pub use self::event::Event as ClientEvent; pub struct Client { pub jid: Jid, @@ -99,13 +101,6 @@ impl Client { } } -#[derive(Debug)] -pub enum ClientEvent { - Online, - Disconnected, - Stanza(xml::Element), -} - impl Stream for Client { type Item = ClientEvent; type Error = String; From e2c6a6ed37250d5c7b0bf140574938d1237d39b5 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 13 Jul 2017 02:56:02 +0200 Subject: [PATCH 0367/1020] implement SRV lookup --- Cargo.toml | 3 +- src/client/mod.rs | 48 +++++++++++------------ src/happy_eyeballs.rs | 91 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +- src/tcp.rs | 8 ++++ 5 files changed, 125 insertions(+), 28 deletions(-) create mode 100644 src/happy_eyeballs.rs diff --git a/Cargo.toml b/Cargo.toml index 12e818a70061ce8dffe43d691054bcd8a9589708..6410ca2a79a76462a1e7ff382338f09cdbd07d79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Astro "] [dependencies] futures = "*" -tokio-core = "*" +tokio-core = "0.1.7" tokio-io = "*" bytes = "*" RustyXML = "*" @@ -14,3 +14,4 @@ tokio-tls = "*" sasl = "*" rustc-serialize = "*" jid = "*" +domain = "0.2.1" diff --git a/src/client/mod.rs b/src/client/mod.rs index 3f962d9148ba6c16827cf7d53cb4f94705107317..d0be336a62b527f7ae27fb7240df2d7ff152fa47 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -14,6 +14,7 @@ use super::xmpp_codec::Packet; use super::xmpp_stream; use super::tcp::TcpClient; use super::starttls::{NS_XMPP_TLS, StartTlsClient}; +use super::happy_eyeballs::Connecter; mod auth; use self::auth::*; @@ -37,7 +38,7 @@ enum ClientState { } impl Client { - pub fn new(jid: &str, password: &str, handle: &Handle) -> Result { + pub fn new(jid: &str, password: &str, handle: Handle) -> Result { let jid = try!(Jid::from_str(jid)); let password = password.to_owned(); let connect = Self::make_connect(jid.clone(), password.clone(), handle); @@ -47,34 +48,29 @@ impl Client { }) } - fn make_connect(jid: Jid, password: String, handle: &Handle) -> Box> { - // TODO: implement proper DNS SRV lookup - use std::net::ToSocketAddrs; - let addr = "89.238.79.220:5222" - .to_socket_addrs().unwrap() - .next().unwrap(); + fn make_connect(jid: Jid, password: String, handle: Handle) -> Box> { let username = jid.node.as_ref().unwrap().to_owned(); let password = password; Box::new( - TcpClient::connect( - jid, - &addr, - handle - ).map_err(|e| format!("{}", e) - ).and_then(|stream| { - if Self::can_starttls(&stream) { - Self::starttls(stream) - } else { - panic!("No STARTTLS") - } - }).and_then(move |stream| { - Self::auth(stream, username, password).expect("auth") - }).and_then(|stream| { - Self::bind(stream) - }).and_then(|stream| { - println!("Bound to {}", stream.jid); - Ok(stream) - }) + Connecter::from_lookup(handle, &jid.domain, "_xmpp-client._tcp", 5222) + .expect("Connector::from_lookup") + .and_then(|tcp_stream| + TcpClient::from_stream(jid, tcp_stream) + .map_err(|e| format!("{}", e)) + ).and_then(|stream| { + if Self::can_starttls(&stream) { + Self::starttls(stream) + } else { + panic!("No STARTTLS") + } + }).and_then(move |stream| { + Self::auth(stream, username, password).expect("auth") + }).and_then(|stream| { + Self::bind(stream) + }).and_then(|stream| { + println!("Bound to {}", stream.jid); + Ok(stream) + }) ) } diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs new file mode 100644 index 0000000000000000000000000000000000000000..aa80df20a0e2033d4591e33f9b6eda2e5fcc6b98 --- /dev/null +++ b/src/happy_eyeballs.rs @@ -0,0 +1,91 @@ +use std::str::FromStr; +use futures::*; +use tokio_core::reactor::Handle; +use tokio_core::net::{TcpStream, TcpStreamNew}; +use domain::resolv::Resolver; +use domain::resolv::lookup::srv::*; +use domain::bits::DNameBuf; + +pub struct Connecter { + handle: Handle, + resolver: Resolver, + lookup: Option, + srvs: Option, + connects: Vec, +} + +impl Connecter { + pub fn from_lookup(handle: Handle, domain: &str, srv: &str, fallback_port: u16) -> Result { + let domain = try!( + DNameBuf::from_str(domain) + .map_err(|e| format!("{}", e)) + ); + let srv = try!( + DNameBuf::from_str(srv) + .map_err(|e| format!("{}", e)) + ); + + let resolver = Resolver::new(&handle); + let lookup = lookup_srv(resolver.clone(), srv, domain, fallback_port); + + Ok(Connecter { + handle, + resolver, + lookup: Some(lookup), + srvs: None, + connects: vec![], + }) + } +} + +impl Future for Connecter { + type Item = TcpStream; + type Error = String; + + fn poll(&mut self) -> Poll { + match self.lookup.as_mut().map(|mut lookup| lookup.poll()) { + None => (), + Some(Ok(Async::NotReady)) => (), + Some(Ok(Async::Ready(found_srvs))) => { + self.lookup = None; + match found_srvs { + Some(srvs) => + self.srvs = Some(srvs.to_stream(self.resolver.clone())), + None => + return Err("No SRV records".to_owned()), + } + }, + Some(Err(e)) => + return Err(format!("{}", e)), + } + + match self.srvs.as_mut().map(|mut srv| srv.poll()) { + None => (), + Some(Ok(Async::NotReady)) => (), + Some(Ok(Async::Ready(None))) => + self.srvs = None, + Some(Ok(Async::Ready(Some(srv_item)))) => { + for addr in srv_item.to_socket_addrs() { + println!("Connect to {}", addr); + let connect = TcpStream::connect(&addr, &self.handle); + self.connects.push(connect); + } + }, + Some(Err(e)) => + return Err(format!("{}", e)), + } + + for mut connect in &mut self.connects { + match connect.poll() { + Ok(Async::NotReady) => (), + Ok(Async::Ready(tcp_stream)) => + // Success! + return Ok(Async::Ready(tcp_stream)), + Err(e) => + return Err(format!("{}", e)), + } + } + + Ok(Async::NotReady) + } +} diff --git a/src/lib.rs b/src/lib.rs index 6f9cf09c786f1a876ced54613678eb378791cad0..bbc5d2ed4842e4382d1f5c23ab4870d7650300f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ extern crate tokio_tls; extern crate sasl; extern crate rustc_serialize as serialize; extern crate jid; - +extern crate domain; pub mod xmpp_codec; pub mod xmpp_stream; @@ -18,5 +18,6 @@ mod tcp; pub use tcp::*; mod starttls; pub use starttls::*; +mod happy_eyeballs; mod client; pub use client::{Client, ClientEvent}; diff --git a/src/tcp.rs b/src/tcp.rs index 2f207ec513a00998dea4e03745fb28a6c7172a5e..06aa7b83efd151520458218c2dfb52ee1961d95e 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -27,6 +27,14 @@ impl TcpClient { jid, } } + + pub fn from_stream(jid: Jid, tcp_stream: TcpStream) -> Self { + let start = XMPPStream::from_stream(tcp_stream, jid.clone()); + TcpClient { + state: TcpClientState::Start(start), + jid, + } + } } impl Future for TcpClient { From 7e18db0717736d7db1e422ed51724b44995f088e Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 13 Jul 2017 22:04:54 +0200 Subject: [PATCH 0368/1020] echo_bot: document, simplify --- examples/echo_bot.rs | 61 ++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index f5923f016833b3a3d1ad4c1f1db1b77c23028ad3..7f27cfa8a5dc40cabc07b97fea2bf33decb4651e 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -9,10 +9,16 @@ use futures::{Future, Stream, Sink, future}; use tokio_xmpp::Client; fn main() { + // Tokio_core context let mut core = Core::new().unwrap(); + // Client instance let client = Client::new("astrobot@example.org", "", &core.handle()).unwrap(); + // Make the two interfaces for sending and receiving independent + // of each other so we can move one into a closure. let (sink, stream) = client.split(); + // Wrap sink in Option so that we can take() it for the send(self) + // to consume and return it back when ready. let mut sink = Some(sink); let mut send = move |stanza| { sink = Some( @@ -23,38 +29,35 @@ fn main() { .expect("sink.send") ); }; + // Main loop, processes events let done = stream.for_each(|event| { - let result: Box> = - if event.is_online() { - println!("Online!"); + if event.is_online() { + println!("Online!"); - let presence = make_presence(); - send(presence); - Box::new( - future::ok(()) - ) - } else if let Some(stanza) = event.as_stanza() { - if stanza.name == "message" && - stanza.get_attribute("type", None) != Some("error") { - let from = stanza.get_attribute("from", None); - let body = stanza.get_child("body", Some("jabber:client")) - .map(|el| el.content_str()); + let presence = make_presence(); + send(presence); + } else if let Some(stanza) = event.as_stanza() { + if stanza.name == "message" && + stanza.get_attribute("type", None) != Some("error") { + // This is a message we'll echo + let from = stanza.get_attribute("from", None); + let body = stanza.get_child("body", Some("jabber:client")) + .map(|el| el.content_str()); - match (from.as_ref(), body) { - (Some(from), Some(body)) => { - let reply = make_reply(from, body); - send(reply); - }, - _ => (), - }; - } - Box::new(future::ok(())) - } else { - Box::new(future::ok(())) - }; - result + match (from.as_ref(), body) { + (Some(from), Some(body)) => { + let reply = make_reply(from, body); + send(reply); + }, + _ => (), + }; + } + } + + Box::new(future::ok(())) }); - + + // Start polling `done` match core.run(done) { Ok(_) => (), Err(e) => { @@ -64,6 +67,7 @@ fn main() { } } +// Construct a fn make_presence() -> xml::Element { let mut presence = xml::Element::new("presence".to_owned(), None, vec![]); presence.tag(xml::Element::new("status".to_owned(), None, vec![])) @@ -73,6 +77,7 @@ fn make_presence() -> xml::Element { presence } +// Construct a chat fn make_reply(to: &str, body: String) -> xml::Element { let mut message = xml::Element::new( "message".to_owned(), From 2335bab8443322f5483441b4e124dd0a4a6615d0 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 13 Jul 2017 22:17:29 +0200 Subject: [PATCH 0369/1020] happy_eyeballs: improvements --- src/happy_eyeballs.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index aa80df20a0e2033d4591e33f9b6eda2e5fcc6b98..039007bfb43ab1170ab7b5cdc777f6fb062326a1 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -1,4 +1,6 @@ use std::str::FromStr; +use std::collections::HashMap; +use std::net::SocketAddr; use futures::*; use tokio_core::reactor::Handle; use tokio_core::net::{TcpStream, TcpStreamNew}; @@ -11,7 +13,7 @@ pub struct Connecter { resolver: Resolver, lookup: Option, srvs: Option, - connects: Vec, + connects: HashMap, } impl Connecter { @@ -33,7 +35,7 @@ impl Connecter { resolver, lookup: Some(lookup), srvs: None, - connects: vec![], + connects: HashMap::new(), }) } } @@ -65,27 +67,37 @@ impl Future for Connecter { Some(Ok(Async::Ready(None))) => self.srvs = None, Some(Ok(Async::Ready(Some(srv_item)))) => { + let handle = &self.handle; for addr in srv_item.to_socket_addrs() { - println!("Connect to {}", addr); - let connect = TcpStream::connect(&addr, &self.handle); - self.connects.push(connect); + self.connects.entry(addr) + .or_insert_with(|| { + println!("Connect to {}", addr); + TcpStream::connect(&addr, handle) + }); } }, Some(Err(e)) => return Err(format!("{}", e)), } - for mut connect in &mut self.connects { + for mut connect in self.connects.values_mut() { match connect.poll() { Ok(Async::NotReady) => (), Ok(Async::Ready(tcp_stream)) => // Success! return Ok(Async::Ready(tcp_stream)), Err(e) => - return Err(format!("{}", e)), + println!("{}", e), } } - + + if self.lookup.is_none() && + self.srvs.is_none() && + self.connects.is_empty() + { + return Err("All connection attempts failed".to_owned()); + } + Ok(Async::NotReady) } } From 6e5f86632f78f40589f90d8af4a00b794bfc6d19 Mon Sep 17 00:00:00 2001 From: Astro Date: Fri, 14 Jul 2017 01:58:25 +0200 Subject: [PATCH 0370/1020] xmpp_codec: add remedies for truncated utf8 --- src/xmpp_codec.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 3 deletions(-) diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 2551cc44fa13e8310ca066fe32e8397e668d465a..1069372f2280aa0112a222c7bd3b220e03fe4c56 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -56,6 +56,7 @@ pub enum Packet { pub struct XMPPCodec { parser: xml::Parser, root: Option, + buf: Vec, } impl XMPPCodec { @@ -63,6 +64,7 @@ impl XMPPCodec { XMPPCodec { parser: xml::Parser::new(), root: None, + buf: vec![], } } } @@ -72,15 +74,40 @@ impl Decoder for XMPPCodec { type Error = Error; fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { - match from_utf8(buf.take().as_ref()) { + let buf1: Box> = + if self.buf.len() > 0 && buf.len() > 0 { + let mut prefix = std::mem::replace(&mut self.buf, vec![]); + prefix.extend_from_slice(buf.take().as_ref()); + Box::new(prefix) + } else { + Box::new(buf.take()) + }; + let buf1 = buf1.as_ref().as_ref(); + match from_utf8(buf1) { Ok(s) => { if s.len() > 0 { println!("<< {}", s); self.parser.feed_str(s); } }, - Err(e) => - return Err(Error::new(ErrorKind::InvalidInput, e)), + // Remedies for truncated utf8 + Err(e) if e.valid_up_to() >= buf1.len() - 3 => { + // Prepare all the valid data + let mut b = BytesMut::with_capacity(e.valid_up_to()); + b.put(&buf1[0..e.valid_up_to()]); + + // Retry + let result = self.decode(&mut b); + + // Keep the tail back in + self.buf.extend_from_slice(&buf1[e.valid_up_to()..]); + + return result; + }, + Err(e) => { + println!("error {} at {}/{} in {:?}", e, e.valid_up_to(), buf1.len(), buf1); + return Err(Error::new(ErrorKind::InvalidInput, e)); + }, } let mut new_root: Option = None; @@ -171,3 +198,83 @@ impl Encoder for XMPPCodec { .map_err(|_| Error::from(ErrorKind::InvalidInput)) } } + +#[cfg(test)] +mod tests { + use super::*; + use bytes::BytesMut; + + #[test] + fn test_stream_start() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + } + + #[test] + fn test_truncated_stanza() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + + b.clear(); + b.put(r"ß true, + _ => false, + }); + + b.clear(); + b.put(r">"); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::Stanza(ref el))) + if el.name == "test" + && el.content_str() == "ß" + => true, + _ => false, + }); + } + + #[test] + fn test_truncated_utf8() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + + b.clear(); + b.put(&b"\xc3"[..]); + let r = c.decode(&mut b); + assert!(match r { + Ok(None) => true, + _ => false, + }); + + b.clear(); + b.put(&b"\x9f"[..]); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::Stanza(ref el))) + if el.name == "test" + && el.content_str() == "ß" + => true, + _ => false, + }); + } +} From 161e5dcc9bd63fcd1c25084e8475c711bda2918c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 15 Jul 2017 11:37:29 +0100 Subject: [PATCH 0371/1020] jingle: Type Content::name better. --- src/jingle.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 224bb54332349cd299f4d057233434480576a84f..ece7fa023a89c934ad4400dd93047c8368e731f8 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -43,11 +43,13 @@ generate_attribute!(Senders, "senders", { Responder => "responder", }, Default = Both); +generate_id!(ContentId); + #[derive(Debug, Clone)] pub struct Content { pub creator: Creator, pub disposition: String, // TODO: the list of values is defined, use an enum! - pub name: String, + pub name: ContentId, pub senders: Senders, pub description: Option, pub transport: Option, @@ -353,7 +355,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].creator, Creator::Initiator); - assert_eq!(jingle.contents[0].name, "coucou"); + assert_eq!(jingle.contents[0].name, ContentId(String::from("coucou"))); assert_eq!(jingle.contents[0].senders, Senders::Both); assert_eq!(jingle.contents[0].disposition, "session"); From 945ae350ac650e503352f6b095c9286f82e7921b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 15 Jul 2017 11:38:44 +0100 Subject: [PATCH 0372/1020] Add a jingle message parser and serialiser. --- src/jingle_message.rs | 132 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +- src/ns.rs | 3 + 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/jingle_message.rs diff --git a/src/jingle_message.rs b/src/jingle_message.rs new file mode 100644 index 0000000000000000000000000000000000000000..d61133427655ebeb9037c259279bdc37dd91341b --- /dev/null +++ b/src/jingle_message.rs @@ -0,0 +1,132 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::convert::TryFrom; + +use minidom::Element; + +use error::Error; + +use jingle::SessionId; + +use ns; + +#[derive(Debug, Clone)] +pub enum JingleMI { + Propose { + sid: SessionId, + // TODO: Use a more specialised type here. + description: Element, + }, + Retract(SessionId), + Accept(SessionId), + Proceed(SessionId), + Reject(SessionId), +} + +fn get_sid(elem: Element) -> Result { + for (attr, _) in elem.attrs() { + if attr != "id" { + return Err(Error::ParseError("Unknown attribute in Jingle message element.")); + } + } + Ok(SessionId(get_attr!(elem, "id", required))) +} + +fn check_empty_and_get_sid(elem: Element) -> Result { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in Jingle message element.")); + } + get_sid(elem) +} + +impl TryFrom for JingleMI { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if elem.ns() != Some(ns::JINGLE_MESSAGE) { + return Err(Error::ParseError("This is not a Jingle message element.")); + } + Ok(match elem.name() { + "propose" => { + let mut description = None; + for child in elem.children() { + if child.name() != "description" { + return Err(Error::ParseError("Unknown child in propose element.")); + } + if description.is_some() { + return Err(Error::ParseError("Too many children in propose element.")); + } + description = Some(child.clone()); + } + JingleMI::Propose { + sid: get_sid(elem)?, + description: description.ok_or(Error::ParseError("Propose element doesn’t contain a description."))?, + } + }, + "retract" => JingleMI::Retract(check_empty_and_get_sid(elem)?), + "accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?), + "proceed" => JingleMI::Proceed(check_empty_and_get_sid(elem)?), + "reject" => JingleMI::Reject(check_empty_and_get_sid(elem)?), + _ => return Err(Error::ParseError("This is not a Jingle message element.")), + }) + } +} + +impl From for Element { + fn from(jingle_mi: JingleMI) -> Element { + match jingle_mi { + JingleMI::Propose { sid, description } => { + Element::builder("propose") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + .append(description) + }, + JingleMI::Retract(sid) => { + Element::builder("retract") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + } + JingleMI::Accept(sid) => { + Element::builder("accept") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + } + JingleMI::Proceed(sid) => { + Element::builder("proceed") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + } + JingleMI::Reject(sid) => { + Element::builder("reject") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + } + }.build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + JingleMI::try_from(elem).unwrap(); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = JingleMI::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in propose element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index cb8b9d4a42755afdb996f960be57b3697e27dbfb..f82e6df349d6d63df7f87a9fb64fb7e2261f8e51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ macro_rules! generate_attribute { macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $elem(String); + pub struct $elem(pub String); impl FromStr for $elem { type Err = Error; fn from_str(s: &str) -> Result<$elem, Error> { @@ -214,6 +214,9 @@ pub mod mam; /// XEP-0319: Last User Interaction in Presence pub mod idle; +/// XEP-0353: Jingle Message Initiation +pub mod jingle_message; + /// XEP-0359: Unique and Stable Stanza IDs pub mod stanza_id; diff --git a/src/ns.rs b/src/ns.rs index fbc0048f952bb04d7691628a65692b818e845468..7edee1e4aec9af43771c630cdd6380871c075971 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -104,6 +104,9 @@ pub const MAM: &str = "urn:xmpp:mam:2"; /// XEP-0319: Last User Interaction in Presence pub const IDLE: &str = "urn:xmpp:idle:1"; +/// XEP-0353: Jingle Message Initiation +pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; + /// XEP-0359: Unique and Stable Stanza IDs pub const SID: &str = "urn:xmpp:sid:0"; From fa08591162709ad3ea50853319cf9d714be35e87 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 15 Jul 2017 22:35:50 +0200 Subject: [PATCH 0373/1020] echo_bot: take jid, password from cmdline args --- examples/echo_bot.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 7f27cfa8a5dc40cabc07b97fea2bf33decb4651e..ee9925084bfb02e2d5959f2dccc5c61dba6511b0 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -4,15 +4,25 @@ extern crate tokio_xmpp; extern crate jid; extern crate xml; +use std::env::args; +use std::process::exit; use tokio_core::reactor::Core; use futures::{Future, Stream, Sink, future}; use tokio_xmpp::Client; fn main() { - // Tokio_core context + let args: Vec = args().collect(); + if args.len() != 3 { + println!("Usage: {} ", args[0]); + exit(1); + } + let jid = &args[1]; + let password = &args[2]; + + // tokio_core context let mut core = Core::new().unwrap(); // Client instance - let client = Client::new("astrobot@example.org", "", &core.handle()).unwrap(); + let client = Client::new(jid, password, core.handle()).unwrap(); // Make the two interfaces for sending and receiving independent // of each other so we can move one into a closure. From d4bd64370ce93218de436d1f380a23888e2766aa Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 17 Jul 2017 20:53:00 +0200 Subject: [PATCH 0374/1020] switch from rustxml to minidom, doesn't work --- Cargo.toml | 6 +- examples/echo_bot.rs | 49 ++++----- src/client/auth.rs | 56 +++++----- src/client/bind.rs | 57 +++++------ src/client/event.rs | 8 +- src/client/mod.rs | 6 +- src/lib.rs | 4 +- src/starttls.rs | 11 +- src/stream_start.rs | 4 +- src/xmpp_codec.rs | 239 ++++++++++++++++++++++++++----------------- src/xmpp_stream.rs | 6 +- 11 files changed, 241 insertions(+), 205 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6410ca2a79a76462a1e7ff382338f09cdbd07d79..e80f7bbc51da09d90ef29fde73df50fe8579e5c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,10 @@ authors = ["Astro "] futures = "*" tokio-core = "0.1.7" tokio-io = "*" -bytes = "*" -RustyXML = "*" +bytes = "0.4.4" +xml5ever = "*" +tendril = "*" +minidom = { path = "../../programs/minidom-rs" } native-tls = "*" tokio-tls = "*" sasl = "*" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index ee9925084bfb02e2d5959f2dccc5c61dba6511b0..8ba944ef5b2d50e6fb11b1cd979bab17b479607a 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -2,13 +2,14 @@ extern crate futures; extern crate tokio_core; extern crate tokio_xmpp; extern crate jid; -extern crate xml; +extern crate minidom; use std::env::args; use std::process::exit; use tokio_core::reactor::Core; use futures::{Future, Stream, Sink, future}; use tokio_xmpp::Client; +use minidom::Element; fn main() { let args: Vec = args().collect(); @@ -47,14 +48,14 @@ fn main() { let presence = make_presence(); send(presence); } else if let Some(stanza) = event.as_stanza() { - if stanza.name == "message" && - stanza.get_attribute("type", None) != Some("error") { + if stanza.name() == "message" && + stanza.attr("type") != Some("error") { // This is a message we'll echo - let from = stanza.get_attribute("from", None); - let body = stanza.get_child("body", Some("jabber:client")) - .map(|el| el.content_str()); + let from = stanza.attr("from"); + let body = stanza.get_child("body", "jabber:client") + .map(|el| el.text()); - match (from.as_ref(), body) { + match (from, body) { (Some(from), Some(body)) => { let reply = make_reply(from, body); send(reply); @@ -78,24 +79,24 @@ fn main() { } // Construct a -fn make_presence() -> xml::Element { - let mut presence = xml::Element::new("presence".to_owned(), None, vec![]); - presence.tag(xml::Element::new("status".to_owned(), None, vec![])) - .text("chat".to_owned()); - presence.tag(xml::Element::new("show".to_owned(), None, vec![])) - .text("Echoing messages".to_owned()); - presence +fn make_presence() -> Element { + Element::builder("presence") + .append(Element::builder("status") + .append("chat") + .build()) + .append(Element::builder("show") + .append("Echoing messages") + .build()) + .build() } // Construct a chat -fn make_reply(to: &str, body: String) -> xml::Element { - let mut message = xml::Element::new( - "message".to_owned(), - None, - vec![("type".to_owned(), None, "chat".to_owned()), - ("to".to_owned(), None, to.to_owned())] - ); - message.tag(xml::Element::new("body".to_owned(), None, vec![])) - .text(body); - message +fn make_reply(to: &str, body: String) -> Element { + Element::builder("message") + .attr("type", "chat") + .attr("to", to) + .append(Element::builder("body") + .append(body) + .build()) + .build() } diff --git a/src/client/auth.rs b/src/client/auth.rs index c6aa79d67975e09a4aedc2f76b9a3fe5a5ac3f06..8716fea76edf9d0c6aa67086f1685fc00ee4dbd8 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -2,7 +2,7 @@ use std::mem::replace; use futures::*; use futures::sink; use tokio_io::{AsyncRead, AsyncWrite}; -use xml; +use minidom::Element; use sasl::common::Credentials; use sasl::common::scram::*; use sasl::client::Mechanism; @@ -37,12 +37,13 @@ impl ClientAuth { ]; let mech_names: Vec = - match stream.stream_features.get_child("mechanisms", Some(NS_XMPP_SASL)) { + match stream.stream_features.get_child("mechanisms", NS_XMPP_SASL) { None => return Err("No auth mechanisms".to_owned()), Some(mechs) => - mechs.get_children("mechanism", Some(NS_XMPP_SASL)) - .map(|mech_el| mech_el.content_str()) + mechs.children() + .filter(|child| child.is("mechanism", NS_XMPP_SASL)) + .map(|mech_el| mech_el.text()) .collect(), }; println!("SASL mechanisms offered: {:?}", mech_names); @@ -58,7 +59,7 @@ impl ClientAuth { }; this.send( stream, - "auth", &[("mechanism".to_owned(), name)], + "auth", &[("mechanism", &name)], &initial ); return Ok(this); @@ -68,15 +69,13 @@ impl ClientAuth { Err("No supported SASL mechanism available".to_owned()) } - fn send(&mut self, stream: XMPPStream, nonza_name: &str, attrs: &[(String, String)], content: &[u8]) { - let mut nonza = xml::Element::new( - nonza_name.to_owned(), - Some(NS_XMPP_SASL.to_owned()), - attrs.iter() - .map(|&(ref name, ref value)| (name.clone(), None, value.clone())) - .collect() - ); - nonza.text(content.to_base64(base64::URL_SAFE)); + fn send(&mut self, stream: XMPPStream, nonza_name: &str, attrs: &[(&str, &str)], content: &[u8]) { + let nonza = Element::builder(nonza_name) + .ns(NS_XMPP_SASL); + let nonza = attrs.iter() + .fold(nonza, |nonza, &(name, value)| nonza.attr(name, value)) + .append(content.to_base64(base64::URL_SAFE)) + .build(); let send = stream.send(Packet::Stanza(nonza)); @@ -108,11 +107,11 @@ impl Future for ClientAuth { ClientAuthState::WaitRecv(mut stream) => match stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.name == "challenge" - && stanza.ns == Some(NS_XMPP_SASL.to_owned()) => + if stanza.name() == "challenge" + && stanza.ns() == Some(NS_XMPP_SASL) => { let content = try!( - stanza.content_str() + stanza.text() .from_base64() .map_err(|e| format!("{}", e)) ); @@ -121,29 +120,24 @@ impl Future for ClientAuth { self.poll() }, Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.name == "success" - && stanza.ns == Some(NS_XMPP_SASL.to_owned()) => + if stanza.name() == "success" + && stanza.ns() == Some(NS_XMPP_SASL) => { let start = stream.restart(); self.state = ClientAuthState::Start(start); self.poll() }, Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.name == "failure" - && stanza.ns == Some(NS_XMPP_SASL.to_owned()) => + if stanza.name() == "failure" + && stanza.ns() == Some(NS_XMPP_SASL) => { let mut e = None; - for child in &stanza.children { - match child { - &xml::Xml::ElementNode(ref child) => { - e = Some(child.name.clone()); - break - }, - _ => (), - } + for child in stanza.children() { + e = Some(child.name().clone()); + break } - let e = e.unwrap_or_else(|| "Authentication failure".to_owned()); - Err(e) + let e = e.unwrap_or_else(|| "Authentication failure"); + Err(e.to_owned()) }, Ok(Async::Ready(event)) => { println!("ClientAuth ignore {:?}", event); diff --git a/src/client/bind.rs b/src/client/bind.rs index 45f68e6f6f5a8ccf16b8c99dd01dd581c2f3990e..6a59503c4b50c53ba9d4be5b5ff9542b1b03fd96 100644 --- a/src/client/bind.rs +++ b/src/client/bind.rs @@ -4,8 +4,8 @@ use std::str::FromStr; use futures::*; use futures::sink; use tokio_io::{AsyncRead, AsyncWrite}; -use xml; use jid::Jid; +use minidom::Element; use xmpp_codec::*; use xmpp_stream::*; @@ -25,7 +25,7 @@ impl ClientBind { /// the stream for anything else until the resource binding /// req/resp are done. pub fn new(stream: XMPPStream) -> Self { - match stream.stream_features.get_child("bind", Some(NS_XMPP_BIND)) { + match stream.stream_features.get_child("bind", NS_XMPP_BIND) { None => // No resource binding available, // return the (probably // usable) stream immediately @@ -39,31 +39,22 @@ impl ClientBind { } } -fn make_bind_request(resource: Option<&String>) -> xml::Element { - let mut iq = xml::Element::new( - "iq".to_owned(), - None, - vec![("type".to_owned(), None, "set".to_owned()), - ("id".to_owned(), None, BIND_REQ_ID.to_owned())] - ); - { - let bind_el = iq.tag( - xml::Element::new( - "bind".to_owned(), - Some(NS_XMPP_BIND.to_owned()), - vec![] - )); - resource.map(|resource| { - let resource_el = bind_el.tag( - xml::Element::new( - "resource".to_owned(), - Some(NS_XMPP_BIND.to_owned()), - vec![] - )); - resource_el.text(resource.clone()); - }); +fn make_bind_request(resource: Option<&String>) -> Element { + let iq = Element::builder("iq") + .attr("type", "set") + .attr("id", BIND_REQ_ID); + let mut bind_el = Element::builder("bind") + .ns(NS_XMPP_BIND); + match resource { + Some(resource) => { + let resource_el = Element::builder("resource") + .append(resource); + bind_el = bind_el.append(resource_el.build()); + }, + None => (), } - iq + iq.append(bind_el.build()) + .build() } impl Future for ClientBind { @@ -93,9 +84,9 @@ impl Future for ClientBind { ClientBind::WaitRecv(mut stream) => { match stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(ref iq)))) - if iq.name == "iq" - && iq.get_attribute("id", None) == Some(BIND_REQ_ID) => { - match iq.get_attribute("type", None) { + if iq.name() == "iq" + && iq.attr("id") == Some(BIND_REQ_ID) => { + match iq.attr("type") { Some("result") => { get_bind_response_jid(&iq) .map(|jid| stream.jid = jid); @@ -123,13 +114,13 @@ impl Future for ClientBind { } } -fn get_bind_response_jid(iq: &xml::Element) -> Option { - iq.get_child("bind", Some(NS_XMPP_BIND)) +fn get_bind_response_jid(iq: &Element) -> Option { + iq.get_child("bind", NS_XMPP_BIND) .and_then(|bind_el| - bind_el.get_child("jid", Some(NS_XMPP_BIND)) + bind_el.get_child("jid", NS_XMPP_BIND) ) .and_then(|jid_el| - Jid::from_str(&jid_el.content_str()) + Jid::from_str(&jid_el.text()) .ok() ) } diff --git a/src/client/event.rs b/src/client/event.rs index e54cff4121e0f60bdd0d24ea379d3ddce3c75f48..7aba984a480ef6e44421c4f0da488f45577453f3 100644 --- a/src/client/event.rs +++ b/src/client/event.rs @@ -1,10 +1,10 @@ -use xml; +use minidom::Element; #[derive(Debug)] pub enum Event { Online, Disconnected, - Stanza(xml::Element), + Stanza(Element), } impl Event { @@ -17,12 +17,12 @@ impl Event { pub fn is_stanza(&self, name: &str) -> bool { match self { - &Event::Stanza(ref stanza) => stanza.name == name, + &Event::Stanza(ref stanza) => stanza.name() == name, _ => false, } } - pub fn as_stanza(&self) -> Option<&xml::Element> { + pub fn as_stanza(&self) -> Option<&Element> { match self { &Event::Stanza(ref stanza) => Some(stanza), _ => None, diff --git a/src/client/mod.rs b/src/client/mod.rs index d0be336a62b527f7ae27fb7240df2d7ff152fa47..de0bdb6664bdb0e326e778f307ae97372628a9c7 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -6,8 +6,8 @@ use tokio_core::net::TcpStream; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_tls::TlsStream; use futures::*; +use minidom::Element; use jid::{Jid, JidParseError}; -use xml; use sasl::common::{Credentials, ChannelBinding}; use super::xmpp_codec::Packet; @@ -76,7 +76,7 @@ impl Client { fn can_starttls(stream: &xmpp_stream::XMPPStream) -> bool { stream.stream_features - .get_child("starttls", Some(NS_XMPP_TLS)) + .get_child("starttls", NS_XMPP_TLS) .is_some() } @@ -151,7 +151,7 @@ impl Stream for Client { } impl Sink for Client { - type SinkItem = xml::Element; + type SinkItem = Element; type SinkError = String; fn start_send(&mut self, item: Self::SinkItem) -> StartSend { diff --git a/src/lib.rs b/src/lib.rs index bbc5d2ed4842e4382d1f5c23ab4870d7650300f9..c150760d0785c679929f8d8c430b6eba20eff210 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,9 @@ extern crate futures; extern crate tokio_core; extern crate tokio_io; extern crate bytes; -extern crate xml; +extern crate xml5ever; +extern crate tendril; +extern crate minidom; extern crate native_tls; extern crate tokio_tls; extern crate sasl; diff --git a/src/starttls.rs b/src/starttls.rs index 79ee731c236e444cd50a541dfb3225ad9ab8dee3..fe6c64105805fe9d08d68b2a37588f2c591c22d5 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -5,7 +5,7 @@ use futures::sink; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_tls::*; use native_tls::TlsConnector; -use xml; +use minidom::Element; use jid::Jid; use xmpp_codec::*; @@ -34,10 +34,9 @@ impl StartTlsClient { pub fn from_stream(xmpp_stream: XMPPStream) -> Self { let jid = xmpp_stream.jid.clone(); - let nonza = xml::Element::new( - "starttls".to_owned(), Some(NS_XMPP_TLS.to_owned()), - vec![] - ); + let nonza = Element::builder("starttls") + .ns(NS_XMPP_TLS) + .build(); let packet = Packet::Stanza(nonza); let send = xmpp_stream.send(packet); @@ -72,7 +71,7 @@ impl Future for StartTlsClient { StartTlsClientState::AwaitProceed(mut xmpp_stream) => match xmpp_stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.name == "proceed" => + if stanza.name() == "proceed" => { let stream = xmpp_stream.stream.into_inner(); let connect = TlsConnector::builder().unwrap() diff --git a/src/stream_start.rs b/src/stream_start.rs index 11b074e6c7cb119c186b6211ab4961cecf936a5e..bbccadd139a2611915c82f4957fba7e31b53358b 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -76,8 +76,8 @@ impl Future for StreamStart { StreamStartState::RecvFeatures(mut stream, stream_attrs) => match stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => - if stanza.name == "features" - && stanza.ns == Some(NS_XMPP_STREAM.to_owned()) { + if stanza.name() == "features" + && stanza.ns() == Some(NS_XMPP_STREAM) { let stream = XMPPStream::new(self.jid.clone(), stream, stream_attrs, stanza); (StreamStartState::Invalid, Ok(Async::Ready(stream))) } else { diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 1069372f2280aa0112a222c7bd3b220e03fe4c56..e6a1bd2c543f94ebd3949d453a377998b7e91f53 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -1,69 +1,139 @@ use std; +use std::default::Default; +use std::iter::FromIterator; +use std::cell::RefCell; +use std::rc::Rc; use std::fmt::Write; use std::str::from_utf8; use std::io::{Error, ErrorKind}; use std::collections::HashMap; +use std::collections::vec_deque::VecDeque; use tokio_io::codec::{Encoder, Decoder}; -use xml; +use minidom::Element; +use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind}; use bytes::*; -const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/"; +// const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/"; -pub type Attributes = HashMap<(String, Option), String>; +#[derive(Debug)] +pub enum Packet { + Error(Box), + StreamStart(HashMap), + Stanza(Element), + Text(String), + StreamEnd, +} -struct XMPPRoot { - builder: xml::ElementBuilder, - pub attributes: Attributes, +struct ParserSink { + // Ready stanzas + queue: Rc>>, + // Parsing stack + stack: Vec, } -impl XMPPRoot { - fn new(root: xml::StartTag) -> Self { - let mut builder = xml::ElementBuilder::new(); - let mut attributes = HashMap::new(); - for (name_ns, value) in root.attributes { - match name_ns { - (ref name, None) if name == "xmlns" => - builder.set_default_ns(value), - (ref prefix, Some(ref ns)) if ns == NS_XMLNS => - builder.define_prefix(prefix.to_owned(), value), - _ => { - attributes.insert(name_ns, value); - }, - } +impl ParserSink { + pub fn new(queue: Rc>>) -> Self { + ParserSink { + queue, + stack: vec![], } + } - XMPPRoot { - builder: builder, - attributes: attributes, - } + fn push_queue(&self, pkt: Packet) { + println!("push: {:?}", pkt); + self.queue.borrow_mut().push_back(pkt); } - fn handle_event(&mut self, event: Result) - -> Option> { - self.builder.handle_event(event) + fn handle_start_tag(&mut self, tag: Tag) { + let el = tag_to_element(&tag); + self.stack.push(el); + } + + fn handle_end_tag(&mut self) { + let el = self.stack.pop().unwrap(); + match self.stack.len() { + // + 0 => + self.push_queue(Packet::StreamEnd), + // + 1 => + self.push_queue(Packet::Stanza(el)), + len => { + let parent = &mut self.stack[len - 1]; + parent.append_child(el); + }, + } } } -#[derive(Debug)] -pub enum Packet { - Error(Box), - StreamStart(HashMap), - Stanza(xml::Element), - Text(String), - StreamEnd, +fn tag_to_element(tag: &Tag) -> Element { + let el_builder = Element::builder(tag.name.local.as_ref()) + .ns(tag.name.ns.as_ref()); + let el_builder = tag.attrs.iter().fold( + el_builder, + |el_builder, attr| el_builder.attr( + attr.name.local.as_ref(), + attr.value.as_ref() + ) + ); + el_builder.build() +} + +impl TokenSink for ParserSink { + fn process_token(&mut self, token: Token) { + println!("Token: {:?}", token); + match token { + Token::TagToken(tag) => match tag.kind { + TagKind::StartTag => + self.handle_start_tag(tag), + TagKind::EndTag => + self.handle_end_tag(), + TagKind::EmptyTag => { + self.handle_start_tag(tag); + self.handle_end_tag(); + }, + TagKind::ShortTag => + self.push_queue(Packet::Error(Box::new(Error::new(ErrorKind::InvalidInput, "ShortTag")))), + }, + Token::CharacterTokens(tendril) => + match self.stack.len() { + 0 | 1 => + self.push_queue(Packet::Text(tendril.into())), + len => { + let el = &mut self.stack[len - 1]; + el.append_text_node(tendril); + }, + }, + Token::EOFToken => + self.push_queue(Packet::StreamEnd), + Token::ParseError(s) => { + println!("ParseError: {:?}", s); + self.push_queue(Packet::Error(Box::new(Error::new(ErrorKind::InvalidInput, (*s).to_owned())))) + }, + _ => (), + } + } + + // fn end(&mut self) { + // } } pub struct XMPPCodec { - parser: xml::Parser, - root: Option, + parser: XmlTokenizer, + // For handling truncated utf8 buf: Vec, + queue: Rc>>, } impl XMPPCodec { pub fn new() -> Self { + let queue = Rc::new(RefCell::new((VecDeque::new()))); + let sink = ParserSink::new(queue.clone()); + // TODO: configure parser? + let parser = XmlTokenizer::new(sink, Default::default()); XMPPCodec { - parser: xml::Parser::new(), - root: None, + parser, + queue, buf: vec![], } } @@ -74,6 +144,7 @@ impl Decoder for XMPPCodec { type Error = Error; fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { + println!("decode {} bytes", buf.len()); let buf1: Box> = if self.buf.len() > 0 && buf.len() > 0 { let mut prefix = std::mem::replace(&mut self.buf, vec![]); @@ -87,7 +158,8 @@ impl Decoder for XMPPCodec { Ok(s) => { if s.len() > 0 { println!("<< {}", s); - self.parser.feed_str(s); + let tendril = FromIterator::from_iter(s.chars()); + self.parser.feed(tendril); } }, // Remedies for truncated utf8 @@ -110,57 +182,11 @@ impl Decoder for XMPPCodec { }, } - let mut new_root: Option = None; - let mut result = None; - for event in &mut self.parser { - match self.root { - None => { - // Expecting - match event { - Ok(xml::Event::ElementStart(start_tag)) => { - let mut attrs: HashMap = HashMap::new(); - for (&(ref name, _), value) in &start_tag.attributes { - attrs.insert(name.to_owned(), value.to_owned()); - } - result = Some(Packet::StreamStart(attrs)); - self.root = Some(XMPPRoot::new(start_tag)); - break - }, - Err(e) => { - result = Some(Packet::Error(Box::new(e))); - break - }, - _ => - (), - } - } - - Some(ref mut root) => { - match root.handle_event(event) { - None => (), - Some(Ok(stanza)) => { - // Emit the stanza - result = Some(Packet::Stanza(stanza)); - break - }, - Some(Err(e)) => { - result = Some(Packet::Error(Box::new(e))); - break - } - }; - }, - } - - match new_root.take() { - None => (), - Some(root) => self.root = Some(root), - } - } - + let result = self.queue.borrow_mut().pop_front(); Ok(result) } - fn decode_eof(&mut self, buf: &mut BytesMut) -> Result, Error> { + fn decode_eof(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { self.decode(buf) } } @@ -170,35 +196,56 @@ impl Encoder for XMPPCodec { type Error = Error; fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { + println!("encode {:?}", item); match item { Packet::StreamStart(start_attrs) => { let mut buf = String::new(); write!(buf, "\n").unwrap(); print!(">> {}", buf); write!(dst, "{}", buf) + .map_err(|_| Error::from(ErrorKind::InvalidInput)) }, Packet::Stanza(stanza) => { - println!(">> {}", stanza); - write!(dst, "{}", stanza) + println!(">> {:?}", stanza); + let mut root_ns = None; // TODO + stanza.write_to_inner(&mut dst.clone().writer(), &mut root_ns) + .map_err(|_| Error::from(ErrorKind::InvalidInput)) }, Packet::Text(text) => { - let escaped = xml::escape(&text); + let escaped = escape(&text); println!(">> {}", escaped); write!(dst, "{}", escaped) + .map_err(|_| Error::from(ErrorKind::InvalidInput)) }, // TODO: Implement all _ => Ok(()) } - .map_err(|_| Error::from(ErrorKind::InvalidInput)) } } +/// Copied from RustyXML for now +pub fn escape(input: &str) -> String { + let mut result = String::with_capacity(input.len()); + + for c in input.chars() { + match c { + '&' => result.push_str("&"), + '<' => result.push_str("<"), + '>' => result.push_str(">"), + '\'' => result.push_str("'"), + '"' => result.push_str("""), + o => result.push(o) + } + } + result +} + #[cfg(test)] mod tests { use super::*; @@ -240,8 +287,8 @@ mod tests { let r = c.decode(&mut b); assert!(match r { Ok(Some(Packet::Stanza(ref el))) - if el.name == "test" - && el.content_str() == "ß" + if el.name() == "test" + && el.text() == "ß" => true, _ => false, }); @@ -271,8 +318,8 @@ mod tests { let r = c.decode(&mut b); assert!(match r { Ok(Some(Packet::Stanza(ref el))) - if el.name == "test" - && el.content_str() == "ß" + if el.name() == "test" + && el.text() == "ß" => true, _ => false, }); diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index a09b5a984c0b625dcd5342f2411536d53a47cb16..932cbcef3e5d3d1100ea50818a78a154915504c7 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use futures::*; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; -use xml; +use minidom::Element; use jid::Jid; use xmpp_codec::*; @@ -14,14 +14,14 @@ pub struct XMPPStream { pub jid: Jid, pub stream: Framed, pub stream_attrs: HashMap, - pub stream_features: xml::Element, + pub stream_features: Element, } impl XMPPStream { pub fn new(jid: Jid, stream: Framed, stream_attrs: HashMap, - stream_features: xml::Element) -> Self { + stream_features: Element) -> Self { XMPPStream { jid, stream, stream_attrs, stream_features } } From 36d7ab474e4a8baf3c41e509e11f21665747d745 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 18 Jul 2017 20:04:34 +0200 Subject: [PATCH 0375/1020] client::auth: fix base64 encoding --- src/client/auth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/auth.rs b/src/client/auth.rs index 8716fea76edf9d0c6aa67086f1685fc00ee4dbd8..a366b2ffa8ca4aae6624a674020f515c364edada 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -74,7 +74,7 @@ impl ClientAuth { .ns(NS_XMPP_SASL); let nonza = attrs.iter() .fold(nonza, |nonza, &(name, value)| nonza.attr(name, value)) - .append(content.to_base64(base64::URL_SAFE)) + .append(content.to_base64(base64::STANDARD)) .build(); let send = stream.send(Packet::Stanza(nonza)); From b944ca4ac7a2f9b4b606af03529c607e7e885963 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 18 Jul 2017 20:12:17 +0200 Subject: [PATCH 0376/1020] make it work with minidom --- src/xmpp_codec.rs | 159 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 128 insertions(+), 31 deletions(-) diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index e6a1bd2c543f94ebd3949d453a377998b7e91f53..808f238929db867967b03a6e08428f778dd28850 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -9,8 +9,9 @@ use std::io::{Error, ErrorKind}; use std::collections::HashMap; use std::collections::vec_deque::VecDeque; use tokio_io::codec::{Encoder, Decoder}; -use minidom::Element; +use minidom::{Element, Node}; use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind}; +use xml5ever::interface::Attribute; use bytes::*; // const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/"; @@ -25,10 +26,11 @@ pub enum Packet { } struct ParserSink { - // Ready stanzas + // Ready stanzas, shared with XMPPCodec queue: Rc>>, // Parsing stack stack: Vec, + ns_stack: Vec, String>>, } impl ParserSink { @@ -36,21 +38,79 @@ impl ParserSink { ParserSink { queue, stack: vec![], + ns_stack: vec![], } } fn push_queue(&self, pkt: Packet) { - println!("push: {:?}", pkt); self.queue.borrow_mut().push_back(pkt); } + fn lookup_ns(&self, prefix: &Option) -> Option<&str> { + for nss in self.ns_stack.iter().rev() { + match nss.get(prefix) { + Some(ns) => return Some(ns), + None => (), + } + } + + None + } + fn handle_start_tag(&mut self, tag: Tag) { - let el = tag_to_element(&tag); + let mut nss = HashMap::new(); + let is_prefix_xmlns = |attr: &Attribute| attr.name.prefix.as_ref() + .map(|prefix| prefix.eq_str_ignore_ascii_case("xmlns")) + .unwrap_or(false); + for attr in &tag.attrs { + match attr.name.local.as_ref() { + "xmlns" => { + nss.insert(None, attr.value.as_ref().to_owned()); + }, + prefix if is_prefix_xmlns(attr) => { + nss.insert(Some(prefix.to_owned()), attr.value.as_ref().to_owned()); + }, + _ => (), + } + } + self.ns_stack.push(nss); + + let el = { + let mut el_builder = Element::builder(tag.name.local.as_ref()); + match self.lookup_ns(&tag.name.prefix.map(|prefix| prefix.as_ref().to_owned())) { + Some(el_ns) => el_builder = el_builder.ns(el_ns), + None => (), + } + for attr in &tag.attrs { + match attr.name.local.as_ref() { + "xmlns" => (), + _ if is_prefix_xmlns(attr) => (), + _ => { + el_builder = el_builder.attr( + attr.name.local.as_ref(), + attr.value.as_ref() + ); + }, + } + } + el_builder.build() + }; + + if self.stack.is_empty() { + let attrs = HashMap::from_iter( + el.attrs() + .map(|(name, value)| (name.to_owned(), value.to_owned())) + ); + self.push_queue(Packet::StreamStart(attrs)); + } + self.stack.push(el); } fn handle_end_tag(&mut self) { let el = self.stack.pop().unwrap(); + self.ns_stack.pop(); + match self.stack.len() { // 0 => @@ -66,22 +126,8 @@ impl ParserSink { } } -fn tag_to_element(tag: &Tag) -> Element { - let el_builder = Element::builder(tag.name.local.as_ref()) - .ns(tag.name.ns.as_ref()); - let el_builder = tag.attrs.iter().fold( - el_builder, - |el_builder, attr| el_builder.attr( - attr.name.local.as_ref(), - attr.value.as_ref() - ) - ); - el_builder.build() -} - impl TokenSink for ParserSink { fn process_token(&mut self, token: Token) { - println!("Token: {:?}", token); match token { Token::TagToken(tag) => match tag.kind { TagKind::StartTag => @@ -119,9 +165,14 @@ impl TokenSink for ParserSink { } pub struct XMPPCodec { + /// Outgoing + ns: Option, + /// Incoming parser: XmlTokenizer, - // For handling truncated utf8 + /// For handling incoming truncated utf8 + // TODO: optimize using tendrils? buf: Vec, + /// Shared with ParserSink queue: Rc>>, } @@ -132,6 +183,7 @@ impl XMPPCodec { // TODO: configure parser? let parser = XmlTokenizer::new(sink, Default::default()); XMPPCodec { + ns: None, parser, queue, buf: vec![], @@ -144,7 +196,6 @@ impl Decoder for XMPPCodec { type Error = Error; fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { - println!("decode {} bytes", buf.len()); let buf1: Box> = if self.buf.len() > 0 && buf.len() > 0 { let mut prefix = std::mem::replace(&mut self.buf, vec![]); @@ -196,32 +247,39 @@ impl Encoder for XMPPCodec { type Error = Error; fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { - println!("encode {:?}", item); match item { Packet::StreamStart(start_attrs) => { let mut buf = String::new(); write!(buf, "\n").unwrap(); print!(">> {}", buf); write!(dst, "{}", buf) - .map_err(|_| Error::from(ErrorKind::InvalidInput)) + .map_err(|e| Error::new(ErrorKind::InvalidInput, e)) }, Packet::Stanza(stanza) => { - println!(">> {:?}", stanza); - let mut root_ns = None; // TODO - stanza.write_to_inner(&mut dst.clone().writer(), &mut root_ns) - .map_err(|_| Error::from(ErrorKind::InvalidInput)) + let root_ns = self.ns.as_ref().map(|s| s.as_ref()); + write_element(&stanza, dst, root_ns) + .and_then(|_| { + println!(">> {:?}", dst); + Ok(()) + }) + .map_err(|e| Error::new(ErrorKind::InvalidInput, format!("{}", e))) }, Packet::Text(text) => { - let escaped = escape(&text); - println!(">> {}", escaped); - write!(dst, "{}", escaped) - .map_err(|_| Error::from(ErrorKind::InvalidInput)) + write_text(&text, dst) + .and_then(|_| { + println!(">> {:?}", dst); + Ok(()) + }) + .map_err(|e| Error::new(ErrorKind::InvalidInput, format!("{}", e))) }, // TODO: Implement all _ => Ok(()) @@ -229,6 +287,45 @@ impl Encoder for XMPPCodec { } } +pub fn write_text(text: &str, writer: &mut W) -> Result<(), std::fmt::Error> { + write!(writer, "{}", text) +} + +// TODO: escape everything? +pub fn write_element(el: &Element, writer: &mut W, parent_ns: Option<&str>) -> Result<(), std::fmt::Error> { + write!(writer, "<")?; + write!(writer, "{}", el.name())?; + + if let Some(ref ns) = el.ns() { + if parent_ns.map(|s| s.as_ref()) != el.ns() { + write!(writer, " xmlns=\"{}\"", ns)?; + } + } + + for (key, value) in el.attrs() { + write!(writer, " {}=\"{}\"", key, value)?; + } + + if ! el.nodes().any(|_| true) { + write!(writer, " />")?; + return Ok(()) + } + + write!(writer, ">")?; + + for node in el.nodes() { + match node { + &Node::Element(ref child) => + write_element(child, writer, el.ns())?, + &Node::Text(ref text) => + write_text(text, writer)?, + } + } + + write!(writer, "", el.name())?; + Ok(()) +} + /// Copied from RustyXML for now pub fn escape(input: &str) -> String { let mut result = String::with_capacity(input.len()); From c6351e8efaaa76a51c81018b85b157c4dae31728 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 18 Jul 2017 21:22:12 +0200 Subject: [PATCH 0377/1020] Cargo: fix minidom dependency --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e80f7bbc51da09d90ef29fde73df50fe8579e5c2..827b324f4b32409ffb70f7d0b2b4c3d274fe1f59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ tokio-io = "*" bytes = "0.4.4" xml5ever = "*" tendril = "*" -minidom = { path = "../../programs/minidom-rs" } +minidom = "0.5" native-tls = "*" tokio-tls = "*" sasl = "*" From 68af9d2110db49bd13f31ede5a1b8003c7b2aad4 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 18 Jul 2017 22:12:00 +0200 Subject: [PATCH 0378/1020] xmpp_codec: fix large stanzas --- src/xmpp_codec.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 808f238929db867967b03a6e08428f778dd28850..e6d260451c2b9f23f778a626b9fcaf811515a247 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -247,6 +247,12 @@ impl Encoder for XMPPCodec { type Error = Error; fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { + let remaining = dst.capacity() - dst.len(); + let max_stanza_size: usize = 2usize.pow(16); + if remaining < max_stanza_size { + dst.reserve(max_stanza_size - remaining); + } + match item { Packet::StreamStart(start_attrs) => { let mut buf = String::new(); @@ -421,4 +427,28 @@ mod tests { _ => false, }); } + + /// By default, encode() only get's a BytesMut that has 8kb space reserved. + #[test] + fn test_large_stanza() { + use std::io::Cursor; + use futures::{Future, Sink}; + use tokio_io::codec::FramedWrite; + let framed = FramedWrite::new(Cursor::new(vec![]), XMPPCodec::new()); + let mut text = "".to_owned(); + for _ in 0..2usize.pow(15) { + text = text + "A"; + } + let stanza = Element::builder("message") + .append( + Element::builder("body") + .append(&text) + .build() + ) + .build(); + let framed = framed.send(Packet::Stanza(stanza)) + .wait() + .expect("send"); + assert_eq!(framed.get_ref().get_ref(), &("".to_owned() + &text + "").as_bytes()); + } } From 0ec0f3413e993e255f5b51ac174810db182b7e0c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Jul 2017 20:41:57 +0100 Subject: [PATCH 0379/1020] echo_bot: use xmpp-parsers to generate stanzas This also fixes a bug where show and status were inverted while creating the presence element. --- Cargo.toml | 1 + examples/echo_bot.rs | 26 +++++++++++--------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 827b324f4b32409ffb70f7d0b2b4c3d274fe1f59..16b9d4ebf026ab29d3da4a52bc0998068d97be7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ sasl = "*" rustc-serialize = "*" jid = "*" domain = "0.2.1" +xmpp-parsers = "0.6.0" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 8ba944ef5b2d50e6fb11b1cd979bab17b479607a..528ac341252ce584afbfea6008075f7482533cd7 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -3,6 +3,7 @@ extern crate tokio_core; extern crate tokio_xmpp; extern crate jid; extern crate minidom; +extern crate xmpp_parsers; use std::env::args; use std::process::exit; @@ -10,6 +11,8 @@ use tokio_core::reactor::Core; use futures::{Future, Stream, Sink, future}; use tokio_xmpp::Client; use minidom::Element; +use xmpp_parsers::presence::{Presence, Type as PresenceType, Show as PresenceShow}; +use xmpp_parsers::message::Message; fn main() { let args: Vec = args().collect(); @@ -80,23 +83,16 @@ fn main() { // Construct a fn make_presence() -> Element { - Element::builder("presence") - .append(Element::builder("status") - .append("chat") - .build()) - .append(Element::builder("show") - .append("Echoing messages") - .build()) - .build() + let mut presence = Presence::new(PresenceType::None); + presence.show = PresenceShow::Chat; + presence.statuses.insert(String::from("en"), String::from("Echoing messages.")); + Element::from(presence) } // Construct a chat fn make_reply(to: &str, body: String) -> Element { - Element::builder("message") - .attr("type", "chat") - .attr("to", to) - .append(Element::builder("body") - .append(body) - .build()) - .build() + let jid = to.parse().unwrap(); + let mut message = Message::new(Some(jid)); + message.bodies.insert(String::new(), body); + Element::from(message) } From 794a99472082ec05cae863d3eb0fabd81541ec6d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Jul 2017 21:54:10 +0100 Subject: [PATCH 0380/1020] =?UTF-8?q?don=E2=80=99t=20use=20wildcard=20use?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/auth.rs | 13 ++++++------- src/client/bind.rs | 7 +++---- src/client/mod.rs | 6 +++--- src/happy_eyeballs.rs | 4 ++-- src/lib.rs | 4 ++-- src/starttls.rs | 6 +++--- src/stream_start.rs | 6 +++--- src/tcp.rs | 2 +- src/xmpp_codec.rs | 2 +- src/xmpp_stream.rs | 6 +++--- 10 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/client/auth.rs b/src/client/auth.rs index a366b2ffa8ca4aae6624a674020f515c364edada..dce2f86c831c38103f007c88c7dcb00730f0cec7 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -1,17 +1,16 @@ use std::mem::replace; -use futures::*; -use futures::sink; +use futures::{Future, Poll, Async, sink, Sink, Stream}; use tokio_io::{AsyncRead, AsyncWrite}; use minidom::Element; use sasl::common::Credentials; -use sasl::common::scram::*; +use sasl::common::scram::{Sha1, Sha256}; use sasl::client::Mechanism; -use sasl::client::mechanisms::*; +use sasl::client::mechanisms::{Scram, Plain, Anonymous}; use serialize::base64::{self, ToBase64, FromBase64}; -use xmpp_codec::*; -use xmpp_stream::*; -use stream_start::*; +use xmpp_codec::Packet; +use xmpp_stream::XMPPStream; +use stream_start::StreamStart; const NS_XMPP_SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; diff --git a/src/client/bind.rs b/src/client/bind.rs index 6a59503c4b50c53ba9d4be5b5ff9542b1b03fd96..7800bbcca160a3acec647fa8cd9a64f20dee4dde 100644 --- a/src/client/bind.rs +++ b/src/client/bind.rs @@ -1,14 +1,13 @@ use std::mem::replace; use std::error::Error; use std::str::FromStr; -use futures::*; -use futures::sink; +use futures::{Future, Poll, Async, sink, Sink, Stream}; use tokio_io::{AsyncRead, AsyncWrite}; use jid::Jid; use minidom::Element; -use xmpp_codec::*; -use xmpp_stream::*; +use xmpp_codec::Packet; +use xmpp_stream::XMPPStream; const NS_XMPP_BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind"; const BIND_REQ_ID: &str = "resource-bind"; diff --git a/src/client/mod.rs b/src/client/mod.rs index de0bdb6664bdb0e326e778f307ae97372628a9c7..a45cede8d9bd3b6509006fdf538a0faf0bf6b83c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -5,7 +5,7 @@ use tokio_core::reactor::Handle; use tokio_core::net::TcpStream; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_tls::TlsStream; -use futures::*; +use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; use minidom::Element; use jid::{Jid, JidParseError}; use sasl::common::{Credentials, ChannelBinding}; @@ -17,9 +17,9 @@ use super::starttls::{NS_XMPP_TLS, StartTlsClient}; use super::happy_eyeballs::Connecter; mod auth; -use self::auth::*; +use self::auth::ClientAuth; mod bind; -use self::bind::*; +use self::bind::ClientBind; mod event; pub use self::event::Event as ClientEvent; diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 039007bfb43ab1170ab7b5cdc777f6fb062326a1..f529a87a7ba0f92b69702d2d623a36b1c85750f3 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -1,11 +1,11 @@ use std::str::FromStr; use std::collections::HashMap; use std::net::SocketAddr; -use futures::*; +use futures::{Future, Poll, Async, Stream}; use tokio_core::reactor::Handle; use tokio_core::net::{TcpStream, TcpStreamNew}; use domain::resolv::Resolver; -use domain::resolv::lookup::srv::*; +use domain::resolv::lookup::srv::{lookup_srv, LookupSrv, LookupSrvStream}; use domain::bits::DNameBuf; pub struct Connecter { diff --git a/src/lib.rs b/src/lib.rs index c150760d0785c679929f8d8c430b6eba20eff210..0cea1db29ecd5478e29da6b3303476e9766198e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,9 +17,9 @@ pub mod xmpp_codec; pub mod xmpp_stream; mod stream_start; mod tcp; -pub use tcp::*; +pub use tcp::TcpClient; mod starttls; -pub use starttls::*; +pub use starttls::StartTlsClient; mod happy_eyeballs; mod client; pub use client::{Client, ClientEvent}; diff --git a/src/starttls.rs b/src/starttls.rs index fe6c64105805fe9d08d68b2a37588f2c591c22d5..91a7ea260e138bed8b8d2f289168d3c1727ae47f 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -3,13 +3,13 @@ use futures::{Future, Sink, Poll, Async}; use futures::stream::Stream; use futures::sink; use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_tls::*; +use tokio_tls::{TlsStream, TlsConnectorExt, ConnectAsync}; use native_tls::TlsConnector; use minidom::Element; use jid::Jid; -use xmpp_codec::*; -use xmpp_stream::*; +use xmpp_codec::Packet; +use xmpp_stream::XMPPStream; use stream_start::StreamStart; diff --git a/src/stream_start.rs b/src/stream_start.rs index bbccadd139a2611915c82f4957fba7e31b53358b..0b4fd9e79f682a3831bcea73c5541bec92ab28bf 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -1,13 +1,13 @@ use std::mem::replace; use std::io::{Error, ErrorKind}; use std::collections::HashMap; -use futures::*; +use futures::{Future, Async, Poll, Stream, sink, Sink}; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; use jid::Jid; -use xmpp_codec::*; -use xmpp_stream::*; +use xmpp_codec::{XMPPCodec, Packet}; +use xmpp_stream::XMPPStream; const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; diff --git a/src/tcp.rs b/src/tcp.rs index 06aa7b83efd151520458218c2dfb52ee1961d95e..156fa4f5cb600981dd75e73389df038580f81898 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -5,7 +5,7 @@ use tokio_core::reactor::Handle; use tokio_core::net::{TcpStream, TcpStreamNew}; use jid::Jid; -use xmpp_stream::*; +use xmpp_stream::XMPPStream; use stream_start::StreamStart; pub struct TcpClient { diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index e6d260451c2b9f23f778a626b9fcaf811515a247..7504dfff28baed3de4cab1a9dc61672886d8fcef 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -12,7 +12,7 @@ use tokio_io::codec::{Encoder, Decoder}; use minidom::{Element, Node}; use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind}; use xml5ever::interface::Attribute; -use bytes::*; +use bytes::{BytesMut, BufMut}; // const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/"; diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index 932cbcef3e5d3d1100ea50818a78a154915504c7..9ff6916fad61c64483de126b40a7bbc20755f679 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; -use futures::*; +use futures::{Poll, Stream, Sink, StartSend}; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; use minidom::Element; use jid::Jid; -use xmpp_codec::*; -use stream_start::*; +use xmpp_codec::XMPPCodec; +use stream_start::StreamStart; pub const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; From 35be5a43239a12c754aa866d0e33954d011fa4b5 Mon Sep 17 00:00:00 2001 From: Astro Date: Wed, 19 Jul 2017 01:00:17 +0200 Subject: [PATCH 0381/1020] Cargo, echo_bot: fix xmpp-parsers usage --- Cargo.toml | 2 +- examples/echo_bot.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 16b9d4ebf026ab29d3da4a52bc0998068d97be7b..cf7735a0e1f554f6b12dfdab56f76889eb6503a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ tokio-io = "*" bytes = "0.4.4" xml5ever = "*" tendril = "*" -minidom = "0.5" +minidom = "0.4.3" native-tls = "*" tokio-tls = "*" sasl = "*" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 528ac341252ce584afbfea6008075f7482533cd7..b3be75cd1a7e26189ae669f03ac51c43b5cd62f9 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -86,7 +86,7 @@ fn make_presence() -> Element { let mut presence = Presence::new(PresenceType::None); presence.show = PresenceShow::Chat; presence.statuses.insert(String::from("en"), String::from("Echoing messages.")); - Element::from(presence) + presence.into() } // Construct a chat @@ -94,5 +94,5 @@ fn make_reply(to: &str, body: String) -> Element { let jid = to.parse().unwrap(); let mut message = Message::new(Some(jid)); message.bodies.insert(String::new(), body); - Element::from(message) + message.into() } From 7b7f2866fc4fbc9724d6fc07fdfe75f5489b2456 Mon Sep 17 00:00:00 2001 From: Astro Date: Wed, 19 Jul 2017 01:02:45 +0200 Subject: [PATCH 0382/1020] move stream_start out of places --- src/client/mod.rs | 31 ++++++++++++--------- src/lib.rs | 3 -- src/starttls.rs | 22 ++------------- src/stream_start.rs | 30 +++++++++++--------- src/tcp.rs | 68 --------------------------------------------- src/xmpp_codec.rs | 4 +-- src/xmpp_stream.rs | 13 ++++----- 7 files changed, 46 insertions(+), 125 deletions(-) delete mode 100644 src/tcp.rs diff --git a/src/client/mod.rs b/src/client/mod.rs index a45cede8d9bd3b6509006fdf538a0faf0bf6b83c..aca0fdd20294cc45324a2492361eac8a12062a5b 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -12,7 +12,6 @@ use sasl::common::{Credentials, ChannelBinding}; use super::xmpp_codec::Packet; use super::xmpp_stream; -use super::tcp::TcpClient; use super::starttls::{NS_XMPP_TLS, StartTlsClient}; use super::happy_eyeballs::Connecter; @@ -29,6 +28,7 @@ pub struct Client { } type XMPPStream = xmpp_stream::XMPPStream>; +const NS_JABBER_CLIENT: &str = "jabber:client"; enum ClientState { Invalid, @@ -50,26 +50,31 @@ impl Client { fn make_connect(jid: Jid, password: String, handle: Handle) -> Box> { let username = jid.node.as_ref().unwrap().to_owned(); + let jid1 = jid.clone(); + let jid2 = jid.clone(); let password = password; Box::new( Connecter::from_lookup(handle, &jid.domain, "_xmpp-client._tcp", 5222) .expect("Connector::from_lookup") - .and_then(|tcp_stream| - TcpClient::from_stream(jid, tcp_stream) + .and_then(move |tcp_stream| + xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_CLIENT.to_owned()) .map_err(|e| format!("{}", e)) - ).and_then(|stream| { - if Self::can_starttls(&stream) { - Self::starttls(stream) + ).and_then(|xmpp_stream| { + if Self::can_starttls(&xmpp_stream) { + Self::starttls(xmpp_stream) } else { panic!("No STARTTLS") } - }).and_then(move |stream| { - Self::auth(stream, username, password).expect("auth") - }).and_then(|stream| { - Self::bind(stream) - }).and_then(|stream| { - println!("Bound to {}", stream.jid); - Ok(stream) + }).and_then(|tls_stream| + XMPPStream::start(tls_stream, jid2, NS_JABBER_CLIENT.to_owned()) + .map_err(|e| format!("{}", e)) + ).and_then(move |xmpp_stream| { + Self::auth(xmpp_stream, username, password).expect("auth") + }).and_then(|xmpp_stream| { + Self::bind(xmpp_stream) + }).and_then(|xmpp_stream| { + println!("Bound to {}", xmpp_stream.jid); + Ok(xmpp_stream) }) ) } diff --git a/src/lib.rs b/src/lib.rs index 0cea1db29ecd5478e29da6b3303476e9766198e8..348c03b2b2f118a20fa856b1948331836b1a356e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -#[macro_use] extern crate futures; extern crate tokio_core; extern crate tokio_io; @@ -16,8 +15,6 @@ extern crate domain; pub mod xmpp_codec; pub mod xmpp_stream; mod stream_start; -mod tcp; -pub use tcp::TcpClient; mod starttls; pub use starttls::StartTlsClient; mod happy_eyeballs; diff --git a/src/starttls.rs b/src/starttls.rs index 91a7ea260e138bed8b8d2f289168d3c1727ae47f..7338b55cd10f34fdf4112c6c3d68361335a1e9af 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -10,7 +10,6 @@ use jid::Jid; use xmpp_codec::Packet; use xmpp_stream::XMPPStream; -use stream_start::StreamStart; pub const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; @@ -26,7 +25,6 @@ enum StartTlsClientState { SendStartTls(sink::Send>), AwaitProceed(XMPPStream), StartingTls(ConnectAsync), - Start(StreamStart>), } impl StartTlsClient { @@ -48,7 +46,7 @@ impl StartTlsClient { } impl Future for StartTlsClient { - type Item = XMPPStream>; + type Item = TlsStream; type Error = String; fn poll(&mut self) -> Poll { @@ -92,24 +90,10 @@ impl Future for StartTlsClient { }, StartTlsClientState::StartingTls(mut connect) => match connect.poll() { - Ok(Async::Ready(tls_stream)) => { - println!("TLS stream established"); - let start = XMPPStream::from_stream(tls_stream, self.jid.clone()); - let new_state = StartTlsClientState::Start(start); - retry = true; - (new_state, Ok(Async::NotReady)) - }, + Ok(Async::Ready(tls_stream)) => + (StartTlsClientState::Invalid, Ok(Async::Ready(tls_stream))), Ok(Async::NotReady) => (StartTlsClientState::StartingTls(connect), Ok(Async::NotReady)), - Err(e) => - (StartTlsClientState::StartingTls(connect), Err(format!("{}", e))), - }, - StartTlsClientState::Start(mut start) => - match start.poll() { - Ok(Async::Ready(xmpp_stream)) => - (StartTlsClientState::Invalid, Ok(Async::Ready(xmpp_stream))), - Ok(Async::NotReady) => - (StartTlsClientState::Start(start), Ok(Async::NotReady)), Err(e) => (StartTlsClientState::Invalid, Err(format!("{}", e))), }, diff --git a/src/stream_start.rs b/src/stream_start.rs index 0b4fd9e79f682a3831bcea73c5541bec92ab28bf..b194b5bb4f0e7b5353524a75e9c9602d9b159ceb 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -1,6 +1,5 @@ use std::mem::replace; use std::io::{Error, ErrorKind}; -use std::collections::HashMap; use futures::{Future, Async, Poll, Stream, sink, Sink}; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; @@ -14,20 +13,21 @@ const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; pub struct StreamStart { state: StreamStartState, jid: Jid, + ns: String, } enum StreamStartState { SendStart(sink::Send>), RecvStart(Framed), - RecvFeatures(Framed, HashMap), + RecvFeatures(Framed, String), Invalid, } impl StreamStart { - pub fn from_stream(stream: Framed, jid: Jid) -> Self { + pub fn from_stream(stream: Framed, jid: Jid, ns: String) -> Self { let attrs = [("to".to_owned(), jid.domain.clone()), ("version".to_owned(), "1.0".to_owned()), - ("xmlns".to_owned(), "jabber:client".to_owned()), + ("xmlns".to_owned(), ns.clone()), ("xmlns:stream".to_owned(), NS_XMPP_STREAM.to_owned()), ].iter().cloned().collect(); let send = stream.send(Packet::StreamStart(attrs)); @@ -35,6 +35,7 @@ impl StreamStart { StreamStart { state: StreamStartState::SendStart(send), jid, + ns, } } } @@ -63,8 +64,13 @@ impl Future for StreamStart { match stream.poll() { Ok(Async::Ready(Some(Packet::StreamStart(stream_attrs)))) => { retry = true; + let stream_ns = match stream_attrs.get("xmlns") { + Some(ns) => ns.clone(), + None => + return Err(Error::from(ErrorKind::InvalidData)), + }; // TODO: skip RecvFeatures for version < 1.0 - (StreamStartState::RecvFeatures(stream, stream_attrs), Ok(Async::NotReady)) + (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)) }, Ok(Async::Ready(_)) => return Err(Error::from(ErrorKind::InvalidData)), @@ -73,22 +79,20 @@ impl Future for StreamStart { Err(e) => return Err(e), }, - StreamStartState::RecvFeatures(mut stream, stream_attrs) => + StreamStartState::RecvFeatures(mut stream, stream_ns) => match stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => if stanza.name() == "features" && stanza.ns() == Some(NS_XMPP_STREAM) { - let stream = XMPPStream::new(self.jid.clone(), stream, stream_attrs, stanza); + let stream = XMPPStream::new(self.jid.clone(), stream, self.ns.clone(), stanza); (StreamStartState::Invalid, Ok(Async::Ready(stream))) } else { - (StreamStartState::RecvFeatures(stream, stream_attrs), Ok(Async::NotReady)) + (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)) }, - Ok(Async::Ready(item)) => { - println!("StreamStart skip {:?}", item); - (StreamStartState::RecvFeatures(stream, stream_attrs), Ok(Async::NotReady)) - }, + Ok(Async::Ready(item)) => + (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)), Ok(Async::NotReady) => - (StreamStartState::RecvFeatures(stream, stream_attrs), Ok(Async::NotReady)), + (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)), Err(e) => return Err(e), }, diff --git a/src/tcp.rs b/src/tcp.rs deleted file mode 100644 index 156fa4f5cb600981dd75e73389df038580f81898..0000000000000000000000000000000000000000 --- a/src/tcp.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::net::SocketAddr; -use std::io::Error; -use futures::{Future, Poll, Async}; -use tokio_core::reactor::Handle; -use tokio_core::net::{TcpStream, TcpStreamNew}; -use jid::Jid; - -use xmpp_stream::XMPPStream; -use stream_start::StreamStart; - -pub struct TcpClient { - state: TcpClientState, - jid: Jid, -} - -enum TcpClientState { - Connecting(TcpStreamNew), - Start(StreamStart), - Established, -} - -impl TcpClient { - pub fn connect(jid: Jid, addr: &SocketAddr, handle: &Handle) -> Self { - let tcp_stream_new = TcpStream::connect(addr, handle); - TcpClient { - state: TcpClientState::Connecting(tcp_stream_new), - jid, - } - } - - pub fn from_stream(jid: Jid, tcp_stream: TcpStream) -> Self { - let start = XMPPStream::from_stream(tcp_stream, jid.clone()); - TcpClient { - state: TcpClientState::Start(start), - jid, - } - } -} - -impl Future for TcpClient { - type Item = XMPPStream; - type Error = Error; - - fn poll(&mut self) -> Poll { - let (new_state, result) = match self.state { - TcpClientState::Connecting(ref mut tcp_stream_new) => { - let tcp_stream = try_ready!(tcp_stream_new.poll()); - let start = XMPPStream::from_stream(tcp_stream, self.jid.clone()); - let new_state = TcpClientState::Start(start); - (new_state, Ok(Async::NotReady)) - }, - TcpClientState::Start(ref mut start) => { - let xmpp_stream = try_ready!(start.poll()); - let new_state = TcpClientState::Established; - (new_state, Ok(Async::Ready(xmpp_stream))) - }, - TcpClientState::Established => - unreachable!(), - }; - - self.state = new_state; - match result { - // by polling again, we register new future - Ok(Async::NotReady) => self.poll(), - result => result - } - } -} diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 7504dfff28baed3de4cab1a9dc61672886d8fcef..6686ed2b6c9b23b8a0f64f462e602ac63b07bc44 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -98,8 +98,8 @@ impl ParserSink { if self.stack.is_empty() { let attrs = HashMap::from_iter( - el.attrs() - .map(|(name, value)| (name.to_owned(), value.to_owned())) + tag.attrs.iter() + .map(|attr| (attr.name.local.as_ref().to_owned(), attr.value.as_ref().to_owned())) ); self.push_queue(Packet::StreamStart(attrs)); } diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index 9ff6916fad61c64483de126b40a7bbc20755f679..2b9c7aeac5a48c8a8bf3b78a3f1404079197f365 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use futures::{Poll, Stream, Sink, StartSend}; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; @@ -13,21 +12,21 @@ pub const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; pub struct XMPPStream { pub jid: Jid, pub stream: Framed, - pub stream_attrs: HashMap, pub stream_features: Element, + pub ns: String, } impl XMPPStream { pub fn new(jid: Jid, stream: Framed, - stream_attrs: HashMap, + ns: String, stream_features: Element) -> Self { - XMPPStream { jid, stream, stream_attrs, stream_features } + XMPPStream { jid, stream, stream_features, ns } } - pub fn from_stream(stream: S, jid: Jid) -> StreamStart { + pub fn start(stream: S, jid: Jid, ns: String) -> StreamStart { let xmpp_stream = AsyncRead::framed(stream, XMPPCodec::new()); - StreamStart::from_stream(xmpp_stream, jid) + StreamStart::from_stream(xmpp_stream, jid, ns) } pub fn into_inner(self) -> S { @@ -35,7 +34,7 @@ impl XMPPStream { } pub fn restart(self) -> StreamStart { - Self::from_stream(self.stream.into_inner(), self.jid) + Self::start(self.stream.into_inner(), self.jid, self.ns) } } From 76a46559b81f7ee56df4533e4381df9e325423f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 17:39:59 +0100 Subject: [PATCH 0383/1020] disco: Split query and result. --- src/caps.rs | 17 ++++++------- src/disco.rs | 69 +++++++++++++++++++++++++++++++++++++-------------- src/ecaps2.rs | 17 ++++++------- src/iq.rs | 19 ++++++++++---- 4 files changed, 78 insertions(+), 44 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index b7753a195ed15fad27bd54ea4c60ae425e79da09..e883a64a7863f73979b902e9207e3622890146f2 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; -use disco::{Feature, Identity, Disco}; +use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; use hashes::{Hash, Algo}; @@ -122,7 +122,7 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec { }) } -pub fn compute_disco(disco: &Disco) -> Vec { +pub fn compute_disco(disco: &DiscoInfoResult) -> Vec { let identities_string = compute_identities(&disco.identities); let features_string = compute_features(&disco.features); let extensions_string = compute_extensions(&disco.extensions); @@ -193,12 +193,9 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { }) } -pub fn query_caps(caps: Caps) -> Disco { - Disco { +pub fn query_caps(caps: Caps) -> DiscoInfoQuery { + DiscoInfoQuery { node: Some(format!("{}#{}", caps.node, base64::encode(&caps.hash.hash))), - identities: vec!(), - features: vec!(), - extensions: vec!(), } } @@ -231,7 +228,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let disco = Disco::try_from(elem).unwrap(); + let disco = DiscoInfoResult::try_from(elem).unwrap(); let caps = caps::compute_disco(&disco); assert_eq!(caps.len(), 50); } @@ -252,7 +249,7 @@ mod tests { let data = b"client/pc//Exodus 0.9.1, +} + +impl TryFrom for DiscoInfoQuery { + type Error = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::DISCO_INFO) { + return Err(Error::ParseError("This is not a disco#info element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in disco#info.")); + } + for (attr, _) in elem.attrs() { + if attr != "node" { + return Err(Error::ParseError("Unknown attribute in disco#info.")); + } + } + Ok(DiscoInfoQuery { + node: get_attr!(elem, "node", optional), + }) + } +} + +impl From for Element { + fn from(disco: DiscoInfoQuery) -> Element { + Element::builder("query") + .ns(ns::DISCO_INFO) + .attr("node", disco.node) + .build() + } +} + #[derive(Debug, Clone, PartialEq)] pub struct Feature { pub var: String, @@ -60,17 +95,17 @@ impl IntoElements for Identity { } #[derive(Debug, Clone)] -pub struct Disco { +pub struct DiscoInfoResult { pub node: Option, pub identities: Vec, pub features: Vec, pub extensions: Vec, } -impl TryFrom for Disco { +impl TryFrom for DiscoInfoResult { type Error = Error; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { if !elem.is("query", ns::DISCO_INFO) { return Err(Error::ParseError("This is not a disco#info element.")); } @@ -120,8 +155,6 @@ impl TryFrom for Disco { } } - /* - // TODO: encode these restrictions only for result disco#info, not get ones. if identities.is_empty() { return Err(Error::ParseError("There must be at least one identity in disco#info.")); } @@ -131,9 +164,8 @@ impl TryFrom for Disco { if !features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { return Err(Error::ParseError("disco#info feature not present in disco#info.")); } - */ - Ok(Disco { + Ok(DiscoInfoResult { node: node, identities: identities, features: features, @@ -142,7 +174,7 @@ impl TryFrom for Disco { } } -impl Into for Disco { +impl Into for DiscoInfoResult { fn into(self) -> Element { for _ in self.extensions { panic!("Not yet implemented!"); @@ -163,7 +195,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let query = Disco::try_from(elem).unwrap(); + let query = DiscoInfoResult::try_from(elem).unwrap(); assert!(query.node.is_none()); assert_eq!(query.identities.len(), 1); assert_eq!(query.features.len(), 1); @@ -173,7 +205,7 @@ mod tests { #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -184,7 +216,7 @@ mod tests { #[test] fn test_invalid_identity() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -192,7 +224,7 @@ mod tests { assert_eq!(message, "Required attribute 'category' missing."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -200,7 +232,7 @@ mod tests { assert_eq!(message, "Identity must have a non-empty 'category' attribute."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -208,7 +240,7 @@ mod tests { assert_eq!(message, "Required attribute 'type' missing."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -219,7 +251,7 @@ mod tests { #[test] fn test_invalid_feature() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -228,10 +260,9 @@ mod tests { } #[test] - #[ignore] fn test_invalid_result() { let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -239,7 +270,7 @@ mod tests { assert_eq!(message, "There must be at least one identity in disco#info."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -247,7 +278,7 @@ mod tests { assert_eq!(message, "There must be at least one feature in disco#info."); let elem: Element = "".parse().unwrap(); - let error = Disco::try_from(elem).unwrap_err(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 7d9279bb2d73d1ae88a04b1e7add7559b8398009..da74533d8e366f8135d9f2f7699201423fb7b059 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; -use disco::{Feature, Identity, Disco}; +use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; use hashes::{Hash, Algo}; @@ -106,7 +106,7 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec { }) } -pub fn compute_disco(disco: &Disco) -> Vec { +pub fn compute_disco(disco: &DiscoInfoResult) -> Vec { let features_string = compute_features(&disco.features); let identities_string = compute_identities(&disco.identities); let extensions_string = compute_extensions(&disco.extensions); @@ -172,12 +172,9 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { }) } -pub fn query_ecaps2(hash: Hash) -> Disco { - Disco { +pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery { + DiscoInfoQuery { node: Some(format!("{}#{}.{}", ns::ECAPS2, String::from(hash.algo), base64::encode(&hash.hash))), - identities: vec!(), - features: vec!(), - extensions: vec!(), } } @@ -211,7 +208,7 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let disco = Disco::try_from(elem).unwrap(); + let disco = DiscoInfoResult::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 54); } @@ -274,7 +271,7 @@ mod tests { 105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, 100, 31, 30, 28, 28]; - let disco = Disco::try_from(elem).unwrap(); + let disco = DiscoInfoResult::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); @@ -446,7 +443,7 @@ mod tests { 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; - let disco = Disco::try_from(elem).unwrap(); + let disco = DiscoInfoResult::try_from(elem).unwrap(); let ecaps2 = ecaps2::compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); diff --git a/src/iq.rs b/src/iq.rs index 76a97b1f217408f2973030d93241ed3120b62d2e..9420b44ccddc2c7227b14016205fe98edfecf3f5 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -18,7 +18,7 @@ use ns; use stanza_error::StanzaError; use roster::Roster; -use disco::Disco; +use disco::{DiscoInfoResult, DiscoInfoQuery}; use ibb::IBB; use jingle::Jingle; use ping::Ping; @@ -28,7 +28,8 @@ use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; #[derive(Debug, Clone)] pub enum IqPayload { Roster(Roster), - Disco(Disco), + DiscoInfoResult(DiscoInfoResult), + DiscoInfoQuery(DiscoInfoQuery), IBB(IBB), Jingle(Jingle), Ping(Ping), @@ -48,7 +49,14 @@ impl TryFrom for IqPayload { ("query", ns::ROSTER) => IqPayload::Roster(Roster::try_from(elem)?), // XEP-0030 - ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?), + ("query", ns::DISCO_INFO) => { + // TODO: separate all three types of payloads. + match DiscoInfoQuery::try_from(elem.clone()) { + Ok(payload) => IqPayload::DiscoInfoQuery(payload), + // TODO: put that error somewhere too? + Err(_) => IqPayload::DiscoInfoResult(DiscoInfoResult::try_from(elem)?), + } + }, // XEP-0047 ("open", ns::IBB) @@ -171,7 +179,8 @@ impl Into for IqPayload { fn into(self) -> Element { match self { IqPayload::Roster(roster) => roster.into(), - IqPayload::Disco(disco) => disco.into(), + IqPayload::DiscoInfoResult(disco) => disco.into(), + IqPayload::DiscoInfoQuery(disco) => disco.into(), IqPayload::IBB(ibb) => ibb.into(), IqPayload::Jingle(jingle) => jingle.into(), IqPayload::Ping(ping) => ping.into(), @@ -339,7 +348,7 @@ mod tests { _ => panic!(), }; assert!(match payload { - IqPayload::Disco(Disco { .. }) => true, + IqPayload::DiscoInfoQuery(DiscoInfoQuery { .. }) => true, _ => false, }); } From 0fcbf11644b2efe25527d6db87268cff9b3cea6a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 18:31:17 +0100 Subject: [PATCH 0384/1020] iq: Split IqPayload into all three possible types. --- src/iq.rs | 162 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 118 insertions(+), 44 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 9420b44ccddc2c7227b14016205fe98edfecf3f5..976d79f8f7656b1ba1a61b7fa5c509b5b1057308 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -24,15 +24,35 @@ use jingle::Jingle; use ping::Ping; use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; -/// Lists every known payload of a ``. +/// Lists every known payload of an ``. #[derive(Debug, Clone)] -pub enum IqPayload { +pub enum IqGetPayload { + Roster(Roster), + DiscoInfo(DiscoInfoQuery), + Ping(Ping), + MamQuery(MamQuery), + MamPrefs(MamPrefs), + + Unknown(Element), +} + +/// Lists every known payload of an ``. +#[derive(Debug, Clone)] +pub enum IqSetPayload { Roster(Roster), - DiscoInfoResult(DiscoInfoResult), - DiscoInfoQuery(DiscoInfoQuery), IBB(IBB), Jingle(Jingle), - Ping(Ping), + MamQuery(MamQuery), + MamPrefs(MamPrefs), + + Unknown(Element), +} + +/// Lists every known payload of an ``. +#[derive(Debug, Clone)] +pub enum IqResultPayload { + Roster(Roster), + DiscoInfo(DiscoInfoResult), MamQuery(MamQuery), MamFin(MamFin), MamPrefs(MamPrefs), @@ -40,45 +60,117 @@ pub enum IqPayload { Unknown(Element), } -impl TryFrom for IqPayload { +impl TryFrom for IqGetPayload { type Error = Error; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { // RFC-6121 - ("query", ns::ROSTER) => IqPayload::Roster(Roster::try_from(elem)?), + ("query", ns::ROSTER) => IqGetPayload::Roster(Roster::try_from(elem)?), // XEP-0030 - ("query", ns::DISCO_INFO) => { - // TODO: separate all three types of payloads. - match DiscoInfoQuery::try_from(elem.clone()) { - Ok(payload) => IqPayload::DiscoInfoQuery(payload), - // TODO: put that error somewhere too? - Err(_) => IqPayload::DiscoInfoResult(DiscoInfoResult::try_from(elem)?), - } - }, + ("query", ns::DISCO_INFO) => IqGetPayload::DiscoInfo(DiscoInfoQuery::try_from(elem)?), + + // XEP-0199 + ("ping", ns::PING) => IqGetPayload::Ping(Ping::try_from(elem)?), + + // XEP-0313 + ("query", ns::MAM) => IqGetPayload::MamQuery(MamQuery::try_from(elem)?), + ("prefs", ns::MAM) => IqGetPayload::MamPrefs(MamPrefs::try_from(elem)?), + + _ => IqGetPayload::Unknown(elem), + }) + } +} + +impl Into for IqGetPayload { + fn into(self) -> Element { + match self { + IqGetPayload::Roster(roster) => roster.into(), + IqGetPayload::DiscoInfo(disco) => disco.into(), + IqGetPayload::Ping(ping) => ping.into(), + IqGetPayload::MamQuery(query) => query.into(), + IqGetPayload::MamPrefs(prefs) => prefs.into(), + + IqGetPayload::Unknown(elem) => elem, + } + } +} + +impl TryFrom for IqSetPayload { + type Error = Error; + + fn try_from(elem: Element) -> Result { + Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + // RFC-6121 + ("query", ns::ROSTER) => IqSetPayload::Roster(Roster::try_from(elem)?), // XEP-0047 ("open", ns::IBB) | ("data", ns::IBB) - | ("close", ns::IBB) => IqPayload::IBB(IBB::try_from(elem)?), + | ("close", ns::IBB) => IqSetPayload::IBB(IBB::try_from(elem)?), // XEP-0166 - ("jingle", ns::JINGLE) => IqPayload::Jingle(Jingle::try_from(elem)?), + ("jingle", ns::JINGLE) => IqSetPayload::Jingle(Jingle::try_from(elem)?), - // XEP-0199 - ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?), + // XEP-0313 + ("query", ns::MAM) => IqSetPayload::MamQuery(MamQuery::try_from(elem)?), + ("prefs", ns::MAM) => IqSetPayload::MamPrefs(MamPrefs::try_from(elem)?), + + _ => IqSetPayload::Unknown(elem), + }) + } +} + +impl Into for IqSetPayload { + fn into(self) -> Element { + match self { + IqSetPayload::Roster(roster) => roster.into(), + IqSetPayload::IBB(ibb) => ibb.into(), + IqSetPayload::Jingle(jingle) => jingle.into(), + IqSetPayload::MamQuery(query) => query.into(), + IqSetPayload::MamPrefs(prefs) => prefs.into(), + + IqSetPayload::Unknown(elem) => elem, + } + } +} + +impl TryFrom for IqResultPayload { + type Error = Error; + + fn try_from(elem: Element) -> Result { + Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { + // RFC-6121 + ("query", ns::ROSTER) => IqResultPayload::Roster(Roster::try_from(elem)?), + + // XEP-0030 + ("query", ns::DISCO_INFO) => IqResultPayload::DiscoInfo(DiscoInfoResult::try_from(elem)?), // XEP-0313 - ("query", ns::MAM) => IqPayload::MamQuery(MamQuery::try_from(elem)?), - ("fin", ns::MAM) => IqPayload::MamFin(MamFin::try_from(elem)?), - ("prefs", ns::MAM) => IqPayload::MamPrefs(MamPrefs::try_from(elem)?), + ("query", ns::MAM) => IqResultPayload::MamQuery(MamQuery::try_from(elem)?), + ("fin", ns::MAM) => IqResultPayload::MamFin(MamFin::try_from(elem)?), + ("prefs", ns::MAM) => IqResultPayload::MamPrefs(MamPrefs::try_from(elem)?), - _ => IqPayload::Unknown(elem), + _ => IqResultPayload::Unknown(elem), }) } } +impl Into for IqResultPayload { + fn into(self) -> Element { + match self { + IqResultPayload::Roster(roster) => roster.into(), + IqResultPayload::DiscoInfo(disco) => disco.into(), + IqResultPayload::MamQuery(query) => query.into(), + IqResultPayload::MamFin(fin) => fin.into(), + IqResultPayload::MamPrefs(prefs) => prefs.into(), + + IqResultPayload::Unknown(elem) => elem, + } + } +} + #[derive(Debug, Clone)] pub enum IqType { Get(Element), @@ -175,24 +267,6 @@ impl TryFrom for Iq { } } -impl Into for IqPayload { - fn into(self) -> Element { - match self { - IqPayload::Roster(roster) => roster.into(), - IqPayload::DiscoInfoResult(disco) => disco.into(), - IqPayload::DiscoInfoQuery(disco) => disco.into(), - IqPayload::IBB(ibb) => ibb.into(), - IqPayload::Jingle(jingle) => jingle.into(), - IqPayload::Ping(ping) => ping.into(), - IqPayload::MamQuery(query) => query.into(), - IqPayload::MamFin(fin) => fin.into(), - IqPayload::MamPrefs(prefs) => prefs.into(), - - IqPayload::Unknown(elem) => elem, - } - } -} - impl Into for Iq { fn into(self) -> Element { let mut stanza = Element::builder("iq") @@ -344,11 +418,11 @@ mod tests { let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); let payload = match iq.payload { - IqType::Get(payload) => IqPayload::try_from(payload).unwrap(), + IqType::Get(payload) => IqGetPayload::try_from(payload).unwrap(), _ => panic!(), }; assert!(match payload { - IqPayload::DiscoInfoQuery(DiscoInfoQuery { .. }) => true, + IqGetPayload::DiscoInfo(DiscoInfoQuery { .. }) => true, _ => false, }); } From 1f43cd934b56bda603deb49efa22f6f07f0ed8b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 20:03:15 +0100 Subject: [PATCH 0385/1020] Use TryFrom from the try_from crate, instead of the unstable feature. This makes xmpp-parsers usable on stable. --- Cargo.toml | 1 + src/attention.rs | 6 +++--- src/caps.rs | 4 ++-- src/chatstates.rs | 4 ++-- src/data_forms.rs | 4 ++-- src/delay.rs | 4 ++-- src/disco.rs | 6 +++--- src/ecaps2.rs | 4 ++-- src/eme.rs | 4 ++-- src/forwarding.rs | 4 ++-- src/hashes.rs | 4 ++-- src/ibb.rs | 4 ++-- src/ibr.rs | 4 ++-- src/idle.rs | 4 ++-- src/iq.rs | 10 +++++----- src/jingle.rs | 8 ++++---- src/jingle_ft.rs | 4 ++-- src/jingle_ibb.rs | 4 ++-- src/jingle_message.rs | 4 ++-- src/jingle_s5b.rs | 4 ++-- src/lib.rs | 3 +-- src/mam.rs | 10 +++++----- src/media_element.rs | 4 ++-- src/message.rs | 6 +++--- src/message_correct.rs | 4 ++-- src/muc/muc.rs | 4 ++-- src/muc/user.rs | 15 +++++++-------- src/ping.rs | 4 ++-- src/presence.rs | 6 +++--- src/pubsub/event.rs | 4 ++-- src/receipts.rs | 4 ++-- src/roster.rs | 6 +++--- src/rsm.rs | 4 ++-- src/stanza_error.rs | 4 ++-- src/stanza_id.rs | 4 ++-- 35 files changed, 86 insertions(+), 87 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 608f2ff536685715ccc4a39e5d221499772a85ce..b70538110dc033d592a6b06bab35c010d605c298 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,4 @@ sha2 = "0.6.0" sha3 = "0.6.0" blake2 = "0.6.0" chrono = "0.4.0" +try_from = "0.2.2" diff --git a/src/attention.rs b/src/attention.rs index b28b5cc300967f03c455814f1008cd06c93c264b..c53040c4876bc0046e7f0b5974b13f5a5d87b9eb 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -16,7 +16,7 @@ use ns; pub struct Attention; impl TryFrom for Attention { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("attention", ns::ATTENTION) { @@ -39,7 +39,7 @@ impl Into for Attention { #[cfg(test)] mod tests { - use std::convert::TryFrom; + use try_from::TryFrom; use minidom::Element; use error::Error; use super::Attention; diff --git a/src/caps.rs b/src/caps.rs index e883a64a7863f73979b902e9207e3622890146f2..aad50eb2d644807a73f50922ce8fe7058f0f7ec5 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; @@ -29,7 +29,7 @@ pub struct Caps { } impl TryFrom for Caps { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("c", ns::CAPS) { diff --git a/src/chatstates.rs b/src/chatstates.rs index 47df01e0cd63330b65a1517c0c40fa3e40f97b33..ff1baa9563bf82dad351fcfe15aeb5002b0e9b5e 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -22,7 +22,7 @@ pub enum ChatState { } impl TryFrom for ChatState { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if elem.ns() != Some(ns::CHATSTATES) { diff --git a/src/data_forms.rs b/src/data_forms.rs index 7e187ec194b60a1269e1fa78d5d72c467386d1cb..d4678c097573e6795345449330c3f67b248a87d5 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; @@ -103,7 +103,7 @@ pub struct DataForm { } impl TryFrom for DataForm { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("x", ns::DATA_FORMS) { diff --git a/src/delay.rs b/src/delay.rs index d744751da9ec01e743ae9358d0a3bf1319a6666b..7b56ab7eeeff733bb0f587ea4b2e280464b804bc 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; use chrono::{DateTime, FixedOffset}; @@ -22,7 +22,7 @@ pub struct Delay { } impl TryFrom for Delay { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("delay", ns::DELAY) { diff --git a/src/disco.rs b/src/disco.rs index a36f997e6c89e2d7bd475bf404f94ef16e53b6b0..43727d51d82ba3ebf1d4ce1a6a04e200bee7dfb5 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::{Element, IntoElements, ElementEmitter}; @@ -19,7 +19,7 @@ pub struct DiscoInfoQuery { } impl TryFrom for DiscoInfoQuery { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("query", ns::DISCO_INFO) { @@ -103,7 +103,7 @@ pub struct DiscoInfoResult { } impl TryFrom for DiscoInfoResult { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("query", ns::DISCO_INFO) { diff --git a/src/ecaps2.rs b/src/ecaps2.rs index da74533d8e366f8135d9f2f7699201423fb7b059..0e92796de154b539ffd0323840d60fcba3599916 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; @@ -26,7 +26,7 @@ pub struct ECaps2 { } impl TryFrom for ECaps2 { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("c", ns::ECAPS2) { diff --git a/src/eme.rs b/src/eme.rs index fe9738613ecafe0d5b7e7e13b7a280643bf3955e..ee71e21c75cd96c3a888654a6602c3955a9eb769 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -19,7 +19,7 @@ pub struct ExplicitMessageEncryption { } impl TryFrom for ExplicitMessageEncryption { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("encryption", ns::EME) { diff --git a/src/forwarding.rs b/src/forwarding.rs index 9ad416b8fcf5ea23775bcac763b37235763c46c6..d10860ff30a9cee175461c38b6b26eca99ff7077 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -23,7 +23,7 @@ pub struct Forwarded { } impl TryFrom for Forwarded { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("forwarded", ns::FORWARD) { diff --git a/src/hashes.rs b/src/hashes.rs index 07f2822c479fbe9feb8e06ec93308caaad00920f..6520fede0a90e880aa2ccee3df8276b24ed615aa 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -75,7 +75,7 @@ pub struct Hash { } impl TryFrom for Hash { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("hash", ns::HASHES) { diff --git a/src/ibb.rs b/src/ibb.rs index ad9873778afcfc4cb362fecb38fdd59ab0e9991d..969f34194fd25fe8836689b6b90d4aee4577976b 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -37,7 +37,7 @@ pub enum IBB { } impl TryFrom for IBB { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if elem.is("open", ns::IBB) { diff --git a/src/ibr.rs b/src/ibr.rs index 183d371638f203b948f522411a690a85192005e1..a0c7cc723b7f0e1947a618a054e8f012b1f532fc 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::collections::HashMap; -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::{Element, IntoElements, ElementEmitter}; @@ -26,7 +26,7 @@ pub struct Query { } impl TryFrom for Query { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("query", ns::REGISTER) { diff --git a/src/idle.rs b/src/idle.rs index 82009668bfbeed21044f8a7ef0b61f47222d2fd3..3809ff8b0e05b208291d504bf2d761caa5cd5067 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; use chrono::{DateTime, FixedOffset}; @@ -19,7 +19,7 @@ pub struct Idle { } impl TryFrom for Idle { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("idle", ns::IDLE) { diff --git a/src/iq.rs b/src/iq.rs index 976d79f8f7656b1ba1a61b7fa5c509b5b1057308..1016102194fd1bf6846be50b96d8c78afb61e546 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; use minidom::IntoAttributeValue; @@ -61,7 +61,7 @@ pub enum IqResultPayload { } impl TryFrom for IqGetPayload { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { @@ -98,7 +98,7 @@ impl Into for IqGetPayload { } impl TryFrom for IqSetPayload { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { @@ -137,7 +137,7 @@ impl Into for IqSetPayload { } impl TryFrom for IqResultPayload { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { @@ -199,7 +199,7 @@ pub struct Iq { } impl TryFrom for Iq { - type Error = Error; + type Err = Error; fn try_from(root: Element) -> Result { if !root.is("iq", ns::JABBER_CLIENT) { diff --git a/src/jingle.rs b/src/jingle.rs index ece7fa023a89c934ad4400dd93047c8368e731f8..c9470326f32803c1f85c4714dd8be12f85c6993a 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; @@ -57,7 +57,7 @@ pub struct Content { } impl TryFrom for Content { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("content", ns::JINGLE) { @@ -196,7 +196,7 @@ pub struct ReasonElement { } impl TryFrom for ReasonElement { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("reason", ns::JINGLE) { @@ -261,7 +261,7 @@ pub struct Jingle { } impl TryFrom for Jingle { - type Error = Error; + type Err = Error; fn try_from(root: Element) -> Result { if !root.is("jingle", ns::JINGLE) { diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index a70b012b200b15dbec53db1bc0e44c2ca2d650a7..5e070cd5e6d8f4291ebf87da8c1e31749049914c 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use hashes::Hash; @@ -85,7 +85,7 @@ impl IntoElements for Received { } impl TryFrom for Description { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("description", ns::JINGLE_FT) { diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 72ebba550ea5ae6e9ac6d02faadd8a7d210624fa..08a313297e88c1b16a5b3d9a83229679c54f0b3a 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -25,7 +25,7 @@ pub struct Transport { } impl TryFrom for Transport { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("transport", ns::JINGLE_IBB) { diff --git a/src/jingle_message.rs b/src/jingle_message.rs index d61133427655ebeb9037c259279bdc37dd91341b..216e1877386fa185894a9bb0936c111b833693b9 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -44,7 +44,7 @@ fn check_empty_and_get_sid(elem: Element) -> Result { } impl TryFrom for JingleMI { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if elem.ns() != Some(ns::JINGLE_MESSAGE) { diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 57807be313ec817c1fbd9d7c8f44791c568d2e7f..603dbb242f7246e7ed4e388e3d895f90bb31020d 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -73,7 +73,7 @@ pub struct Transport { } impl TryFrom for Transport { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if elem.is("transport", ns::JINGLE_S5B) { diff --git a/src/lib.rs b/src/lib.rs index f82e6df349d6d63df7f87a9fb64fb7e2261f8e51..35dacabd7cf1c705514a6ce56cf46383b2d6ba58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,8 +13,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![feature(try_from)] - extern crate minidom; extern crate jid; extern crate base64; @@ -24,6 +22,7 @@ extern crate sha2; extern crate sha3; extern crate blake2; extern crate chrono; +extern crate try_from; macro_rules! get_attr { ($elem:ident, $attr:tt, $type:tt) => ( diff --git a/src/mam.rs b/src/mam.rs index 920cf8d01a76ae44d95839ad658405e99372dcd6..62b035e1691487c1932bdf1dd54de955c3b92db6 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -53,7 +53,7 @@ pub struct Prefs { } impl TryFrom for Query { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("query", ns::MAM) { @@ -77,7 +77,7 @@ impl TryFrom for Query { } impl TryFrom for Result_ { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("result", ns::MAM) { @@ -103,7 +103,7 @@ impl TryFrom for Result_ { } impl TryFrom for Fin { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("fin", ns::MAM) { @@ -129,7 +129,7 @@ impl TryFrom for Fin { } impl TryFrom for Prefs { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("prefs", ns::MAM) { diff --git a/src/media_element.rs b/src/media_element.rs index b1c36a64638fe7ee28a8de7ab376e4007a1f3e55..aa15efb5637e8d440f40a7de5f1d58d6fedd5fd1 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::{Element, IntoElements, ElementEmitter}; @@ -42,7 +42,7 @@ pub struct MediaElement { } impl TryFrom for MediaElement { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("media", ns::MEDIA_ELEMENT) { diff --git a/src/message.rs b/src/message.rs index 47c8db16eaebfbbb8256a7a45a2ce91a589bb4d0..3c7fefc8e4065e11e29d149157d8b833ca4ea2dd 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -43,7 +43,7 @@ pub enum MessagePayload { } impl TryFrom for MessagePayload { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { @@ -143,7 +143,7 @@ impl Message { } impl TryFrom for Message { - type Error = Error; + type Err = Error; fn try_from(root: Element) -> Result { if !root.is("message", ns::JABBER_CLIENT) { diff --git a/src/message_correct.rs b/src/message_correct.rs index 8ce747f114d9139b843c58bf34d52d8a46ec5043..5376f8604370de2e889bd3a642cb521a1e81d9f7 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -18,7 +18,7 @@ pub struct Replace { } impl TryFrom for Replace { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("replace", ns::MESSAGE_CORRECT) { diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 75667d877352ccd6e38404a1a16ef4e1431cf7db..e7e6a238e0b64329df12150a4ab1de9cb990b7e7 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -18,7 +18,7 @@ pub struct Muc { } impl TryFrom for Muc { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("x", ns::MUC) { diff --git a/src/muc/user.rs b/src/muc/user.rs index 833b5604a959b43f9455f0003331b001f1646574..1609b0c8d1680df7d7a4f66e18d282116e43e61a 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -4,8 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; -use std::convert::TryInto; +use try_from::{TryFrom, TryInto}; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; @@ -74,7 +73,7 @@ pub enum Status { } impl TryFrom for Status { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("status", ns::MUC_USER) { @@ -160,7 +159,7 @@ pub enum Actor { } impl TryFrom for Actor { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("actor", ns::MUC_USER) { @@ -210,7 +209,7 @@ pub struct Continue { } impl TryFrom for Continue { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("continue", ns::MUC_USER) { @@ -247,7 +246,7 @@ impl IntoElements for Continue { pub struct Reason(String); impl TryFrom for Reason { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("reason", ns::MUC_USER) { @@ -305,7 +304,7 @@ pub struct Item { } impl TryFrom for Item { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("item", ns::MUC_USER) { @@ -374,7 +373,7 @@ pub struct MucUser { } impl TryFrom for MucUser { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("x", ns::MUC_USER) { diff --git a/src/ping.rs b/src/ping.rs index 84a7cebf0d9796abc09046bb29346a617c84b99c..272044517ae4b2873212364011b8946f284e3e26 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -17,7 +17,7 @@ use ns; pub struct Ping; impl TryFrom for Ping { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("ping", ns::PING) { diff --git a/src/presence.rs b/src/presence.rs index 74458797a457e320a0e158790cca120f5f8b90c4..1eb0b23fb86f56faec9065f417d0ddaa78c4dc95 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -91,7 +91,7 @@ pub enum PresencePayload { } impl TryFrom for PresencePayload { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { @@ -214,7 +214,7 @@ impl Presence { } impl TryFrom for Presence { - type Error = Error; + type Err = Error; fn try_from(root: Element) -> Result { if !root.is("presence", ns::JABBER_CLIENT) { diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 07e465c89a59dc3c7bdc3af520b1f787751dd588..ef5e994b8abb4471c07d78374e17e0a0eae660d7 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; @@ -138,7 +138,7 @@ fn parse_items(elem: Element, node: String) -> Result { } impl TryFrom for PubSubEvent { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("event", ns::PUBSUB_EVENT) { diff --git a/src/receipts.rs b/src/receipts.rs index 72b357a1de277ef15308ed3704a6c0a58fe17cb7..ae564f747066bdb0b3ec0244abb5507d5b51245b 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -19,7 +19,7 @@ pub enum Receipt { } impl TryFrom for Receipt { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { for _ in elem.children() { diff --git a/src/roster.rs b/src/roster.rs index db939ad8135ae537040af90125947eacdca36e4e..4e6a38c6d1bf9d6684db15746b6a56688adf50dc 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; @@ -32,7 +32,7 @@ pub struct Item { } impl TryFrom for Item { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("item", ns::ROSTER) { @@ -83,7 +83,7 @@ pub struct Roster { } impl TryFrom for Roster { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("query", ns::ROSTER) { diff --git a/src/rsm.rs b/src/rsm.rs index e8d7109d8465dc7867077cfa0375bb5aa0ce2d05..cfb6f29243140d517ff9b07b3bbc93f3aa916d90 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; @@ -25,7 +25,7 @@ pub struct Set { } impl TryFrom for Set { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("set", ns::RSM) { diff --git a/src/stanza_error.rs b/src/stanza_error.rs index badd1efab7606026dde90871142add5083bb35a7..c25c5792ec0ac234f3d9fc34f714dffa14ead38c 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -122,7 +122,7 @@ pub struct StanzaError { } impl TryFrom for StanzaError { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { if !elem.is("error", ns::JABBER_CLIENT) { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 38008d0c5c8230050c7a12c8f200e9dccebe88fc..634def50a063c0ecbc159e0012fe7df270c2bee1 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::convert::TryFrom; +use try_from::TryFrom; use minidom::Element; use jid::Jid; @@ -25,7 +25,7 @@ pub enum StanzaId { } impl TryFrom for StanzaId { - type Error = Error; + type Err = Error; fn try_from(elem: Element) -> Result { let is_stanza_id = elem.is("stanza-id", ns::SID); From 5ec921fa6fa5a0e9888396286c593c45d80ba240 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 20:11:00 +0100 Subject: [PATCH 0386/1020] attention: Add missing unknown attribute check. --- src/attention.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/attention.rs b/src/attention.rs index c53040c4876bc0046e7f0b5974b13f5a5d87b9eb..333d19000e740c2c4d3f1543849667bfef8cc241 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -25,6 +25,9 @@ impl TryFrom for Attention { for _ in elem.children() { return Err(Error::ParseError("Unknown child in attention element.")); } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in attention element.")); + } Ok(Attention) } } @@ -61,6 +64,17 @@ mod tests { assert_eq!(message, "Unknown child in attention element."); } + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = Attention::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in attention element."); + } + #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); From 487dbdc6de0e429ac5c0bef88ac6f4ef1709452a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 20:36:13 +0100 Subject: [PATCH 0387/1020] =?UTF-8?q?Replace=20Into=20with=20From?= =?UTF-8?q?<=E2=80=A6>=20for=20Element.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows Element::from() to work, and since Into is automatically implemented for any type implementing From there is no change to existing code. --- src/attention.rs | 4 +-- src/chatstates.rs | 6 ++-- src/delay.rs | 10 +++---- src/disco.rs | 30 ++++++++++---------- src/ecaps2.rs | 10 +++---- src/eme.rs | 8 +++--- src/forwarding.rs | 8 +++--- src/hashes.rs | 8 +++--- src/ibb.rs | 6 ++-- src/ibr.rs | 12 ++++---- src/idle.rs | 6 ++-- src/iq.rs | 32 ++++++++++----------- src/jingle.rs | 49 ++++++++++++++++---------------- src/jingle_ft.rs | 24 ++++++++-------- src/jingle_ibb.rs | 10 +++---- src/jingle_s5b.rs | 28 +++++++++--------- src/mam.rs | 44 ++++++++++++++--------------- src/message.rs | 64 +++++++++++++++++++++--------------------- src/message_correct.rs | 6 ++-- src/muc/muc.rs | 7 +++-- src/muc/user.rs | 49 ++++++++++++++++---------------- src/ping.rs | 4 +-- src/presence.rs | 26 ++++++++--------- src/receipts.rs | 6 ++-- src/roster.rs | 20 ++++++------- src/rsm.rs | 34 +++++++++++----------- src/stanza_error.rs | 14 ++++----- src/stanza_id.rs | 6 ++-- 28 files changed, 266 insertions(+), 265 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 333d19000e740c2c4d3f1543849667bfef8cc241..b72079b9599de39e50cfa2f9664ace6c6cfe531c 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -32,8 +32,8 @@ impl TryFrom for Attention { } } -impl Into for Attention { - fn into(self) -> Element { +impl From for Element { + fn from(_: Attention) -> Element { Element::builder("attention") .ns(ns::ATTENTION) .build() diff --git a/src/chatstates.rs b/src/chatstates.rs index ff1baa9563bf82dad351fcfe15aeb5002b0e9b5e..a5af910c6dca44762f9305f30f15ed5723e653b1 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -45,9 +45,9 @@ impl TryFrom for ChatState { } } -impl Into for ChatState { - fn into(self) -> Element { - Element::builder(match self { +impl From for Element { + fn from(chatstate: ChatState) -> Element { + Element::builder(match chatstate { ChatState::Active => "active", ChatState::Composing => "composing", ChatState::Gone => "gone", diff --git a/src/delay.rs b/src/delay.rs index 7b56ab7eeeff733bb0f587ea4b2e280464b804bc..b96db2bdc2440081688b5c45cf460ab622812f98 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -45,13 +45,13 @@ impl TryFrom for Delay { } } -impl Into for Delay { - fn into(self) -> Element { +impl From for Element { + fn from(delay: Delay) -> Element { Element::builder("delay") .ns(ns::DELAY) - .attr("from", self.from.and_then(|value| Some(String::from(value)))) - .attr("stamp", self.stamp.to_rfc3339()) - .append(self.data) + .attr("from", delay.from.and_then(|value| Some(String::from(value)))) + .attr("stamp", delay.stamp.to_rfc3339()) + .append(delay.data) .build() } } diff --git a/src/disco.rs b/src/disco.rs index 43727d51d82ba3ebf1d4ce1a6a04e200bee7dfb5..aee3de6ae612cacb8e40e35ef6bb66c0f8baab23 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -53,11 +53,11 @@ pub struct Feature { pub var: String, } -impl Into for Feature { - fn into(self) -> Element { +impl From for Element { + fn from(feature: Feature) -> Element { Element::builder("feature") .ns(ns::DISCO_INFO) - .attr("var", self.var) + .attr("var", feature.var) .build() } } @@ -76,14 +76,14 @@ pub struct Identity { pub name: Option, } -impl Into for Identity { - fn into(self) -> Element { +impl From for Element { + fn from(identity: Identity) -> Element { Element::builder("identity") .ns(ns::DISCO_INFO) - .attr("category", self.category) - .attr("type", self.type_) - .attr("xml:lang", self.lang) - .attr("name", self.name) + .attr("category", identity.category) + .attr("type", identity.type_) + .attr("xml:lang", identity.lang) + .attr("name", identity.name) .build() } } @@ -174,16 +174,16 @@ impl TryFrom for DiscoInfoResult { } } -impl Into for DiscoInfoResult { - fn into(self) -> Element { - for _ in self.extensions { +impl From for Element { + fn from(disco: DiscoInfoResult) -> Element { + for _ in disco.extensions { panic!("Not yet implemented!"); } Element::builder("query") .ns(ns::DISCO_INFO) - .attr("node", self.node) - .append(self.identities) - .append(self.features) + .attr("node", disco.node) + .append(disco.identities) + .append(disco.features) .build() } } diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 0e92796de154b539ffd0323840d60fcba3599916..54228c8d78a9c0cd4d61033532c25fd2720712df 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -47,13 +47,13 @@ impl TryFrom for ECaps2 { } } -impl Into for ECaps2 { - fn into(mut self) -> Element { +impl From for Element { + fn from(mut ecaps2: ECaps2) -> Element { Element::builder("c") .ns(ns::ECAPS2) - .append(self.hashes.drain(..) - .map(|hash| hash.into()) - .collect::>()) + .append(ecaps2.hashes.drain(..) + .map(|hash| hash.into()) + .collect::>()) .build() } } diff --git a/src/eme.rs b/src/eme.rs index ee71e21c75cd96c3a888654a6602c3955a9eb769..ba07f95cef5613123e180f88bd1148cc93970510 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -35,12 +35,12 @@ impl TryFrom for ExplicitMessageEncryption { } } -impl Into for ExplicitMessageEncryption { - fn into(self) -> Element { +impl From for Element { + fn from(eme: ExplicitMessageEncryption) -> Element { Element::builder("encryption") .ns(ns::EME) - .attr("namespace", self.namespace) - .attr("name", self.name) + .attr("namespace", eme.namespace) + .attr("name", eme.name) .build() } } diff --git a/src/forwarding.rs b/src/forwarding.rs index d10860ff30a9cee175461c38b6b26eca99ff7077..3afaabdf6a1af5d36e8f22e265a3c35a2fe0a830 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -48,12 +48,12 @@ impl TryFrom for Forwarded { } } -impl Into for Forwarded { - fn into(self) -> Element { +impl From for Element { + fn from(forwarded: Forwarded) -> Element { Element::builder("forwarded") .ns(ns::FORWARD) - .append(match self.delay { Some(delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) - .append(match self.stanza { Some(stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) + .append(match forwarded.delay { Some(delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) + .append(match forwarded.stanza { Some(stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) .build() } } diff --git a/src/hashes.rs b/src/hashes.rs index 6520fede0a90e880aa2ccee3df8276b24ed615aa..bcaf94ac1e874ca641239c74a8987711364bd500 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -96,12 +96,12 @@ impl TryFrom for Hash { } } -impl Into for Hash { - fn into(self) -> Element { +impl From for Element { + fn from(hash: Hash) -> Element { Element::builder("hash") .ns(ns::HASHES) - .attr("algo", self.algo) - .append(base64::encode(&self.hash)) + .attr("algo", hash.algo) + .append(base64::encode(&hash.hash)) .build() } } diff --git a/src/ibb.rs b/src/ibb.rs index 969f34194fd25fe8836689b6b90d4aee4577976b..cdc9b5cdb2e5395d6fe18ce8be1478be0f64dff3 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -78,9 +78,9 @@ impl TryFrom for IBB { } } -impl Into for IBB { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(ibb: IBB) -> Element { + match ibb { IBB::Open { block_size, sid, stanza } => { Element::builder("open") .ns(ns::IBB) diff --git a/src/ibr.rs b/src/ibr.rs index a0c7cc723b7f0e1947a618a054e8f012b1f532fc..37c54f7b3428424a1ce8ce9b3b8704e4ea326ebe 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -64,16 +64,16 @@ impl TryFrom for Query { } } -impl Into for Query { - fn into(self) -> Element { +impl From for Element { + fn from(query: Query) -> Element { Element::builder("query") .ns(ns::REGISTER) - .append(if self.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None }) - .append(self.fields.iter().map(|(name, value)| { + .append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None }) + .append(query.fields.iter().map(|(name, value)| { Element::builder(name.clone()).ns(ns::REGISTER).append(value.clone()) }).collect::>()) - .append(if self.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None }) - .append(self.form) + .append(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None }) + .append(query.form) .build() } } diff --git a/src/idle.rs b/src/idle.rs index 3809ff8b0e05b208291d504bf2d761caa5cd5067..51c203934056755d7a07bc771aea6837ced92d63 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -33,11 +33,11 @@ impl TryFrom for Idle { } } -impl Into for Idle { - fn into(self) -> Element { +impl From for Element { + fn from(idle: Idle) -> Element { Element::builder("idle") .ns(ns::IDLE) - .attr("since", self.since.to_rfc3339()) + .attr("since", idle.since.to_rfc3339()) .build() } } diff --git a/src/iq.rs b/src/iq.rs index 1016102194fd1bf6846be50b96d8c78afb61e546..d1078b348706686a8e1f917cc0cff0d14654ae05 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -83,9 +83,9 @@ impl TryFrom for IqGetPayload { } } -impl Into for IqGetPayload { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(payload: IqGetPayload) -> Element { + match payload { IqGetPayload::Roster(roster) => roster.into(), IqGetPayload::DiscoInfo(disco) => disco.into(), IqGetPayload::Ping(ping) => ping.into(), @@ -122,9 +122,9 @@ impl TryFrom for IqSetPayload { } } -impl Into for IqSetPayload { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(payload: IqSetPayload) -> Element { + match payload { IqSetPayload::Roster(roster) => roster.into(), IqSetPayload::IBB(ibb) => ibb.into(), IqSetPayload::Jingle(jingle) => jingle.into(), @@ -157,9 +157,9 @@ impl TryFrom for IqResultPayload { } } -impl Into for IqResultPayload { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(payload: IqResultPayload) -> Element { + match payload { IqResultPayload::Roster(roster) => roster.into(), IqResultPayload::DiscoInfo(disco) => disco.into(), IqResultPayload::MamQuery(query) => query.into(), @@ -267,16 +267,16 @@ impl TryFrom for Iq { } } -impl Into for Iq { - fn into(self) -> Element { +impl From for Element { + fn from(iq: Iq) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) - .attr("from", self.from.and_then(|value| Some(String::from(value)))) - .attr("to", self.to.and_then(|value| Some(String::from(value)))) - .attr("id", self.id) - .attr("type", &self.payload) + .attr("from", iq.from.and_then(|value| Some(String::from(value)))) + .attr("to", iq.to.and_then(|value| Some(String::from(value)))) + .attr("id", iq.id) + .attr("type", &iq.payload) .build(); - let elem = match self.payload { + let elem = match iq.payload { IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem, diff --git a/src/jingle.rs b/src/jingle.rs index c9470326f32803c1f85c4714dd8be12f85c6993a..1d0c07f15434050eb1dfdc39c4a1ca3afaf57aac 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -95,17 +95,17 @@ impl TryFrom for Content { } } -impl Into for Content { - fn into(self) -> Element { +impl From for Element { + fn from(content: Content) -> Element { Element::builder("content") .ns(ns::JINGLE) - .attr("creator", self.creator) - .attr("disposition", self.disposition) - .attr("name", self.name) - .attr("senders", self.senders) - .append(self.description) - .append(self.transport) - .append(self.security) + .attr("creator", content.creator) + .attr("disposition", content.disposition) + .attr("name", content.name) + .attr("senders", content.senders) + .append(content.description) + .append(content.transport) + .append(content.security) .build() } } @@ -165,9 +165,9 @@ impl FromStr for Reason { } } -impl Into for Reason { - fn into(self) -> Element { - Element::builder(match self { +impl From for Element { + fn from(reason: Reason) -> Element { + Element::builder(match reason { Reason::AlternativeSession => "alternative-session", Reason::Busy => "busy", Reason::Cancel => "cancel", @@ -231,12 +231,11 @@ impl TryFrom for ReasonElement { } } -impl Into for ReasonElement { - fn into(self) -> Element { - let reason: Element = self.reason.into(); +impl From for Element { + fn from(reason: ReasonElement) -> Element { Element::builder("reason") - .append(reason) - .append(self.text) + .append(Element::from(reason.reason)) + .append(reason.text) .build() } } @@ -297,16 +296,16 @@ impl TryFrom for Jingle { } } -impl Into for Jingle { - fn into(self) -> Element { +impl From for Element { + fn from(jingle: Jingle) -> Element { Element::builder("jingle") .ns(ns::JINGLE) - .attr("action", self.action) - .attr("initiator", match self.initiator { Some(initiator) => Some(String::from(initiator)), None => None }) - .attr("responder", match self.responder { Some(responder) => Some(String::from(responder)), None => None }) - .attr("sid", self.sid) - .append(self.contents) - .append(self.reason) + .attr("action", jingle.action) + .attr("initiator", match jingle.initiator { Some(initiator) => Some(String::from(initiator)), None => None }) + .attr("responder", match jingle.responder { Some(responder) => Some(String::from(responder)), None => None }) + .attr("sid", jingle.sid) + .append(jingle.contents) + .append(jingle.reason) .build() } } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 5e070cd5e6d8f4291ebf87da8c1e31749049914c..7bd0df8d5f2462c8cd740ddb7fc8b765141b661b 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -172,57 +172,57 @@ impl TryFrom for Description { } } -impl Into for File { - fn into(self) -> Element { +impl From for Element { + fn from(file: File) -> Element { let mut root = Element::builder("file") .ns(ns::JINGLE_FT) .build(); - if let Some(date) = self.date { + if let Some(date) = file.date { root.append_child(Element::builder("date") .ns(ns::JINGLE_FT) .append(date.to_rfc3339()) .build()); } - if let Some(media_type) = self.media_type { + if let Some(media_type) = file.media_type { root.append_child(Element::builder("media-type") .ns(ns::JINGLE_FT) .append(media_type) .build()); } - if let Some(name) = self.name { + if let Some(name) = file.name { root.append_child(Element::builder("name") .ns(ns::JINGLE_FT) .append(name) .build()); } - if let Some(desc) = self.desc { + if let Some(desc) = file.desc { root.append_child(Element::builder("desc") .ns(ns::JINGLE_FT) .append(desc) .build()); } - if let Some(size) = self.size { + if let Some(size) = file.size { root.append_child(Element::builder("size") .ns(ns::JINGLE_FT) .append(format!("{}", size)) .build()); } - if let Some(range) = self.range { + if let Some(range) = file.range { root.append_child(Element::builder("range") .ns(ns::JINGLE_FT) .append(range) .build()); } - for hash in self.hashes { + for hash in file.hashes { root.append_child(hash.into()); } root } } -impl Into for Description { - fn into(self) -> Element { - let file: Element = self.file.into(); +impl From for Element { + fn from(description: Description) -> Element { + let file: Element = description.file.into(); Element::builder("description") .ns(ns::JINGLE_FT) .append(file) diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 08a313297e88c1b16a5b3d9a83229679c54f0b3a..ed1c6d189ec5c5662aa4aab3682bd58cde9223a9 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -42,13 +42,13 @@ impl TryFrom for Transport { } } -impl Into for Transport { - fn into(self) -> Element { +impl From for Element { + fn from(transport: Transport) -> Element { Element::builder("transport") .ns(ns::JINGLE_IBB) - .attr("block-size", self.block_size) - .attr("sid", self.sid) - .attr("stanza", self.stanza) + .attr("block-size", transport.block_size) + .attr("sid", transport.sid) + .attr("stanza", transport.stanza) .build() } } diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 603dbb242f7246e7ed4e388e3d895f90bb31020d..268529047efa6b0eb497cdf3f8752c7c247db109 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -40,16 +40,16 @@ pub struct Candidate { pub type_: Type, } -impl Into for Candidate { - fn into(self) -> Element { +impl From for Element { + fn from(candidate: Candidate) -> Element { Element::builder("candidate") .ns(ns::JINGLE_S5B) - .attr("cid", self.cid) - .attr("host", self.host) - .attr("jid", String::from(self.jid)) - .attr("port", self.port) - .attr("priority", self.priority) - .attr("type", self.type_) + .attr("cid", candidate.cid) + .attr("host", candidate.host) + .attr("jid", String::from(candidate.jid)) + .attr("port", candidate.port) + .attr("priority", candidate.priority) + .attr("type", candidate.type_) .build() } } @@ -137,14 +137,14 @@ impl TryFrom for Transport { } } -impl Into for Transport { - fn into(self) -> Element { +impl From for Element { + fn from(transport: Transport) -> Element { Element::builder("transport") .ns(ns::JINGLE_S5B) - .attr("sid", self.sid) - .attr("dstaddr", self.dstaddr) - .attr("mode", self.mode) - .append(match self.payload { + .attr("sid", transport.sid) + .attr("dstaddr", transport.dstaddr) + .attr("mode", transport.mode) + .append(match transport.payload { TransportPayload::Candidates(mut candidates) => { candidates.drain(..) .map(|candidate| candidate.into()) diff --git a/src/mam.rs b/src/mam.rs index 62b035e1691487c1932bdf1dd54de955c3b92db6..bbc08a6d19b1736779d54cf5d522f3101c55eb8e 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -161,52 +161,52 @@ impl TryFrom for Prefs { } } -impl Into for Query { - fn into(self) -> Element { +impl From for Element { + fn from(query: Query) -> Element { Element::builder("query") .ns(ns::MAM) - .attr("queryid", self.queryid) - .attr("node", self.node) - //.append(self.form.map(|form| -> Element { form.into() })) - .append(self.set.map(|set| -> Element { set.into() })) + .attr("queryid", query.queryid) + .attr("node", query.node) + //.append(query.form.map(|form| -> Element { form.into() })) + .append(query.set.map(|set| -> Element { set.into() })) .build() } } -impl Into for Result_ { - fn into(self) -> Element { +impl From for Element { + fn from(result: Result_) -> Element { let mut elem = Element::builder("result") .ns(ns::MAM) - .attr("queryid", self.queryid) - .attr("id", self.id) + .attr("queryid", result.queryid) + .attr("id", result.id) .build(); - elem.append_child(self.forwarded.into()); + elem.append_child(result.forwarded.into()); elem } } -impl Into for Fin { - fn into(self) -> Element { +impl From for Element { + fn from(fin: Fin) -> Element { let mut elem = Element::builder("fin") .ns(ns::MAM) - .attr("complete", if self.complete { Some("true") } else { None }) + .attr("complete", if fin.complete { Some("true") } else { None }) .build(); - elem.append_child(self.set.into()); + elem.append_child(fin.set.into()); elem } } -impl Into for Prefs { - fn into(self) -> Element { +impl From for Element { + fn from(prefs: Prefs) -> Element { let mut elem = Element::builder("prefs") .ns(ns::MAM) - .attr("default", self.default_) + .attr("default", prefs.default_) .build(); - if !self.always.is_empty() { + if !prefs.always.is_empty() { let mut always = Element::builder("always") .ns(ns::RSM) .build(); - for jid in self.always { + for jid in prefs.always { always.append_child(Element::builder("jid") .ns(ns::RSM) .append(String::from(jid)) @@ -214,11 +214,11 @@ impl Into for Prefs { } elem.append_child(always); } - if !self.never.is_empty() { + if !prefs.never.is_empty() { let mut never = Element::builder("never") .ns(ns::RSM) .build(); - for jid in self.never { + for jid in prefs.never { never.append_child(Element::builder("jid") .ns(ns::RSM) .append(String::from(jid)) diff --git a/src/message.rs b/src/message.rs index 3c7fefc8e4065e11e29d149157d8b833ca4ea2dd..03fc2e8c79dc970ecc3e6edae9a499813abd1b1f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -84,9 +84,9 @@ impl TryFrom for MessagePayload { } } -impl Into for MessagePayload { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(payload: MessagePayload) -> Element { + match payload { MessagePayload::StanzaError(stanza_error) => stanza_error.into(), MessagePayload::Attention(attention) => attention.into(), MessagePayload::ChatState(chatstate) => chatstate.into(), @@ -199,37 +199,37 @@ impl TryFrom for Message { } } -impl Into for Message { - fn into(self) -> Element { +impl From for Element { + fn from(message: Message) -> Element { Element::builder("message") .ns(ns::JABBER_CLIENT) - .attr("from", self.from.and_then(|value| Some(String::from(value)))) - .attr("to", self.to.and_then(|value| Some(String::from(value)))) - .attr("id", self.id) - .attr("type", self.type_) - .append(self.subjects.iter() - .map(|(lang, subject)| { - Element::builder("subject") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(subject) - .build() }) - .collect::>()) - .append(self.bodies.iter() - .map(|(lang, body)| { - Element::builder("body") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(body) - .build() }) - .collect::>()) - .append(self.payloads) + .attr("from", message.from.and_then(|value| Some(String::from(value)))) + .attr("to", message.to.and_then(|value| Some(String::from(value)))) + .attr("id", message.id) + .attr("type", message.type_) + .append(message.subjects.iter() + .map(|(lang, subject)| { + Element::builder("subject") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(subject) + .build() }) + .collect::>()) + .append(message.bodies.iter() + .map(|(lang, body)| { + Element::builder("body") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(body) + .build() }) + .collect::>()) + .append(message.payloads) .build() } } diff --git a/src/message_correct.rs b/src/message_correct.rs index 5376f8604370de2e889bd3a642cb521a1e81d9f7..bb5c2b2e25cc037aa1305f52ac11bf47067b6200 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -37,11 +37,11 @@ impl TryFrom for Replace { } } -impl Into for Replace { - fn into(self) -> Element { +impl From for Element { + fn from(replace: Replace) -> Element { Element::builder("replace") .ns(ns::MESSAGE_CORRECT) - .attr("id", self.id) + .attr("id", replace.id) .build() } } diff --git a/src/muc/muc.rs b/src/muc/muc.rs index e7e6a238e0b64329df12150a4ab1de9cb990b7e7..550cc61e6c603215cbfb94994570ce9eebf7fee7 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -1,4 +1,5 @@ // Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -44,11 +45,11 @@ impl TryFrom for Muc { } } -impl Into for Muc { - fn into(self) -> Element { +impl From for Element { + fn from(muc: Muc) -> Element { Element::builder("x") .ns(ns::MUC) - .append(self.password) + .append(muc.password) .build() } } diff --git a/src/muc/user.rs b/src/muc/user.rs index 1609b0c8d1680df7d7a4f66e18d282116e43e61a..48da79666889ebb5357f15ba3d50c0fa0ef5e03e 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -1,4 +1,5 @@ // Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -113,11 +114,11 @@ impl TryFrom for Status { } } -impl Into for Status { - fn into(self) -> Element { +impl From for Element { + fn from(status: Status) -> Element { Element::builder("status") .ns(ns::MUC_USER) - .attr("code", match self { + .attr("code", match status { Status::NonAnonymousRoom => 100, Status::AffiliationChange => 101, Status::ConfigShowsUnavailableMembers => 102, @@ -186,11 +187,11 @@ impl TryFrom for Actor { } } -impl Into for Actor { - fn into(self) -> Element { +impl From for Element { + fn from(actor: Actor) -> Element { let elem = Element::builder("actor").ns(ns::MUC_USER); - (match self { + (match actor { Actor::Jid(jid) => elem.attr("jid", String::from(jid)), Actor::Nick(nick) => elem.attr("nick", nick), }).build() @@ -227,11 +228,11 @@ impl TryFrom for Continue { } } -impl Into for Continue { - fn into(self) -> Element { +impl From for Element { + fn from(cont: Continue) -> Element { Element::builder("continue") .ns(ns::MUC_USER) - .attr("thread", self.thread) + .attr("thread", cont.thread) .build() } } @@ -262,11 +263,11 @@ impl TryFrom for Reason { } } -impl Into for Reason { - fn into(self) -> Element { +impl From for Element { + fn from(reason: Reason) -> Element { Element::builder("reason") .ns(ns::MUC_USER) - .append(self.0) + .append(reason.0) .build() } } @@ -348,20 +349,20 @@ impl TryFrom for Item { } } -impl Into for Item { - fn into(self) -> Element { +impl From for Element { + fn from(item: Item) -> Element { Element::builder("item") .ns(ns::MUC_USER) - .attr("affiliation", self.affiliation) - .attr("jid", match self.jid { + .attr("affiliation", item.affiliation) + .attr("jid", match item.jid { Some(jid) => Some(String::from(jid)), None => None, }) - .attr("nick", self.nick) - .attr("role", self.role) - .append(self.actor) - .append(self.continue_) - .append(self.reason) + .attr("nick", item.nick) + .attr("role", item.role) + .append(item.actor) + .append(item.continue_) + .append(item.reason) .build() } } @@ -400,11 +401,11 @@ impl TryFrom for MucUser { } } -impl Into for MucUser { - fn into(self) -> Element { +impl From for Element { + fn from(muc_user: MucUser) -> Element { Element::builder("x") .ns(ns::MUC_USER) - .append(self.status) + .append(muc_user.status) .build() } } diff --git a/src/ping.rs b/src/ping.rs index 272044517ae4b2873212364011b8946f284e3e26..df8b193997ad413ad71960289d6a1c26075deb69 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -33,8 +33,8 @@ impl TryFrom for Ping { } } -impl Into for Ping { - fn into(self) -> Element { +impl From for Element { + fn from(_: Ping) -> Element { Element::builder("ping") .ns(ns::PING) .build() diff --git a/src/presence.rs b/src/presence.rs index 1eb0b23fb86f56faec9065f417d0ddaa78c4dc95..e74a6e2855cd3f8b87b56a653f02733497276abb 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -117,9 +117,9 @@ impl TryFrom for PresencePayload { } } -impl Into for PresencePayload { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(payload: PresencePayload) -> Element { + match payload { PresencePayload::StanzaError(stanza_error) => stanza_error.into(), PresencePayload::Muc(muc) => muc.into(), PresencePayload::Caps(caps) => caps.into(), @@ -282,16 +282,16 @@ impl TryFrom for Presence { } } -impl Into for Presence { - fn into(self) -> Element { +impl From for Element { + fn from(presence: Presence) -> Element { Element::builder("presence") .ns(ns::JABBER_CLIENT) - .attr("from", self.from.and_then(|value| Some(String::from(value)))) - .attr("to", self.to.and_then(|value| Some(String::from(value)))) - .attr("id", self.id) - .attr("type", self.type_) - .append(self.show) - .append(self.statuses.iter().map(|(lang, status)| { + .attr("from", presence.from.and_then(|value| Some(String::from(value)))) + .attr("to", presence.to.and_then(|value| Some(String::from(value)))) + .attr("id", presence.id) + .attr("type", presence.type_) + .append(presence.show) + .append(presence.statuses.iter().map(|(lang, status)| { Element::builder("status") .attr("xml:lang", match lang.as_ref() { "" => None, @@ -300,8 +300,8 @@ impl Into for Presence { .append(status) .build() }).collect::>()) - .append(if self.priority == 0 { None } else { Some(format!("{}", self.priority)) }) - .append(self.payloads) + .append(if presence.priority == 0 { None } else { Some(format!("{}", presence.priority)) }) + .append(presence.payloads) .build() } } diff --git a/src/receipts.rs b/src/receipts.rs index ae564f747066bdb0b3ec0244abb5507d5b51245b..7c3625b8daac481a120d068a52e100d17f825891 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -44,9 +44,9 @@ impl TryFrom for Receipt { } } -impl Into for Receipt { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(receipt: Receipt) -> Element { + match receipt { Receipt::Request => Element::builder("request") .ns(ns::RECEIPTS), Receipt::Received(id) => Element::builder("received") diff --git a/src/roster.rs b/src/roster.rs index 4e6a38c6d1bf9d6684db15746b6a56688adf50dc..73522c282709e79fe2544b23e9d419e68bab8761 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -58,14 +58,14 @@ impl TryFrom for Item { } } -impl Into for Item { - fn into(self) -> Element { +impl From for Element { + fn from(item: Item) -> Element { Element::builder("item") .ns(ns::ROSTER) - .attr("jid", String::from(self.jid)) - .attr("name", self.name) - .attr("subscription", self.subscription) - .append(self.groups.iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) + .attr("jid", String::from(item.jid)) + .attr("name", item.name) + .attr("subscription", item.subscription) + .append(item.groups.iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) .build() } } @@ -110,12 +110,12 @@ impl TryFrom for Roster { } } -impl Into for Roster { - fn into(self) -> Element { +impl From for Element { + fn from(roster: Roster) -> Element { Element::builder("query") .ns(ns::ROSTER) - .attr("ver", self.ver) - .append(self.items) + .attr("ver", roster.ver) + .append(roster.items) .build() } } diff --git a/src/rsm.rs b/src/rsm.rs index cfb6f29243140d517ff9b07b3bbc93f3aa916d90..008757dde50077dc620385da3044356bb9750c25 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -86,34 +86,34 @@ impl TryFrom for Set { } } -impl Into for Set { - fn into(self) -> Element { +impl From for Element { + fn from(set: Set) -> Element { let mut elem = Element::builder("set") .ns(ns::RSM) .build(); - if self.after.is_some() { - elem.append_child(Element::builder("after").ns(ns::RSM).append(self.after).build()); + if set.after.is_some() { + elem.append_child(Element::builder("after").ns(ns::RSM).append(set.after).build()); } - if self.before.is_some() { - elem.append_child(Element::builder("before").ns(ns::RSM).append(self.before).build()); + if set.before.is_some() { + elem.append_child(Element::builder("before").ns(ns::RSM).append(set.before).build()); } - if self.count.is_some() { - elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", self.count.unwrap())).build()); + if set.count.is_some() { + elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", set.count.unwrap())).build()); } - if self.first.is_some() { + if set.first.is_some() { elem.append_child(Element::builder("first") .ns(ns::RSM) - .attr("index", self.first_index) - .append(self.first).build()); + .attr("index", set.first_index) + .append(set.first).build()); } - if self.index.is_some() { - elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", self.index.unwrap())).build()); + if set.index.is_some() { + elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", set.index.unwrap())).build()); } - if self.last.is_some() { - elem.append_child(Element::builder("last").ns(ns::RSM).append(self.last).build()); + if set.last.is_some() { + elem.append_child(Element::builder("last").ns(ns::RSM).append(set.last).build()); } - if self.max.is_some() { - elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", self.max.unwrap())).build()); + if set.max.is_some() { + elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", set.max.unwrap())).build()); } elem } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index c25c5792ec0ac234f3d9fc34f714dffa14ead38c..4a83b8dce9ec2d7720134c378b183bc52a823e1a 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -172,15 +172,15 @@ impl TryFrom for StanzaError { } } -impl Into for StanzaError { - fn into(self) -> Element { +impl From for Element { + fn from(err: StanzaError) -> Element { let mut root = Element::builder("error") .ns(ns::JABBER_CLIENT) - .attr("type", self.type_) - .attr("by", self.by.and_then(|by| Some(String::from(by)))) - .append(self.defined_condition) + .attr("type", err.type_) + .attr("by", err.by.and_then(|by| Some(String::from(by)))) + .append(err.defined_condition) .build(); - for (lang, text) in self.texts { + for (lang, text) in err.texts { let elem = Element::builder("text") .ns(ns::XMPP_STANZAS) .attr("xml:lang", lang) @@ -188,7 +188,7 @@ impl Into for StanzaError { .build(); root.append_child(elem); } - if let Some(other) = self.other { + if let Some(other) = err.other { root.append_child(other); } root diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 634def50a063c0ecbc159e0012fe7df270c2bee1..72687d9b3dbd335dadf16d14fea9262344ba4852 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -45,9 +45,9 @@ impl TryFrom for StanzaId { } } -impl Into for StanzaId { - fn into(self) -> Element { - match self { +impl From for Element { + fn from(stanza_id: StanzaId) -> Element { + match stanza_id { StanzaId::StanzaId { id, by } => { Element::builder("stanza-id") .ns(ns::SID) From cc563b46d27bf2abbb0a01761c81f560b55c97af Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 20:52:25 +0100 Subject: [PATCH 0388/1020] ecaps2, forwarding, jingle_s5b, mam: Replace complex into expressions with .map(Element::from). --- src/ecaps2.rs | 4 ++-- src/forwarding.rs | 4 ++-- src/jingle_s5b.rs | 4 ++-- src/mam.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 54228c8d78a9c0cd4d61033532c25fd2720712df..be80ffd53d3e2f5c88990c8ced7c93e422802bc8 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -52,8 +52,8 @@ impl From for Element { Element::builder("c") .ns(ns::ECAPS2) .append(ecaps2.hashes.drain(..) - .map(|hash| hash.into()) - .collect::>()) + .map(Element::from) + .collect::>()) .build() } } diff --git a/src/forwarding.rs b/src/forwarding.rs index 3afaabdf6a1af5d36e8f22e265a3c35a2fe0a830..86474dd1b6cd0456887ca2648cdb6a4fc7c68e6c 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -52,8 +52,8 @@ impl From for Element { fn from(forwarded: Forwarded) -> Element { Element::builder("forwarded") .ns(ns::FORWARD) - .append(match forwarded.delay { Some(delay) => { let elem: Element = delay.into(); Some(elem) }, None => None }) - .append(match forwarded.stanza { Some(stanza) => { let elem: Element = stanza.into(); Some(elem) }, None => None }) + .append(forwarded.delay.map(Element::from)) + .append(forwarded.stanza.map(Element::from)) .build() } } diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 268529047efa6b0eb497cdf3f8752c7c247db109..7ef1e5020420cab1b3306a6ae795417ba617e770 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -147,8 +147,8 @@ impl From for Element { .append(match transport.payload { TransportPayload::Candidates(mut candidates) => { candidates.drain(..) - .map(|candidate| candidate.into()) - .collect::>() + .map(Element::from) + .collect::>() }, TransportPayload::Activated(cid) => { vec!(Element::builder("activated") diff --git a/src/mam.rs b/src/mam.rs index bbc08a6d19b1736779d54cf5d522f3101c55eb8e..b52e80413b69a5028fc03674b95f5fe4b8f96570 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -167,8 +167,8 @@ impl From for Element { .ns(ns::MAM) .attr("queryid", query.queryid) .attr("node", query.node) - //.append(query.form.map(|form| -> Element { form.into() })) - .append(query.set.map(|set| -> Element { set.into() })) + //.append(query.form.map(Element::from)) + .append(query.set.map(Element::from)) .build() } } From 5543d715994f9bccd1c6600ecf98232cd240afa6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 20:53:59 +0100 Subject: [PATCH 0389/1020] attention: Use super::* in tests like every other module. --- src/attention.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index b72079b9599de39e50cfa2f9664ace6c6cfe531c..e394785f76737b0b85b646be69760477be5f64a5 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -42,10 +42,7 @@ impl From for Element { #[cfg(test)] mod tests { - use try_from::TryFrom; - use minidom::Element; - use error::Error; - use super::Attention; + use super::*; #[test] fn test_simple() { From 2571aa7666055c548f7c99f96a86008c12e9650f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 19 Jul 2017 00:21:44 +0100 Subject: [PATCH 0390/1020] presence: Add with_ helpers to build a Presence --- src/presence.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index e74a6e2855cd3f8b87b56a653f02733497276abb..30a2615dd94e7d834b837365be35f6b18c2843fd 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -211,6 +211,36 @@ impl Presence { payloads: vec!(), } } + + pub fn with_from(mut self, from: Option) -> Presence { + self.from = from; + self + } + + pub fn with_to(mut self, to: Option) -> Presence { + self.to = to; + self + } + + pub fn with_id(mut self, id: Option) -> Presence { + self.id = id; + self + } + + pub fn with_show(mut self, show: Show) -> Presence { + self.show = show; + self + } + + pub fn with_priority(mut self, priority: i8) -> Presence { + self.priority = priority; + self + } + + pub fn with_payloads(mut self, payloads: Vec) -> Presence { + self.payloads = payloads; + self + } } impl TryFrom for Presence { From 8850c95155196a9941bd27b920bad24be0f43d01 Mon Sep 17 00:00:00 2001 From: Astro Date: Wed, 19 Jul 2017 01:32:53 +0200 Subject: [PATCH 0391/1020] happy_eyeballs: don't retain errored connects --- src/happy_eyeballs.rs | 26 ++++++++++++++++++++------ src/stream_start.rs | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index f529a87a7ba0f92b69702d2d623a36b1c85750f3..3cd42f4e615d0ad47514b4e93142266ef759f844 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -80,15 +80,29 @@ impl Future for Connecter { return Err(format!("{}", e)), } - for mut connect in self.connects.values_mut() { + let mut connected_stream = None; + self.connects.retain(|_, connect| { + if connected_stream.is_some() { + return false; + } + match connect.poll() { - Ok(Async::NotReady) => (), - Ok(Async::Ready(tcp_stream)) => + Ok(Async::NotReady) => true, + Ok(Async::Ready(tcp_stream)) => { // Success! - return Ok(Async::Ready(tcp_stream)), - Err(e) => - println!("{}", e), + connected_stream = Some(tcp_stream); + false + }, + Err(e) => { + println!("{}", e); + false + }, } + }); + match connected_stream { + Some(tcp_stream) => + return Ok(Async::Ready(tcp_stream)), + None => (), } if self.lookup.is_none() && diff --git a/src/stream_start.rs b/src/stream_start.rs index b194b5bb4f0e7b5353524a75e9c9602d9b159ceb..4d79d4d9286cd99d74c25f36846df4bae2806734 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -89,7 +89,7 @@ impl Future for StreamStart { } else { (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)) }, - Ok(Async::Ready(item)) => + Ok(Async::Ready(_)) => (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)), Ok(Async::NotReady) => (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)), From 6ae3292d0c1b9735ca61d2efa30c89cb462a27a4 Mon Sep 17 00:00:00 2001 From: Astro Date: Wed, 19 Jul 2017 01:57:46 +0200 Subject: [PATCH 0392/1020] add .travis.yml --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..9fdb54bc1da3ce5ba281caa0d4b8174685f26644 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +sudo: false + +language: rust + +rust: + - stable + - beta + - nightly From fc0973539f1a750f2a5a570c6b2a5dfcdfb9867d Mon Sep 17 00:00:00 2001 From: Astro Date: Wed, 19 Jul 2017 01:57:54 +0200 Subject: [PATCH 0393/1020] add README with TODOs --- README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ea4beac8ccdda18dc2359b507aaaf62ed5bd290e --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# TODO + +- [ ] Error type +- [ ] doc +- [ ] tests From 00e2daaecde9d203b6e4beabff9bf1c8b881f759 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 20 Jul 2017 01:07:07 +0200 Subject: [PATCH 0394/1020] echo_bot: use xmpp-parsers to parse message --- examples/echo_bot.rs | 26 +++++++++++++------------- src/client/event.rs | 7 +++++++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index b3be75cd1a7e26189ae669f03ac51c43b5cd62f9..2307cdf643416b46a20b3ee50d7eaf3f64bdba05 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,3 +1,5 @@ +#![feature(try_from)] + extern crate futures; extern crate tokio_core; extern crate tokio_xmpp; @@ -7,12 +9,14 @@ extern crate xmpp_parsers; use std::env::args; use std::process::exit; +use std::convert::TryFrom; use tokio_core::reactor::Core; use futures::{Future, Stream, Sink, future}; use tokio_xmpp::Client; use minidom::Element; use xmpp_parsers::presence::{Presence, Type as PresenceType, Show as PresenceShow}; -use xmpp_parsers::message::Message; +use xmpp_parsers::message::{Message, MessageType}; +use jid::Jid; fn main() { let args: Vec = args().collect(); @@ -50,15 +54,11 @@ fn main() { let presence = make_presence(); send(presence); - } else if let Some(stanza) = event.as_stanza() { - if stanza.name() == "message" && - stanza.attr("type") != Some("error") { + } else if let Some(stanza) = event.into_stanza() { + if let Ok(message) = Message::try_from(stanza) { + if message.type_ != MessageType::Error { // This is a message we'll echo - let from = stanza.attr("from"); - let body = stanza.get_child("body", "jabber:client") - .map(|el| el.text()); - - match (from, body) { + match (message.from, message.bodies.get("")) { (Some(from), Some(body)) => { let reply = make_reply(from, body); send(reply); @@ -66,6 +66,7 @@ fn main() { _ => (), }; } + } } Box::new(future::ok(())) @@ -90,9 +91,8 @@ fn make_presence() -> Element { } // Construct a chat -fn make_reply(to: &str, body: String) -> Element { - let jid = to.parse().unwrap(); - let mut message = Message::new(Some(jid)); - message.bodies.insert(String::new(), body); +fn make_reply(to: Jid, body: &str) -> Element { + let mut message = Message::new(Some(to)); + message.bodies.insert(String::new(), body.to_owned()); message.into() } diff --git a/src/client/event.rs b/src/client/event.rs index 7aba984a480ef6e44421c4f0da488f45577453f3..cae6ceaafa8bef99dbacd39db3bae778d126184c 100644 --- a/src/client/event.rs +++ b/src/client/event.rs @@ -28,4 +28,11 @@ impl Event { _ => None, } } + + pub fn into_stanza(self) -> Option { + match self { + Event::Stanza(stanza) => Some(stanza), + _ => None, + } + } } From f6f6faeb77bceff83a4cf2d6ea467b90b3597b67 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 23:08:23 +0100 Subject: [PATCH 0395/1020] iq, jingle_ft: Simplify item counting with Iterator::count(). --- src/iq.rs | 2 +- src/jingle_ft.rs | 2 +- src/presence.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index d1078b348706686a8e1f917cc0cff0d14654ae05..aed6a83e39d12a1dbc15f923d055be5b861b6d17 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -222,7 +222,7 @@ impl TryFrom for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } error_payload = Some(StanzaError::try_from(elem.clone())?); - } else if root.children().collect::>().len() != 2 { + } else if root.children().count() != 2 { return Err(Error::ParseError("Wrong number of children in iq element.")); } } else { diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 7bd0df8d5f2462c8cd740ddb7fc8b765141b661b..85ac2dfac7e8504d4d7aee11811836a9764fd228 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -91,7 +91,7 @@ impl TryFrom for Description { if !elem.is("description", ns::JINGLE_FT) { return Err(Error::ParseError("This is not a JingleFT description element.")); } - if elem.children().collect::>().len() != 1 { + if elem.children().count() != 1 { return Err(Error::ParseError("JingleFT description element must have exactly one child.")); } diff --git a/src/presence.rs b/src/presence.rs index 30a2615dd94e7d834b837365be35f6b18c2843fd..a0147c7d02d1770e491c9864247caaf87fd7dca1 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -485,6 +485,6 @@ mod tests { presence.statuses.insert(String::from(""), status); let elem: Element = presence.into(); assert!(elem.is("presence", ns::JABBER_CLIENT)); - assert!(elem.children().collect::>()[0].is("status", ns::JABBER_CLIENT)); + assert!(elem.children().next().unwrap().is("status", ns::JABBER_CLIENT)); } } From 5df585ca40239282fb0c05d7ef0633e5784a4876 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 23:09:22 +0100 Subject: [PATCH 0396/1020] data_forms, ibr, message, presence, roster: Always use into_iter. --- src/data_forms.rs | 2 +- src/ibr.rs | 4 ++-- src/message.rs | 4 ++-- src/presence.rs | 2 +- src/roster.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index d4678c097573e6795345449330c3f67b248a87d5..bccdca0928e1964efa2f932ec1983709dd9fda82 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -72,7 +72,7 @@ impl From for Element { .attr("label", field.label) .append(if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None }) .append(field.options) - .append(field.values.iter().map(|value| { + .append(field.values.into_iter().map(|value| { Element::builder("value").ns(ns::DATA_FORMS).append(value).build() }).collect::>()) .append(field.media) diff --git a/src/ibr.rs b/src/ibr.rs index 37c54f7b3428424a1ce8ce9b3b8704e4ea326ebe..59dc15d81e1c689fe2494746214b1b67fa55394c 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -69,8 +69,8 @@ impl From for Element { Element::builder("query") .ns(ns::REGISTER) .append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None }) - .append(query.fields.iter().map(|(name, value)| { - Element::builder(name.clone()).ns(ns::REGISTER).append(value.clone()) + .append(query.fields.into_iter().map(|(name, value)| { + Element::builder(name).ns(ns::REGISTER).append(value) }).collect::>()) .append(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None }) .append(query.form) diff --git a/src/message.rs b/src/message.rs index 03fc2e8c79dc970ecc3e6edae9a499813abd1b1f..bbb6a5e075cfa35698619440563a52dea65072a7 100644 --- a/src/message.rs +++ b/src/message.rs @@ -207,7 +207,7 @@ impl From for Element { .attr("to", message.to.and_then(|value| Some(String::from(value)))) .attr("id", message.id) .attr("type", message.type_) - .append(message.subjects.iter() + .append(message.subjects.into_iter() .map(|(lang, subject)| { Element::builder("subject") .ns(ns::JABBER_CLIENT) @@ -218,7 +218,7 @@ impl From for Element { .append(subject) .build() }) .collect::>()) - .append(message.bodies.iter() + .append(message.bodies.into_iter() .map(|(lang, body)| { Element::builder("body") .ns(ns::JABBER_CLIENT) diff --git a/src/presence.rs b/src/presence.rs index a0147c7d02d1770e491c9864247caaf87fd7dca1..098c401ad94a478479e480586aa0194ca36c1c05 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -321,7 +321,7 @@ impl From for Element { .attr("id", presence.id) .attr("type", presence.type_) .append(presence.show) - .append(presence.statuses.iter().map(|(lang, status)| { + .append(presence.statuses.into_iter().map(|(lang, status)| { Element::builder("status") .attr("xml:lang", match lang.as_ref() { "" => None, diff --git a/src/roster.rs b/src/roster.rs index 73522c282709e79fe2544b23e9d419e68bab8761..a1ed2dc541e78ea639e0a24b299ec6459f969ee0 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -65,7 +65,7 @@ impl From for Element { .attr("jid", String::from(item.jid)) .attr("name", item.name) .attr("subscription", item.subscription) - .append(item.groups.iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) + .append(item.groups.into_iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) .build() } } From 87af0f36155aed0ea6bf003ea6c03061a17f87bd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 23:10:13 +0100 Subject: [PATCH 0397/1020] message, presence, iq: Improve documentation. --- src/iq.rs | 1 + src/lib.rs | 12 ++++++++++-- src/message.rs | 1 + src/presence.rs | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index aed6a83e39d12a1dbc15f923d055be5b861b6d17..aab1efcbd09145df9823383e989134d23f86b6ad 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -190,6 +190,7 @@ impl<'a> IntoAttributeValue for &'a IqType { } } +/// The main structure representing the `` stanza. #[derive(Debug, Clone)] pub struct Iq { pub from: Option, diff --git a/src/lib.rs b/src/lib.rs index 35dacabd7cf1c705514a6ce56cf46383b2d6ba58..359ba7b0c940dc13d64d762f4f9b1ffbe84f3219 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,11 @@ macro_rules! generate_attribute { ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => ( #[derive(Debug, Clone, PartialEq)] pub enum $elem { - $($a),+ + $( + #[doc=$b] + #[doc="value for this attribute."] + $a + ),+ } impl FromStr for $elem { type Err = Error; @@ -80,7 +84,11 @@ macro_rules! generate_attribute { ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => ( #[derive(Debug, Clone, PartialEq)] pub enum $elem { - $($a),+ + $( + #[doc=$b] + #[doc="value for this attribute."] + $a + ),+ } impl FromStr for $elem { type Err = Error; diff --git a/src/message.rs b/src/message.rs index bbb6a5e075cfa35698619440563a52dea65072a7..ea56042501228f54ceec2913c68d909c464415ff 100644 --- a/src/message.rs +++ b/src/message.rs @@ -115,6 +115,7 @@ type Body = String; type Subject = String; type Thread = String; +/// The main structure representing the `` stanza. #[derive(Debug, Clone)] pub struct Message { pub from: Option, diff --git a/src/presence.rs b/src/presence.rs index 098c401ad94a478479e480586aa0194ca36c1c05..fe970e3b28a7fda338465a4324855b8496706cf6 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -186,6 +186,7 @@ impl IntoAttributeValue for Type { } } +/// The main structure representing the `` stanza. #[derive(Debug, Clone)] pub struct Presence { pub from: Option, From 7cd31bd425efc599f1c54534cc2ad109dd07d14d Mon Sep 17 00:00:00 2001 From: Astro Date: Fri, 21 Jul 2017 00:19:08 +0200 Subject: [PATCH 0398/1020] delint (clippy) --- examples/echo_bot.rs | 23 +++++++++++------------ src/client/auth.rs | 9 +++------ src/client/bind.rs | 13 +++++-------- src/client/event.rs | 12 ++++++------ src/client/mod.rs | 9 +++------ src/happy_eyeballs.rs | 12 ++++-------- src/stream_start.rs | 4 +--- src/xmpp_codec.rs | 37 ++++++++++++++++++++++--------------- 8 files changed, 55 insertions(+), 64 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 2307cdf643416b46a20b3ee50d7eaf3f64bdba05..40b20ac95fb86f3fed6afd51a65a761b37f7fb24 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -54,18 +54,17 @@ fn main() { let presence = make_presence(); send(presence); - } else if let Some(stanza) = event.into_stanza() { - if let Ok(message) = Message::try_from(stanza) { - if message.type_ != MessageType::Error { - // This is a message we'll echo - match (message.from, message.bodies.get("")) { - (Some(from), Some(body)) => { - let reply = make_reply(from, body); - send(reply); - }, - _ => (), - }; - } + } else if let Some(message) = event.into_stanza() + .and_then(|stanza| Message::try_from(stanza).ok()) + { + // This is a message we'll echo + match (message.from, message.bodies.get("")) { + (Some(from), Some(body)) => + if message.type_ != MessageType::Error { + let reply = make_reply(from, body); + send(reply); + }, + _ => (), } } diff --git a/src/client/auth.rs b/src/client/auth.rs index dce2f86c831c38103f007c88c7dcb00730f0cec7..05006fdd5053fe7b767a448c08275b61ba7c48f1 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -130,12 +130,9 @@ impl Future for ClientAuth { if stanza.name() == "failure" && stanza.ns() == Some(NS_XMPP_SASL) => { - let mut e = None; - for child in stanza.children() { - e = Some(child.name().clone()); - break - } - let e = e.unwrap_or_else(|| "Authentication failure"); + let e = stanza.children().next() + .map(|child| child.name()) + .unwrap_or("Authentication failure"); Err(e.to_owned()) }, Ok(Async::Ready(event)) => { diff --git a/src/client/bind.rs b/src/client/bind.rs index 7800bbcca160a3acec647fa8cd9a64f20dee4dde..1541afd3543c4284cae66c45405d83869243c650 100644 --- a/src/client/bind.rs +++ b/src/client/bind.rs @@ -44,13 +44,10 @@ fn make_bind_request(resource: Option<&String>) -> Element { .attr("id", BIND_REQ_ID); let mut bind_el = Element::builder("bind") .ns(NS_XMPP_BIND); - match resource { - Some(resource) => { - let resource_el = Element::builder("resource") - .append(resource); - bind_el = bind_el.append(resource_el.build()); - }, - None => (), + if let Some(resource) = resource { + let resource_el = Element::builder("resource") + .append(resource); + bind_el = bind_el.append(resource_el.build()); } iq.append(bind_el.build()) .build() @@ -87,7 +84,7 @@ impl Future for ClientBind { && iq.attr("id") == Some(BIND_REQ_ID) => { match iq.attr("type") { Some("result") => { - get_bind_response_jid(&iq) + get_bind_response_jid(iq) .map(|jid| stream.jid = jid); Ok(Async::Ready(stream)) }, diff --git a/src/client/event.rs b/src/client/event.rs index cae6ceaafa8bef99dbacd39db3bae778d126184c..8ff44bd70a84ded7200eace6cea4055015a7e29a 100644 --- a/src/client/event.rs +++ b/src/client/event.rs @@ -9,22 +9,22 @@ pub enum Event { impl Event { pub fn is_online(&self) -> bool { - match self { - &Event::Online => true, + match *self { + Event::Online => true, _ => false, } } pub fn is_stanza(&self, name: &str) -> bool { - match self { - &Event::Stanza(ref stanza) => stanza.name() == name, + match *self { + Event::Stanza(ref stanza) => stanza.name() == name, _ => false, } } pub fn as_stanza(&self) -> Option<&Element> { - match self { - &Event::Stanza(ref stanza) => Some(stanza), + match *self { + Event::Stanza(ref stanza) => Some(stanza), _ => None, } } diff --git a/src/client/mod.rs b/src/client/mod.rs index aca0fdd20294cc45324a2492361eac8a12062a5b..b5d7e8fcef6b52554faff5f6c1f702471f169ca7 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -130,10 +130,6 @@ impl Stream for Client { }, ClientState::Connected(mut stream) => { match stream.poll() { - Ok(Async::NotReady) => { - self.state = ClientState::Connected(stream); - Ok(Async::NotReady) - }, Ok(Async::Ready(None)) => { // EOF self.state = ClientState::Disconnected; @@ -143,6 +139,7 @@ impl Stream for Client { self.state = ClientState::Connected(stream); Ok(Async::Ready(Some(ClientEvent::Stanza(stanza)))) }, + Ok(Async::NotReady) | Ok(Async::Ready(_)) => { self.state = ClientState::Connected(stream); Ok(Async::NotReady) @@ -179,8 +176,8 @@ impl Sink for Client { } fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { - match &mut self.state { - &mut ClientState::Connected(ref mut stream) => + match self.state { + ClientState::Connected(ref mut stream) => stream.poll_complete() .map_err(|e| e.description().to_owned()), _ => diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 3cd42f4e615d0ad47514b4e93142266ef759f844..806472c5d602b4b32e082841a997f7b8634cc0ed 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -46,8 +46,7 @@ impl Future for Connecter { fn poll(&mut self) -> Poll { match self.lookup.as_mut().map(|mut lookup| lookup.poll()) { - None => (), - Some(Ok(Async::NotReady)) => (), + None | Some(Ok(Async::NotReady)) => (), Some(Ok(Async::Ready(found_srvs))) => { self.lookup = None; match found_srvs { @@ -62,8 +61,7 @@ impl Future for Connecter { } match self.srvs.as_mut().map(|mut srv| srv.poll()) { - None => (), - Some(Ok(Async::NotReady)) => (), + None | Some(Ok(Async::NotReady)) => (), Some(Ok(Async::Ready(None))) => self.srvs = None, Some(Ok(Async::Ready(Some(srv_item)))) => { @@ -99,10 +97,8 @@ impl Future for Connecter { }, } }); - match connected_stream { - Some(tcp_stream) => - return Ok(Async::Ready(tcp_stream)), - None => (), + if let Some(tcp_stream) = connected_stream { + return Ok(Async::Ready(tcp_stream)); } if self.lookup.is_none() && diff --git a/src/stream_start.rs b/src/stream_start.rs index 4d79d4d9286cd99d74c25f36846df4bae2806734..b97e87d99d80ecbc734a1c848ab033180d2180a8 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -89,9 +89,7 @@ impl Future for StreamStart { } else { (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)) }, - Ok(Async::Ready(_)) => - (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)), - Ok(Async::NotReady) => + Ok(Async::Ready(_)) | Ok(Async::NotReady) => (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)), Err(e) => return Err(e), diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 6686ed2b6c9b23b8a0f64f462e602ac63b07bc44..bb725835d57949a97e1be48cf82a8ff9dfc21994 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -48,9 +48,8 @@ impl ParserSink { fn lookup_ns(&self, prefix: &Option) -> Option<&str> { for nss in self.ns_stack.iter().rev() { - match nss.get(prefix) { - Some(ns) => return Some(ns), - None => (), + if let Some(ns) = nss.get(prefix) { + return Some(ns); } } @@ -77,9 +76,11 @@ impl ParserSink { let el = { let mut el_builder = Element::builder(tag.name.local.as_ref()); - match self.lookup_ns(&tag.name.prefix.map(|prefix| prefix.as_ref().to_owned())) { - Some(el_ns) => el_builder = el_builder.ns(el_ns), - None => (), + if let Some(el_ns) = self.lookup_ns( + &tag.name.prefix.map(|prefix| + prefix.as_ref().to_owned()) + ) { + el_builder = el_builder.ns(el_ns); } for attr in &tag.attrs { match attr.name.local.as_ref() { @@ -178,7 +179,7 @@ pub struct XMPPCodec { impl XMPPCodec { pub fn new() -> Self { - let queue = Rc::new(RefCell::new((VecDeque::new()))); + let queue = Rc::new(RefCell::new(VecDeque::new())); let sink = ParserSink::new(queue.clone()); // TODO: configure parser? let parser = XmlTokenizer::new(sink, Default::default()); @@ -191,13 +192,19 @@ impl XMPPCodec { } } +impl Default for XMPPCodec { + fn default() -> Self { + Self::new() + } +} + impl Decoder for XMPPCodec { type Item = Packet; type Error = Error; fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { let buf1: Box> = - if self.buf.len() > 0 && buf.len() > 0 { + if ! self.buf.is_empty() && ! buf.is_empty() { let mut prefix = std::mem::replace(&mut self.buf, vec![]); prefix.extend_from_slice(buf.take().as_ref()); Box::new(prefix) @@ -207,7 +214,7 @@ impl Decoder for XMPPCodec { let buf1 = buf1.as_ref().as_ref(); match from_utf8(buf1) { Ok(s) => { - if s.len() > 0 { + if ! s.is_empty() { println!("<< {}", s); let tendril = FromIterator::from_iter(s.chars()); self.parser.feed(tendril); @@ -257,7 +264,7 @@ impl Encoder for XMPPCodec { Packet::StreamStart(start_attrs) => { let mut buf = String::new(); write!(buf, "(el: &Element, writer: &mut W, parent_ns: Option<& write!(writer, "<")?; write!(writer, "{}", el.name())?; - if let Some(ref ns) = el.ns() { + if let Some(ns) = el.ns() { if parent_ns.map(|s| s.as_ref()) != el.ns() { write!(writer, " xmlns=\"{}\"", ns)?; } @@ -320,10 +327,10 @@ pub fn write_element(el: &Element, writer: &mut W, parent_ns: Option<& write!(writer, ">")?; for node in el.nodes() { - match node { - &Node::Element(ref child) => + match *node { + Node::Element(ref child) => write_element(child, writer, el.ns())?, - &Node::Text(ref text) => + Node::Text(ref text) => write_text(text, writer)?, } } @@ -332,7 +339,7 @@ pub fn write_element(el: &Element, writer: &mut W, parent_ns: Option<& Ok(()) } -/// Copied from RustyXML for now +/// Copied from `RustyXML` for now pub fn escape(input: &str) -> String { let mut result = String::with_capacity(input.len()); From dc5ddc73f6cd776224ca8b9414871b07bfd690dd Mon Sep 17 00:00:00 2001 From: Astro Date: Fri, 21 Jul 2017 00:39:29 +0200 Subject: [PATCH 0399/1020] client: use idna --- Cargo.toml | 1 + src/client/mod.rs | 19 ++++++++++++++----- src/lib.rs | 1 + 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cf7735a0e1f554f6b12dfdab56f76889eb6503a7..aacdbc4c0f9bcbd02d51ac8a6e8fb5215fd930f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ rustc-serialize = "*" jid = "*" domain = "0.2.1" xmpp-parsers = "0.6.0" +idna = "*" diff --git a/src/client/mod.rs b/src/client/mod.rs index b5d7e8fcef6b52554faff5f6c1f702471f169ca7..77641110d11a3c980714400faa7e180135d97125 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -5,10 +5,11 @@ use tokio_core::reactor::Handle; use tokio_core::net::TcpStream; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_tls::TlsStream; -use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; +use futures::{future, Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; use minidom::Element; use jid::{Jid, JidParseError}; use sasl::common::{Credentials, ChannelBinding}; +use idna; use super::xmpp_codec::Packet; use super::xmpp_stream; @@ -53,19 +54,27 @@ impl Client { let jid1 = jid.clone(); let jid2 = jid.clone(); let password = password; + let domain = match idna::domain_to_ascii(&jid.domain) { + Ok(domain) => + domain, + Err(e) => + return Box::new(future::err(format!("{:?}", e))), + }; Box::new( - Connecter::from_lookup(handle, &jid.domain, "_xmpp-client._tcp", 5222) + Connecter::from_lookup(handle, &domain, "_xmpp-client._tcp", 5222) .expect("Connector::from_lookup") .and_then(move |tcp_stream| xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_CLIENT.to_owned()) .map_err(|e| format!("{}", e)) ).and_then(|xmpp_stream| { if Self::can_starttls(&xmpp_stream) { - Self::starttls(xmpp_stream) + Ok(Self::starttls(xmpp_stream)) } else { - panic!("No STARTTLS") + Err("No STARTTLS".to_owned()) } - }).and_then(|tls_stream| + }).and_then(|starttls| + starttls + ).and_then(|tls_stream| XMPPStream::start(tls_stream, jid2, NS_JABBER_CLIENT.to_owned()) .map_err(|e| format!("{}", e)) ).and_then(move |xmpp_stream| { diff --git a/src/lib.rs b/src/lib.rs index 348c03b2b2f118a20fa856b1948331836b1a356e..ce8ccb7d7ceca60bb53f4514a48b024eeda18cdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ extern crate sasl; extern crate rustc_serialize as serialize; extern crate jid; extern crate domain; +extern crate idna; pub mod xmpp_codec; pub mod xmpp_stream; From fd31e691af6e03b2235a9449e61f2c48c437daec Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 23:46:44 +0100 Subject: [PATCH 0400/1020] lib: Improve the wording of the docstring. --- src/lib.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 359ba7b0c940dc13d64d762f4f9b1ffbe84f3219..b3f57fdf675ebffca54f744d5efd7b68eca9b97f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,19 @@ //! A crate parsing common XMPP elements into Rust structures. //! -//! Each module implements the `TryFrom<&minidom::Element>` trait, which takes -//! a minidom `Element` reference and returns a `Result`. +//! Each module implements the [`TryFrom`] trait, which takes a minidom +//! [`Element`] reference and returns a `Result` whose value is `Ok` if the +//! element parsed correctly, `Err(error::Error)` otherwise. //! -//! Parsed structs can then be manipulated manually, and must be serialised -//! back before being sent over the wire. +//! The returned structure can be manipuled as any Rust structure, with each +//! field being public. You can also create the same structure manually, with +//! some having `new()` and `with_*()` helper methods to create them. +//! +//! Once you are happy with your structure, you can serialise it back to an +//! [`Element`], using either `From` or `Into`, which give you what +//! you want to be sending on the wire. +//! +//! [`TryFrom`]: ../try_from/trait.TryFrom.html +//! [`Element`]: ../minidom/element/struct.Element.html // Copyright (c) 2017 Emmanuel Gil Peyrot // Copyright (c) 2017 Maxime “pep” Buquet From 7612c53f9af82c7cba7548c21e9ce519c838a13f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Jul 2017 23:47:21 +0100 Subject: [PATCH 0401/1020] attention, chatstates, eme, ping: Add a docstring on the structs. --- src/attention.rs | 1 + src/chatstates.rs | 11 +++++++++++ src/eme.rs | 5 +++++ src/ping.rs | 1 + 4 files changed, 18 insertions(+) diff --git a/src/attention.rs b/src/attention.rs index e394785f76737b0b85b646be69760477be5f64a5..7d0b1f24ff3835472c464c9915973a1629fd2780 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -12,6 +12,7 @@ use error::Error; use ns; +/// Structure representing an `` element. #[derive(Debug, Clone)] pub struct Attention; diff --git a/src/chatstates.rs b/src/chatstates.rs index a5af910c6dca44762f9305f30f15ed5723e653b1..4093edbd29c805a39dbe65731dbf365ff3341413 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -12,12 +12,23 @@ use error::Error; use ns; +/// Enum representing chatstate elements part of the +/// `http://jabber.org/protocol/chatstates` namespace. #[derive(Debug, Clone)] pub enum ChatState { + /// `` Active, + + /// `` Composing, + + /// `` Gone, + + /// `` Inactive, + + /// `` Paused, } diff --git a/src/eme.rs b/src/eme.rs index ba07f95cef5613123e180f88bd1148cc93970510..288431b9c975b95ebc180c7bfeb74c43da63d72a 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -12,9 +12,14 @@ use error::Error; use ns; +/// Structure representing an `` element. #[derive(Debug, Clone)] pub struct ExplicitMessageEncryption { + /// Namespace of the encryption scheme used. pub namespace: String, + + /// User-friendly name for the encryption scheme, should be `None` for OTR, + /// legacy OpenPGP and OX. pub name: Option, } diff --git a/src/ping.rs b/src/ping.rs index df8b193997ad413ad71960289d6a1c26075deb69..c97fa5026ac5595ac3db83332afa492746a3ea3b 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -13,6 +13,7 @@ use error::Error; use ns; +/// Structure representing a `` element. #[derive(Debug, Clone)] pub struct Ping; From 21cee25b27e9c55025d317d66c2ac5862c11baa8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Jul 2017 01:19:34 +0100 Subject: [PATCH 0402/1020] Replace .and_then() with .map() wherever it makes sense. --- src/delay.rs | 2 +- src/iq.rs | 4 ++-- src/message.rs | 4 ++-- src/presence.rs | 4 ++-- src/pubsub/event.rs | 16 ++++++++-------- src/stanza_error.rs | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index b96db2bdc2440081688b5c45cf460ab622812f98..e5123ec2d208493227b3c00888ab39bf914da7e7 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -49,7 +49,7 @@ impl From for Element { fn from(delay: Delay) -> Element { Element::builder("delay") .ns(ns::DELAY) - .attr("from", delay.from.and_then(|value| Some(String::from(value)))) + .attr("from", delay.from.map(String::from)) .attr("stamp", delay.stamp.to_rfc3339()) .append(delay.data) .build() diff --git a/src/iq.rs b/src/iq.rs index aab1efcbd09145df9823383e989134d23f86b6ad..1eae2549cb66a24330e719622615b78c728f2465 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -272,8 +272,8 @@ impl From for Element { fn from(iq: Iq) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) - .attr("from", iq.from.and_then(|value| Some(String::from(value)))) - .attr("to", iq.to.and_then(|value| Some(String::from(value)))) + .attr("from", iq.from.map(String::from)) + .attr("to", iq.to.map(String::from)) .attr("id", iq.id) .attr("type", &iq.payload) .build(); diff --git a/src/message.rs b/src/message.rs index ea56042501228f54ceec2913c68d909c464415ff..29234321f4baabaef635a52593ba4b9e60b6ee11 100644 --- a/src/message.rs +++ b/src/message.rs @@ -204,8 +204,8 @@ impl From for Element { fn from(message: Message) -> Element { Element::builder("message") .ns(ns::JABBER_CLIENT) - .attr("from", message.from.and_then(|value| Some(String::from(value)))) - .attr("to", message.to.and_then(|value| Some(String::from(value)))) + .attr("from", message.from.map(String::from)) + .attr("to", message.to.map(String::from)) .attr("id", message.id) .attr("type", message.type_) .append(message.subjects.into_iter() diff --git a/src/presence.rs b/src/presence.rs index fe970e3b28a7fda338465a4324855b8496706cf6..93c5cf941b246dcf775f683743fc0a8636a59f1e 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -317,8 +317,8 @@ impl From for Element { fn from(presence: Presence) -> Element { Element::builder("presence") .ns(ns::JABBER_CLIENT) - .attr("from", presence.from.and_then(|value| Some(String::from(value)))) - .attr("to", presence.to.and_then(|value| Some(String::from(value)))) + .attr("from", presence.from.map(String::from)) + .attr("to", presence.to.map(String::from)) .attr("id", presence.id) .attr("type", presence.type_) .append(presence.show) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index ef5e994b8abb4471c07d78374e17e0a0eae660d7..04519e96e462cc60ff8a778f707b7132cd1eb0d7 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -31,7 +31,7 @@ impl From for Element { .ns(ns::PUBSUB_EVENT) .attr("id", item.id) .attr("node", item.node) - .attr("publisher", item.publisher.and_then(|publisher| Some(String::from(publisher)))) + .attr("publisher", item.publisher.map(String::from)) .append(item.payload) .build() } @@ -222,11 +222,11 @@ impl From for Element { Element::builder("purge") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append(redirect.and_then(|redirect| { - Some(Element::builder("redirect") - .ns(ns::PUBSUB_EVENT) - .attr("uri", redirect) - .build()) + .append(redirect.map(|redirect| { + Element::builder("redirect") + .ns(ns::PUBSUB_EVENT) + .attr("uri", redirect) + .build() })) .build() }, @@ -260,8 +260,8 @@ impl From for Element { Element::builder("subscription") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .attr("expiry", expiry.and_then(|expiry| Some(expiry.to_rfc3339()))) - .attr("jid", jid.and_then(|jid| Some(String::from(jid)))) + .attr("expiry", expiry.map(|expiry| expiry.to_rfc3339())) + .attr("jid", jid.map(String::from)) .attr("subid", subid) .attr("subscription", subscription) .build() diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 4a83b8dce9ec2d7720134c378b183bc52a823e1a..3b7273b3fa8db3d7498de02f779bb8c1b28be6da 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -177,7 +177,7 @@ impl From for Element { let mut root = Element::builder("error") .ns(ns::JABBER_CLIENT) .attr("type", err.type_) - .attr("by", err.by.and_then(|by| Some(String::from(by)))) + .attr("by", err.by.map(String::from)) .append(err.defined_condition) .build(); for (lang, text) in err.texts { From 3b6733f38b4e748e18021202a772e6f76f5b8751 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Jul 2017 01:20:29 +0100 Subject: [PATCH 0403/1020] =?UTF-8?q?Upgrade=20to=20minidom=C2=A00.4.4=20t?= =?UTF-8?q?o=20avoid=20having=20to=20redefine=20IntoElements=20for=20each?= =?UTF-8?q?=20Into.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 +- src/data_forms.rs | 20 +------------------- src/disco.rs | 14 +------------- src/ibr.rs | 8 +------- src/jingle.rs | 14 +------------- src/media_element.rs | 14 +------------- src/muc/user.rs | 26 +------------------------- src/pubsub/event.rs | 8 +------- src/roster.rs | 8 +------- 9 files changed, 9 insertions(+), 105 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b70538110dc033d592a6b06bab35c010d605c298..b4a760b270f509f5a6344b3f0246c9ba04e1898c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.4.3" +minidom = "0.4.4" jid = "0.2.0" base64 = "0.6.0" digest = "0.6.0" diff --git a/src/data_forms.rs b/src/data_forms.rs index bccdca0928e1964efa2f932ec1983709dd9fda82..eb751e0ba27632389faa0f286dfff851eea24c44 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use error::Error; use ns; @@ -46,12 +46,6 @@ impl From for Element { } } -impl IntoElements for Option_ { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone)] pub struct Field { pub var: String, @@ -80,12 +74,6 @@ impl From for Element { } } -impl IntoElements for Field { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - generate_attribute!(DataFormType, "type", { Cancel => "cancel", Form => "form", @@ -236,12 +224,6 @@ impl From for Element { } } -impl IntoElements for DataForm { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/disco.rs b/src/disco.rs index aee3de6ae612cacb8e40e35ef6bb66c0f8baab23..167440edabd21a8afebbed84ec33af01cc6efce1 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -6,7 +6,7 @@ use try_from::TryFrom; -use minidom::{Element, IntoElements, ElementEmitter}; +use minidom::Element; use error::Error; use ns; @@ -62,12 +62,6 @@ impl From for Element { } } -impl IntoElements for Feature { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone)] pub struct Identity { pub category: String, // TODO: use an enum here. @@ -88,12 +82,6 @@ impl From for Element { } } -impl IntoElements for Identity { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone)] pub struct DiscoInfoResult { pub node: Option, diff --git a/src/ibr.rs b/src/ibr.rs index 59dc15d81e1c689fe2494746214b1b67fa55394c..c51153e65f2f72bfd503d8ff3bbed07cc501fc3d 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; use try_from::TryFrom; -use minidom::{Element, IntoElements, ElementEmitter}; +use minidom::Element; use error::Error; @@ -78,12 +78,6 @@ impl From for Element { } } -impl IntoElements for Query { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/jingle.rs b/src/jingle.rs index 1d0c07f15434050eb1dfdc39c4a1ca3afaf57aac..62fb4b5afdc4b6c387bac2f434b998dcb5e96c10 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; use error::Error; @@ -110,12 +110,6 @@ impl From for Element { } } -impl IntoElements for Content { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone, PartialEq)] pub enum Reason { AlternativeSession, //(String), @@ -240,12 +234,6 @@ impl From for Element { } } -impl IntoElements for ReasonElement { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - generate_id!(SessionId); #[derive(Debug, Clone)] diff --git a/src/media_element.rs b/src/media_element.rs index aa15efb5637e8d440f40a7de5f1d58d6fedd5fd1..b2e5857d449630ab36956cd4b2bcc46f736b558f 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -6,7 +6,7 @@ use try_from::TryFrom; -use minidom::{Element, IntoElements, ElementEmitter}; +use minidom::Element; use error::Error; @@ -28,12 +28,6 @@ impl From for Element { } } -impl IntoElements for URI { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone)] pub struct MediaElement { pub width: Option, @@ -81,12 +75,6 @@ impl From for Element { } } -impl IntoElements for MediaElement { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/muc/user.rs b/src/muc/user.rs index 48da79666889ebb5357f15ba3d50c0fa0ef5e03e..afc9e72187705a9117b436ad45c252587c47e505 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -8,7 +8,7 @@ use try_from::{TryFrom, TryInto}; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -142,12 +142,6 @@ impl From for Element { } } -impl IntoElements for Status { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - /// Optional element used in elements inside presence stanzas of type /// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking /// purposes. -- CHANGELOG 0.17 (2002-10-23) @@ -198,12 +192,6 @@ impl From for Element { } } -impl IntoElements for Actor { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone, PartialEq)] pub struct Continue { thread: Option, @@ -237,12 +225,6 @@ impl From for Element { } } -impl IntoElements for Continue { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone, PartialEq)] pub struct Reason(String); @@ -272,12 +254,6 @@ impl From for Element { } } -impl IntoElements for Reason { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - generate_attribute!(Affiliation, "affiliation", { Owner => "owner", Admin => "admin", diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 04519e96e462cc60ff8a778f707b7132cd1eb0d7..a6fa4550a6775751a465a13a855e82235770508f 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; use chrono::{DateTime, FixedOffset}; @@ -37,12 +37,6 @@ impl From for Element { } } -impl IntoElements for Item { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - generate_attribute!(Subscription, "subscription", { None => "none", Pending => "pending", diff --git a/src/roster.rs b/src/roster.rs index a1ed2dc541e78ea639e0a24b299ec6459f969ee0..1a4cc50e8df99c8b3ba89478364ef6339eaa4af7 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use jid::Jid; use error::Error; @@ -70,12 +70,6 @@ impl From for Element { } } -impl IntoElements for Item { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - #[derive(Debug, Clone)] pub struct Roster { pub ver: Option, From 4454da15b600025c3b4f338313a8883d3d281df5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Jul 2017 17:33:58 +0100 Subject: [PATCH 0404/1020] disco: Implement disco#items. --- src/disco.rs | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 2 + 2 files changed, 162 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index 167440edabd21a8afebbed84ec33af01cc6efce1..aa816decd0bbbe07bd61f0b4fdb339c96c2e640c 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -7,6 +7,7 @@ use try_from::TryFrom; use minidom::Element; +use jid::Jid; use error::Error; use ns; @@ -176,9 +177,131 @@ impl From for Element { } } +#[derive(Debug, Clone)] +pub struct DiscoItemsQuery { + pub node: Option, +} + +impl TryFrom for DiscoItemsQuery { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::DISCO_ITEMS) { + return Err(Error::ParseError("This is not a disco#items element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in disco#items.")); + } + for (attr, _) in elem.attrs() { + if attr != "node" { + return Err(Error::ParseError("Unknown attribute in disco#items.")); + } + } + Ok(DiscoItemsQuery { + node: get_attr!(elem, "node", optional), + }) + } +} + +impl From for Element { + fn from(disco: DiscoItemsQuery) -> Element { + Element::builder("query") + .ns(ns::DISCO_ITEMS) + .attr("node", disco.node) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct Item { + pub jid: Jid, + pub node: Option, + pub name: Option, +} + +impl TryFrom for Item { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("item", ns::DISCO_ITEMS) { + return Err(Error::ParseError("This is not an item element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in item element.")); + } + for (attr, _) in elem.attrs() { + if attr != "jid" && attr != "node" && attr != "name" { + return Err(Error::ParseError("Unknown attribute in item element.")); + } + } + Ok(Item { + jid: get_attr!(elem, "jid", required), + node: get_attr!(elem, "node", optional), + name: get_attr!(elem, "name", optional), + }) + } +} + +impl From for Element { + fn from(item: Item) -> Element { + Element::builder("item") + .ns(ns::DISCO_ITEMS) + .attr("jid", String::from(item.jid)) + .attr("node", item.node) + .attr("name", item.name) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct DiscoItemsResult { + pub node: Option, + pub items: Vec, +} + +impl TryFrom for DiscoItemsResult { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::DISCO_ITEMS) { + return Err(Error::ParseError("This is not a disco#items element.")); + } + for (attr, _) in elem.attrs() { + if attr != "node" { + return Err(Error::ParseError("Unknown attribute in disco#items.")); + } + } + + let mut items: Vec = vec!(); + for child in elem.children() { + if child.is("item", ns::DISCO_ITEMS) { + items.push(Item::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown element in disco#items.")); + } + } + + Ok(DiscoItemsResult { + node: get_attr!(elem, "node", optional), + items: items, + }) + } +} + +impl From for Element { + fn from(disco: DiscoItemsResult) -> Element { + Element::builder("query") + .ns(ns::DISCO_ITEMS) + .attr("node", disco.node) + .append(disco.items) + .build() + } +} + #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; #[test] fn test_simple() { @@ -273,4 +396,41 @@ mod tests { }; assert_eq!(message, "disco#info feature not present in disco#info."); } + + #[test] + fn test_simple_items() { + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsQuery::try_from(elem).unwrap(); + assert!(query.node.is_none()); + + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsQuery::try_from(elem).unwrap(); + assert_eq!(query.node, Some(String::from("coucou"))); + } + + #[test] + fn test_simple_items_result() { + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsResult::try_from(elem).unwrap(); + assert!(query.node.is_none()); + assert!(query.items.is_empty()); + + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsResult::try_from(elem).unwrap(); + assert_eq!(query.node, Some(String::from("coucou"))); + assert!(query.items.is_empty()); + } + + #[test] + fn test_answers_items_result() { + let elem: Element = "".parse().unwrap(); + let query = DiscoItemsResult::try_from(elem).unwrap(); + assert_eq!(query.items.len(), 2); + assert_eq!(query.items[0].jid, Jid::from_str("component").unwrap()); + assert_eq!(query.items[0].node, None); + assert_eq!(query.items[0].name, None); + assert_eq!(query.items[1].jid, Jid::from_str("component2").unwrap()); + assert_eq!(query.items[1].node, Some(String::from("test"))); + assert_eq!(query.items[1].name, Some(String::from("A component"))); + } } diff --git a/src/ns.rs b/src/ns.rs index 7edee1e4aec9af43771c630cdd6380871c075971..0d06a1d7ae483136a705ecaa1fa96cf72f9cc8bd 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -18,6 +18,8 @@ pub const DATA_FORMS: &str = "jabber:x:data"; /// XEP-0030: Service Discovery pub const DISCO_INFO: &str = "http://jabber.org/protocol/disco#info"; +/// XEP-0030: Service Discovery +pub const DISCO_ITEMS: &str = "http://jabber.org/protocol/disco#items"; /// XEP-0045: Multi-User Chat pub const MUC: &str = "http://jabber.org/protocol/muc"; From 430cd4b44d50f93cd630eaa37e9206c19e71e4f1 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 23 Jul 2017 02:04:46 +0200 Subject: [PATCH 0405/1020] README: TODO --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ea4beac8ccdda18dc2359b507aaaf62ed5bd290e..c3b636a02cd135b1efd309efdc4564b84a0c6c12 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # TODO +- [ ] minidom ns +- [ ] customize tls verify? - [ ] Error type +- [ ] unexpected event errors - [ ] doc - [ ] tests From c0f3fc4afbb78092cf48d1c62d3dd67de5ddc145 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 22 Jul 2017 01:59:51 +0100 Subject: [PATCH 0406/1020] lib: add a component connection method --- Cargo.toml | 2 + examples/echo_component.rs | 103 +++++++++++++++++++++++++ src/component/auth.rs | 101 ++++++++++++++++++++++++ src/component/event.rs | 38 +++++++++ src/component/mod.rs | 154 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/stream_start.rs | 18 ++++- 7 files changed, 416 insertions(+), 3 deletions(-) create mode 100644 examples/echo_component.rs create mode 100644 src/component/auth.rs create mode 100644 src/component/event.rs create mode 100644 src/component/mod.rs diff --git a/Cargo.toml b/Cargo.toml index aacdbc4c0f9bcbd02d51ac8a6e8fb5215fd930f6..560305c8038cdd26bf5bc8057a3d1b0b265013fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,5 @@ jid = "*" domain = "0.2.1" xmpp-parsers = "0.6.0" idna = "*" +try_from = "0.2.2" +sha-1 = "0.4.1" diff --git a/examples/echo_component.rs b/examples/echo_component.rs new file mode 100644 index 0000000000000000000000000000000000000000..7ea1b7dd7577fa8b21a5f85d833ca45471b57afd --- /dev/null +++ b/examples/echo_component.rs @@ -0,0 +1,103 @@ +extern crate futures; +extern crate tokio_core; +extern crate tokio_xmpp; +extern crate jid; +extern crate minidom; +extern crate xmpp_parsers; +extern crate try_from; + +use std::env::args; +use std::process::exit; +use std::str::FromStr; +use try_from::TryFrom; +use tokio_core::reactor::Core; +use futures::{Future, Stream, Sink, future}; +use tokio_xmpp::Component; +use minidom::Element; +use xmpp_parsers::presence::{Presence, Type as PresenceType, Show as PresenceShow}; +use xmpp_parsers::message::{Message, MessageType}; +use jid::Jid; + +fn main() { + let args: Vec = args().collect(); + if args.len() < 3 || args.len() > 5 { + println!("Usage: {} [server] [port]", args[0]); + exit(1); + } + let jid = &args[1]; + let password = &args[2]; + let server = &args.get(3).unwrap().parse().unwrap_or("127.0.0.1".to_owned()); + let port: u16 = args.get(4).unwrap().parse().unwrap_or(5347u16); + + // tokio_core context + let mut core = Core::new().unwrap(); + // Component instance + println!("{} {} {} {} {:?}", jid, password, server, port, core.handle()); + let component = Component::new(jid, password, server, port, core.handle()).unwrap(); + + // Make the two interfaces for sending and receiving independent + // of each other so we can move one into a closure. + println!("Got it: {}", component.jid); + let (sink, stream) = component.split(); + // Wrap sink in Option so that we can take() it for the send(self) + // to consume and return it back when ready. + let mut sink = Some(sink); + let mut send = move |stanza| { + sink = Some( + sink.take(). + expect("sink") + .send(stanza) + .wait() + .expect("sink.send") + ); + }; + // Main loop, processes events + let done = stream.for_each(|event| { + if event.is_online() { + println!("Online!"); + + let presence = make_presence(Jid::from_str("test@component.linkmauve.fr/coucou").unwrap(), Jid::from_str("linkmauve@linkmauve.fr").unwrap()); + send(presence); + } else if let Some(message) = event.into_stanza() + .and_then(|stanza| Message::try_from(stanza).ok()) + { + // This is a message we'll echo + match (message.from, message.bodies.get("")) { + (Some(from), Some(body)) => + if message.type_ != MessageType::Error { + let reply = make_reply(from, body); + send(reply); + }, + _ => (), + } + } + + Box::new(future::ok(())) + }); + + // Start polling `done` + match core.run(done) { + Ok(_) => (), + Err(e) => { + println!("Fatal: {}", e); + () + } + } +} + +// Construct a +fn make_presence(from: Jid, to: Jid) -> Element { + let mut presence = Presence::new(PresenceType::None); + presence.from = Some(from); + presence.to = Some(to); + presence.show = PresenceShow::Chat; + presence.statuses.insert(String::from("en"), String::from("Echoing messages.")); + presence.into() +} + +// Construct a chat +fn make_reply(to: Jid, body: &str) -> Element { + let mut message = Message::new(Some(to)); + message.bodies.insert(String::new(), body.to_owned()); + message.into() +} diff --git a/src/component/auth.rs b/src/component/auth.rs new file mode 100644 index 0000000000000000000000000000000000000000..773e453d68c2b26155698242d95c521068169d8a --- /dev/null +++ b/src/component/auth.rs @@ -0,0 +1,101 @@ +use std::mem::replace; +use futures::{Future, Poll, Async, sink, Sink, Stream}; +use tokio_io::{AsyncRead, AsyncWrite}; +use minidom::Element; +use sha_1::{Sha1, Digest}; + +use xmpp_codec::Packet; +use xmpp_stream::XMPPStream; + +const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; + +pub struct ComponentAuth { + state: ComponentAuthState, +} + +enum ComponentAuthState { + WaitSend(sink::Send>), + WaitRecv(XMPPStream), + Invalid, +} + +impl ComponentAuth { + pub fn new(stream: XMPPStream, password: String) -> Result { + // FIXME: huge hack, shouldn’t be an element! + let sid = stream.stream_features.name().to_owned(); + let mut this = ComponentAuth { + state: ComponentAuthState::Invalid, + }; + this.send( + stream, + "handshake", + // TODO: sha1(sid + password) + &format!("{:x}", Sha1::digest((sid + &password).as_bytes())) + ); + return Ok(this); + } + + fn send(&mut self, stream: XMPPStream, nonza_name: &str, handshake: &str) { + let nonza = Element::builder(nonza_name) + .ns(NS_JABBER_COMPONENT_ACCEPT) + .append(handshake) + .build(); + + let send = stream.send(Packet::Stanza(nonza)); + + self.state = ComponentAuthState::WaitSend(send); + } +} + +impl Future for ComponentAuth { + type Item = XMPPStream; + type Error = String; + + fn poll(&mut self) -> Poll { + let state = replace(&mut self.state, ComponentAuthState::Invalid); + + match state { + ComponentAuthState::WaitSend(mut send) => + match send.poll() { + Ok(Async::Ready(stream)) => { + self.state = ComponentAuthState::WaitRecv(stream); + self.poll() + }, + Ok(Async::NotReady) => { + self.state = ComponentAuthState::WaitSend(send); + Ok(Async::NotReady) + }, + Err(e) => + Err(format!("{}", e)), + }, + ComponentAuthState::WaitRecv(mut stream) => + match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.name() == "handshake" + && stanza.ns() == Some(NS_JABBER_COMPONENT_ACCEPT) => + { + self.state = ComponentAuthState::Invalid; + Ok(Async::Ready(stream)) + }, + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.is("error", "http://etherx.jabber.org/streams") => + { + let e = "Authentication failure"; + Err(e.to_owned()) + }, + Ok(Async::Ready(event)) => { + println!("ComponentAuth ignore {:?}", event); + Ok(Async::NotReady) + }, + Ok(_) => { + self.state = ComponentAuthState::WaitRecv(stream); + Ok(Async::NotReady) + }, + Err(e) => + Err(format!("{}", e)), + }, + ComponentAuthState::Invalid => + unreachable!(), + } + } +} diff --git a/src/component/event.rs b/src/component/event.rs new file mode 100644 index 0000000000000000000000000000000000000000..8ff44bd70a84ded7200eace6cea4055015a7e29a --- /dev/null +++ b/src/component/event.rs @@ -0,0 +1,38 @@ +use minidom::Element; + +#[derive(Debug)] +pub enum Event { + Online, + Disconnected, + Stanza(Element), +} + +impl Event { + pub fn is_online(&self) -> bool { + match *self { + Event::Online => true, + _ => false, + } + } + + pub fn is_stanza(&self, name: &str) -> bool { + match *self { + Event::Stanza(ref stanza) => stanza.name() == name, + _ => false, + } + } + + pub fn as_stanza(&self) -> Option<&Element> { + match *self { + Event::Stanza(ref stanza) => Some(stanza), + _ => None, + } + } + + pub fn into_stanza(self) -> Option { + match self { + Event::Stanza(stanza) => Some(stanza), + _ => None, + } + } +} diff --git a/src/component/mod.rs b/src/component/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..95790f000546e2e2a851583332411a90af179257 --- /dev/null +++ b/src/component/mod.rs @@ -0,0 +1,154 @@ +use std::mem::replace; +use std::str::FromStr; +use std::error::Error; +use tokio_core::reactor::Handle; +use tokio_core::net::TcpStream; +use tokio_io::{AsyncRead, AsyncWrite}; +use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; +use minidom::Element; +use jid::{Jid, JidParseError}; + +use super::xmpp_codec::Packet; +use super::xmpp_stream; +use super::happy_eyeballs::Connecter; + +mod auth; +use self::auth::ComponentAuth; +mod event; +pub use self::event::Event as ComponentEvent; + +pub struct Component { + pub jid: Jid, + state: ComponentState, +} + +type XMPPStream = xmpp_stream::XMPPStream; +const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; + +enum ComponentState { + Invalid, + Disconnected, + Connecting(Box>), + Connected(XMPPStream), +} + +impl Component { + pub fn new(jid: &str, password: &str, server: &str, port: u16, handle: Handle) -> Result { + let jid = try!(Jid::from_str(jid)); + let password = password.to_owned(); + let connect = Self::make_connect(jid.clone(), password, server, port, handle); + Ok(Component { + jid, + state: ComponentState::Connecting(connect), + }) + } + + fn make_connect(jid: Jid, password: String, server: &str, port: u16, handle: Handle) -> Box> { + let jid1 = jid.clone(); + let password = password; + Box::new( + Connecter::from_lookup(handle, server, "_xmpp-component._tcp", port) + .expect("Connector::from_lookup") + .and_then(move |tcp_stream| { + xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_COMPONENT_ACCEPT.to_owned()) + .map_err(|e| format!("{}", e)) + }).and_then(move |xmpp_stream| { + Self::auth(xmpp_stream, password).expect("auth") + }).and_then(|xmpp_stream| { + println!("Bound to {}", xmpp_stream.jid); + Ok(xmpp_stream) + }) + ) + } + + fn auth(stream: xmpp_stream::XMPPStream, password: String) -> Result, String> { + ComponentAuth::new(stream, password) + } +} + +impl Stream for Component { + type Item = ComponentEvent; + type Error = String; + + fn poll(&mut self) -> Poll, Self::Error> { + let state = replace(&mut self.state, ComponentState::Invalid); + + match state { + ComponentState::Invalid => + Err("invalid client state".to_owned()), + ComponentState::Disconnected => + Ok(Async::Ready(None)), + ComponentState::Connecting(mut connect) => { + match connect.poll() { + Ok(Async::Ready(stream)) => { + self.state = ComponentState::Connected(stream); + Ok(Async::Ready(Some(ComponentEvent::Online))) + }, + Ok(Async::NotReady) => { + self.state = ComponentState::Connecting(connect); + Ok(Async::NotReady) + }, + Err(e) => + Err(e), + } + }, + ComponentState::Connected(mut stream) => { + match stream.poll() { + Ok(Async::NotReady) => { + self.state = ComponentState::Connected(stream); + Ok(Async::NotReady) + }, + Ok(Async::Ready(None)) => { + // EOF + self.state = ComponentState::Disconnected; + Ok(Async::Ready(Some(ComponentEvent::Disconnected))) + }, + Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { + self.state = ComponentState::Connected(stream); + Ok(Async::Ready(Some(ComponentEvent::Stanza(stanza)))) + }, + Ok(Async::Ready(_)) => { + self.state = ComponentState::Connected(stream); + Ok(Async::NotReady) + }, + Err(e) => + Err(e.description().to_owned()), + } + }, + } + } +} + +impl Sink for Component { + type SinkItem = Element; + type SinkError = String; + + fn start_send(&mut self, item: Self::SinkItem) -> StartSend { + match self.state { + ComponentState::Connected(ref mut stream) => + match stream.start_send(Packet::Stanza(item)) { + Ok(AsyncSink::NotReady(Packet::Stanza(stanza))) => + Ok(AsyncSink::NotReady(stanza)), + Ok(AsyncSink::NotReady(_)) => + panic!("Component.start_send with stanza but got something else back"), + Ok(AsyncSink::Ready) => { + Ok(AsyncSink::Ready) + }, + Err(e) => + Err(e.description().to_owned()), + }, + _ => + Ok(AsyncSink::NotReady(item)), + } + } + + fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { + match &mut self.state { + &mut ComponentState::Connected(ref mut stream) => + stream.poll_complete() + .map_err(|e| e.description().to_owned()), + _ => + Ok(Async::Ready(())), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index ce8ccb7d7ceca60bb53f4514a48b024eeda18cdf..81f3b1c6ac4d9ff63c6cc4796f50fad95d0a5e2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ extern crate rustc_serialize as serialize; extern crate jid; extern crate domain; extern crate idna; +extern crate sha_1; pub mod xmpp_codec; pub mod xmpp_stream; @@ -21,3 +22,5 @@ pub use starttls::StartTlsClient; mod happy_eyeballs; mod client; pub use client::{Client, ClientEvent}; +mod component; +pub use component::{Component, ComponentEvent}; diff --git a/src/stream_start.rs b/src/stream_start.rs index b97e87d99d80ecbc734a1c848ab033180d2180a8..1d1813a383209784d6e367b0f17901527db3434a 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -4,6 +4,7 @@ use futures::{Future, Async, Poll, Stream, sink, Sink}; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::Framed; use jid::Jid; +use minidom::Element; use xmpp_codec::{XMPPCodec, Packet}; use xmpp_stream::XMPPStream; @@ -63,14 +64,25 @@ impl Future for StreamStart { StreamStartState::RecvStart(mut stream) => match stream.poll() { Ok(Async::Ready(Some(Packet::StreamStart(stream_attrs)))) => { - retry = true; let stream_ns = match stream_attrs.get("xmlns") { Some(ns) => ns.clone(), None => return Err(Error::from(ErrorKind::InvalidData)), }; - // TODO: skip RecvFeatures for version < 1.0 - (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)) + if self.ns == "jabber:client" { + retry = true; + // TODO: skip RecvFeatures for version < 1.0 + (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)) + } else { + let id = match stream_attrs.get("id") { + Some(id) => id.clone(), + None => + return Err(Error::from(ErrorKind::InvalidData)), + }; + // FIXME: huge hack, shouldn’t be an element! + let stream = XMPPStream::new(self.jid.clone(), stream, self.ns.clone(), Element::builder(id).build()); + (StreamStartState::Invalid, Ok(Async::Ready(stream))) + } }, Ok(Async::Ready(_)) => return Err(Error::from(ErrorKind::InvalidData)), From 993fdcab8f85e14f999871da85c0c975402b6c37 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 23 Jul 2017 02:46:47 +0200 Subject: [PATCH 0407/1020] unify Client::Event and Component::Event into Event --- src/client/mod.rs | 11 +++++------ src/component/event.rs | 38 -------------------------------------- src/component/mod.rs | 11 +++++------ src/{client => }/event.rs | 0 src/lib.rs | 6 ++++-- 5 files changed, 14 insertions(+), 52 deletions(-) delete mode 100644 src/component/event.rs rename src/{client => }/event.rs (100%) diff --git a/src/client/mod.rs b/src/client/mod.rs index 77641110d11a3c980714400faa7e180135d97125..e814f9b6e269f2ac978db41dcd91d769211dc19c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -15,13 +15,12 @@ use super::xmpp_codec::Packet; use super::xmpp_stream; use super::starttls::{NS_XMPP_TLS, StartTlsClient}; use super::happy_eyeballs::Connecter; +use super::event::Event; mod auth; use self::auth::ClientAuth; mod bind; use self::bind::ClientBind; -mod event; -pub use self::event::Event as ClientEvent; pub struct Client { pub jid: Jid, @@ -112,7 +111,7 @@ impl Client { } impl Stream for Client { - type Item = ClientEvent; + type Item = Event; type Error = String; fn poll(&mut self) -> Poll, Self::Error> { @@ -127,7 +126,7 @@ impl Stream for Client { match connect.poll() { Ok(Async::Ready(stream)) => { self.state = ClientState::Connected(stream); - Ok(Async::Ready(Some(ClientEvent::Online))) + Ok(Async::Ready(Some(Event::Online))) }, Ok(Async::NotReady) => { self.state = ClientState::Connecting(connect); @@ -142,11 +141,11 @@ impl Stream for Client { Ok(Async::Ready(None)) => { // EOF self.state = ClientState::Disconnected; - Ok(Async::Ready(Some(ClientEvent::Disconnected))) + Ok(Async::Ready(Some(Event::Disconnected))) }, Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { self.state = ClientState::Connected(stream); - Ok(Async::Ready(Some(ClientEvent::Stanza(stanza)))) + Ok(Async::Ready(Some(Event::Stanza(stanza)))) }, Ok(Async::NotReady) | Ok(Async::Ready(_)) => { diff --git a/src/component/event.rs b/src/component/event.rs deleted file mode 100644 index 8ff44bd70a84ded7200eace6cea4055015a7e29a..0000000000000000000000000000000000000000 --- a/src/component/event.rs +++ /dev/null @@ -1,38 +0,0 @@ -use minidom::Element; - -#[derive(Debug)] -pub enum Event { - Online, - Disconnected, - Stanza(Element), -} - -impl Event { - pub fn is_online(&self) -> bool { - match *self { - Event::Online => true, - _ => false, - } - } - - pub fn is_stanza(&self, name: &str) -> bool { - match *self { - Event::Stanza(ref stanza) => stanza.name() == name, - _ => false, - } - } - - pub fn as_stanza(&self) -> Option<&Element> { - match *self { - Event::Stanza(ref stanza) => Some(stanza), - _ => None, - } - } - - pub fn into_stanza(self) -> Option { - match self { - Event::Stanza(stanza) => Some(stanza), - _ => None, - } - } -} diff --git a/src/component/mod.rs b/src/component/mod.rs index 95790f000546e2e2a851583332411a90af179257..2964700ffefa4268ca45cb6cea50b6faec809c18 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -11,11 +11,10 @@ use jid::{Jid, JidParseError}; use super::xmpp_codec::Packet; use super::xmpp_stream; use super::happy_eyeballs::Connecter; +use super::event::Event; mod auth; use self::auth::ComponentAuth; -mod event; -pub use self::event::Event as ComponentEvent; pub struct Component { pub jid: Jid, @@ -67,7 +66,7 @@ impl Component { } impl Stream for Component { - type Item = ComponentEvent; + type Item = Event; type Error = String; fn poll(&mut self) -> Poll, Self::Error> { @@ -82,7 +81,7 @@ impl Stream for Component { match connect.poll() { Ok(Async::Ready(stream)) => { self.state = ComponentState::Connected(stream); - Ok(Async::Ready(Some(ComponentEvent::Online))) + Ok(Async::Ready(Some(Event::Online))) }, Ok(Async::NotReady) => { self.state = ComponentState::Connecting(connect); @@ -101,11 +100,11 @@ impl Stream for Component { Ok(Async::Ready(None)) => { // EOF self.state = ComponentState::Disconnected; - Ok(Async::Ready(Some(ComponentEvent::Disconnected))) + Ok(Async::Ready(Some(Event::Disconnected))) }, Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { self.state = ComponentState::Connected(stream); - Ok(Async::Ready(Some(ComponentEvent::Stanza(stanza)))) + Ok(Async::Ready(Some(Event::Stanza(stanza)))) }, Ok(Async::Ready(_)) => { self.state = ComponentState::Connected(stream); diff --git a/src/client/event.rs b/src/event.rs similarity index 100% rename from src/client/event.rs rename to src/event.rs diff --git a/src/lib.rs b/src/lib.rs index 81f3b1c6ac4d9ff63c6cc4796f50fad95d0a5e2d..ddb4f9a3b1bd2704c32775ba5d73da3e3223f4d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,9 @@ mod stream_start; mod starttls; pub use starttls::StartTlsClient; mod happy_eyeballs; +mod event; +pub use event::Event; mod client; -pub use client::{Client, ClientEvent}; +pub use client::Client; mod component; -pub use component::{Component, ComponentEvent}; +pub use component::Component; From a204e667098c13ceee8e16630b7426a11c7f93ef Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 23 Jul 2017 02:47:07 +0200 Subject: [PATCH 0408/1020] echo_component: TODO --- examples/echo_component.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/echo_component.rs b/examples/echo_component.rs index 7ea1b7dd7577fa8b21a5f85d833ca45471b57afd..076bb170c8245a1f9e25f04ede365793eecf350f 100644 --- a/examples/echo_component.rs +++ b/examples/echo_component.rs @@ -56,6 +56,7 @@ fn main() { if event.is_online() { println!("Online!"); + // TODO: replace these hardcoded JIDs let presence = make_presence(Jid::from_str("test@component.linkmauve.fr/coucou").unwrap(), Jid::from_str("linkmauve@linkmauve.fr").unwrap()); send(presence); } else if let Some(message) = event.into_stanza() From 85ea78d5a73a8aa20477859da2bcd0b81c7949ef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Jul 2017 18:46:40 +0100 Subject: [PATCH 0409/1020] ChangeLog: Add 0.7.0 release notes. --- ChangeLog | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5edf6648790440e927d0026eb6ffbf044a801d6e..e10497cef567b7f3679308811b7925c495a610e8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +Version 0.7.0: +2017-07-23 Emmanuel Gil Peyrot + * New parsers/serialisers: + - Jingle Message Initialisation (XEP-0353) was added. + - The disco#items query (XEP-0030) is now supported, in + addition to the existing disco#info one. + * Breaking changes: + - Replaced many type aliases with proper wrapping structs. + - Split Disco into a query and a result part, since they have + very different constraints. + - Split IqPayload in three to avoid parsing queries as results + for example. + * Improvements: + - Use TryFrom from the try_from crate, thus removing the + dependency on nightly! + - Always implement From instead of Into, the latter is + generated anyway. + - Add helpers to construct your Presence stanza. + Version 0.6.0: 2017-06-27 Emmanuel Gil Peyrot * New parsers/serialisers: From e26bd3306e98d65224dec0e2deb2083239177e1f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Jul 2017 18:47:46 +0100 Subject: [PATCH 0410/1020] Release version 0.7.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b4a760b270f509f5a6344b3f0246c9ba04e1898c..6f27f34abe1190d0de070392d5bab336eed8dee1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.6.0" +version = "0.7.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 6e506fbc57313fd14d0dbe78e46559b8e477807c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Jul 2017 19:03:29 +0100 Subject: [PATCH 0412/1020] Cargo.toml, echo_bot: update xmpp-parsers to 0.7.0 --- Cargo.toml | 2 +- examples/echo_bot.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 560305c8038cdd26bf5bc8057a3d1b0b265013fc..42de0ff804c0b34695d93dec85134267c985372a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ sasl = "*" rustc-serialize = "*" jid = "*" domain = "0.2.1" -xmpp-parsers = "0.6.0" +xmpp-parsers = "0.7.0" idna = "*" try_from = "0.2.2" sha-1 = "0.4.1" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 40b20ac95fb86f3fed6afd51a65a761b37f7fb24..3f6a4ab52d9e316fd117b9eaa240679b99998a63 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,15 +1,14 @@ -#![feature(try_from)] - extern crate futures; extern crate tokio_core; extern crate tokio_xmpp; extern crate jid; extern crate minidom; extern crate xmpp_parsers; +extern crate try_from; use std::env::args; use std::process::exit; -use std::convert::TryFrom; +use try_from::TryFrom; use tokio_core::reactor::Core; use futures::{Future, Stream, Sink, future}; use tokio_xmpp::Client; From ff165d745d27bb06c144eac9e5073e22524e8359 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 23 Jul 2017 23:53:08 +0200 Subject: [PATCH 0413/1020] Cargo.toml: update minidom to 0.4.4 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 42de0ff804c0b34695d93dec85134267c985372a..b5d8eeeaa5e0944241f805fa3b280efc73d084af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ tokio-io = "*" bytes = "0.4.4" xml5ever = "*" tendril = "*" -minidom = "0.4.3" +minidom = "0.4.4" native-tls = "*" tokio-tls = "*" sasl = "*" From 83c9713ab949929e996283da730a7443135bf639 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Jul 2017 23:11:10 +0100 Subject: [PATCH 0414/1020] caps, ecaps2: Make Blake2b panic and update the blake2 crate to 0.6.1. See https://github.com/RustCrypto/hashes/issues/34 for more information. --- Cargo.toml | 2 +- src/caps.rs | 9 +++++++-- src/ecaps2.rs | 10 ++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6f27f34abe1190d0de070392d5bab336eed8dee1..7b425d4453207e9d05292c7e7dffa2a73e5ac348 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,6 @@ digest = "0.6.0" sha-1 = "0.4.0" sha2 = "0.6.0" sha3 = "0.6.0" -blake2 = "0.6.0" +blake2 = "0.6.1" chrono = "0.4.0" try_from = "0.2.2" diff --git a/src/caps.rs b/src/caps.rs index aad50eb2d644807a73f50922ce8fe7058f0f7ec5..a2bdd11bed7d92720a3e383980c4c7f2a7ea3ddd 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -15,11 +15,12 @@ use error::Error; use ns; use base64; +use digest::Digest; use sha_1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use blake2::Blake2b; -use digest::{Digest, VariableOutput}; +//use blake2::Blake2b; +//use digest::{Digest, VariableOutput}; #[derive(Debug, Clone)] pub struct Caps { @@ -173,6 +174,9 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { let hash = hasher.result(); get_hash_vec(hash.as_slice()) }, + Algo::Blake2b_256 + | Algo::Blake2b_512 => panic!("See https://github.com/RustCrypto/hashes/issues/34"), + /* Algo::Blake2b_256 => { let mut hasher = Blake2b::default(); hasher.input(data); @@ -187,6 +191,7 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { let hash = hasher.variable_result(&mut buf).unwrap(); get_hash_vec(hash) }, + */ Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, algo: algo, diff --git a/src/ecaps2.rs b/src/ecaps2.rs index be80ffd53d3e2f5c88990c8ced7c93e422802bc8..593acec073db802114d157456dcab35da7d19483 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -15,10 +15,11 @@ use error::Error; use ns; use base64; +use digest::Digest; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use blake2::Blake2b; -use digest::{Digest, VariableOutput}; +//use blake2::Blake2b; +//use digest::{Digest, VariableOutput}; #[derive(Debug, Clone)] pub struct ECaps2 { @@ -151,6 +152,9 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { let hash = hasher.result(); get_hash_vec(hash.as_slice()) }, + Algo::Blake2b_256 + | Algo::Blake2b_512 => panic!("See https://github.com/RustCrypto/hashes/issues/34"), + /* Algo::Blake2b_256 => { let mut hasher = Blake2b::default(); hasher.input(data); @@ -165,6 +169,7 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { let hash = hasher.variable_result(&mut buf).unwrap(); get_hash_vec(hash) }, + */ Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, @@ -455,6 +460,7 @@ mod tests { } #[test] + #[ignore] fn test_blake2b_512() { let hash = ecaps2::hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); let known_hash: Vec = vec!( From 712e6dfe1156abacd6da71fdacfd097a0729ef52 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 24 Jul 2017 23:11:31 +0100 Subject: [PATCH 0415/1020] Release version 0.7.1. --- Cargo.toml | 2 +- ChangeLog | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7b425d4453207e9d05292c7e7dffa2a73e5ac348..c5ff5e0977e3a3026cafca7507ae8d4ac2486c91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.7.0" +version = "0.7.1" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", diff --git a/ChangeLog b/ChangeLog index e10497cef567b7f3679308811b7925c495a610e8..a780194d330fdc8b629141fa96167beafa2a9bd2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Version 0.7.0: +2017-07-24 Emmanuel Gil Peyrot + * Hotfixes: + - Stub out blake2 support, since the blake2 crate broke its API + between their 0.6.0 and 0.6.1 releases… + Version 0.7.0: 2017-07-23 Emmanuel Gil Peyrot * New parsers/serialisers: From 4b3ced042e54c8d3e4491b664e7af839ae413c3a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 26 Jul 2017 00:04:20 +0100 Subject: [PATCH 0417/1020] ecaps2, jingle_s5b: Replace drain() with into_iter(). --- src/ecaps2.rs | 4 ++-- src/jingle_s5b.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 593acec073db802114d157456dcab35da7d19483..8166e5b0f3e525f4019c7628fd4d9f24ce0c3200 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -49,10 +49,10 @@ impl TryFrom for ECaps2 { } impl From for Element { - fn from(mut ecaps2: ECaps2) -> Element { + fn from(ecaps2: ECaps2) -> Element { Element::builder("c") .ns(ns::ECAPS2) - .append(ecaps2.hashes.drain(..) + .append(ecaps2.hashes.into_iter() .map(Element::from) .collect::>()) .build() diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 7ef1e5020420cab1b3306a6ae795417ba617e770..d454a3d99e060473c3348fe1c55736ec679f0904 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -145,8 +145,8 @@ impl From for Element { .attr("dstaddr", transport.dstaddr) .attr("mode", transport.mode) .append(match transport.payload { - TransportPayload::Candidates(mut candidates) => { - candidates.drain(..) + TransportPayload::Candidates(candidates) => { + candidates.into_iter() .map(Element::from) .collect::>() }, @@ -161,7 +161,7 @@ impl From for Element { .ns(ns::JINGLE_S5B) .build()) }, - TransportPayload::CandidateUsed(ref cid) => { + TransportPayload::CandidateUsed(cid) => { vec!(Element::builder("candidate-used") .ns(ns::JINGLE_S5B) .attr("cid", cid) From 75ae6100b4dbf7f59f4f54acc6c645139bcd4ac1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 02:41:42 +0100 Subject: [PATCH 0418/1020] data_forms: Remove unneccessary swallowing of an error. --- src/data_forms.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index eb751e0ba27632389faa0f286dfff851eea24c44..a2330a52dd616df97e8dc81da19e51f898349917 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -186,10 +186,8 @@ impl TryFrom for DataForm { value: value, }); } else if element.is("media", ns::MEDIA_ELEMENT) { - match MediaElement::try_from(element.clone()) { - Ok(media_element) => field.media.push(media_element), - Err(_) => (), // TODO: is it really nice to swallow this error? - } + let media_element = MediaElement::try_from(element.clone())?; + field.media.push(media_element); } else { return Err(Error::ParseError("Field child isn’t a value or media element.")); } From f83fe922446ef55cfd688f4c10d674253d18b31d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 02:47:33 +0100 Subject: [PATCH 0419/1020] jingle: Wrap the disposition in a struct, to get better type safety. --- src/jingle.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 62fb4b5afdc4b6c387bac2f434b998dcb5e96c10..364ee4b82d6a57579a75e236a98b4ef14dc9347e 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -45,10 +45,19 @@ generate_attribute!(Senders, "senders", { generate_id!(ContentId); +// TODO: the list of values is defined, use an enum! +generate_id!(Disposition); + +impl Default for Disposition { + fn default() -> Disposition { + Disposition(String::from("session")) + } +} + #[derive(Debug, Clone)] pub struct Content { pub creator: Creator, - pub disposition: String, // TODO: the list of values is defined, use an enum! + pub disposition: Disposition, pub name: ContentId, pub senders: Senders, pub description: Option, @@ -66,7 +75,7 @@ impl TryFrom for Content { let mut content = Content { creator: get_attr!(elem, "creator", required), - disposition: get_attr!(elem, "disposition", optional).unwrap_or(String::from("session")), + disposition: get_attr!(elem, "disposition", default), name: get_attr!(elem, "name", required), senders: get_attr!(elem, "senders", default), description: None, @@ -344,7 +353,7 @@ mod tests { assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, ContentId(String::from("coucou"))); assert_eq!(jingle.contents[0].senders, Senders::Both); - assert_eq!(jingle.contents[0].disposition, "session"); + assert_eq!(jingle.contents[0].disposition, Disposition(String::from("session"))); let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); @@ -352,7 +361,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); - assert_eq!(jingle.contents[0].disposition, "early-session"); + assert_eq!(jingle.contents[0].disposition, Disposition(String::from("early-session"))); } #[test] From dfb736a9731973626ec7c4c73dd991eb4e72f9e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 03:03:45 +0100 Subject: [PATCH 0420/1020] rsm: Change if .is_some() { .unwrap() } into if let Some(). --- src/rsm.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rsm.rs b/src/rsm.rs index 008757dde50077dc620385da3044356bb9750c25..fa6f6c814fdbcedf798cb7588cc8da5d37706b33 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -97,8 +97,8 @@ impl From for Element { if set.before.is_some() { elem.append_child(Element::builder("before").ns(ns::RSM).append(set.before).build()); } - if set.count.is_some() { - elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", set.count.unwrap())).build()); + if let Some(count) = set.count { + elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build()); } if set.first.is_some() { elem.append_child(Element::builder("first") @@ -106,14 +106,14 @@ impl From for Element { .attr("index", set.first_index) .append(set.first).build()); } - if set.index.is_some() { - elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", set.index.unwrap())).build()); + if let Some(index) = set.index { + elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build()); } if set.last.is_some() { elem.append_child(Element::builder("last").ns(ns::RSM).append(set.last).build()); } - if set.max.is_some() { - elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", set.max.unwrap())).build()); + if let Some(max) = set.max { + elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build()); } elem } From db35d28c9c62484e397ac591377efc9cb6afa1d2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 03:44:35 +0100 Subject: [PATCH 0421/1020] ibb: Split the stupid enum into three different structs. --- src/ibb.rs | 207 ++++++++++++++++++++++++++--------------------------- src/iq.rs | 16 +++-- 2 files changed, 113 insertions(+), 110 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index cdc9b5cdb2e5395d6fe18ce8be1478be0f64dff3..2c01e1c741aa9785c5a308471f5bbfea2ed9e9d5 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -20,90 +20,104 @@ generate_attribute!(Stanza, "stanza", { }, Default = Iq); #[derive(Debug, Clone)] -pub enum IBB { - Open { - block_size: u16, - sid: String, - stanza: Stanza, - }, - Data { - seq: u16, - sid: String, - data: Vec, - }, - Close { - sid: String, - }, +pub struct Open { + pub block_size: u16, + pub sid: String, + pub stanza: Stanza, } -impl TryFrom for IBB { +impl TryFrom for Open { type Err = Error; - fn try_from(elem: Element) -> Result { - if elem.is("open", ns::IBB) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in open element.")); - } - let block_size = get_attr!(elem, "block-size", required); - let sid = get_attr!(elem, "sid", required); - let stanza = get_attr!(elem, "stanza", default); - Ok(IBB::Open { - block_size: block_size, - sid: sid, - stanza: stanza - }) - } else if elem.is("data", ns::IBB) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in data element.")); - } - let seq = get_attr!(elem, "seq", required); - let sid = get_attr!(elem, "sid", required); - let data = base64::decode(&elem.text())?; - Ok(IBB::Data { - seq: seq, - sid: sid, - data: data - }) - } else if elem.is("close", ns::IBB) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in close element.")); - } - let sid = get_attr!(elem, "sid", required); - Ok(IBB::Close { - sid: sid, - }) - } else { - Err(Error::ParseError("This is not an ibb element.")) + fn try_from(elem: Element) -> Result { + if !elem.is("open", ns::IBB) { + return Err(Error::ParseError("This is not an open element.")); } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in open element.")); + } + Ok(Open { + block_size: get_attr!(elem, "block-size", required), + sid: get_attr!(elem, "sid", required), + stanza: get_attr!(elem, "stanza", default), + }) + } +} + +impl From for Element { + fn from(open: Open) -> Element { + Element::builder("open") + .ns(ns::IBB) + .attr("block-size", open.block_size) + .attr("sid", open.sid) + .attr("stanza", open.stanza) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct Data { + pub seq: u16, + pub sid: String, + pub data: Vec, +} + +impl TryFrom for Data { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("data", ns::IBB) { + return Err(Error::ParseError("This is not a data element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in data element.")); + } + Ok(Data { + seq: get_attr!(elem, "seq", required), + sid: get_attr!(elem, "sid", required), + data: base64::decode(&elem.text())?, + }) + } +} + +impl From for Element { + fn from(data: Data) -> Element { + Element::builder("data") + .ns(ns::IBB) + .attr("seq", data.seq) + .attr("sid", data.sid) + .append(base64::encode(&data.data)) + .build() } } -impl From for Element { - fn from(ibb: IBB) -> Element { - match ibb { - IBB::Open { block_size, sid, stanza } => { - Element::builder("open") - .ns(ns::IBB) - .attr("block-size", block_size) - .attr("sid", sid) - .attr("stanza", stanza) - .build() - }, - IBB::Data { seq, sid, data } => { - Element::builder("data") - .ns(ns::IBB) - .attr("seq", seq) - .attr("sid", sid) - .append(base64::encode(&data)) - .build() - }, - IBB::Close { sid } => { - Element::builder("close") - .ns(ns::IBB) - .attr("sid", sid) - .build() - }, +#[derive(Debug, Clone)] +pub struct Close { + pub sid: String, +} + +impl TryFrom for Close { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("close", ns::IBB) { + return Err(Error::ParseError("This is not a close element.")); } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in close element.")); + } + Ok(Close { + sid: get_attr!(elem, "sid", required), + }) + } +} + +impl From for Element { + fn from(close: Close) -> Element { + Element::builder("close") + .ns(ns::IBB) + .attr("sid", close.sid) + .build() } } @@ -115,41 +129,26 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - let open = IBB::try_from(elem).unwrap(); - match open { - IBB::Open { block_size, sid, stanza } => { - assert_eq!(block_size, 3); - assert_eq!(sid, "coucou"); - assert_eq!(stanza, Stanza::Iq); - }, - _ => panic!(), - } + let open = Open::try_from(elem).unwrap(); + assert_eq!(open.block_size, 3); + assert_eq!(open.sid, "coucou"); + assert_eq!(open.stanza, Stanza::Iq); let elem: Element = "AAAA".parse().unwrap(); - let data = IBB::try_from(elem).unwrap(); - match data { - IBB::Data { seq, sid, data } => { - assert_eq!(seq, 0); - assert_eq!(sid, "coucou"); - assert_eq!(data, vec!(0, 0, 0)); - }, - _ => panic!(), - } + let data = Data::try_from(elem).unwrap(); + assert_eq!(data.seq, 0); + assert_eq!(data.sid, "coucou"); + assert_eq!(data.data, vec!(0, 0, 0)); let elem: Element = "".parse().unwrap(); - let close = IBB::try_from(elem).unwrap(); - match close { - IBB::Close { sid } => { - assert_eq!(sid, "coucou"); - }, - _ => panic!(), - } + let close = Close::try_from(elem).unwrap(); + assert_eq!(close.sid, "coucou"); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(elem).unwrap_err(); + let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -157,7 +156,7 @@ mod tests { assert_eq!(message, "Required attribute 'block-size' missing."); let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(elem).unwrap_err(); + let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, _ => panic!(), @@ -165,7 +164,7 @@ mod tests { assert_eq!(message.description(), "invalid digit found in string"); let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(elem).unwrap_err(); + let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(error) => error, _ => panic!(), @@ -176,7 +175,7 @@ mod tests { #[test] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); - let error = IBB::try_from(elem).unwrap_err(); + let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), diff --git a/src/iq.rs b/src/iq.rs index 1eae2549cb66a24330e719622615b78c728f2465..2584ebeb66f1c081cce8e3f2aa9bdded33bb8a08 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -19,7 +19,7 @@ use ns; use stanza_error::StanzaError; use roster::Roster; use disco::{DiscoInfoResult, DiscoInfoQuery}; -use ibb::IBB; +use ibb::{Open as IbbOpen, Data as IbbData, Close as IbbClose}; use jingle::Jingle; use ping::Ping; use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; @@ -40,7 +40,9 @@ pub enum IqGetPayload { #[derive(Debug, Clone)] pub enum IqSetPayload { Roster(Roster), - IBB(IBB), + IbbOpen(IbbOpen), + IbbData(IbbData), + IbbClose(IbbClose), Jingle(Jingle), MamQuery(MamQuery), MamPrefs(MamPrefs), @@ -106,9 +108,9 @@ impl TryFrom for IqSetPayload { ("query", ns::ROSTER) => IqSetPayload::Roster(Roster::try_from(elem)?), // XEP-0047 - ("open", ns::IBB) - | ("data", ns::IBB) - | ("close", ns::IBB) => IqSetPayload::IBB(IBB::try_from(elem)?), + ("open", ns::IBB) => IqSetPayload::IbbOpen(IbbOpen::try_from(elem)?), + ("data", ns::IBB) => IqSetPayload::IbbData(IbbData::try_from(elem)?), + ("close", ns::IBB) => IqSetPayload::IbbClose(IbbClose::try_from(elem)?), // XEP-0166 ("jingle", ns::JINGLE) => IqSetPayload::Jingle(Jingle::try_from(elem)?), @@ -126,7 +128,9 @@ impl From for Element { fn from(payload: IqSetPayload) -> Element { match payload { IqSetPayload::Roster(roster) => roster.into(), - IqSetPayload::IBB(ibb) => ibb.into(), + IqSetPayload::IbbOpen(open) => open.into(), + IqSetPayload::IbbData(data) => data.into(), + IqSetPayload::IbbClose(close) => close.into(), IqSetPayload::Jingle(jingle) => jingle.into(), IqSetPayload::MamQuery(query) => query.into(), IqSetPayload::MamPrefs(prefs) => prefs.into(), From a0b1d93ff09398474110088b0f77d3ba8ce3f11d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 03:51:41 +0100 Subject: [PATCH 0422/1020] stanza_id: Split the stupid enum into two different structs. --- src/message.rs | 8 ++-- src/stanza_id.rs | 95 +++++++++++++++++++++++++----------------------- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/src/message.rs b/src/message.rs index 29234321f4baabaef635a52593ba4b9e60b6ee11..90bce3dbf9d022a95f1cf056f51084a72a2bf2c5 100644 --- a/src/message.rs +++ b/src/message.rs @@ -23,7 +23,7 @@ use delay::Delay; use attention::Attention; use message_correct::Replace; use eme::ExplicitMessageEncryption; -use stanza_id::StanzaId; +use stanza_id::{StanzaId, OriginId}; use mam::Result_ as MamResult; /// Lists every known payload of a ``. @@ -37,6 +37,7 @@ pub enum MessagePayload { MessageCorrect(Replace), ExplicitMessageEncryption(ExplicitMessageEncryption), StanzaId(StanzaId), + OriginId(OriginId), MamResult(MamResult), Unknown(Element), @@ -73,8 +74,8 @@ impl TryFrom for MessagePayload { ("result", ns::MAM) => MessagePayload::MamResult(MamResult::try_from(elem)?), // XEP-0359 - ("stanza-id", ns::SID) - | ("origin-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), + ("stanza-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), + ("origin-id", ns::SID) => MessagePayload::OriginId(OriginId::try_from(elem)?), // XEP-0380 ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), @@ -95,6 +96,7 @@ impl From for Element { MessagePayload::MessageCorrect(replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(eme) => eme.into(), MessagePayload::StanzaId(stanza_id) => stanza_id.into(), + MessagePayload::OriginId(origin_id) => origin_id.into(), MessagePayload::MamResult(result) => result.into(), MessagePayload::Unknown(elem) => elem, diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 72687d9b3dbd335dadf16d14fea9262344ba4852..43ce96452983f6ccaeee0fe68a952bfbcc75aba8 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -14,54 +14,65 @@ use error::Error; use ns; #[derive(Debug, Clone)] -pub enum StanzaId { - StanzaId { - id: String, - by: Jid, - }, - OriginId { - id: String, - }, +pub struct StanzaId { + pub id: String, + pub by: Jid, } impl TryFrom for StanzaId { type Err = Error; fn try_from(elem: Element) -> Result { - let is_stanza_id = elem.is("stanza-id", ns::SID); - if !is_stanza_id && !elem.is("origin-id", ns::SID) { - return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); + if !elem.is("stanza-id", ns::SID) { + return Err(Error::ParseError("This is not a stanza-id element.")); } for _ in elem.children() { - return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); + return Err(Error::ParseError("Unknown child in stanza-id element.")); } - let id = get_attr!(elem, "id", required); - Ok(if is_stanza_id { - let by = get_attr!(elem, "by", required); - StanzaId::StanzaId { id, by } - } else { - StanzaId::OriginId { id } + Ok(StanzaId { + id: get_attr!(elem, "id", required), + by: get_attr!(elem, "by", required), }) } } impl From for Element { fn from(stanza_id: StanzaId) -> Element { - match stanza_id { - StanzaId::StanzaId { id, by } => { - Element::builder("stanza-id") - .ns(ns::SID) - .attr("id", id) - .attr("by", String::from(by)) - .build() - }, - StanzaId::OriginId { id } => { - Element::builder("origin-id") - .ns(ns::SID) - .attr("id", id) - .build() - }, + Element::builder("stanza-id") + .ns(ns::SID) + .attr("id", stanza_id.id) + .attr("by", String::from(stanza_id.by)) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct OriginId { + pub id: String, +} + +impl TryFrom for OriginId { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("origin-id", ns::SID) { + return Err(Error::ParseError("This is not an origin-id element.")); } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in origin-id element.")); + } + Ok(OriginId { + id: get_attr!(elem, "id", required), + }) + } +} + +impl From for Element { + fn from(origin_id: OriginId) -> Element { + Element::builder("origin-id") + .ns(ns::SID) + .attr("id", origin_id.id) + .build() } } @@ -74,20 +85,12 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let stanza_id = StanzaId::try_from(elem).unwrap(); - if let StanzaId::StanzaId { id, by } = stanza_id { - assert_eq!(id, String::from("coucou")); - assert_eq!(by, Jid::from_str("coucou@coucou").unwrap()); - } else { - panic!(); - } + assert_eq!(stanza_id.id, String::from("coucou")); + assert_eq!(stanza_id.by, Jid::from_str("coucou@coucou").unwrap()); let elem: Element = "".parse().unwrap(); - let stanza_id = StanzaId::try_from(elem).unwrap(); - if let StanzaId::OriginId { id } = stanza_id { - assert_eq!(id, String::from("coucou")); - } else { - panic!(); - } + let origin_id = OriginId::try_from(elem).unwrap(); + assert_eq!(origin_id.id, String::from("coucou")); } #[test] @@ -98,7 +101,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in stanza-id or origin-id element."); + assert_eq!(message, "Unknown child in stanza-id element."); } #[test] @@ -126,7 +129,7 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let stanza_id = StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; + let stanza_id = StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; let elem2 = stanza_id.into(); assert_eq!(elem, elem2); } From 37d1ae64ad49e128cd89cbfbb9b1f33b7cad6d24 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:00:25 +0100 Subject: [PATCH 0423/1020] receipts: Split the stupid enum into two different structs. --- src/message.rs | 12 ++++--- src/receipts.rs | 90 ++++++++++++++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/src/message.rs b/src/message.rs index 90bce3dbf9d022a95f1cf056f51084a72a2bf2c5..759b7d2304c1653177693dad8cbe30763e151cf1 100644 --- a/src/message.rs +++ b/src/message.rs @@ -18,7 +18,7 @@ use ns; use stanza_error::StanzaError; use chatstates::ChatState; -use receipts::Receipt; +use receipts::{Request as ReceiptRequest, Received as ReceiptReceived}; use delay::Delay; use attention::Attention; use message_correct::Replace; @@ -31,7 +31,8 @@ use mam::Result_ as MamResult; pub enum MessagePayload { StanzaError(StanzaError), ChatState(ChatState), - Receipt(Receipt), + ReceiptRequest(ReceiptRequest), + ReceiptReceived(ReceiptReceived), Delay(Delay), Attention(Attention), MessageCorrect(Replace), @@ -58,8 +59,8 @@ impl TryFrom for MessagePayload { | ("gone", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), // XEP-0184 - ("request", ns::RECEIPTS) - | ("received", ns::RECEIPTS) => MessagePayload::Receipt(Receipt::try_from(elem)?), + ("request", ns::RECEIPTS) => MessagePayload::ReceiptRequest(ReceiptRequest::try_from(elem)?), + ("received", ns::RECEIPTS) => MessagePayload::ReceiptReceived(ReceiptReceived::try_from(elem)?), // XEP-0203 ("delay", ns::DELAY) => MessagePayload::Delay(Delay::try_from(elem)?), @@ -91,7 +92,8 @@ impl From for Element { MessagePayload::StanzaError(stanza_error) => stanza_error.into(), MessagePayload::Attention(attention) => attention.into(), MessagePayload::ChatState(chatstate) => chatstate.into(), - MessagePayload::Receipt(receipt) => receipt.into(), + MessagePayload::ReceiptRequest(request) => request.into(), + MessagePayload::ReceiptReceived(received) => received.into(), MessagePayload::Delay(delay) => delay.into(), MessagePayload::MessageCorrect(replace) => replace.into(), MessagePayload::ExplicitMessageEncryption(eme) => eme.into(), diff --git a/src/receipts.rs b/src/receipts.rs index 7c3625b8daac481a120d068a52e100d17f825891..6ccec7eae1f500062175bfdc776eb5e3ac178106 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -13,46 +13,65 @@ use error::Error; use ns; #[derive(Debug, Clone)] -pub enum Receipt { - Request, - Received(Option), +pub struct Request; + +impl TryFrom for Request { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("request", ns::RECEIPTS) { + return Err(Error::ParseError("This is not a request element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in request element.")); + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown attribute in request element.")); + } + Ok(Request) + } } -impl TryFrom for Receipt { +impl From for Element { + fn from(_: Request) -> Element { + Element::builder("request") + .ns(ns::RECEIPTS) + .build() + } +} + +#[derive(Debug, Clone)] +pub struct Received { + pub id: Option, +} + +impl TryFrom for Received { type Err = Error; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { + if !elem.is("received", ns::RECEIPTS) { + return Err(Error::ParseError("This is not a received element.")); + } for _ in elem.children() { - return Err(Error::ParseError("Unknown child in receipt element.")); + return Err(Error::ParseError("Unknown child in received element.")); } - if elem.is("request", ns::RECEIPTS) { - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in request element.")); - } - Ok(Receipt::Request) - } else if elem.is("received", ns::RECEIPTS) { - for (attr, _) in elem.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in received element.")); - } + for (attr, _) in elem.attrs() { + if attr != "id" { + return Err(Error::ParseError("Unknown attribute in received element.")); } - let id = get_attr!(elem, "id", optional); - Ok(Receipt::Received(id)) - } else { - Err(Error::ParseError("This is not a receipt element.")) } + Ok(Received { + id: get_attr!(elem, "id", optional), + }) } } -impl From for Element { - fn from(receipt: Receipt) -> Element { - match receipt { - Receipt::Request => Element::builder("request") - .ns(ns::RECEIPTS), - Receipt::Received(id) => Element::builder("received") - .ns(ns::RECEIPTS) - .attr("id", id), - }.build() +impl From for Element { + fn from(received: Received) -> Element { + Element::builder("received") + .ns(ns::RECEIPTS) + .attr("id", received.id) + .build() } } @@ -63,22 +82,25 @@ mod tests { #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); - Receipt::try_from(elem).unwrap(); + Request::try_from(elem).unwrap(); let elem: Element = "".parse().unwrap(); - Receipt::try_from(elem).unwrap(); + Received::try_from(elem).unwrap(); let elem: Element = "".parse().unwrap(); - Receipt::try_from(elem).unwrap(); + Received::try_from(elem).unwrap(); } #[test] fn test_serialise() { - let receipt = Receipt::Request; + let receipt = Request; let elem: Element = receipt.into(); assert!(elem.is("request", ns::RECEIPTS)); + assert_eq!(elem.attrs().count(), 0); - let receipt = Receipt::Received(Some(String::from("coucou"))); + let receipt = Received { + id: Some(String::from("coucou")), + }; let elem: Element = receipt.into(); assert!(elem.is("received", ns::RECEIPTS)); assert_eq!(elem.attr("id"), Some("coucou")); From 58760fc28dc8349f993bf764e12340845cf5ff69 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:19:58 +0100 Subject: [PATCH 0424/1020] data_forms: Split field parsing into its own TryFrom impl. --- src/data_forms.rs | 138 +++++++++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 64 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index a2330a52dd616df97e8dc81da19e51f898349917..15135ef3df2edfde96108bf0015bce6bf59e46ed 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -57,6 +57,78 @@ pub struct Field { pub media: Vec, } +impl Field { + fn is_list(&self) -> bool { + self.type_ == FieldType::ListSingle || + self.type_ == FieldType::ListMulti + } +} + +impl TryFrom for Field { + type Err = Error; + + fn try_from(elem: Element) -> Result { + let mut field = Field { + var: get_attr!(elem, "var", required), + type_: get_attr!(elem, "type", default), + label: get_attr!(elem, "label", optional), + required: false, + options: vec!(), + values: vec!(), + media: vec!(), + }; + for element in elem.children() { + if element.is("value", ns::DATA_FORMS) { + for _ in element.children() { + return Err(Error::ParseError("Value element must not have any child.")); + } + for _ in element.attrs() { + return Err(Error::ParseError("Value element must not have any attribute.")); + } + field.values.push(element.text()); + } else if element.is("required", ns::DATA_FORMS) { + if field.required { + return Err(Error::ParseError("More than one required element.")); + } + for _ in element.children() { + return Err(Error::ParseError("Required element must not have any child.")); + } + for _ in element.attrs() { + return Err(Error::ParseError("Required element must not have any attribute.")); + } + field.required = true; + } else if element.is("option", ns::DATA_FORMS) { + if !field.is_list() { + return Err(Error::ParseError("Option element found in non-list field.")); + } + let label = get_attr!(element, "label", optional); + let mut value = None; + for child2 in element.children() { + if child2.is("value", ns::DATA_FORMS) { + if value.is_some() { + return Err(Error::ParseError("More than one value element in option element")); + } + value = Some(child2.text()); + } else { + return Err(Error::ParseError("Non-value element in option element")); + } + } + let value = value.ok_or(Error::ParseError("No value element in option element"))?; + field.options.push(Option_ { + label: label, + value: value, + }); + } else if element.is("media", ns::MEDIA_ELEMENT) { + let media_element = MediaElement::try_from(element.clone())?; + field.media.push(media_element); + } else { + return Err(Error::ParseError("Field child isn’t a value or media element.")); + } + } + Ok(field) + } +} + impl From for Element { fn from(field: Field) -> Element { Element::builder("field") @@ -129,70 +201,8 @@ impl TryFrom for DataForm { } form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { - let var: String = get_attr!(child, "var", required); - let field_type = get_attr!(child, "type", default); - let label = get_attr!(child, "label", optional); - - let is_form_type = var == "FORM_TYPE" && field_type == FieldType::Hidden; - let is_list = field_type == FieldType::ListSingle || field_type == FieldType::ListMulti; - let mut field = Field { - var: var, - type_: field_type, - label: label, - required: false, - options: vec!(), - values: vec!(), - media: vec!(), - }; - for element in child.children() { - if element.is("value", ns::DATA_FORMS) { - for _ in element.children() { - return Err(Error::ParseError("Value element must not have any child.")); - } - for _ in element.attrs() { - return Err(Error::ParseError("Value element must not have any attribute.")); - } - field.values.push(element.text()); - } else if element.is("required", ns::DATA_FORMS) { - if field.required { - return Err(Error::ParseError("More than one required element.")); - } - for _ in element.children() { - return Err(Error::ParseError("Required element must not have any child.")); - } - for _ in element.attrs() { - return Err(Error::ParseError("Required element must not have any attribute.")); - } - field.required = true; - } else if element.is("option", ns::DATA_FORMS) { - if !is_list { - return Err(Error::ParseError("Option element found in non-list field.")); - } - let label = get_attr!(element, "label", optional); - let mut value = None; - for child2 in element.children() { - if child2.is("value", ns::DATA_FORMS) { - if value.is_some() { - return Err(Error::ParseError("More than one value element in option element")); - } - value = Some(child2.text()); - } else { - return Err(Error::ParseError("Non-value element in option element")); - } - } - let value = value.ok_or(Error::ParseError("No value element in option element"))?; - field.options.push(Option_ { - label: label, - value: value, - }); - } else if element.is("media", ns::MEDIA_ELEMENT) { - let media_element = MediaElement::try_from(element.clone())?; - field.media.push(media_element); - } else { - return Err(Error::ParseError("Field child isn’t a value or media element.")); - } - } - if is_form_type { + let field = Field::try_from(child.clone())?; + if field.var == "FORM_TYPE" && field.type_ == FieldType::Hidden { if form.form_type.is_some() { return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); } From 5ece20a029f27bac74eab2e954c8f692f0bf9595 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:25:55 +0100 Subject: [PATCH 0425/1020] disco: Create a mutable DiscoInfoResult at the beginning of its parsing. --- src/disco.rs | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index aa816decd0bbbe07bd61f0b4fdb339c96c2e640c..1649682dd53a3d98961b2d14a250320100aca41a 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -99,16 +99,17 @@ impl TryFrom for DiscoInfoResult { return Err(Error::ParseError("This is not a disco#info element.")); } - let mut identities: Vec = vec!(); - let mut features: Vec = vec!(); - let mut extensions: Vec = vec!(); - - let node = get_attr!(elem, "node", optional); + let mut result = DiscoInfoResult { + node: get_attr!(elem, "node", optional), + identities: vec!(), + features: vec!(), + extensions: vec!(), + }; for child in elem.children() { if child.is("feature", ns::DISCO_INFO) { let feature = get_attr!(child, "var", required); - features.push(Feature { + result.features.push(Feature { var: feature, }); } else if child.is("identity", ns::DISCO_INFO) { @@ -124,7 +125,7 @@ impl TryFrom for DiscoInfoResult { let lang = get_attr!(child, "xml:lang", optional); let name = get_attr!(child, "name", optional); - identities.push(Identity { + result.identities.push(Identity { category: category, type_: type_, lang: lang, @@ -135,31 +136,26 @@ impl TryFrom for DiscoInfoResult { if data_form.type_ != DataFormType::Result_ { return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")); } - match data_form.form_type { - Some(_) => extensions.push(data_form), - None => return Err(Error::ParseError("Data form found without a FORM_TYPE.")), + if data_form.form_type.is_none() { + return Err(Error::ParseError("Data form found without a FORM_TYPE.")); } + result.extensions.push(data_form); } else { return Err(Error::ParseError("Unknown element in disco#info.")); } } - if identities.is_empty() { + if result.identities.is_empty() { return Err(Error::ParseError("There must be at least one identity in disco#info.")); } - if features.is_empty() { + if result.features.is_empty() { return Err(Error::ParseError("There must be at least one feature in disco#info.")); } - if !features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { + if !result.features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) { return Err(Error::ParseError("disco#info feature not present in disco#info.")); } - Ok(DiscoInfoResult { - node: node, - identities: identities, - features: features, - extensions: extensions - }) + Ok(result) } } From 4d3717d17095fcea8f1a1aad5d5a5039500dc2bf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:35:15 +0100 Subject: [PATCH 0426/1020] disco: Split Feature and Identity parsing out of DiscoQueryResult. --- src/disco.rs | 74 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 1649682dd53a3d98961b2d14a250320100aca41a..f3fad62e85352ddf55bada8b9331137dd333c3c4 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -54,6 +54,27 @@ pub struct Feature { pub var: String, } +impl TryFrom for Feature { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("feature", ns::DISCO_INFO) { + return Err(Error::ParseError("This is not a disco#info feature element.")); + } + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in disco#info feature element.")); + } + for (attr, _) in elem.attrs() { + if attr != "var" { + return Err(Error::ParseError("Unknown attribute in disco#info feature element.")); + } + } + Ok(Feature { + var: get_attr!(elem, "var", required) + }) + } +} + impl From for Element { fn from(feature: Feature) -> Element { Element::builder("feature") @@ -71,6 +92,33 @@ pub struct Identity { pub name: Option, } +impl TryFrom for Identity { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("identity", ns::DISCO_INFO) { + return Err(Error::ParseError("This is not a disco#info identity element.")); + } + + let category = get_attr!(elem, "category", required); + if category == "" { + return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) + } + + let type_ = get_attr!(elem, "type", required); + if type_ == "" { + return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) + } + + Ok(Identity { + category: category, + type_: type_, + lang: get_attr!(elem, "xml:lang", optional), + name: get_attr!(elem, "name", optional), + }) + } +} + impl From for Element { fn from(identity: Identity) -> Element { Element::builder("identity") @@ -108,29 +156,11 @@ impl TryFrom for DiscoInfoResult { for child in elem.children() { if child.is("feature", ns::DISCO_INFO) { - let feature = get_attr!(child, "var", required); - result.features.push(Feature { - var: feature, - }); + let feature = Feature::try_from(child.clone())?; + result.features.push(feature); } else if child.is("identity", ns::DISCO_INFO) { - let category = get_attr!(child, "category", required); - if category == "" { - return Err(Error::ParseError("Identity must have a non-empty 'category' attribute.")) - } - - let type_ = get_attr!(child, "type", required); - if type_ == "" { - return Err(Error::ParseError("Identity must have a non-empty 'type' attribute.")) - } - - let lang = get_attr!(child, "xml:lang", optional); - let name = get_attr!(child, "name", optional); - result.identities.push(Identity { - category: category, - type_: type_, - lang: lang, - name: name, - }); + let identity = Identity::try_from(child.clone())?; + result.identities.push(identity); } else if child.is("x", ns::DATA_FORMS) { let data_form = DataForm::try_from(child.clone())?; if data_form.type_ != DataFormType::Result_ { From 67e72b009e0e5b12c74c308e89bb415826e4f30d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:39:50 +0100 Subject: [PATCH 0427/1020] disco: Fix serialisation of extensions, and add a test. --- src/disco.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index f3fad62e85352ddf55bada8b9331137dd333c3c4..88e105ce4c1ab69c9c923663fdeb07f88a313c35 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -191,14 +191,12 @@ impl TryFrom for DiscoInfoResult { impl From for Element { fn from(disco: DiscoInfoResult) -> Element { - for _ in disco.extensions { - panic!("Not yet implemented!"); - } Element::builder("query") .ns(ns::DISCO_INFO) .attr("node", disco.node) .append(disco.identities) .append(disco.features) + .append(disco.extensions) .build() } } @@ -339,6 +337,21 @@ mod tests { assert!(query.extensions.is_empty()); } + #[test] + fn test_extension() { + let elem: Element = "example".parse().unwrap(); + let elem1 = elem.clone(); + let query = DiscoInfoResult::try_from(elem).unwrap(); + assert!(query.node.is_none()); + assert_eq!(query.identities.len(), 1); + assert_eq!(query.features.len(), 1); + assert_eq!(query.extensions.len(), 1); + assert_eq!(query.extensions[0].form_type, Some(String::from("example"))); + + let elem2 = query.into(); + assert_eq!(elem1, elem2); + } + #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); From 45d196463c236e70d0b967f339fcbc9bc39f6268 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 04:51:51 +0100 Subject: [PATCH 0428/1020] disco: Document every struct and their fields. --- src/disco.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index 88e105ce4c1ab69c9c923663fdeb07f88a313c35..b976e362d2c13c2993b3367b1707ab0e7266fbe5 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#![deny(missing_docs)] + use try_from::TryFrom; use minidom::Element; @@ -14,8 +16,13 @@ use ns; use data_forms::{DataForm, DataFormType}; +/// Structure representing a `` element. +/// +/// It should only be used in an ``, as it can only represent +/// the request, and not a result. #[derive(Debug, Clone)] pub struct DiscoInfoQuery { + /// Node on which we are doing the discovery. pub node: Option, } @@ -49,8 +56,10 @@ impl From for Element { } } +/// Structure representing a `` element. #[derive(Debug, Clone, PartialEq)] pub struct Feature { + /// Namespace of the feature we want to represent. pub var: String, } @@ -84,11 +93,19 @@ impl From for Element { } } +/// Structure representing an `` element. #[derive(Debug, Clone)] pub struct Identity { + /// Category of this identity. pub category: String, // TODO: use an enum here. + + /// Type of this identity. pub type_: String, // TODO: use an enum here. + + /// Lang of the name of this identity. pub lang: Option, + + /// Name of this identity. pub name: Option, } @@ -131,11 +148,22 @@ impl From for Element { } } +/// Structure representing a `` element. +/// +/// It should only be used in an ``, as it can only +/// represent the result, and not a request. #[derive(Debug, Clone)] pub struct DiscoInfoResult { + /// Node on which we have done this discovery. pub node: Option, + + /// List of identities exposed by this entity. pub identities: Vec, + + /// List of features supported by this entity. pub features: Vec, + + /// List of extensions reported by this entity. pub extensions: Vec, } @@ -201,8 +229,13 @@ impl From for Element { } } +/// Structure representing a `` element. +/// +/// It should only be used in an ``, as it can only represent +/// the request, and not a result. #[derive(Debug, Clone)] pub struct DiscoItemsQuery { + /// Node on which we are doing the discovery. pub node: Option, } @@ -236,10 +269,14 @@ impl From for Element { } } +/// Structure representing an `` element. #[derive(Debug, Clone)] pub struct Item { + /// JID of the entity pointed by this item. pub jid: Jid, + /// Node of the entity pointed by this item. pub node: Option, + /// Name of the entity pointed by this item. pub name: Option, } @@ -277,9 +314,17 @@ impl From for Element { } } +/// Structure representing a `` element. +/// +/// It should only be used in an ``, as it can only +/// represent the result, and not a request. #[derive(Debug, Clone)] pub struct DiscoItemsResult { + /// Node on which we have done this discovery. pub node: Option, + + /// List of items pointed by this entity. pub items: Vec, } From 5bd0feb17852e0113ec9263a7503d148f519d4d4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 05:11:30 +0100 Subject: [PATCH 0429/1020] optionally implement minidom::IntoAttributeValue --- Cargo.toml | 1 + src/lib.rs | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 715fc12844732dd8cf389a2934562545edb51e38..97c9cf5c61033264f22b55ff447f77564167b258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ license = "LGPL-3.0+" gitlab = { repository = "lumi/jid-rs" } [dependencies] +minidom = { version = "0.4.4", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 3f39cbcfc0378a5589e1ad0e48e2a2d4909b33f3..30ba7f7a03ce71dce5861600c2849e53e008be9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -359,6 +359,19 @@ impl Jid { } +#[cfg(feature = "minidom")] +extern crate minidom; + +#[cfg(feature = "minidom")] +use minidom::IntoAttributeValue; + +#[cfg(feature = "minidom")] +impl IntoAttributeValue for Jid { + fn into_attribute_value(self) -> Option { + Some(String::from(self)) + } +} + #[cfg(test)] mod tests { use super::*; @@ -380,4 +393,12 @@ mod tests { fn serialise() { assert_eq!(String::from(Jid::full("a", "b", "c")), String::from("a@b/c")); } + + #[cfg(feature = "minidom")] + #[test] + fn minidom() { + let elem: minidom::Element = "".parse().unwrap(); + let to: Jid = elem.attr("from").unwrap().parse().unwrap(); + assert_eq!(to, Jid::full("a", "b", "c")); + } } From 5388696b9526c3378db93a325822e7f5b45a8ce3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 05:24:20 +0100 Subject: [PATCH 0430/1020] =?UTF-8?q?lib:=20Remove=20erroneous=20=E2=80=9C?= =?UTF-8?q?reference=E2=80=9D=20mention=20in=20the=20module=20docstring.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b3f57fdf675ebffca54f744d5efd7b68eca9b97f..ce1610e445721161cf7f46c1182d74598a1f5e13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! A crate parsing common XMPP elements into Rust structures. //! -//! Each module implements the [`TryFrom`] trait, which takes a minidom -//! [`Element`] reference and returns a `Result` whose value is `Ok` if the +//! Each module implements the [`TryFrom`] trait, which takes a +//! minidom [`Element`] and returns a `Result` whose value is `Ok` if the //! element parsed correctly, `Err(error::Error)` otherwise. //! //! The returned structure can be manipuled as any Rust structure, with each From 6ec1e46953029a14ea45290e2351354ad1abc4d0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 05:36:59 +0100 Subject: [PATCH 0431/1020] roster: Make Group a proper struct. --- src/lib.rs | 22 ++++++++++++++++++++++ src/roster.rs | 18 +++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ce1610e445721161cf7f46c1182d74598a1f5e13..f88108238fd885afeb7dcea4212fe08c4079edb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,6 +144,28 @@ macro_rules! generate_id { ); } +macro_rules! generate_elem_id { + ($elem:ident, $name:tt, $ns:expr) => ( + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $elem(pub String); + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + // TODO: add a way to parse that differently when needed. + Ok($elem(String::from(s))) + } + } + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + .append(elem.0) + .build() + } + } + ); +} + /// Error type returned by every parser on failure. pub mod error; /// XML namespace definitions used through XMPP. diff --git a/src/roster.rs b/src/roster.rs index 1a4cc50e8df99c8b3ba89478364ef6339eaa4af7..a1a07eb10d9da0ec19a89c1052ab7834c1f2ac94 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -13,7 +13,7 @@ use jid::Jid; use error::Error; use ns; -type Group = String; +generate_elem_id!(Group, "group", ns::ROSTER); generate_attribute!(Subscription, "subscription", { None => "none", @@ -52,7 +52,11 @@ impl TryFrom for Item { for _ in child.children() { return Err(Error::ParseError("Roster item group can’t have children.")); } - item.groups.push(child.text()); + for _ in child.attrs() { + return Err(Error::ParseError("Roster item group can’t have attributes.")); + } + let group = Group(child.text()); + item.groups.push(group); } Ok(item) } @@ -65,7 +69,7 @@ impl From for Element { .attr("jid", String::from(item.jid)) .attr("name", item.name) .attr("subscription", item.subscription) - .append(item.groups.into_iter().map(|group| Element::builder("group").ns(ns::ROSTER).append(group)).collect::>()) + .append(item.groups) .build() } } @@ -163,7 +167,7 @@ mod tests { assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap()); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); assert_eq!(roster.items[0].subscription, Some(Subscription::Both)); - assert_eq!(roster.items[0].groups, vec!(String::from("Friends"))); + assert_eq!(roster.items[0].groups, vec!(Group::from_str("Friends").unwrap())); } #[test] @@ -183,8 +187,8 @@ mod tests { assert_eq!(roster.items[0].jid, Jid::from_str("test@example.org").unwrap()); assert_eq!(roster.items[0].name, None); assert_eq!(roster.items[0].groups.len(), 2); - assert_eq!(roster.items[0].groups[0], String::from("A")); - assert_eq!(roster.items[0].groups[1], String::from("B")); + assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap()); + assert_eq!(roster.items[0].groups[1], Group::from_str("B").unwrap()); let elem2 = roster.into(); assert_eq!(elem1, elem2); } @@ -210,7 +214,7 @@ mod tests { assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); assert_eq!(roster.items[0].name, Some(String::from("Nurse"))); assert_eq!(roster.items[0].groups.len(), 1); - assert_eq!(roster.items[0].groups[0], String::from("Servants")); + assert_eq!(roster.items[0].groups[0], Group::from_str("Servants").unwrap()); let elem: Element = r#" From 56a66f8c4b1e7f1d2efdd227b04aaab9f33860b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 06:11:48 +0100 Subject: [PATCH 0432/1020] message: Enforce more type safety on Body, Subject and Thread. --- src/message.rs | 51 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/message.rs b/src/message.rs index 759b7d2304c1653177693dad8cbe30763e151cf1..80a0e0445fd4fbc2a440b4f38364328c38401c69 100644 --- a/src/message.rs +++ b/src/message.rs @@ -115,9 +115,10 @@ generate_attribute!(MessageType, "type", { }, Default = Normal); type Lang = String; -type Body = String; -type Subject = String; -type Thread = String; + +generate_elem_id!(Body, "body", ns::JABBER_CLIENT); +generate_elem_id!(Subject, "subject", ns::JABBER_CLIENT); +generate_elem_id!(Thread, "thread", ns::JABBER_CLIENT); /// The main structure representing the `` stanza. #[derive(Debug, Clone)] @@ -168,7 +169,8 @@ impl TryFrom for Message { return Err(Error::ParseError("Unknown child in body element.")); } let lang = get_attr!(root, "xml:lang", default); - if bodies.insert(lang, elem.text()).is_some() { + let body = Body(elem.text()); + if bodies.insert(lang, body).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } } else if elem.is("subject", ns::JABBER_CLIENT) { @@ -176,7 +178,8 @@ impl TryFrom for Message { return Err(Error::ParseError("Unknown child in subject element.")); } let lang = get_attr!(root, "xml:lang", default); - if subjects.insert(lang, elem.text()).is_some() { + let subject = Subject(elem.text()); + if subjects.insert(lang, subject).is_some() { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); } } else if elem.is("thread", ns::JABBER_CLIENT) { @@ -186,7 +189,7 @@ impl TryFrom for Message { for _ in elem.children() { return Err(Error::ParseError("Unknown child in thread element.")); } - thread = Some(elem.text()); + thread = Some(Thread(elem.text())); } else { payloads.push(elem.clone()) } @@ -214,25 +217,23 @@ impl From for Element { .attr("type", message.type_) .append(message.subjects.into_iter() .map(|(lang, subject)| { - Element::builder("subject") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(subject) - .build() }) + let mut subject = Element::from(subject); + subject.set_attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }); + subject + }) .collect::>()) .append(message.bodies.into_iter() .map(|(lang, body)| { - Element::builder("body") - .ns(ns::JABBER_CLIENT) - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(body) - .build() }) + let mut body = Element::from(body); + body.set_attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }); + body + }) .collect::>()) .append(message.payloads) .build() @@ -268,7 +269,7 @@ mod tests { let elem: Element = "Hello world!".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); - assert_eq!(message.bodies[""], "Hello world!"); + assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); assert_eq!(elem1, elem2); @@ -278,7 +279,7 @@ mod tests { fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); - message.bodies.insert(String::from(""), String::from("Hello world!")); + message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); assert_eq!(elem, elem2); } @@ -288,7 +289,7 @@ mod tests { let elem: Element = "Hello world!".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); - assert_eq!(message.subjects[""], "Hello world!"); + assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); let elem2 = message.into(); assert_eq!(elem1, elem2); From a04c39512202a83422b64e279cfda31bb3ad3bd2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 06:22:35 +0100 Subject: [PATCH 0433/1020] roster: Document most structs and their fields. --- src/roster.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/roster.rs b/src/roster.rs index a1a07eb10d9da0ec19a89c1052ab7834c1f2ac94..82488a344837c2410df71ddad086f3962c6e1f70 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -23,11 +23,19 @@ generate_attribute!(Subscription, "subscription", { Remove => "remove", }); +/// Contact from the user’s contact list. #[derive(Debug, Clone, PartialEq)] pub struct Item { + /// JID of this contact. pub jid: Jid, + + /// Name of this contact. pub name: Option, + + /// Subscription status of this contact. pub subscription: Option, + + /// Groups this contact is part of. pub groups: Vec, } @@ -74,9 +82,17 @@ impl From for Element { } } +/// The contact list of the user. #[derive(Debug, Clone)] pub struct Roster { + /// Version of the contact list. + /// + /// This is an opaque string that should only be sent back to the server on + /// a new connection, if this client is storing the contact list between + /// connections. pub ver: Option, + + /// List of the contacts of the user. pub items: Vec, } From d55fa8e5ddc3fb228f37676ea7e0ce8afb2452a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 06:26:50 +0100 Subject: [PATCH 0434/1020] =?UTF-8?q?chatstates:=20Prevent=20compilation?= =?UTF-8?q?=20if=20the=20module=20isn=E2=80=99t=20properly=20documented.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chatstates.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/chatstates.rs b/src/chatstates.rs index 4093edbd29c805a39dbe65731dbf365ff3341413..2cb657f6fcc2c3f885eb18516e3afd33ef9ef11e 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#![deny(missing_docs)] + use try_from::TryFrom; use minidom::Element; From 99b9525e6fde72f0e392e28370ea13663d7b92d6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 06:28:20 +0100 Subject: [PATCH 0435/1020] Update to jid-rs 0.2.3, which implements IntoAttributeValue on Jid. --- Cargo.toml | 5 ++++- src/delay.rs | 2 +- src/disco.rs | 2 +- src/iq.rs | 4 ++-- src/jingle.rs | 4 ++-- src/message.rs | 4 ++-- src/muc/user.rs | 7 ++----- src/presence.rs | 4 ++-- src/pubsub/event.rs | 4 ++-- src/roster.rs | 2 +- src/stanza_error.rs | 2 +- src/stanza_id.rs | 2 +- 12 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c5ff5e0977e3a3026cafca7507ae8d4ac2486c91..c1f48c005b2e9e29c3e14e689ef380e0d52f4dd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ license = "MPL-2.0" [dependencies] minidom = "0.4.4" -jid = "0.2.0" base64 = "0.6.0" digest = "0.6.0" sha-1 = "0.4.0" @@ -23,3 +22,7 @@ sha3 = "0.6.0" blake2 = "0.6.1" chrono = "0.4.0" try_from = "0.2.2" + +[dependencies.jid] +version = "0.2.3" +features = ["minidom"] diff --git a/src/delay.rs b/src/delay.rs index e5123ec2d208493227b3c00888ab39bf914da7e7..97c10ad76423132acb7d86782cccb0e4bd02f819 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -49,7 +49,7 @@ impl From for Element { fn from(delay: Delay) -> Element { Element::builder("delay") .ns(ns::DELAY) - .attr("from", delay.from.map(String::from)) + .attr("from", delay.from) .attr("stamp", delay.stamp.to_rfc3339()) .append(delay.data) .build() diff --git a/src/disco.rs b/src/disco.rs index b976e362d2c13c2993b3367b1707ab0e7266fbe5..b1d21680691242ab22fe6d05e4c2738510debcf0 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -307,7 +307,7 @@ impl From for Element { fn from(item: Item) -> Element { Element::builder("item") .ns(ns::DISCO_ITEMS) - .attr("jid", String::from(item.jid)) + .attr("jid", item.jid) .attr("node", item.node) .attr("name", item.name) .build() diff --git a/src/iq.rs b/src/iq.rs index 2584ebeb66f1c081cce8e3f2aa9bdded33bb8a08..1fd308c0174ca509059c427d58a39c09654a719f 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -276,8 +276,8 @@ impl From for Element { fn from(iq: Iq) -> Element { let mut stanza = Element::builder("iq") .ns(ns::JABBER_CLIENT) - .attr("from", iq.from.map(String::from)) - .attr("to", iq.to.map(String::from)) + .attr("from", iq.from) + .attr("to", iq.to) .attr("id", iq.id) .attr("type", &iq.payload) .build(); diff --git a/src/jingle.rs b/src/jingle.rs index 364ee4b82d6a57579a75e236a98b4ef14dc9347e..d36d0862b951794c5da49e39bc6bf649422e2fa2 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -298,8 +298,8 @@ impl From for Element { Element::builder("jingle") .ns(ns::JINGLE) .attr("action", jingle.action) - .attr("initiator", match jingle.initiator { Some(initiator) => Some(String::from(initiator)), None => None }) - .attr("responder", match jingle.responder { Some(responder) => Some(String::from(responder)), None => None }) + .attr("initiator", jingle.initiator) + .attr("responder", jingle.responder) .attr("sid", jingle.sid) .append(jingle.contents) .append(jingle.reason) diff --git a/src/message.rs b/src/message.rs index 80a0e0445fd4fbc2a440b4f38364328c38401c69..3bf73ae89436d41bbd6774f4937a28b62e504edf 100644 --- a/src/message.rs +++ b/src/message.rs @@ -211,8 +211,8 @@ impl From for Element { fn from(message: Message) -> Element { Element::builder("message") .ns(ns::JABBER_CLIENT) - .attr("from", message.from.map(String::from)) - .attr("to", message.to.map(String::from)) + .attr("from", message.from) + .attr("to", message.to) .attr("id", message.id) .attr("type", message.type_) .append(message.subjects.into_iter() diff --git a/src/muc/user.rs b/src/muc/user.rs index afc9e72187705a9117b436ad45c252587c47e505..9e141ad2defe1fac3aeb41e91861477f41e22044 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -186,7 +186,7 @@ impl From for Element { let elem = Element::builder("actor").ns(ns::MUC_USER); (match actor { - Actor::Jid(jid) => elem.attr("jid", String::from(jid)), + Actor::Jid(jid) => elem.attr("jid", jid), Actor::Nick(nick) => elem.attr("nick", nick), }).build() } @@ -330,10 +330,7 @@ impl From for Element { Element::builder("item") .ns(ns::MUC_USER) .attr("affiliation", item.affiliation) - .attr("jid", match item.jid { - Some(jid) => Some(String::from(jid)), - None => None, - }) + .attr("jid", item.jid) .attr("nick", item.nick) .attr("role", item.role) .append(item.actor) diff --git a/src/presence.rs b/src/presence.rs index 93c5cf941b246dcf775f683743fc0a8636a59f1e..958a82b3f917e8c558ba49a70f4a06b1b18ed349 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -317,8 +317,8 @@ impl From for Element { fn from(presence: Presence) -> Element { Element::builder("presence") .ns(ns::JABBER_CLIENT) - .attr("from", presence.from.map(String::from)) - .attr("to", presence.to.map(String::from)) + .attr("from", presence.from) + .attr("to", presence.to) .attr("id", presence.id) .attr("type", presence.type_) .append(presence.show) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index a6fa4550a6775751a465a13a855e82235770508f..108d0ab1321cb1067b7f89374f48d8f18c4e0e3a 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -31,7 +31,7 @@ impl From for Element { .ns(ns::PUBSUB_EVENT) .attr("id", item.id) .attr("node", item.node) - .attr("publisher", item.publisher.map(String::from)) + .attr("publisher", item.publisher) .append(item.payload) .build() } @@ -255,7 +255,7 @@ impl From for Element { .ns(ns::PUBSUB_EVENT) .attr("node", node) .attr("expiry", expiry.map(|expiry| expiry.to_rfc3339())) - .attr("jid", jid.map(String::from)) + .attr("jid", jid) .attr("subid", subid) .attr("subscription", subscription) .build() diff --git a/src/roster.rs b/src/roster.rs index 82488a344837c2410df71ddad086f3962c6e1f70..9e2bba31db54f240188b913581e81227e2d5c8be 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -74,7 +74,7 @@ impl From for Element { fn from(item: Item) -> Element { Element::builder("item") .ns(ns::ROSTER) - .attr("jid", String::from(item.jid)) + .attr("jid", item.jid) .attr("name", item.name) .attr("subscription", item.subscription) .append(item.groups) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 3b7273b3fa8db3d7498de02f779bb8c1b28be6da..0c7ca7e84e77a561e5bd2ce79f9291b739d4bd20 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -177,7 +177,7 @@ impl From for Element { let mut root = Element::builder("error") .ns(ns::JABBER_CLIENT) .attr("type", err.type_) - .attr("by", err.by.map(String::from)) + .attr("by", err.by) .append(err.defined_condition) .build(); for (lang, text) in err.texts { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 43ce96452983f6ccaeee0fe68a952bfbcc75aba8..feeeadbe0328d00411e776d557227ed9dc7439c4 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -41,7 +41,7 @@ impl From for Element { Element::builder("stanza-id") .ns(ns::SID) .attr("id", stanza_id.id) - .attr("by", String::from(stanza_id.by)) + .attr("by", stanza_id.by) .build() } } From 69cc83c4562f92fe9e8e8637c374005e58a48380 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 06:49:02 +0100 Subject: [PATCH 0436/1020] message, iq, presence, stanza_error, forwarded: Add support for components hidden behind the component feature flag. --- Cargo.toml | 4 +++ src/forwarding.rs | 4 +-- src/iq.rs | 48 +++++++++++++++++++++++++++++++---- src/mam.rs | 12 +++++++++ src/message.rs | 36 +++++++++++++++++++------- src/ns.rs | 8 ++++++ src/presence.rs | 61 +++++++++++++++++++++++++++++++++++++-------- src/stanza_error.rs | 16 ++++++++++-- 8 files changed, 161 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c1f48c005b2e9e29c3e14e689ef380e0d52f4dd2..a701185456fc1d667cd5a161b3563cf5fd609217 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,7 @@ try_from = "0.2.2" [dependencies.jid] version = "0.2.3" features = ["minidom"] + +[features] +# Build xmpp-parsers to make components instead of clients. +component = [] diff --git a/src/forwarding.rs b/src/forwarding.rs index 86474dd1b6cd0456887ca2648cdb6a4fc7c68e6c..2348068b996e90ecc0f8fbc2902a6d508452ae19 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -34,9 +34,9 @@ impl TryFrom for Forwarded { for child in elem.children() { if child.is("delay", ns::DELAY) { delay = Some(Delay::try_from(child.clone())?); - } else if child.is("message", ns::JABBER_CLIENT) { + } else if child.is("message", ns::DEFAULT_NS) { stanza = Some(Message::try_from(child.clone())?); - // TODO: also handle the five other possibilities. + // TODO: also handle the two other possibilities. } else { return Err(Error::ParseError("Unknown child in forwarded element.")); } diff --git a/src/iq.rs b/src/iq.rs index 1fd308c0174ca509059c427d58a39c09654a719f..7e919985b0bdb6fb27978f008b1d37285af0b0fe 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -207,7 +207,7 @@ impl TryFrom for Iq { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("iq", ns::JABBER_CLIENT) { + if !root.is("iq", ns::DEFAULT_NS) { return Err(Error::ParseError("This is not an iq element.")); } let from = get_attr!(root, "from", optional); @@ -222,7 +222,7 @@ impl TryFrom for Iq { return Err(Error::ParseError("Wrong number of children in iq element.")); } if type_ == "error" { - if elem.is("error", ns::JABBER_CLIENT) { + if elem.is("error", ns::DEFAULT_NS) { if error_payload.is_some() { return Err(Error::ParseError("Wrong number of children in iq element.")); } @@ -275,7 +275,7 @@ impl TryFrom for Iq { impl From for Element { fn from(iq: Iq) -> Element { let mut stanza = Element::builder("iq") - .ns(ns::JABBER_CLIENT) + .ns(ns::DEFAULT_NS) .attr("from", iq.from) .attr("to", iq.to) .attr("id", iq.id) @@ -300,7 +300,10 @@ mod tests { #[test] fn test_require_type() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = Iq::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -311,11 +314,16 @@ mod tests { #[test] fn test_get() { + #[cfg(not(feature = "component"))] let elem: Element = " - + + ".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = " + ".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); - let query: Element = "".parse().unwrap(); + let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); @@ -327,9 +335,14 @@ mod tests { #[test] fn test_set() { + #[cfg(not(feature = "component"))] let elem: Element = " ".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = " + + ".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); let vcard: Element = "".parse().unwrap(); assert_eq!(iq.from, None); @@ -343,7 +356,10 @@ mod tests { #[test] fn test_result_empty() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -356,9 +372,14 @@ mod tests { #[test] fn test_result() { + #[cfg(not(feature = "component"))] let elem: Element = " ".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = " + + ".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); @@ -372,12 +393,20 @@ mod tests { #[test] fn test_error() { + #[cfg(not(feature = "component"))] let elem: Element = " ".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = " + + + + + ".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -396,7 +425,10 @@ mod tests { #[test] fn test_children_invalid() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = Iq::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -407,7 +439,10 @@ mod tests { #[test] fn test_serialise() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let iq2 = Iq { from: None, to: None, @@ -420,7 +455,10 @@ mod tests { #[test] fn test_disco() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); let payload = match iq.payload { IqType::Get(payload) => IqGetPayload::try_from(payload).unwrap(), diff --git a/src/mam.rs b/src/mam.rs index b52e80413b69a5028fc03674b95f5fe4b8f96570..65299b700be715c8efe2d0ee7685fff300fd759c 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -242,6 +242,7 @@ mod tests { #[test] fn test_result() { + #[cfg(not(feature = "component"))] let elem: Element = r#" @@ -251,6 +252,17 @@ mod tests { +"#.parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = r#" + + + + + Hail to thee + + + "#.parse().unwrap(); Result_::try_from(elem).unwrap(); } diff --git a/src/message.rs b/src/message.rs index 3bf73ae89436d41bbd6774f4937a28b62e504edf..5dba839eacd095851d031a7e898b0e2629c4dcbb 100644 --- a/src/message.rs +++ b/src/message.rs @@ -49,7 +49,7 @@ impl TryFrom for MessagePayload { fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - ("error", ns::JABBER_CLIENT) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), + ("error", ns::DEFAULT_NS) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), // XEP-0085 ("active", ns::CHATSTATES) @@ -116,9 +116,9 @@ generate_attribute!(MessageType, "type", { type Lang = String; -generate_elem_id!(Body, "body", ns::JABBER_CLIENT); -generate_elem_id!(Subject, "subject", ns::JABBER_CLIENT); -generate_elem_id!(Thread, "thread", ns::JABBER_CLIENT); +generate_elem_id!(Body, "body", ns::DEFAULT_NS); +generate_elem_id!(Subject, "subject", ns::DEFAULT_NS); +generate_elem_id!(Thread, "thread", ns::DEFAULT_NS); /// The main structure representing the `` stanza. #[derive(Debug, Clone)] @@ -152,7 +152,7 @@ impl TryFrom for Message { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("message", ns::JABBER_CLIENT) { + if !root.is("message", ns::DEFAULT_NS) { return Err(Error::ParseError("This is not a message element.")); } let from = get_attr!(root, "from", optional); @@ -164,7 +164,7 @@ impl TryFrom for Message { let mut thread = None; let mut payloads = vec!(); for elem in root.children() { - if elem.is("body", ns::JABBER_CLIENT) { + if elem.is("body", ns::DEFAULT_NS) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in body element.")); } @@ -173,7 +173,7 @@ impl TryFrom for Message { if bodies.insert(lang, body).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } - } else if elem.is("subject", ns::JABBER_CLIENT) { + } else if elem.is("subject", ns::DEFAULT_NS) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in subject element.")); } @@ -182,7 +182,7 @@ impl TryFrom for Message { if subjects.insert(lang, subject).is_some() { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); } - } else if elem.is("thread", ns::JABBER_CLIENT) { + } else if elem.is("thread", ns::DEFAULT_NS) { if thread.is_some() { return Err(Error::ParseError("Thread element present twice.")); } @@ -210,7 +210,7 @@ impl TryFrom for Message { impl From for Element { fn from(message: Message) -> Element { Element::builder("message") - .ns(ns::JABBER_CLIENT) + .ns(ns::DEFAULT_NS) .attr("from", message.from) .attr("to", message.to) .attr("id", message.id) @@ -246,7 +246,10 @@ mod tests { #[test] fn test_simple() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let message = Message::try_from(elem).unwrap(); assert_eq!(message.from, None); assert_eq!(message.to, None); @@ -257,7 +260,10 @@ mod tests { #[test] fn test_serialise() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let mut message = Message::new(None); message.type_ = MessageType::Normal; let elem2 = message.into(); @@ -266,7 +272,10 @@ mod tests { #[test] fn test_body() { + #[cfg(not(feature = "component"))] let elem: Element = "Hello world!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Hello world!".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); @@ -277,7 +286,10 @@ mod tests { #[test] fn test_serialise_body() { + #[cfg(not(feature = "component"))] let elem: Element = "Hello world!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Hello world!".parse().unwrap(); let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); @@ -286,7 +298,10 @@ mod tests { #[test] fn test_subject() { + #[cfg(not(feature = "component"))] let elem: Element = "Hello world!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Hello world!".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); @@ -297,7 +312,10 @@ mod tests { #[test] fn test_attention() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); let elem2 = message.into(); diff --git a/src/ns.rs b/src/ns.rs index 0d06a1d7ae483136a705ecaa1fa96cf72f9cc8bd..5df077e1d5ccbc50f4d51c38a41ea8c7c45f5cb6 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -47,6 +47,9 @@ pub const REGISTER: &str = "jabber:iq:register"; /// XEP-0085: Chat State Notifications pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; +/// XEP-0114: Jabber Component Protocol +pub const COMPONENT_ACCEPT: &str = "jabber:component:accept"; + /// XEP-0115: Entity Capabilities pub const CAPS: &str = "http://jabber.org/protocol/caps"; @@ -119,3 +122,8 @@ pub const EME: &str = "urn:xmpp:eme:0"; pub const ECAPS2: &str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2_OPTIMIZE: &str = "urn:xmpp:caps:optimize"; + +#[cfg(not(feature = "component"))] +pub const DEFAULT_NS: &str = JABBER_CLIENT; +#[cfg(feature = "component")] +pub const DEFAULT_NS: &str = COMPONENT_ACCEPT; diff --git a/src/presence.rs b/src/presence.rs index 958a82b3f917e8c558ba49a70f4a06b1b18ed349..a1f11fa690319437c1af43cbcc54385d3ddddf1c 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -95,7 +95,7 @@ impl TryFrom for PresencePayload { fn try_from(elem: Element) -> Result { Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - ("error", ns::JABBER_CLIENT) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), + ("error", ns::DEFAULT_NS) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), // XEP-0045 ("x", ns::MUC) => PresencePayload::Muc(Muc::try_from(elem)?), @@ -248,7 +248,7 @@ impl TryFrom for Presence { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("presence", ns::JABBER_CLIENT) { + if !root.is("presence", ns::DEFAULT_NS) { return Err(Error::ParseError("This is not a presence element.")); } let mut show = None; @@ -264,7 +264,7 @@ impl TryFrom for Presence { payloads: vec!(), }; for elem in root.children() { - if elem.is("show", ns::JABBER_CLIENT) { + if elem.is("show", ns::DEFAULT_NS) { if show.is_some() { return Err(Error::ParseError("More than one show element in a presence.")); } @@ -275,7 +275,7 @@ impl TryFrom for Presence { return Err(Error::ParseError("Unknown attribute in show element.")); } show = Some(Show::from_str(elem.text().as_ref())?); - } else if elem.is("status", ns::JABBER_CLIENT) { + } else if elem.is("status", ns::DEFAULT_NS) { for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } @@ -288,7 +288,7 @@ impl TryFrom for Presence { if presence.statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); } - } else if elem.is("priority", ns::JABBER_CLIENT) { + } else if elem.is("priority", ns::DEFAULT_NS) { if priority.is_some() { return Err(Error::ParseError("More than one priority element in a presence.")); } @@ -316,7 +316,7 @@ impl TryFrom for Presence { impl From for Element { fn from(presence: Presence) -> Element { Element::builder("presence") - .ns(ns::JABBER_CLIENT) + .ns(ns::DEFAULT_NS) .attr("from", presence.from) .attr("to", presence.to) .attr("id", presence.id) @@ -343,7 +343,10 @@ mod tests { #[test] fn test_simple() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.from, None); assert_eq!(presence.to, None); @@ -354,7 +357,10 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); + #[cfg(not(feature = "component"))] + let elem: Element = "/>".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "/>".parse().unwrap(); let presence = Presence::new(Type::Unavailable); let elem2 = presence.into(); assert_eq!(elem, elem2); @@ -362,7 +368,10 @@ mod tests { #[test] fn test_show() { + #[cfg(not(feature = "component"))] let elem: Element = "chat".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "chat".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.show, Show::Chat); @@ -370,8 +379,10 @@ mod tests { #[test] fn test_missing_show_value() { - // "online" used to be a pretty common mistake. + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -383,7 +394,10 @@ mod tests { #[test] fn test_invalid_show() { // "online" used to be a pretty common mistake. + #[cfg(not(feature = "component"))] let elem: Element = "online".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "online".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -394,7 +408,10 @@ mod tests { #[test] fn test_empty_status() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); @@ -403,7 +420,10 @@ mod tests { #[test] fn test_status() { + #[cfg(not(feature = "component"))] let elem: Element = "Here!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Here!".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); @@ -412,7 +432,10 @@ mod tests { #[test] fn test_multiple_statuses() { + #[cfg(not(feature = "component"))] let elem: Element = "Here!Là!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Here!Là!".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 2); @@ -422,7 +445,10 @@ mod tests { #[test] fn test_invalid_multiple_statuses() { + #[cfg(not(feature = "component"))] let elem: Element = "Here!Là!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Here!Là!".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -433,7 +459,10 @@ mod tests { #[test] fn test_priority() { + #[cfg(not(feature = "component"))] let elem: Element = "-1".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "-1".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.priority, -1i8); @@ -441,7 +470,10 @@ mod tests { #[test] fn test_invalid_priority() { + #[cfg(not(feature = "component"))] let elem: Element = "128".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "128".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); match error { Error::ParseIntError(_) => (), @@ -451,7 +483,10 @@ mod tests { #[test] fn test_unknown_child() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let presence = Presence::try_from(elem).unwrap(); let payload = &presence.payloads[0]; assert!(payload.is("test", "invalid")); @@ -459,7 +494,10 @@ mod tests { #[test] fn test_invalid_status_child() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -470,7 +508,10 @@ mod tests { #[test] fn test_invalid_attribute() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -485,7 +526,7 @@ mod tests { let mut presence = Presence::new(Type::Unavailable); presence.statuses.insert(String::from(""), status); let elem: Element = presence.into(); - assert!(elem.is("presence", ns::JABBER_CLIENT)); - assert!(elem.children().next().unwrap().is("status", ns::JABBER_CLIENT)); + assert!(elem.is("presence", ns::DEFAULT_NS)); + assert!(elem.children().next().unwrap().is("status", ns::DEFAULT_NS)); } } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 0c7ca7e84e77a561e5bd2ce79f9291b739d4bd20..8ca9c86289aead4f6ec8d756cb26c9bafcf83784 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -125,7 +125,7 @@ impl TryFrom for StanzaError { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("error", ns::JABBER_CLIENT) { + if !elem.is("error", ns::DEFAULT_NS) { return Err(Error::ParseError("This is not an error element.")); } @@ -175,7 +175,7 @@ impl TryFrom for StanzaError { impl From for Element { fn from(err: StanzaError) -> Element { let mut root = Element::builder("error") - .ns(ns::JABBER_CLIENT) + .ns(ns::DEFAULT_NS) .attr("type", err.type_) .attr("by", err.by) .append(err.defined_condition) @@ -201,7 +201,10 @@ mod tests { #[test] fn test_simple() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap(); assert_eq!(error.type_, ErrorType::Cancel); assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition); @@ -209,7 +212,10 @@ mod tests { #[test] fn test_invalid_type() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -217,7 +223,10 @@ mod tests { }; assert_eq!(message, "Required attribute 'type' missing."); + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -228,7 +237,10 @@ mod tests { #[test] fn test_invalid_condition() { + #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, From cbff99b73d27bf8264898e8650053592e11e45ce Mon Sep 17 00:00:00 2001 From: lumi Date: Sat, 29 Jul 2017 12:06:53 +0200 Subject: [PATCH 0437/1020] bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 97c9cf5c61033264f22b55ff447f77564167b258..164e39dcdc892808704d369cc7b74cda6328b38a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.2.2" +version = "0.2.3" authors = ["lumi ", "Emmanuel Gil Peyrot "] description = "A crate which provides a Jid struct for Jabber IDs." homepage = "https://gitlab.com/lumi/jid-rs" From 1af06fdf6d90c29d2bc4997efc3e0fa92779c963 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Jul 2017 11:45:45 +0100 Subject: [PATCH 0438/1020] Add an iq version parser (XEP-0092). --- src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/version.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 src/version.rs diff --git a/src/lib.rs b/src/lib.rs index f88108238fd885afeb7dcea4212fe08c4079edb8..cc9a8c31aa76313664a271fc907b3755f7c78bae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -207,6 +207,9 @@ pub mod ibr; /// XEP-0085: Chat State Notifications pub mod chatstates; +/// XEP-0092: Software Version +pub mod version; + /// XEP-0115: Entity Capabilities pub mod caps; diff --git a/src/ns.rs b/src/ns.rs index 5df077e1d5ccbc50f4d51c38a41ea8c7c45f5cb6..22e5f1566d4a3c06c325aa9f77e6b7ec7a11e1d0 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -47,6 +47,9 @@ pub const REGISTER: &str = "jabber:iq:register"; /// XEP-0085: Chat State Notifications pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; +/// XEP-0092: Software Version +pub const VERSION: &str = "jabber:iq:version"; + /// XEP-0114: Jabber Component Protocol pub const COMPONENT_ACCEPT: &str = "jabber:component:accept"; diff --git a/src/version.rs b/src/version.rs new file mode 100644 index 0000000000000000000000000000000000000000..5e7f0abf829d89e4a5b7ed0351657950a0a5aea8 --- /dev/null +++ b/src/version.rs @@ -0,0 +1,94 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; +use minidom::Element; +use error::Error; +use ns; + +#[derive(Debug, Clone)] +pub struct Version { + pub name: String, + pub version: String, + pub os: Option, +} + +impl TryFrom for Version { + type Err = Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("query", ns::VERSION) { + return Err(Error::ParseError("This is not a version element.")); + } + for _ in elem.attrs() { + return Err(Error::ParseError("Unknown child in version element.")); + } + let mut name = None; + let mut version = None; + let mut os = None; + for child in elem.children() { + if child.is("name", ns::VERSION) { + if name.is_some() { + return Err(Error::ParseError("More than one name in version element.")); + } + name = Some(child.text()); + } else if child.is("version", ns::VERSION) { + if version.is_some() { + return Err(Error::ParseError("More than one version in version element.")); + } + version = Some(child.text()); + } else if child.is("os", ns::VERSION) { + if os.is_some() { + return Err(Error::ParseError("More than one os in version element.")); + } + os = Some(child.text()); + } else { + return Err(Error::ParseError("Unknown child in version element.")); + } + } + let name = name.unwrap(); + let version = version.unwrap(); + Ok(Version { + name, + version, + os, + }) + } +} + +impl From for Element { + fn from(version: Version) -> Element { + Element::builder("query") + .ns(ns::VERSION) + .append(Element::builder("name") + .ns(ns::VERSION) + .append(version.name) + .build()) + .append(Element::builder("version") + .ns(ns::VERSION) + .append(version.version) + .build()) + .append(Element::builder("os") + .ns(ns::VERSION) + .append(version.os) + .build()) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "xmpp-rs0.3.0".parse().unwrap(); + let version = Version::try_from(elem).unwrap(); + assert_eq!(version.name, String::from("xmpp-rs")); + assert_eq!(version.version, String::from("0.3.0")); + assert_eq!(version.os, None); + } +} From 052c46635ac891d1292976853fac5d1fe0ef09c4 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 12 Aug 2017 02:05:18 +0200 Subject: [PATCH 0439/1020] escape text and attribute values --- src/element.rs | 26 +++++++++++++++++++++++--- src/tests.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/element.rs b/src/element.rs index ee2485083823876146d84774dcbde32770a38943..db41fd55e2d663f4845e63e7f4c7df3f074b0ceb 100644 --- a/src/element.rs +++ b/src/element.rs @@ -18,6 +18,22 @@ use std::slice; use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; +/// Escape XML text +pub fn write_escaped(writer: &mut W, input: &str) -> Result<()> { + for c in input.chars() { + match c { + '&' => write!(writer, "&")?, + '<' => write!(writer, "<")?, + '>' => write!(writer, ">")?, + '\'' => write!(writer, "'")?, + '"' => write!(writer, """)?, + _ => write!(writer, "{}", c)?, + } + } + + Ok(()) +} + /// A node in an element tree. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Node { @@ -71,7 +87,7 @@ impl Node { fn write_to_inner(&self, writer: &mut W, last_namespace: &mut Option) -> Result<()>{ match *self { Node::Element(ref elmt) => elmt.write_to_inner(writer, last_namespace)?, - Node::Text(ref s) => write!(writer, "{}", s)?, + Node::Text(ref s) => write_escaped(writer, s)?, } Ok(()) @@ -323,13 +339,17 @@ impl Element { if let Some(ref ns) = self.namespace { if *last_namespace != self.namespace { - write!(writer, " xmlns=\"{}\"", ns)?; + write!(writer, " xmlns=\"")?; + write_escaped(writer, ns)?; + write!(writer, "\"")?; *last_namespace = Some(ns.clone()); } } for (key, value) in &self.attributes { - write!(writer, " {}=\"{}\"", key, value)?; + write!(writer, " {}=\"", key)?; + write_escaped(writer, value)?; + write!(writer, "\"")?; } if self.children.is_empty() { diff --git a/src/tests.rs b/src/tests.rs index bc71ca22c41730f403e52c1506c38dec8be14d5a..6dd6a2ba0026df27291533fe009994798df3ecf2 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -43,6 +43,34 @@ fn writer_works() { assert_eq!(String::from_utf8(writer).unwrap(), TEST_STRING); } +#[test] +fn writer_escapes_attributes() { + let root = Element::builder("root") + .attr("a", "\"Air\" quotes") + .build(); + let mut writer = Vec::new(); + { + root.write_to(&mut writer).unwrap(); + } + assert_eq!(String::from_utf8(writer).unwrap(), + r#""# + ); +} + +#[test] +fn writer_escapes_text() { + let root = Element::builder("root") + .append("<3") + .build(); + let mut writer = Vec::new(); + { + root.write_to(&mut writer).unwrap(); + } + assert_eq!(String::from_utf8(writer).unwrap(), + r#"<3"# + ); +} + #[test] fn builder_works() { let elem = Element::builder("a") From 77bc215cba9a1058f5fd9b1ba4c2bb0f68cc325d Mon Sep 17 00:00:00 2001 From: Astro Date: Wed, 19 Jul 2017 01:14:33 +0200 Subject: [PATCH 0440/1020] tests: add namespace inheritance tests --- src/tests.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/tests.rs b/src/tests.rs index 6dd6a2ba0026df27291533fe009994798df3ecf2..40558eca1d9581d0f270a8ca857d72e325ef3792 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -149,3 +149,50 @@ fn wrongly_closed_elements_error() { let elem1 = "".parse::(); assert!(elem1.is_ok()); } + +#[test] +fn namespace_simple() { + let elem: Element = "".parse().unwrap(); + assert_eq!(elem.name(), "message"); + assert_eq!(elem.ns(), Some("jabber:client")); +} + +#[test] +fn namespace_prefixed() { + let elem: Element = "" + .parse().unwrap(); + assert_eq!(elem.name(), "features"); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); +} + +#[test] +fn namespace_inherited_simple() { + let elem: Element = "".parse().unwrap(); + assert_eq!(elem.name(), "stream"); + assert_eq!(elem.ns(), Some("jabber:client")); + let child = elem.children().next().unwrap(); + assert_eq!(child.name(), "message"); + assert_eq!(child.ns(), Some("jabber:client")); +} + +#[test] +fn namespace_inherited_prefixed1() { + let elem: Element = "" + .parse().unwrap(); + assert_eq!(elem.name(), "features"); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); + let child = elem.children().next().unwrap(); + assert_eq!(child.name(), "message"); + assert_eq!(child.ns(), Some("jabber:client")); +} + +#[test] +fn namespace_inherited_prefixed2() { + let elem: Element = "" + .parse().unwrap(); + assert_eq!(elem.name(), "stream"); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); + let child = elem.children().next().unwrap(); + assert_eq!(child.name(), "message"); + assert_eq!(child.ns(), Some("jabber:client")); +} From a01cf553aed8da17cd0b403dff5b1a289841fa68 Mon Sep 17 00:00:00 2001 From: Astro Date: Fri, 28 Jul 2017 23:58:03 +0200 Subject: [PATCH 0441/1020] switch Element namespaces to NamespaceSet with parent reference --- src/element.rs | 269 +++++++++++++++++++++++++++++++++++-------------- src/error.rs | 5 + src/tests.rs | 21 ++-- 3 files changed, 210 insertions(+), 85 deletions(-) diff --git a/src/element.rs b/src/element.rs index db41fd55e2d663f4845e63e7f4c7df3f074b0ceb..5aecfa86cfe52be4d73904ee200b9f2bc6c8b2a7 100644 --- a/src/element.rs +++ b/src/element.rs @@ -4,6 +4,8 @@ use std::io:: Write; use std::collections::{btree_map, BTreeMap}; use std::str; +use std::cell::RefCell; +use std::rc::Rc; use error::{Error, ErrorKind, Result}; @@ -84,9 +86,9 @@ impl Node { } } - fn write_to_inner(&self, writer: &mut W, last_namespace: &mut Option) -> Result<()>{ + fn write_to_inner(&self, writer: &mut W) -> Result<()>{ match *self { - Node::Element(ref elmt) => elmt.write_to_inner(writer, last_namespace)?, + Node::Element(ref elmt) => elmt.write_to_inner(writer)?, Node::Text(ref s) => write_escaped(writer, s)?, } @@ -94,11 +96,109 @@ impl Node { } } +#[derive(Clone, Debug, PartialEq, Eq)] +struct NamespaceSet { + parent: RefCell>>, + namespaces: BTreeMap, String>, +} + +impl Default for NamespaceSet { + fn default() -> Self { + NamespaceSet { + parent: RefCell::new(None), + namespaces: BTreeMap::new(), + } + } +} + +impl NamespaceSet { + fn get(&self, prefix: &Option) -> Option { + match self.namespaces.get(prefix) { + Some(ns) => Some(ns.clone()), + None => match *self.parent.borrow() { + None => None, + Some(ref parent) => parent.get(prefix) + }, + } + } + + fn has>(&self, prefix: &Option, wanted_ns: NS) -> bool { + match self.namespaces.get(prefix) { + Some(ns) => + ns == wanted_ns.as_ref(), + None => match *self.parent.borrow() { + None => + false, + Some(ref parent) => + parent.has(prefix, wanted_ns), + }, + } + } + + fn set_parent(&self, parent: &Element) { + let mut parent_ns = self.parent.borrow_mut(); + let new_set = ((&parent.namespaces) as &Rc).clone(); + *parent_ns = Some(new_set); + } + +} + +impl From, String>> for NamespaceSet { + fn from(namespaces: BTreeMap, String>) -> Self { + NamespaceSet { + parent: RefCell::new(None), + namespaces: namespaces, + } + } +} + +impl From> for NamespaceSet { + fn from(namespace: Option) -> Self { + match namespace { + None => Self::default(), + Some(namespace) => Self::from(namespace), + } + } +} + +impl From for NamespaceSet { + fn from(namespace: String) -> Self { + let mut namespaces = BTreeMap::new(); + namespaces.insert(None, namespace); + + NamespaceSet { + parent: RefCell::new(None), + namespaces: namespaces, + } + } +} + +impl From<(Option, String)> for NamespaceSet { + fn from(prefix_namespace: (Option, String)) -> Self { + let (prefix, namespace) = prefix_namespace; + let mut namespaces = BTreeMap::new(); + namespaces.insert(prefix, namespace); + + NamespaceSet { + parent: RefCell::new(None), + namespaces: namespaces, + } + } +} + +impl From<(String, String)> for NamespaceSet { + fn from(prefix_namespace: (String, String)) -> Self { + let (prefix, namespace) = prefix_namespace; + Self::from((Some(prefix), namespace)) + } +} + #[derive(Clone, PartialEq, Eq, Debug)] /// A struct representing a DOM Element. pub struct Element { + prefix: Option, name: String, - namespace: Option, + namespaces: Rc, attributes: BTreeMap, children: Vec, } @@ -122,10 +222,10 @@ impl FromStr for Element { } impl Element { - fn new(name: String, namespace: Option, attributes: BTreeMap, children: Vec) -> Element { + fn new>(name: String, prefix: Option, namespaces: NS, attributes: BTreeMap, children: Vec) -> Element { Element { - name: name, - namespace: namespace, + prefix, name, + namespaces: Rc::new(namespaces.into()), attributes: attributes, children: children, } @@ -145,14 +245,16 @@ impl Element { /// .build(); /// /// assert_eq!(elem.name(), "name"); - /// assert_eq!(elem.ns(), Some("namespace")); + /// assert_eq!(elem.ns(), Some("namespace".to_owned())); /// assert_eq!(elem.attr("name"), Some("value")); /// assert_eq!(elem.attr("inexistent"), None); /// assert_eq!(elem.text(), "inner"); /// ``` - pub fn builder>(name: S) -> ElementBuilder { + pub fn builder>(name: S) -> ElementBuilder { + let (prefix, name) = split_element_name(name).unwrap(); ElementBuilder { - root: Element::new(name.into(), None, BTreeMap::new(), Vec::new()), + root: Element::new(name, prefix, None, BTreeMap::new(), Vec::new()), + namespaces: Default::default(), } } @@ -172,8 +274,9 @@ impl Element { /// ``` pub fn bare>(name: S) -> Element { Element { + prefix: None, name: name.into(), - namespace: None, + namespaces: Rc::new(NamespaceSet::default()), attributes: BTreeMap::new(), children: Vec::new(), } @@ -185,9 +288,8 @@ impl Element { } /// Returns a reference to the namespace of this element, if it has one, else `None`. - pub fn ns(&self) -> Option<&str> { - self.namespace.as_ref() - .map(String::as_ref) + pub fn ns(&self) -> Option { + self.namespaces.get(&self.prefix) } /// Returns a reference to the value of the given attribute, if it exists, else `None`. @@ -256,8 +358,8 @@ impl Element { /// assert_eq!(elem.is("wrong", "wrong"), false); /// ``` pub fn is, NS: AsRef>(&self, name: N, namespace: NS) -> bool { - let ns = self.namespace.as_ref().map(String::as_ref); - self.name == name.as_ref() && ns == Some(namespace.as_ref()) + self.name == name.as_ref() && + self.namespaces.has(&self.prefix, namespace) } /// Parse a document from an `EventReader`. @@ -322,27 +424,30 @@ impl Element { /// Output a document to a `Writer`. pub fn write_to(&self, writer: &mut W) -> Result<()> { - let mut last_namespace = None; - write!(writer, "")?; - self.write_to_inner(writer, &mut last_namespace) - } - - /// Output a document to a `Writer` assuming you're already in the provided namespace - pub fn write_to_in_namespace(&self, writer: &mut W, namespace: &str) -> Result<()> { write!(writer, "")?; - self.write_to_inner(writer, &mut Some(namespace.to_owned())) + self.write_to_inner(writer) } - fn write_to_inner(&self, writer: &mut W, last_namespace: &mut Option) -> Result<()> { - write!(writer, "<")?; - write!(writer, "{}", self.name)?; - - if let Some(ref ns) = self.namespace { - if *last_namespace != self.namespace { - write!(writer, " xmlns=\"")?; - write_escaped(writer, ns)?; - write!(writer, "\"")?; - *last_namespace = Some(ns.clone()); + /// Like `write_to()` but without the `` prelude + pub fn write_to_inner(&self, writer: &mut W) -> Result<()> { + let name = match &self.prefix { + &None => self.name.to_owned(), + &Some(ref prefix) => format!("{}:{}", prefix, self.name), + }; + write!(writer, "<{}", name)?; + + for (prefix, ns) in &self.namespaces.namespaces { + match prefix { + &None => { + write!(writer, " xmlns=\"")?; + write_escaped(writer, ns)?; + write!(writer, "\"")?; + }, + &Some(ref prefix) => { + write!(writer, " xmlns:{}=\"", prefix)?; + write_escaped(writer, ns)?; + write!(writer, "\"")?; + }, } } @@ -360,10 +465,10 @@ impl Element { write!(writer, ">")?; for child in &self.children { - child.write_to_inner(writer, last_namespace)?; + child.write_to_inner(writer)?; } - write!(writer, "", self.name)?; + write!(writer, "", name)?; Ok(()) } @@ -472,30 +577,17 @@ impl Element { /// /// assert_eq!(child.name(), "new"); /// ``` - pub fn append_child(&mut self, mut child: Element) -> &mut Element { - if child.namespace.is_none() && self.namespace.is_some() { - child.namespace = self.namespace.clone(); - child.propagate_namespaces(); - } + pub fn append_child(&mut self, child: Element) -> &mut Element { + child.namespaces.set_parent(&self); + self.children.push(Node::Element(child)); if let Node::Element(ref mut cld) = *self.children.last_mut().unwrap() { cld - } - else { + } else { unreachable!() } } - fn propagate_namespaces(&mut self) { - let ns = self.namespace.clone(); - for child in self.children_mut() { - if child.namespace.is_none() { - child.namespace = ns.clone(); - child.propagate_namespaces(); - } - } - } - /// Appends a text node to an `Element`. /// /// # Examples @@ -610,29 +702,42 @@ impl Element { } } +fn split_element_name>(s: S) -> Result<(Option, String)> { + let name_parts = s.as_ref().split(':').collect::>(); + match name_parts.len() { + 2 => Ok((Some(name_parts[0].to_owned()), name_parts[1].to_owned())), + 1 => Ok((None, name_parts[0].to_owned())), + _ => bail!(ErrorKind::InvalidElement), + } +} + fn build_element(event: &BytesStart) -> Result { - let mut attributes = event.attributes() - .map(|o| { - let o = o?; - let key = str::from_utf8(o.key)?.to_owned(); - let value = str::from_utf8(o.value)?.to_owned(); - Ok((key, value)) - } - ) - .collect::>>()?; - let mut ns_key = None; - for (key, _) in &attributes { - if key == "xmlns" || key.starts_with("xmlns:") { - ns_key = Some(key.clone()); + let mut namespaces = BTreeMap::new(); + let attributes = event.attributes() + .map(|o| { + let o = o?; + let key = str::from_utf8(o.key)?.to_owned(); + let value = str::from_utf8(o.value)?.to_owned(); + Ok((key, value)) + }) + .filter(|o| { + match o { + &Ok((ref key, ref value)) if key == "xmlns" => { + namespaces.insert(None, value.to_owned()); + false + }, + &Ok((ref key, ref value)) if key.starts_with("xmlns:") => { + namespaces.insert(Some(key[6..].to_owned()), value.to_owned()); + false + }, + _ => true, } - } + }) + .collect::>>()?; - let ns = match ns_key { - None => None, - Some(key) => attributes.remove(&key), - }; - let name = str::from_utf8(event.name())?.to_owned(); - Ok(Element::new(name, ns, attributes, Vec::new())) + let (prefix, name) = split_element_name(str::from_utf8(event.name())?)?; + let element = Element::new(name, prefix, namespaces, attributes, Vec::new()); + Ok(element) } /// An iterator over references to child elements of an `Element`. @@ -742,12 +847,14 @@ impl<'a> Iterator for AttrsMut<'a> { /// A builder for `Element`s. pub struct ElementBuilder { root: Element, + namespaces: BTreeMap, String>, } impl ElementBuilder { /// Sets the namespace. pub fn ns>(mut self, namespace: S) -> ElementBuilder { - self.root.namespace = Some(namespace.into()); + self.namespaces + .insert(self.root.prefix.clone(), namespace.into()); self } @@ -768,21 +875,33 @@ impl ElementBuilder { /// Builds the `Element`. pub fn build(self) -> Element { - self.root + let mut element = self.root; + // Set namespaces + element.namespaces = Rc::new(NamespaceSet::from(self.namespaces)); + // Propagate namespaces + for node in &element.children { + if let Node::Element(ref e) = *node { + e.namespaces.set_parent(&element); + } + } + + element } } +#[cfg(test)] #[test] fn test_element_new() { use std::iter::FromIterator; let elem = Element::new( "name".to_owned() + , None , Some("namespace".to_owned()) , BTreeMap::from_iter(vec![ ("name".to_string(), "value".to_string()) ].into_iter() ) , Vec::new() ); assert_eq!(elem.name(), "name"); - assert_eq!(elem.ns(), Some("namespace")); + assert_eq!(elem.ns(), Some("namespace".to_owned())); assert_eq!(elem.attr("name"), Some("value")); assert_eq!(elem.attr("inexistent"), None); } diff --git a/src/error.rs b/src/error.rs index 0b8eb23911911c327fb13c14b356685a9373f92e..4079365fa4d028181d140a6d3954421ca2764764 100644 --- a/src/error.rs +++ b/src/error.rs @@ -26,5 +26,10 @@ error_chain! { description("The XML is invalid, an element was wrongly closed") display("the XML is invalid, an element was wrongly closed") } + /// An error which is returned when an elemet's name contains more than one colon + InvalidElement { + description("The XML element is invalid") + display("the XML element is invalid") + } } } diff --git a/src/tests.rs b/src/tests.rs index 40558eca1d9581d0f270a8ca857d72e325ef3792..e787715830823ef66285348a0f67ae63ad6a0ad8 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -80,7 +80,7 @@ fn builder_works() { .append("e") .build(); assert_eq!(elem.name(), "a"); - assert_eq!(elem.ns(), Some("b")); + assert_eq!(elem.ns(), Some("b".to_owned())); assert_eq!(elem.attr("c"), Some("d")); assert_eq!(elem.attr("x"), None); assert_eq!(elem.text(), "e"); @@ -115,6 +115,7 @@ fn namespace_propagation_works() { let grandchild = Element::bare("grandchild"); child.append_child(grandchild); root.append_child(child); + assert_eq!(root.get_child("child", "root_ns").unwrap().ns(), root.ns()); assert_eq!(root.get_child("child", "root_ns").unwrap() .get_child("grandchild", "root_ns").unwrap() @@ -154,7 +155,7 @@ fn wrongly_closed_elements_error() { fn namespace_simple() { let elem: Element = "".parse().unwrap(); assert_eq!(elem.name(), "message"); - assert_eq!(elem.ns(), Some("jabber:client")); + assert_eq!(elem.ns(), Some("jabber:client".to_owned())); } #[test] @@ -162,28 +163,28 @@ fn namespace_prefixed() { let elem: Element = "" .parse().unwrap(); assert_eq!(elem.name(), "features"); - assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams".to_owned())); } #[test] fn namespace_inherited_simple() { let elem: Element = "".parse().unwrap(); assert_eq!(elem.name(), "stream"); - assert_eq!(elem.ns(), Some("jabber:client")); + assert_eq!(elem.ns(), Some("jabber:client".to_owned())); let child = elem.children().next().unwrap(); assert_eq!(child.name(), "message"); - assert_eq!(child.ns(), Some("jabber:client")); + assert_eq!(child.ns(), Some("jabber:client".to_owned())); } #[test] fn namespace_inherited_prefixed1() { - let elem: Element = "" + let elem: Element = "" .parse().unwrap(); assert_eq!(elem.name(), "features"); - assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams".to_owned())); let child = elem.children().next().unwrap(); assert_eq!(child.name(), "message"); - assert_eq!(child.ns(), Some("jabber:client")); + assert_eq!(child.ns(), Some("jabber:client".to_owned())); } #[test] @@ -191,8 +192,8 @@ fn namespace_inherited_prefixed2() { let elem: Element = "" .parse().unwrap(); assert_eq!(elem.name(), "stream"); - assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams".to_owned())); let child = elem.children().next().unwrap(); assert_eq!(child.name(), "message"); - assert_eq!(child.ns(), Some("jabber:client")); + assert_eq!(child.ns(), Some("jabber:client".to_owned())); } From 26f03c27edbe798f0a77f34d4c352f4e4fd79afe Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 12 Aug 2017 02:13:32 +0200 Subject: [PATCH 0442/1020] optimize Element::write_to_inner() with CoW --- src/element.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/element.rs b/src/element.rs index 5aecfa86cfe52be4d73904ee200b9f2bc6c8b2a7..2c16b3c2ce9fc15bab75c8bd3a329c838ec84a8b 100644 --- a/src/element.rs +++ b/src/element.rs @@ -6,6 +6,7 @@ use std::collections::{btree_map, BTreeMap}; use std::str; use std::cell::RefCell; use std::rc::Rc; +use std::borrow::Cow; use error::{Error, ErrorKind, Result}; @@ -431,8 +432,8 @@ impl Element { /// Like `write_to()` but without the `` prelude pub fn write_to_inner(&self, writer: &mut W) -> Result<()> { let name = match &self.prefix { - &None => self.name.to_owned(), - &Some(ref prefix) => format!("{}:{}", prefix, self.name), + &None => Cow::Borrowed(&self.name), + &Some(ref prefix) => Cow::Owned(format!("{}:{}", prefix, self.name)), }; write!(writer, "<{}", name)?; From 98fd71fd3f491a49b042ab63d0a3cc30ffbea34d Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 02:26:45 +0200 Subject: [PATCH 0443/1020] NamespaceSet: rm unneeded cast --- src/element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/element.rs b/src/element.rs index 2c16b3c2ce9fc15bab75c8bd3a329c838ec84a8b..862313147239cbb157ca26e179f5deb1594750db 100644 --- a/src/element.rs +++ b/src/element.rs @@ -138,7 +138,7 @@ impl NamespaceSet { fn set_parent(&self, parent: &Element) { let mut parent_ns = self.parent.borrow_mut(); - let new_set = ((&parent.namespaces) as &Rc).clone(); + let new_set = parent.namespaces.clone(); *parent_ns = Some(new_set); } From 7d2699e08e36d28997f573ca3e76ee09cabe9669 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 02:28:44 +0200 Subject: [PATCH 0444/1020] NamespaceSet::set_parent(): don't require a full Element by passing just a reference to a NamespaceSet we could reuse this for xmlns elision in the serialization code. --- src/element.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/element.rs b/src/element.rs index 862313147239cbb157ca26e179f5deb1594750db..1a9aeb84869421b6fb4080816d9080e9e3e07456 100644 --- a/src/element.rs +++ b/src/element.rs @@ -136,9 +136,9 @@ impl NamespaceSet { } } - fn set_parent(&self, parent: &Element) { + fn set_parent(&self, parent: Rc) { let mut parent_ns = self.parent.borrow_mut(); - let new_set = parent.namespaces.clone(); + let new_set = parent; *parent_ns = Some(new_set); } @@ -579,7 +579,7 @@ impl Element { /// assert_eq!(child.name(), "new"); /// ``` pub fn append_child(&mut self, child: Element) -> &mut Element { - child.namespaces.set_parent(&self); + child.namespaces.set_parent(self.namespaces.clone()); self.children.push(Node::Element(child)); if let Node::Element(ref mut cld) = *self.children.last_mut().unwrap() { @@ -882,7 +882,7 @@ impl ElementBuilder { // Propagate namespaces for node in &element.children { if let Node::Element(ref e) = *node { - e.namespaces.set_parent(&element); + e.namespaces.set_parent(element.namespaces.clone()); } } From 0148790a0137fb480a4adbe35f3594a95354fd68 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 02:35:24 +0200 Subject: [PATCH 0445/1020] move NamespaceSet to namespaces mod --- src/element.rs | 101 +------------------------------------------- src/lib.rs | 1 + src/namespaces.rs | 105 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 99 deletions(-) create mode 100644 src/namespaces.rs diff --git a/src/element.rs b/src/element.rs index 1a9aeb84869421b6fb4080816d9080e9e3e07456..47e25d73cadce76f7881836227ac66d0a9be61ba 100644 --- a/src/element.rs +++ b/src/element.rs @@ -4,7 +4,6 @@ use std::io:: Write; use std::collections::{btree_map, BTreeMap}; use std::str; -use std::cell::RefCell; use std::rc::Rc; use std::borrow::Cow; @@ -20,6 +19,7 @@ use std::str::FromStr; use std::slice; use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; +use namespaces::NamespaceSet; /// Escape XML text pub fn write_escaped(writer: &mut W, input: &str) -> Result<()> { @@ -97,103 +97,6 @@ impl Node { } } -#[derive(Clone, Debug, PartialEq, Eq)] -struct NamespaceSet { - parent: RefCell>>, - namespaces: BTreeMap, String>, -} - -impl Default for NamespaceSet { - fn default() -> Self { - NamespaceSet { - parent: RefCell::new(None), - namespaces: BTreeMap::new(), - } - } -} - -impl NamespaceSet { - fn get(&self, prefix: &Option) -> Option { - match self.namespaces.get(prefix) { - Some(ns) => Some(ns.clone()), - None => match *self.parent.borrow() { - None => None, - Some(ref parent) => parent.get(prefix) - }, - } - } - - fn has>(&self, prefix: &Option, wanted_ns: NS) -> bool { - match self.namespaces.get(prefix) { - Some(ns) => - ns == wanted_ns.as_ref(), - None => match *self.parent.borrow() { - None => - false, - Some(ref parent) => - parent.has(prefix, wanted_ns), - }, - } - } - - fn set_parent(&self, parent: Rc) { - let mut parent_ns = self.parent.borrow_mut(); - let new_set = parent; - *parent_ns = Some(new_set); - } - -} - -impl From, String>> for NamespaceSet { - fn from(namespaces: BTreeMap, String>) -> Self { - NamespaceSet { - parent: RefCell::new(None), - namespaces: namespaces, - } - } -} - -impl From> for NamespaceSet { - fn from(namespace: Option) -> Self { - match namespace { - None => Self::default(), - Some(namespace) => Self::from(namespace), - } - } -} - -impl From for NamespaceSet { - fn from(namespace: String) -> Self { - let mut namespaces = BTreeMap::new(); - namespaces.insert(None, namespace); - - NamespaceSet { - parent: RefCell::new(None), - namespaces: namespaces, - } - } -} - -impl From<(Option, String)> for NamespaceSet { - fn from(prefix_namespace: (Option, String)) -> Self { - let (prefix, namespace) = prefix_namespace; - let mut namespaces = BTreeMap::new(); - namespaces.insert(prefix, namespace); - - NamespaceSet { - parent: RefCell::new(None), - namespaces: namespaces, - } - } -} - -impl From<(String, String)> for NamespaceSet { - fn from(prefix_namespace: (String, String)) -> Self { - let (prefix, namespace) = prefix_namespace; - Self::from((Some(prefix), namespace)) - } -} - #[derive(Clone, PartialEq, Eq, Debug)] /// A struct representing a DOM Element. pub struct Element { @@ -437,7 +340,7 @@ impl Element { }; write!(writer, "<{}", name)?; - for (prefix, ns) in &self.namespaces.namespaces { + for (prefix, ns) in self.namespaces.declared_ns() { match prefix { &None => { write!(writer, " xmlns=\"")?; diff --git a/src/lib.rs b/src/lib.rs index 2e00b9fd17be4ddb88e0ec7566d8863d223fe487..eabd0e7c6e9a6595d77e9f7e75fa406e3c37d67b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,7 @@ extern crate quick_xml; pub mod error; pub mod element; pub mod convert; +mod namespaces; #[cfg(test)] mod tests; diff --git a/src/namespaces.rs b/src/namespaces.rs new file mode 100644 index 0000000000000000000000000000000000000000..590d9cbb57b41024edfecc46b9734c7a64cb105c --- /dev/null +++ b/src/namespaces.rs @@ -0,0 +1,105 @@ +use std::collections::BTreeMap; +use std::cell::RefCell; +use std::rc::Rc; + + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct NamespaceSet { + parent: RefCell>>, + namespaces: BTreeMap, String>, +} + +impl Default for NamespaceSet { + fn default() -> Self { + NamespaceSet { + parent: RefCell::new(None), + namespaces: BTreeMap::new(), + } + } +} + +impl NamespaceSet { + pub fn declared_ns(&self) -> &BTreeMap, String> { + &self.namespaces + } + + pub fn get(&self, prefix: &Option) -> Option { + match self.namespaces.get(prefix) { + Some(ns) => Some(ns.clone()), + None => match *self.parent.borrow() { + None => None, + Some(ref parent) => parent.get(prefix) + }, + } + } + + pub fn has>(&self, prefix: &Option, wanted_ns: NS) -> bool { + match self.namespaces.get(prefix) { + Some(ns) => + ns == wanted_ns.as_ref(), + None => match *self.parent.borrow() { + None => + false, + Some(ref parent) => + parent.has(prefix, wanted_ns), + }, + } + } + + pub fn set_parent(&self, parent: Rc) { + let mut parent_ns = self.parent.borrow_mut(); + let new_set = parent; + *parent_ns = Some(new_set); + } + +} + +impl From, String>> for NamespaceSet { + fn from(namespaces: BTreeMap, String>) -> Self { + NamespaceSet { + parent: RefCell::new(None), + namespaces: namespaces, + } + } +} + +impl From> for NamespaceSet { + fn from(namespace: Option) -> Self { + match namespace { + None => Self::default(), + Some(namespace) => Self::from(namespace), + } + } +} + +impl From for NamespaceSet { + fn from(namespace: String) -> Self { + let mut namespaces = BTreeMap::new(); + namespaces.insert(None, namespace); + + NamespaceSet { + parent: RefCell::new(None), + namespaces: namespaces, + } + } +} + +impl From<(Option, String)> for NamespaceSet { + fn from(prefix_namespace: (Option, String)) -> Self { + let (prefix, namespace) = prefix_namespace; + let mut namespaces = BTreeMap::new(); + namespaces.insert(prefix, namespace); + + NamespaceSet { + parent: RefCell::new(None), + namespaces: namespaces, + } + } +} + +impl From<(String, String)> for NamespaceSet { + fn from(prefix_namespace: (String, String)) -> Self { + let (prefix, namespace) = prefix_namespace; + Self::from((Some(prefix), namespace)) + } +} From 7297dbc5fd37ac9c50e4626f82f984a79758710e Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 02:48:28 +0200 Subject: [PATCH 0446/1020] namespaces: add some tests --- src/namespaces.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/namespaces.rs b/src/namespaces.rs index 590d9cbb57b41024edfecc46b9734c7a64cb105c..767106280929bbccea5d4dbd4ce7c8b1e966b142 100644 --- a/src/namespaces.rs +++ b/src/namespaces.rs @@ -103,3 +103,48 @@ impl From<(String, String)> for NamespaceSet { Self::from((Some(prefix), namespace)) } } + +#[cfg(test)] +mod tests { + use super::*; + use std::rc::Rc; + + #[test] + fn get_has() { + let namespaces = NamespaceSet::from("foo".to_owned()); + assert_eq!(namespaces.get(&None), Some("foo".to_owned())); + assert!(namespaces.has(&None, "foo")); + } + + #[test] + fn get_has_prefixed() { + let namespaces = NamespaceSet::from(("x".to_owned(), "bar".to_owned())); + assert_eq!(namespaces.get(&Some("x".to_owned())), Some("bar".to_owned())); + assert!(namespaces.has(&Some("x".to_owned()), "bar")); + } + + #[test] + fn get_has_recursive() { + let mut parent = NamespaceSet::from("foo".to_owned()); + for _ in 0..1000 { + let namespaces = NamespaceSet::default(); + namespaces.set_parent(Rc::new(parent)); + assert_eq!(namespaces.get(&None), Some("foo".to_owned())); + assert!(namespaces.has(&None, "foo")); + parent = namespaces; + } + } + + #[test] + fn get_has_prefixed_recursive() { + let mut parent = NamespaceSet::from(("x".to_owned(), "bar".to_owned())); + for _ in 0..1000 { + let namespaces = NamespaceSet::default(); + namespaces.set_parent(Rc::new(parent)); + assert_eq!(namespaces.get(&Some("x".to_owned())), Some("bar".to_owned())); + assert!(namespaces.has(&Some("x".to_owned()), "bar")); + parent = namespaces; + } + } + +} From b14155908115014e91a8e7f4968b518740484ea3 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 17:29:38 +0200 Subject: [PATCH 0447/1020] rename namespaces mod to namespace_set --- src/element.rs | 2 +- src/lib.rs | 2 +- src/{namespaces.rs => namespace_set.rs} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{namespaces.rs => namespace_set.rs} (100%) diff --git a/src/element.rs b/src/element.rs index 47e25d73cadce76f7881836227ac66d0a9be61ba..e0aebf863abad30b8d52cc7efbf043921574e4be 100644 --- a/src/element.rs +++ b/src/element.rs @@ -19,7 +19,7 @@ use std::str::FromStr; use std::slice; use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; -use namespaces::NamespaceSet; +use namespace_set::NamespaceSet; /// Escape XML text pub fn write_escaped(writer: &mut W, input: &str) -> Result<()> { diff --git a/src/lib.rs b/src/lib.rs index eabd0e7c6e9a6595d77e9f7e75fa406e3c37d67b..b11fe0603ddf40c566a5af23675f0a5adab88b50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,7 +70,7 @@ extern crate quick_xml; pub mod error; pub mod element; pub mod convert; -mod namespaces; +mod namespace_set; #[cfg(test)] mod tests; diff --git a/src/namespaces.rs b/src/namespace_set.rs similarity index 100% rename from src/namespaces.rs rename to src/namespace_set.rs From 2398e4f6d1938bad3fe3dabe8aef20bc81c87aea Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 17:29:48 +0200 Subject: [PATCH 0448/1020] Cargo.toml: add Astro to authors --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ebb09d1893cad3bff8654b0da2c514d214fde03e..bb7a765648ae80fffe492f8f2ec23a7b92c28af8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "minidom" version = "0.5.0" -authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] +authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel ", "Astro "] description = "A small, simple DOM implementation on top of quick-xml" homepage = "https://gitlab.com/lumi/minidom-rs" repository = "https://gitlab.com/lumi/minidom-rs" From 4546db95fcdd2c05453d5defcc05104fe3777516 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 13 Aug 2017 20:14:01 +0200 Subject: [PATCH 0449/1020] bump version and update CHANGELOG.md --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5063d8fe24f6667b2020de57e223412c9482cb69..5d4e682056809ad69ad5d882c061a1387c4c06ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +Version 0.6.0, released 2017-08-13: + * Big changes + - Astro added proper support for namespace prefixes. ( https://gitlab.com/lumi/minidom-rs/merge_requests/14 ) + * Fixes + - Astro fixed a regression that caused the writer not to escape its xml output properly. ( https://gitlab.com/lumi/minidom-rs/merge_requests/15 ) Version 0.5.0, released 2017-06-10: * Big changes - Eijebong made parsing a lot faster by switching the crate from xml-rs to quick_xml. ( https://gitlab.com/lumi/minidom-rs/merge_requests/11 ) diff --git a/Cargo.toml b/Cargo.toml index bb7a765648ae80fffe492f8f2ec23a7b92c28af8..ed74ebbc7910461ae3b6aaa0f2fae5037fddf2c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.5.0" +version = "0.6.0" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel ", "Astro "] description = "A small, simple DOM implementation on top of quick-xml" homepage = "https://gitlab.com/lumi/minidom-rs" From 0743249280446bc910eb8fd9062d59f651f23865 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 14 Aug 2017 03:56:08 +0200 Subject: [PATCH 0450/1020] migrate to minidom 0.6.0 --- Cargo.toml | 2 +- src/client/auth.rs | 9 ++---- src/component/auth.rs | 3 +- src/stream_start.rs | 3 +- src/xmpp_codec.rs | 67 ++++++++++++++++++------------------------- 5 files changed, 34 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b5d8eeeaa5e0944241f805fa3b280efc73d084af..c6cd33b70c4d0891e5cae5b16e4064f7db320d65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ tokio-io = "*" bytes = "0.4.4" xml5ever = "*" tendril = "*" -minidom = "0.4.4" +minidom = "0.6.0" native-tls = "*" tokio-tls = "*" sasl = "*" diff --git a/src/client/auth.rs b/src/client/auth.rs index 05006fdd5053fe7b767a448c08275b61ba7c48f1..ef828b37a479e0b8a74bf1e21eced63eaeb7c1d3 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -106,8 +106,7 @@ impl Future for ClientAuth { ClientAuthState::WaitRecv(mut stream) => match stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.name() == "challenge" - && stanza.ns() == Some(NS_XMPP_SASL) => + if stanza.is("challenge", NS_XMPP_SASL) => { let content = try!( stanza.text() @@ -119,16 +118,14 @@ impl Future for ClientAuth { self.poll() }, Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.name() == "success" - && stanza.ns() == Some(NS_XMPP_SASL) => + if stanza.is("success", NS_XMPP_SASL) => { let start = stream.restart(); self.state = ClientAuthState::Start(start); self.poll() }, Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.name() == "failure" - && stanza.ns() == Some(NS_XMPP_SASL) => + if stanza.is("failure", NS_XMPP_SASL) => { let e = stanza.children().next() .map(|child| child.name()) diff --git a/src/component/auth.rs b/src/component/auth.rs index 773e453d68c2b26155698242d95c521068169d8a..729fd3c12652d182cf94f7ca43bdbc43720a9636 100644 --- a/src/component/auth.rs +++ b/src/component/auth.rs @@ -71,8 +71,7 @@ impl Future for ComponentAuth { ComponentAuthState::WaitRecv(mut stream) => match stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.name() == "handshake" - && stanza.ns() == Some(NS_JABBER_COMPONENT_ACCEPT) => + if stanza.is("handshake", NS_JABBER_COMPONENT_ACCEPT) => { self.state = ComponentAuthState::Invalid; Ok(Async::Ready(stream)) diff --git a/src/stream_start.rs b/src/stream_start.rs index 1d1813a383209784d6e367b0f17901527db3434a..5dda29aea435ac1add25bf0d02c65339667f899b 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -94,8 +94,7 @@ impl Future for StreamStart { StreamStartState::RecvFeatures(mut stream, stream_ns) => match stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => - if stanza.name() == "features" - && stanza.ns() == Some(NS_XMPP_STREAM) { + if stanza.is("features", NS_XMPP_STREAM) { let stream = XMPPStream::new(self.jid.clone(), stream, self.ns.clone(), stanza); (StreamStartState::Invalid, Ok(Async::Ready(stream))) } else { diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index bb725835d57949a97e1be48cf82a8ff9dfc21994..5e340a9fb5177f18c37667b4fd071bc1a64b865d 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -9,7 +9,7 @@ use std::io::{Error, ErrorKind}; use std::collections::HashMap; use std::collections::vec_deque::VecDeque; use tokio_io::codec::{Encoder, Decoder}; -use minidom::{Element, Node}; +use minidom::Element; use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind}; use xml5ever::interface::Attribute; use bytes::{BytesMut, BufMut}; @@ -278,8 +278,7 @@ impl Encoder for XMPPCodec { .map_err(|e| Error::new(ErrorKind::InvalidInput, e)) }, Packet::Stanza(stanza) => { - let root_ns = self.ns.as_ref().map(|s| s.as_ref()); - write_element(&stanza, dst, root_ns) + stanza.write_to_inner(&mut WriteBytes::new(dst)) .and_then(|_| { println!(">> {:?}", dst); Ok(()) @@ -301,42 +300,7 @@ impl Encoder for XMPPCodec { } pub fn write_text(text: &str, writer: &mut W) -> Result<(), std::fmt::Error> { - write!(writer, "{}", text) -} - -// TODO: escape everything? -pub fn write_element(el: &Element, writer: &mut W, parent_ns: Option<&str>) -> Result<(), std::fmt::Error> { - write!(writer, "<")?; - write!(writer, "{}", el.name())?; - - if let Some(ns) = el.ns() { - if parent_ns.map(|s| s.as_ref()) != el.ns() { - write!(writer, " xmlns=\"{}\"", ns)?; - } - } - - for (key, value) in el.attrs() { - write!(writer, " {}=\"{}\"", key, value)?; - } - - if ! el.nodes().any(|_| true) { - write!(writer, " />")?; - return Ok(()) - } - - write!(writer, ">")?; - - for node in el.nodes() { - match *node { - Node::Element(ref child) => - write_element(child, writer, el.ns())?, - Node::Text(ref text) => - write_text(text, writer)?, - } - } - - write!(writer, "", el.name())?; - Ok(()) + write!(writer, "{}", escape(text)) } /// Copied from `RustyXML` for now @@ -356,6 +320,31 @@ pub fn escape(input: &str) -> String { result } +/// BytesMut impl only std::fmt::Write but not std::io::Write. The +/// latter trait is required for minidom's +/// `Element::write_to_inner()`. +struct WriteBytes<'a> { + dst: &'a mut BytesMut, +} + +impl<'a> WriteBytes<'a> { + fn new(dst: &'a mut BytesMut) -> Self { + WriteBytes { dst } + } +} + +impl<'a> std::io::Write for WriteBytes<'a> { + fn write(&mut self, buf: &[u8]) -> std::result::Result { + self.dst.put_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::result::Result<(), std::io::Error> { + Ok(()) + } +} + + #[cfg(test)] mod tests { use super::*; From 275719a204f766fcfa841f5ac93f3c6913869522 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 14 Aug 2017 23:32:27 +0200 Subject: [PATCH 0451/1020] bump minidom dependency version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 164e39dcdc892808704d369cc7b74cda6328b38a..44e55843c03fcf6382ce5c2061aab8c13818e5d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ license = "LGPL-3.0+" gitlab = { repository = "lumi/jid-rs" } [dependencies] -minidom = { version = "0.4.4", optional = true } +minidom = { version = "0.6.0", optional = true } From b83905dc96c1174c362622eae12c2e18d438b3f9 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 15 Aug 2017 00:27:40 +0200 Subject: [PATCH 0452/1020] update to newer jid --- Cargo.toml | 2 +- examples/echo_bot.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c6cd33b70c4d0891e5cae5b16e4064f7db320d65..b6c9a710268fb7cb6a3065c7c60b4f4f71b18d60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ native-tls = "*" tokio-tls = "*" sasl = "*" rustc-serialize = "*" -jid = "*" +jid = { version = "0.2.3", features = ["minidom"] } domain = "0.2.1" xmpp-parsers = "0.7.0" idna = "*" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 3f6a4ab52d9e316fd117b9eaa240679b99998a63..2cb09a913c8c05f52b92bc95a7e47c5a7989e5eb 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -14,7 +14,7 @@ use futures::{Future, Stream, Sink, future}; use tokio_xmpp::Client; use minidom::Element; use xmpp_parsers::presence::{Presence, Type as PresenceType, Show as PresenceShow}; -use xmpp_parsers::message::{Message, MessageType}; +use xmpp_parsers::message::{Message, MessageType, Body}; use jid::Jid; fn main() { @@ -60,7 +60,7 @@ fn main() { match (message.from, message.bodies.get("")) { (Some(from), Some(body)) => if message.type_ != MessageType::Error { - let reply = make_reply(from, body); + let reply = make_reply(from, &body.0); send(reply); }, _ => (), @@ -91,6 +91,6 @@ fn make_presence() -> Element { // Construct a chat fn make_reply(to: Jid, body: &str) -> Element { let mut message = Message::new(Some(to)); - message.bodies.insert(String::new(), body.to_owned()); + message.bodies.insert(String::new(), Body(body.to_owned())); message.into() } From 04ec34bed8dcb040967bbe246675ea3b04904f46 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:48:28 +0100 Subject: [PATCH 0453/1020] Cargo.toml: Update minidom to 0.6.2 and jid to 0.3.0. --- Cargo.toml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a701185456fc1d667cd5a161b3563cf5fd609217..2318f0f2f0241651d03912c4a95df44d6a481629 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,8 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.4.4" +minidom = "0.6.2" +jid = { version = "0.3.0", features = ["minidom"] } base64 = "0.6.0" digest = "0.6.0" sha-1 = "0.4.0" @@ -23,10 +24,6 @@ blake2 = "0.6.1" chrono = "0.4.0" try_from = "0.2.2" -[dependencies.jid] -version = "0.2.3" -features = ["minidom"] - [features] # Build xmpp-parsers to make components instead of clients. component = [] From 1b1661fd827fa60503ff13130c7f323e85215b11 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 19 Aug 2017 00:04:18 +0100 Subject: [PATCH 0454/1020] Introduce comparing with namespace support. --- src/chatstates.rs | 3 +- src/compare_elements.rs | 118 ++++++++++++++++++++++++++++++++++++++++ src/disco.rs | 3 +- src/ibr.rs | 5 +- src/iq.rs | 7 ++- src/jingle.rs | 3 +- src/jingle_message.rs | 3 +- src/jingle_s5b.rs | 5 +- src/lib.rs | 4 ++ src/message.rs | 7 ++- src/muc/user.rs | 3 +- src/presence.rs | 3 +- src/pubsub/event.rs | 3 +- src/roster.rs | 3 +- src/rsm.rs | 3 +- src/stanza_error.rs | 3 +- 16 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 src/compare_elements.rs diff --git a/src/chatstates.rs b/src/chatstates.rs index 2cb657f6fcc2c3f885eb18516e3afd33ef9ef11e..9a63a28d23fbcf4fab8e150c46212f1dd91a1f7d 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -38,7 +38,8 @@ impl TryFrom for ChatState { type Err = Error; fn try_from(elem: Element) -> Result { - if elem.ns() != Some(ns::CHATSTATES) { + let ns = elem.ns(); + if ns.as_ref().map(|ns| ns.as_str()) != Some(ns::CHATSTATES) { return Err(Error::ParseError("This is not a chatstate element.")); } for _ in elem.children() { diff --git a/src/compare_elements.rs b/src/compare_elements.rs new file mode 100644 index 0000000000000000000000000000000000000000..d133c85c2c51698b518893458c1cada19c0007a7 --- /dev/null +++ b/src/compare_elements.rs @@ -0,0 +1,118 @@ +use minidom::{Node, Element}; + +pub trait NamespaceAwareCompare { + /// Namespace-aware comparison for tests + fn compare_to(&self, other: &Self) -> bool; +} + +impl NamespaceAwareCompare for Node { + fn compare_to(&self, other: &Self) -> bool { + match (self, other) { + (&Node::Element(ref elem1), &Node::Element(ref elem2)) => + Element::compare_to(elem1, elem2), + (&Node::Text(ref text1), &Node::Text(ref text2)) => + text1 == text2, + _ => false, + } + } +} + +impl NamespaceAwareCompare for Element { + fn compare_to(&self, other: &Self) -> bool { + if self.name() == other.name() && + self.ns() == other.ns() && + self.attrs().eq(other.attrs()) + { + let child_elems = self.children().count(); + let text_is_whitespace = self.texts() + .all(|text| text.chars().all(char::is_whitespace)); + if child_elems > 0 && text_is_whitespace { + // Ignore all the whitespace text nodes + self.children().zip(other.children()) + .all(|(node1, node2)| node1.compare_to(node2)) + } else { + // Compare with text nodes + self.nodes().zip(other.nodes()) + .all(|(node1, node2)| node1.compare_to(node2)) + } + } else { + false + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + + #[test] + fn simple() { + let elem1: Element = "x 3".parse().unwrap(); + let elem2: Element = "x 3".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_attr_name() { + let elem1: Element = "x 3".parse().unwrap(); + let elem2: Element = "x 3".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_attr_value() { + let elem1: Element = "x 3".parse().unwrap(); + let elem2: Element = "x 3".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn attr_order() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_texts() { + let elem1: Element = "foo".parse().unwrap(); + let elem2: Element = "bar".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn children() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_children() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn xmlns_wrong() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn xmlns_other_prefix() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn xmlns_dup() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } +} diff --git a/src/disco.rs b/src/disco.rs index b1d21680691242ab22fe6d05e4c2738510debcf0..e67e7973a6b73d38a2c2e37afc2ed27bbd9f96b8 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -370,6 +370,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[test] @@ -394,7 +395,7 @@ mod tests { assert_eq!(query.extensions[0].form_type, Some(String::from("example"))); let elem2 = query.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] diff --git a/src/ibr.rs b/src/ibr.rs index c51153e65f2f72bfd503d8ff3bbed07cc501fc3d..05281f333255b2d52b6ecdaf63c5284d116bb238 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -81,6 +81,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -157,7 +158,7 @@ mod tests { assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok()); assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err()); let elem2 = query.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] @@ -190,6 +191,6 @@ mod tests { panic!(); } let elem2 = query.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } } diff --git a/src/iq.rs b/src/iq.rs index 7e919985b0bdb6fb27978f008b1d37285af0b0fe..65c13a2411a04894835b873d5199a1be5192e8f1 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -297,6 +297,7 @@ impl From for Element { mod tests { use super::*; use stanza_error::{ErrorType, DefinedCondition}; + use compare_elements::NamespaceAwareCompare; #[test] fn test_require_type() { @@ -328,7 +329,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Get(element) => element == query, + IqType::Get(element) => element.compare_to(&query), _ => false }); } @@ -349,7 +350,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Set(element) => element == vcard, + IqType::Set(element) => element.compare_to(&vcard), _ => false }); } @@ -386,7 +387,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Result(Some(element)) => element == query, + IqType::Result(Some(element)) => element.compare_to(&query), _ => false, }); } diff --git a/src/jingle.rs b/src/jingle.rs index d36d0862b951794c5da49e39bc6bf649422e2fa2..d20ed056c5053f36b76b18a7e95dcf03cba47b37 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -208,7 +208,8 @@ impl TryFrom for ReasonElement { let mut reason = None; let mut text = None; for child in elem.children() { - if child.ns() != Some(ns::JINGLE) { + let child_ns = child.ns(); + if child_ns.as_ref().map(|ns| ns.as_str()) != Some(ns::JINGLE) { return Err(Error::ParseError("Reason contains a foreign element.")); } match child.name() { diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 216e1877386fa185894a9bb0936c111b833693b9..57cebe8012e097b401d199441aa76c05ea6a729d 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -47,7 +47,8 @@ impl TryFrom for JingleMI { type Err = Error; fn try_from(elem: Element) -> Result { - if elem.ns() != Some(ns::JINGLE_MESSAGE) { + let ns = elem.ns(); + if ns.as_ref().map(|ns| ns.as_str()) != Some(ns::JINGLE_MESSAGE) { return Err(Error::ParseError("This is not a Jingle message element.")); } Ok(match elem.name() { diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index d454a3d99e060473c3348fe1c55736ec679f0904..6f8bb902106ebcdbdd1dedae5f00fda9c57d53da 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -181,6 +181,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -205,7 +206,7 @@ mod tests { payload: TransportPayload::Activated(String::from("coucou")), }; let elem2: Element = transport.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] @@ -225,6 +226,6 @@ mod tests { })), }; let elem2: Element = transport.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } } diff --git a/src/lib.rs b/src/lib.rs index cc9a8c31aa76313664a271fc907b3755f7c78bae..75b092fb56de96e1e07b674d9a9afc5781496a81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,6 +171,10 @@ pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; +#[cfg(test)] +/// Namespace-aware comparison for tests +mod compare_elements; + /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core diff --git a/src/message.rs b/src/message.rs index 5dba839eacd095851d031a7e898b0e2629c4dcbb..3a4795b15c59df2a482fe7671347b5e532ecca6f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -243,6 +243,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -281,7 +282,7 @@ mod tests { assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] @@ -293,7 +294,7 @@ mod tests { let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] @@ -307,7 +308,7 @@ mod tests { assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); let elem2 = message.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] diff --git a/src/muc/user.rs b/src/muc/user.rs index 9e141ad2defe1fac3aeb41e91861477f41e22044..322525cbe2a3cadbe0a292f23f401947d8c15b3f 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -387,6 +387,7 @@ impl From for Element { mod tests { use super::*; use std::error::Error as StdError; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -418,7 +419,7 @@ mod tests { ".parse().unwrap(); let muc = MucUser { status: vec!(), items: vec!() }; let elem2 = muc.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] diff --git a/src/presence.rs b/src/presence.rs index a1f11fa690319437c1af43cbcc54385d3ddddf1c..e48e68509dbc8c6f79673527b3fee31d9ecc01d1 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -340,6 +340,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -363,7 +364,7 @@ mod tests { let elem: Element = "/>".parse().unwrap(); let presence = Presence::new(Type::Unavailable); let elem2 = presence.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 108d0ab1321cb1067b7f89374f48d8f18c4e0e3a..d055476d5b66ce4a034f1b8d6178bb6579915eda 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -272,6 +272,7 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -417,6 +418,6 @@ mod tests { } let elem2: Element = event.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } } diff --git a/src/roster.rs b/src/roster.rs index 9e2bba31db54f240188b913581e81227e2d5c8be..ae26c3d25971cf023c1930cf1e91e16535badb0e 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -137,6 +137,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_get() { @@ -206,7 +207,7 @@ mod tests { assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap()); assert_eq!(roster.items[0].groups[1], Group::from_str("B").unwrap()); let elem2 = roster.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] diff --git a/src/rsm.rs b/src/rsm.rs index fa6f6c814fdbcedf798cb7588cc8da5d37706b33..97e7abc6182852a55f2ec36f2d732e3452d6e191 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -122,6 +122,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -197,6 +198,6 @@ mod tests { max: None, }; let elem2 = set2.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 8ca9c86289aead4f6ec8d756cb26c9bafcf83784..1ed542c14239bdf9d44eccffefd2109f303a7afc 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -136,6 +136,7 @@ impl TryFrom for StanzaError { let mut other = None; for child in elem.children() { + let child_ns = child.ns(); if child.is("text", ns::XMPP_STANZAS) { for _ in child.children() { return Err(Error::ParseError("Unknown element in error text.")); @@ -144,7 +145,7 @@ impl TryFrom for StanzaError { if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); } - } else if child.ns() == Some(ns::XMPP_STANZAS) { + } else if child_ns.as_ref().map(|ns| ns.as_str()) == Some(ns::XMPP_STANZAS) { if defined_condition.is_some() { return Err(Error::ParseError("Error must not have more than one defined-condition.")); } From 8af3e50311c808fb65c0dcd051ab8a3a1678951a Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 19 Aug 2017 01:17:45 +0200 Subject: [PATCH 0455/1020] add Element::has_ns(&self, NS) --- src/element.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/element.rs b/src/element.rs index e0aebf863abad30b8d52cc7efbf043921574e4be..1cbdd2722a161a93dc1354c55ad832e8606b3acc 100644 --- a/src/element.rs +++ b/src/element.rs @@ -263,7 +263,23 @@ impl Element { /// ``` pub fn is, NS: AsRef>(&self, name: N, namespace: NS) -> bool { self.name == name.as_ref() && - self.namespaces.has(&self.prefix, namespace) + self.has_ns(namespace) + } + + /// Returns whether the element has the given namespace. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Element; + /// + /// let elem = Element::builder("name").ns("namespace").build(); + /// + /// assert_eq!(elem.has_ns("namespace"), true); + /// assert_eq!(elem.has_ns("wrong"), false); + /// ``` + pub fn has_ns>(&self, namespace: NS) -> bool { + self.namespaces.has(&self.prefix, namespace) } /// Parse a document from an `EventReader`. From 89ab8d9aca4eb79f567925f5fccd6a8967dd51e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 19 Aug 2017 00:34:44 +0100 Subject: [PATCH 0456/1020] fix a FIXME, break value is now stable --- src/element.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/element.rs b/src/element.rs index e0aebf863abad30b8d52cc7efbf043921574e4be..75faec6af8cb97433abf3f0625634cf4890fc045 100644 --- a/src/element.rs +++ b/src/element.rs @@ -269,14 +269,12 @@ impl Element { /// Parse a document from an `EventReader`. pub fn from_reader(reader: &mut EventReader) -> Result { let mut buf = Vec::new(); - let root: Element; - loop { + let root: Element = loop { let e = reader.read_event(&mut buf)?; match e { Event::Empty(ref e) | Event::Start(ref e) => { - root = build_element(e)?; // FIXME: could be break build_element(e)? when break value is stable - break; + break build_element(e)?; }, Event::Eof => { bail!(ErrorKind::EndOfDocument); From fb4e802999c2f4b84fb71dca35e83a579627b1b0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 19 Aug 2017 20:26:41 +0100 Subject: [PATCH 0457/1020] update quick-xml to its latest version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ed74ebbc7910461ae3b6aaa0f2fae5037fddf2c8..6aa5d6cbf6b878993b811cc3475f24e0267c6378 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,5 +14,5 @@ license = "MIT" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -quick-xml = "0.7.3" +quick-xml = "0.9.0" error-chain = "0.10.0" From c82f9b46f443abb60d11361c82d593c1f393dd45 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 20 Aug 2017 17:20:09 +0200 Subject: [PATCH 0458/1020] bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 44e55843c03fcf6382ce5c2061aab8c13818e5d0..07b8713aa9f9ce287bcc8c0351077d3c310d7e2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.2.3" +version = "0.2.4" authors = ["lumi ", "Emmanuel Gil Peyrot "] description = "A crate which provides a Jid struct for Jabber IDs." homepage = "https://gitlab.com/lumi/jid-rs" From 501909c32ded6121f4037b47a04222b9e96842d5 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 20 Aug 2017 17:26:45 +0200 Subject: [PATCH 0459/1020] bump version to 0.6.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6aa5d6cbf6b878993b811cc3475f24e0267c6378..a89aea2cf53fd811d7f22a53b2064cc4312432c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.6.0" +version = "0.6.1" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel ", "Astro "] description = "A small, simple DOM implementation on top of quick-xml" homepage = "https://gitlab.com/lumi/minidom-rs" From 5d0e4721f65ce7862dbaf63bedd3f1ba9d06ccaa Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 20 Aug 2017 17:35:20 +0200 Subject: [PATCH 0460/1020] update change log --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d4e682056809ad69ad5d882c061a1387c4c06ad..b749476eacc492075761dc033c1ab3fda355df60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Version 0.6.1, released 2017-08-20: + * Additions + - Astro added Element::has_ns, which checks whether an element's namespace matches the passed argument. ( https://gitlab.com/lumi/minidom-rs/merge_requests/16 ) + - Link Mauve updated the quick-xml dependency to the latest version. + * Fixes + - Because break value is now stable, Link Mauve rewrote some code marked FIXME to use it. Version 0.6.0, released 2017-08-13: * Big changes - Astro added proper support for namespace prefixes. ( https://gitlab.com/lumi/minidom-rs/merge_requests/14 ) From 30e6d7b47d8df032f5cbdd82c129c12c5aace531 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 20 Aug 2017 17:50:42 +0200 Subject: [PATCH 0461/1020] woops, had to bump to 0.3.0, not 0.2.4 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 07b8713aa9f9ce287bcc8c0351077d3c310d7e2b..49381517a923ad1c44c6c158decf8bd4fb02f17a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.2.4" +version = "0.3.0" authors = ["lumi ", "Emmanuel Gil Peyrot "] description = "A crate which provides a Jid struct for Jabber IDs." homepage = "https://gitlab.com/lumi/jid-rs" From e0124e50f6f4606e0589aaeef1eab587aeabfab8 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 20 Aug 2017 17:52:32 +0200 Subject: [PATCH 0462/1020] bump minidom dependency to 0.6.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 49381517a923ad1c44c6c158decf8bd4fb02f17a..57783567112121287eb378667f1216ce37bfdd1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ license = "LGPL-3.0+" gitlab = { repository = "lumi/jid-rs" } [dependencies] -minidom = { version = "0.6.0", optional = true } +minidom = { version = "0.6.1", optional = true } From a92e7a0ff744ca5de7076f37741be830b93bb3f0 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 24 Aug 2017 19:54:33 +0200 Subject: [PATCH 0463/1020] update deps --- Cargo.toml | 6 +++--- examples/echo_component.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b6c9a710268fb7cb6a3065c7c60b4f4f71b18d60..89e07ba02f67e12b701dcf4447ebc039e9e15e50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,14 @@ tokio-io = "*" bytes = "0.4.4" xml5ever = "*" tendril = "*" -minidom = "0.6.0" +minidom = "0.6.1" native-tls = "*" tokio-tls = "*" sasl = "*" rustc-serialize = "*" -jid = { version = "0.2.3", features = ["minidom"] } +jid = { version = "0.3.0", features = ["minidom"] } domain = "0.2.1" -xmpp-parsers = "0.7.0" +xmpp-parsers = "0.7.2" idna = "*" try_from = "0.2.2" sha-1 = "0.4.1" diff --git a/examples/echo_component.rs b/examples/echo_component.rs index 076bb170c8245a1f9e25f04ede365793eecf350f..ab385732b19b0c848a25c99f36fb0230e487ca9a 100644 --- a/examples/echo_component.rs +++ b/examples/echo_component.rs @@ -15,7 +15,7 @@ use futures::{Future, Stream, Sink, future}; use tokio_xmpp::Component; use minidom::Element; use xmpp_parsers::presence::{Presence, Type as PresenceType, Show as PresenceShow}; -use xmpp_parsers::message::{Message, MessageType}; +use xmpp_parsers::message::{Message, MessageType, Body}; use jid::Jid; fn main() { @@ -66,7 +66,7 @@ fn main() { match (message.from, message.bodies.get("")) { (Some(from), Some(body)) => if message.type_ != MessageType::Error { - let reply = make_reply(from, body); + let reply = make_reply(from, &body.0); send(reply); }, _ => (), @@ -99,6 +99,6 @@ fn make_presence(from: Jid, to: Jid) -> Element { // Construct a chat fn make_reply(to: Jid, body: &str) -> Element { let mut message = Message::new(Some(to)); - message.bodies.insert(String::new(), body.to_owned()); + message.bodies.insert(String::new(), Body(body.to_owned())); message.into() } From 58b5a84391399b31be551371e1986541df873e41 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 24 Aug 2017 20:10:58 +0200 Subject: [PATCH 0464/1020] client: stream.poll_complete() for ease of use --- examples/echo_bot.rs | 13 +++---------- examples/echo_component.rs | 13 +++---------- src/client/mod.rs | 9 +++++++++ src/component/mod.rs | 9 +++++++++ 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 2cb09a913c8c05f52b92bc95a7e47c5a7989e5eb..462f7fe870f1af721d32837b26db62f8e093a922 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -10,7 +10,7 @@ use std::env::args; use std::process::exit; use try_from::TryFrom; use tokio_core::reactor::Core; -use futures::{Future, Stream, Sink, future}; +use futures::{Stream, Sink, future}; use tokio_xmpp::Client; use minidom::Element; use xmpp_parsers::presence::{Presence, Type as PresenceType, Show as PresenceShow}; @@ -33,18 +33,11 @@ fn main() { // Make the two interfaces for sending and receiving independent // of each other so we can move one into a closure. - let (sink, stream) = client.split(); + let (mut sink, stream) = client.split(); // Wrap sink in Option so that we can take() it for the send(self) // to consume and return it back when ready. - let mut sink = Some(sink); let mut send = move |stanza| { - sink = Some( - sink.take(). - expect("sink") - .send(stanza) - .wait() - .expect("sink.send") - ); + sink.start_send(stanza).expect("start_send"); }; // Main loop, processes events let done = stream.for_each(|event| { diff --git a/examples/echo_component.rs b/examples/echo_component.rs index ab385732b19b0c848a25c99f36fb0230e487ca9a..745e0771a6e50514100f0a25db28a5878abf3062 100644 --- a/examples/echo_component.rs +++ b/examples/echo_component.rs @@ -11,7 +11,7 @@ use std::process::exit; use std::str::FromStr; use try_from::TryFrom; use tokio_core::reactor::Core; -use futures::{Future, Stream, Sink, future}; +use futures::{Stream, Sink, future}; use tokio_xmpp::Component; use minidom::Element; use xmpp_parsers::presence::{Presence, Type as PresenceType, Show as PresenceShow}; @@ -38,18 +38,11 @@ fn main() { // Make the two interfaces for sending and receiving independent // of each other so we can move one into a closure. println!("Got it: {}", component.jid); - let (sink, stream) = component.split(); + let (mut sink, stream) = component.split(); // Wrap sink in Option so that we can take() it for the send(self) // to consume and return it back when ready. - let mut sink = Some(sink); let mut send = move |stanza| { - sink = Some( - sink.take(). - expect("sink") - .send(stanza) - .wait() - .expect("sink.send") - ); + sink.start_send(stanza).expect("start_send"); }; // Main loop, processes events let done = stream.for_each(|event| { diff --git a/src/client/mod.rs b/src/client/mod.rs index e814f9b6e269f2ac978db41dcd91d769211dc19c..dc948fa5e0b3e3ef35097f2e77842c99158dbcc5 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -137,6 +137,15 @@ impl Stream for Client { } }, ClientState::Connected(mut stream) => { + // Poll sink + match stream.poll_complete() { + Ok(Async::NotReady) => (), + Ok(Async::Ready(())) => (), + Err(e) => + return Err(e.description().to_owned()), + }; + + // Poll stream match stream.poll() { Ok(Async::Ready(None)) => { // EOF diff --git a/src/component/mod.rs b/src/component/mod.rs index 2964700ffefa4268ca45cb6cea50b6faec809c18..04613c50350817bc2db133e68f09ac2f90b79e84 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -92,6 +92,15 @@ impl Stream for Component { } }, ComponentState::Connected(mut stream) => { + // Poll sink + match stream.poll_complete() { + Ok(Async::NotReady) => (), + Ok(Async::Ready(())) => (), + Err(e) => + return Err(e.description().to_owned()), + }; + + // Poll stream match stream.poll() { Ok(Async::NotReady) => { self.state = ComponentState::Connected(stream); From c853a1ff4be2c1c82e80f09205d58f5570895c96 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:32:15 +0100 Subject: [PATCH 0465/1020] compare_elements: Add missing license notice. --- src/compare_elements.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/compare_elements.rs b/src/compare_elements.rs index d133c85c2c51698b518893458c1cada19c0007a7..85c81e75e8e5168870f9739a7f014886cbea1935 100644 --- a/src/compare_elements.rs +++ b/src/compare_elements.rs @@ -1,3 +1,9 @@ +// Copyright (c) 2017 Astro +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + use minidom::{Node, Element}; pub trait NamespaceAwareCompare { From f69f567448af99c71cb76bf5e80828468740674a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:04:56 +0100 Subject: [PATCH 0466/1020] =?UTF-8?q?Use=20minidom=C2=A00.6.1=E2=80=99s=20?= =?UTF-8?q?Element::has=5Fns(),=20to=20simplify=20namespace=20comparisons.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chatstates.rs | 3 +-- src/jingle.rs | 3 +-- src/jingle_message.rs | 3 +-- src/stanza_error.rs | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index 9a63a28d23fbcf4fab8e150c46212f1dd91a1f7d..ecbecdffc4135dbfe7b50aa68aafbb57a9e5402e 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -38,8 +38,7 @@ impl TryFrom for ChatState { type Err = Error; fn try_from(elem: Element) -> Result { - let ns = elem.ns(); - if ns.as_ref().map(|ns| ns.as_str()) != Some(ns::CHATSTATES) { + if !elem.has_ns(ns::CHATSTATES) { return Err(Error::ParseError("This is not a chatstate element.")); } for _ in elem.children() { diff --git a/src/jingle.rs b/src/jingle.rs index d20ed056c5053f36b76b18a7e95dcf03cba47b37..9d142612e5b74a15606dd8d525556d4a706fe580 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -208,8 +208,7 @@ impl TryFrom for ReasonElement { let mut reason = None; let mut text = None; for child in elem.children() { - let child_ns = child.ns(); - if child_ns.as_ref().map(|ns| ns.as_str()) != Some(ns::JINGLE) { + if !child.has_ns(ns::JINGLE) { return Err(Error::ParseError("Reason contains a foreign element.")); } match child.name() { diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 57cebe8012e097b401d199441aa76c05ea6a729d..779af992d0b43f06b7f7b5104301a95966421cdd 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -47,8 +47,7 @@ impl TryFrom for JingleMI { type Err = Error; fn try_from(elem: Element) -> Result { - let ns = elem.ns(); - if ns.as_ref().map(|ns| ns.as_str()) != Some(ns::JINGLE_MESSAGE) { + if !elem.has_ns(ns::JINGLE_MESSAGE) { return Err(Error::ParseError("This is not a Jingle message element.")); } Ok(match elem.name() { diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 1ed542c14239bdf9d44eccffefd2109f303a7afc..bbc31623bcf98827e438b2470255887de90c1096 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -136,7 +136,6 @@ impl TryFrom for StanzaError { let mut other = None; for child in elem.children() { - let child_ns = child.ns(); if child.is("text", ns::XMPP_STANZAS) { for _ in child.children() { return Err(Error::ParseError("Unknown element in error text.")); @@ -145,7 +144,7 @@ impl TryFrom for StanzaError { if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); } - } else if child_ns.as_ref().map(|ns| ns.as_str()) == Some(ns::XMPP_STANZAS) { + } else if child.has_ns(ns::XMPP_STANZAS) { if defined_condition.is_some() { return Err(Error::ParseError("Error must not have more than one defined-condition.")); } From 833ef068c62f4a20c039ffbc1c7c451b8a7756ad Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:21:07 +0100 Subject: [PATCH 0467/1020] message: Fix wrong element for xml:lang. --- src/message.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/message.rs b/src/message.rs index 3a4795b15c59df2a482fe7671347b5e532ecca6f..add6d1733adbe62b43e116851867364599c13744 100644 --- a/src/message.rs +++ b/src/message.rs @@ -168,7 +168,7 @@ impl TryFrom for Message { for _ in elem.children() { return Err(Error::ParseError("Unknown child in body element.")); } - let lang = get_attr!(root, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", default); let body = Body(elem.text()); if bodies.insert(lang, body).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); @@ -177,7 +177,7 @@ impl TryFrom for Message { for _ in elem.children() { return Err(Error::ParseError("Unknown child in subject element.")); } - let lang = get_attr!(root, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", default); let subject = Subject(elem.text()); if subjects.insert(lang, subject).is_some() { return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); From f51fa15b68a1cdeb6dc027706f0cf26ccfe93419 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:22:15 +0100 Subject: [PATCH 0468/1020] jingle_ft: Add forgotten xml:lang support for . --- src/jingle_ft.rs | 70 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 85ac2dfac7e8504d4d7aee11811836a9764fd228..1931aabd535f76dff19e2bd10029ffee11cc2b1b 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -6,9 +6,12 @@ use try_from::TryFrom; +use std::collections::BTreeMap; +use std::str::FromStr; + use hashes::Hash; -use minidom::{Element, IntoElements, ElementEmitter}; +use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use chrono::{DateTime, FixedOffset}; use error::Error; @@ -35,12 +38,16 @@ impl IntoElements for Range { } } +type Lang = String; + +generate_id!(Desc); + #[derive(Debug, Clone)] pub struct File { pub date: Option>, pub media_type: Option, pub name: Option, - pub desc: Option, + pub descs: BTreeMap, pub size: Option, pub range: Option, pub hashes: Vec, @@ -98,7 +105,7 @@ impl TryFrom for Description { let mut date = None; let mut media_type = None; let mut name = None; - let mut desc = None; + let mut descs = BTreeMap::new(); let mut size = None; let mut range = None; let mut hashes = vec!(); @@ -123,10 +130,11 @@ impl TryFrom for Description { } name = Some(file_payload.text()); } else if file_payload.is("desc", ns::JINGLE_FT) { - if desc.is_some() { - return Err(Error::ParseError("File must not have more than one desc.")); + let lang = get_attr!(file_payload, "xml:lang", default); + let desc = Desc(file_payload.text()); + if descs.insert(lang, desc).is_some() { + return Err(Error::ParseError("Desc element present twice for the same xml:lang.")); } - desc = Some(file_payload.text()); } else if file_payload.is("size", ns::JINGLE_FT) { if size.is_some() { return Err(Error::ParseError("File must not have more than one size.")); @@ -163,7 +171,7 @@ impl TryFrom for Description { date: date, media_type: media_type, name: name, - desc: desc, + descs: descs, size: size, range: range, hashes: hashes, @@ -195,10 +203,11 @@ impl From for Element { .append(name) .build()); } - if let Some(desc) = file.desc { + for (lang, desc) in file.descs.into_iter() { root.append_child(Element::builder("desc") .ns(ns::JINGLE_FT) - .append(desc) + .attr("xml:lang", lang) + .append(desc.0) .build()); } if let Some(size) = file.size { @@ -250,11 +259,10 @@ mod tests { "#.parse().unwrap(); - let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); - assert_eq!(desc.file.desc, None); + assert_eq!(desc.file.descs, BTreeMap::new()); assert_eq!(desc.file.date, Some(DateTime::parse_from_rfc3339("2015-07-26T21:46:00+01:00").unwrap())); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); @@ -272,15 +280,51 @@ mod tests { "#.parse().unwrap(); - let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, None); assert_eq!(desc.file.name, None); - assert_eq!(desc.file.desc, None); + assert_eq!(desc.file.descs, BTreeMap::new()); assert_eq!(desc.file.date, None); assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()); } + + #[test] + fn test_descs() { + let elem: Element = r#" + + + text/plain + Fichier secret ! + Secret file! + w0mcJylzCn+AfvuGdqkty2+KP48= + + +"#.parse().unwrap(); + let desc = Description::try_from(elem).unwrap(); + assert_eq!(desc.file.descs.keys().cloned().collect::>(), ["en", "fr"]); + assert_eq!(desc.file.descs["en"], Desc(String::from("Secret file!"))); + assert_eq!(desc.file.descs["fr"], Desc(String::from("Fichier secret !"))); + + let elem: Element = r#" + + + text/plain + Fichier secret ! + Secret file! + w0mcJylzCn+AfvuGdqkty2+KP48= + + +"#.parse().unwrap(); + let error = Description::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Desc element present twice for the same xml:lang."); + } } From b49ea5175d73d6d2e1863d326d3469e83aeaf682 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 21 Jul 2017 00:02:11 +0100 Subject: [PATCH 0469/1020] implement IntoElements on Into instead of Element --- src/convert.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/convert.rs b/src/convert.rs index 126688b7a5f997a14b996677fe750b2878368649..1b35414c12b0495e693b4d71d0238afd6f3345de 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -51,9 +51,9 @@ impl IntoElements for Option { } } -impl IntoElements for Element { +impl IntoElements for T where T: Into { fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self); + emitter.append_child(self.into()); } } From 6b4061ad8612ffb3ace9b6a8a6230ba5df16aa87 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:44:22 +0100 Subject: [PATCH 0470/1020] update change log --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b749476eacc492075761dc033c1ab3fda355df60..369ef8ad62d81000184f2cf47b2e713ac4d80326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +Version 0.6.2, released 2017-08-27: + * Additions + - Link Mauve added an implementation of IntoElements for all Into ( https://gitlab.com/lumi/minidom-rs/merge_requests/19 ) Version 0.6.1, released 2017-08-20: * Additions - Astro added Element::has_ns, which checks whether an element's namespace matches the passed argument. ( https://gitlab.com/lumi/minidom-rs/merge_requests/16 ) From 8fbb27296923f38be0ad7bc7277ef3c1f6862057 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:40:30 +0100 Subject: [PATCH 0471/1020] bump vesion to 0.6.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a89aea2cf53fd811d7f22a53b2064cc4312432c0..b9fa61ad49721e7ad916477d1e11d507def0cab1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.6.1" +version = "0.6.2" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel ", "Astro "] description = "A small, simple DOM implementation on top of quick-xml" homepage = "https://gitlab.com/lumi/minidom-rs" From 92277ccde0c54afb526b190195b9cc45d46a3dc5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 02:33:37 +0100 Subject: [PATCH 0472/1020] jingle_ft: Add support for empty range elements. These can be used to indicate support for ranged transfers. --- src/jingle_ft.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 1931aabd535f76dff19e2bd10029ffee11cc2b1b..876aae37a76e740ab1547ea340c9eb413815820b 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -28,7 +28,7 @@ impl IntoElements for Range { fn into_elements(self, emitter: &mut ElementEmitter) { let mut elem = Element::builder("range") .ns(ns::JINGLE_FT) - .attr("offset", self.offset) + .attr("offset", if self.offset == 0 { None } else { Some(self.offset) }) .attr("length", self.length) .build(); for hash in self.hashes { From 84437ed03f1bc170e55787ea4f79bead4c9791d6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 01:46:10 +0100 Subject: [PATCH 0473/1020] ChangeLog: Add imminent vesion 0.8.0. --- ChangeLog | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index a780194d330fdc8b629141fa96167beafa2a9bd2..3f86f90e3c06d3db4fee2a1f2d365a0201e208af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,25 @@ -Version 0.7.0: +Version 0.8.0: +2017-08-27 Emmanuel Gil Peyrot + * New parsers/serialisers: + - iq:version (XEP-0092) has been added. + - Finally implement extension serialisation in disco. + * Breaking changes: + - Wrap even more elements into their own type, in jingle, + jingle_ft, roster, message. + - Split loose enums into multiple structs where it makes sense, + such as for IBB, StanzaId, Receipts. + - Split disco query and answer elements into their own struct, + to enforce more guarantees on both. + * Improvements: + - Use Vec::into_iter() more to avoid references and clones. + - Make data_forms propagate a media_element error. + - Document more of disco, roster, chatstates. + - Use the minidom feature of jid, for IntoAttributeValue. + - Add a component feature, changing the default namespace to + jabber:component:accept. + - Add support for indicating ranged transfers in jingle_ft. + +Version 0.7.1: 2017-07-24 Emmanuel Gil Peyrot * Hotfixes: - Stub out blake2 support, since the blake2 crate broke its API From 63c6df59db287ca3eed0a83470abb61ba98ba275 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Aug 2017 14:20:15 +0100 Subject: [PATCH 0474/1020] Release version 0.8.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2318f0f2f0241651d03912c4a95df44d6a481629..9db6539d876dba3d09a6c8dc03ea3a1294647394 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.7.1" +version = "0.8.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 1a46449c16d5a06783514cd14c1958e6f9021343 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 28 Aug 2017 21:00:59 +0200 Subject: [PATCH 0476/1020] Cargo.toml: update xmpp-parsers from 0.7.2 to 0.8.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 89e07ba02f67e12b701dcf4447ebc039e9e15e50..e8386b53b9da8fbce9e9dc85dc012950365ebda8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ sasl = "*" rustc-serialize = "*" jid = { version = "0.3.0", features = ["minidom"] } domain = "0.2.1" -xmpp-parsers = "0.7.2" +xmpp-parsers = "0.8.0" idna = "*" try_from = "0.2.2" sha-1 = "0.4.1" From 33018a5c09a231524647ea7529fea320b6c88bdd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 17:40:29 +0100 Subject: [PATCH 0477/1020] =?UTF-8?q?roster:=20Make=20subscription=3D"none?= =?UTF-8?q?"=20the=20default,=20see=20RFC6121=20=C2=A72.1.2.5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/roster.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/roster.rs b/src/roster.rs index ae26c3d25971cf023c1930cf1e91e16535badb0e..38b183f1d29b6c4dda1a1fb7a750c14e05612724 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -21,7 +21,7 @@ generate_attribute!(Subscription, "subscription", { To => "to", Both => "both", Remove => "remove", -}); +}, Default = None); /// Contact from the user’s contact list. #[derive(Debug, Clone, PartialEq)] @@ -33,7 +33,7 @@ pub struct Item { pub name: Option, /// Subscription status of this contact. - pub subscription: Option, + pub subscription: Subscription, /// Groups this contact is part of. pub groups: Vec, @@ -50,7 +50,7 @@ impl TryFrom for Item { let mut item = Item { jid: get_attr!(elem, "jid", required), name: get_attr!(elem, "name", optional).and_then(|name| if name == "" { None } else { Some(name) }), - subscription: get_attr!(elem, "subscription", optional), + subscription: get_attr!(elem, "subscription", default), groups: vec!(), }; for child in elem.children() { @@ -183,7 +183,7 @@ mod tests { assert_eq!(roster.items.len(), 3); assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap()); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); - assert_eq!(roster.items[0].subscription, Some(Subscription::Both)); + assert_eq!(roster.items[0].subscription, Subscription::Both); assert_eq!(roster.items[0].groups, vec!(Group::from_str("Friends").unwrap())); } @@ -245,7 +245,7 @@ mod tests { assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); assert!(roster.items[0].name.is_none()); assert!(roster.items[0].groups.is_empty()); - assert_eq!(roster.items[0].subscription, Some(Subscription::Remove)); + assert_eq!(roster.items[0].subscription, Subscription::Remove); } #[test] From 6add31b5264cfa6f9c259b2c4e16ee93ca7701ff Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 17:52:14 +0100 Subject: [PATCH 0478/1020] lib: Add check macros, to simplify code. --- src/lib.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 75b092fb56de96e1e07b674d9a9afc5781496a81..2184a843d615b49e6f3f90ebf3b7f4aeae60bc0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -125,6 +125,40 @@ macro_rules! generate_attribute { ); } +macro_rules! check_self { + ($elem:ident, $name:tt, $ns:expr) => ( + if !$elem.is($name, $ns) { + return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); + } + ); + ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( + if !$elem.is($name, $ns) { + return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); + } + ); +} + +macro_rules! check_no_children { + ($elem:ident, $name:tt) => ( + for _ in $elem.children() { + return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); + } + ); +} + +macro_rules! check_no_unknown_attributes { + ($elem:ident, $name:tt, [$($attr:tt),*]) => ( + for (_attr, _) in $elem.attrs() { + $( + if _attr == $attr { + continue; + } + )* + return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + } + ); +} + macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] From cfbfce512e7284b3e65b0ba39401a702d81e4de2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 17:53:25 +0100 Subject: [PATCH 0479/1020] lib, ping, attention: Add a macro for singleton elements. --- src/attention.rs | 29 +---------------------------- src/lib.rs | 30 ++++++++++++++++++++++++++++++ src/ping.rs | 29 +---------------------------- 3 files changed, 32 insertions(+), 56 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 7d0b1f24ff3835472c464c9915973a1629fd2780..da2e6f6494becee18aba3bea136b8e46cf9b19a8 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -12,34 +12,7 @@ use error::Error; use ns; -/// Structure representing an `` element. -#[derive(Debug, Clone)] -pub struct Attention; - -impl TryFrom for Attention { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("attention", ns::ATTENTION) { - return Err(Error::ParseError("This is not an attention element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in attention element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in attention element.")); - } - Ok(Attention) - } -} - -impl From for Element { - fn from(_: Attention) -> Element { - Element::builder("attention") - .ns(ns::ATTENTION) - .build() - } -} +generate_empty_element!(Attention, "attention", ns::ATTENTION); #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index 2184a843d615b49e6f3f90ebf3b7f4aeae60bc0d..9f3b80bb68bf3c236f70389899fc48bda272dfd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -159,6 +159,36 @@ macro_rules! check_no_unknown_attributes { ); } +macro_rules! generate_empty_element { + ($elem:ident, $name:tt, $ns:expr) => ( + // TODO: Find a better way to concatenate doc. + #[doc="Structure representing a "] + #[doc=$name] + #[doc=" element."] + #[derive(Debug, Clone)] + pub struct $elem; + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, []); + Ok($elem) + } + } + + impl From<$elem> for Element { + fn from(_: $elem) -> Element { + Element::builder("attention") + .ns($ns) + .build() + } + } + ); +} + macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/ping.rs b/src/ping.rs index c97fa5026ac5595ac3db83332afa492746a3ea3b..d4044f477d8cd534dbe7ce68356cbe81e010126a 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -13,34 +13,7 @@ use error::Error; use ns; -/// Structure representing a `` element. -#[derive(Debug, Clone)] -pub struct Ping; - -impl TryFrom for Ping { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("ping", ns::PING) { - return Err(Error::ParseError("This is not a ping element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in ping element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in ping element.")); - } - Ok(Ping) - } -} - -impl From for Element { - fn from(_: Ping) -> Element { - Element::builder("ping") - .ns(ns::PING) - .build() - } -} +generate_empty_element!(Ping, "ping", ns::PING); #[cfg(test)] mod tests { From 66fb8fea74894d4f33498b11ffd219e1d36b6e5e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 18:00:15 +0100 Subject: [PATCH 0480/1020] message_correct: Use the new helper macros to simplify parsing. --- src/message_correct.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/message_correct.rs b/src/message_correct.rs index bb5c2b2e25cc037aa1305f52ac11bf47067b6200..d8200ae19b07401e21a548a6e0b0018e7e4efa1e 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -21,17 +21,9 @@ impl TryFrom for Replace { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("replace", ns::MESSAGE_CORRECT) { - return Err(Error::ParseError("This is not a replace element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in replace element.")); - } - for (attr, _) in elem.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in replace element.")); - } - } + check_self!(elem, "replace", ns::MESSAGE_CORRECT); + check_no_children!(elem, "replace"); + check_no_unknown_attributes!(elem, "replace", ["id"]); let id = get_attr!(elem, "id", required); Ok(Replace { id }) } From a2b603333632785b86d868e59fef4ea8131563be Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 18:04:27 +0100 Subject: [PATCH 0481/1020] caps: Use the new helper macros to simplify parsing. --- src/caps.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index a2bdd11bed7d92720a3e383980c4c7f2a7ea3ddd..2b986795daa7ab7b9c243fdd6e1a0654353d27bb 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -33,16 +33,12 @@ impl TryFrom for Caps { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("c", ns::CAPS) { - return Err(Error::ParseError("This is not a caps element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in caps element.")); - } - let hash = get_attr!(elem, "hash", required); + check_self!(elem, "c", ns::CAPS, "caps"); + check_no_children!(elem, "caps"); + check_no_unknown_attributes!(elem, "caps", ["hash", "ver", "ext", "node"]); let ver: String = get_attr!(elem, "ver", required); let hash = Hash { - algo: hash, + algo: get_attr!(elem, "hash", required), hash: base64::decode(&ver)?, }; Ok(Caps { From ee243c47207c95199f80ff5232b61651b2c77951 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 18:09:58 +0100 Subject: [PATCH 0482/1020] chatstates: Use the new helper macros to simplify parsing. --- src/chatstates.rs | 12 +++--------- src/lib.rs | 8 ++++++++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index ecbecdffc4135dbfe7b50aa68aafbb57a9e5402e..995cd11e0a11ccb42f63f79689b0e85634c33c69 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -38,15 +38,9 @@ impl TryFrom for ChatState { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.has_ns(ns::CHATSTATES) { - return Err(Error::ParseError("This is not a chatstate element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in chatstate element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in chatstate element.")); - } + check_ns_only!(elem, "chatstate", ns::CHATSTATES); + check_no_children!(elem, "chatstate"); + check_no_unknown_attributes!(elem, "chatstate", []); Ok(match elem.name() { "active" => ChatState::Active, "composing" => ChatState::Composing, diff --git a/src/lib.rs b/src/lib.rs index 9f3b80bb68bf3c236f70389899fc48bda272dfd2..5b52da0564111cd31c848590aa8ab549ab98ee6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,6 +138,14 @@ macro_rules! check_self { ); } +macro_rules! check_ns_only { + ($elem:ident, $name:tt, $ns:expr) => ( + if !$elem.has_ns($ns) { + return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); + } + ); +} + macro_rules! check_no_children { ($elem:ident, $name:tt) => ( for _ in $elem.children() { From a2c752966881d46a904222ab35d3a551fc7492d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 18:10:52 +0100 Subject: [PATCH 0483/1020] eme: Use the new helper macros to simplify parsing. --- src/eme.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index 288431b9c975b95ebc180c7bfeb74c43da63d72a..ad4b437f8098a5cc18551277f7b03ce4eebf4210 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -27,12 +27,9 @@ impl TryFrom for ExplicitMessageEncryption { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("encryption", ns::EME) { - return Err(Error::ParseError("This is not an encryption element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in encryption element.")); - } + check_self!(elem, "encryption", ns::EME); + check_no_children!(elem, "encryption"); + check_no_unknown_attributes!(elem, "encryption", ["namespace", "name"]); Ok(ExplicitMessageEncryption { namespace: get_attr!(elem, "namespace", required), name: get_attr!(elem, "name", optional), @@ -75,7 +72,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "This is not an encryption element."); + assert_eq!(message, "This is not a encryption element."); } #[test] From 9f27f200ca39d79520ae42aa7db5150396584830 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 18:26:05 +0100 Subject: [PATCH 0484/1020] data_forms: Use the new helper macros to simplify parsing. --- src/data_forms.rs | 109 ++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 58 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 15135ef3df2edfde96108bf0015bce6bf59e46ed..e3a68f4680079b7069a89befb8de7ed4b16fca7b 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -14,25 +14,35 @@ use ns; use media_element::MediaElement; -generate_attribute!(FieldType, "type", { - Boolean => "boolean", - Fixed => "fixed", - Hidden => "hidden", - JidMulti => "jid-multi", - JidSingle => "jid-single", - ListMulti => "list-multi", - ListSingle => "list-single", - TextMulti => "text-multi", - TextPrivate => "text-private", - TextSingle => "text-single", -}, Default = TextSingle); - #[derive(Debug, Clone)] pub struct Option_ { pub label: Option, pub value: String, } +impl TryFrom for Option_ { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "option", ns::DATA_FORMS); + check_no_unknown_attributes!(elem, "option", ["label"]); + let mut value = None; + for child in elem.children() { + if !child.is("value", ns::DATA_FORMS) { + return Err(Error::ParseError("Non-value element in option element")); + } + if value.is_some() { + return Err(Error::ParseError("More than one value element in option element")); + } + value = Some(child.text()); + } + Ok(Option_ { + label: get_attr!(elem, "label", optional), + value: value.ok_or(Error::ParseError("No value element in option element"))?, + }) + } +} + impl From for Element { fn from(option: Option_) -> Element { Element::builder("option") @@ -46,6 +56,19 @@ impl From for Element { } } +generate_attribute!(FieldType, "type", { + Boolean => "boolean", + Fixed => "fixed", + Hidden => "hidden", + JidMulti => "jid-multi", + JidSingle => "jid-single", + ListMulti => "list-multi", + ListSingle => "list-single", + TextMulti => "text-multi", + TextPrivate => "text-private", + TextSingle => "text-single", +}, Default = TextSingle); + #[derive(Debug, Clone)] pub struct Field { pub var: String, @@ -68,6 +91,8 @@ impl TryFrom for Field { type Err = Error; fn try_from(elem: Element) -> Result { + check_self!(elem, "field", ns::DATA_FORMS); + check_no_unknown_attributes!(elem, "field", ["label", "type", "var"]); let mut field = Field { var: get_attr!(elem, "var", required), type_: get_attr!(elem, "type", default), @@ -79,50 +104,27 @@ impl TryFrom for Field { }; for element in elem.children() { if element.is("value", ns::DATA_FORMS) { - for _ in element.children() { - return Err(Error::ParseError("Value element must not have any child.")); - } - for _ in element.attrs() { - return Err(Error::ParseError("Value element must not have any attribute.")); - } + check_no_children!(element, "value"); + check_no_unknown_attributes!(element, "value", []); field.values.push(element.text()); } else if element.is("required", ns::DATA_FORMS) { if field.required { return Err(Error::ParseError("More than one required element.")); } - for _ in element.children() { - return Err(Error::ParseError("Required element must not have any child.")); - } - for _ in element.attrs() { - return Err(Error::ParseError("Required element must not have any attribute.")); - } + check_no_children!(element, "required"); + check_no_unknown_attributes!(element, "required", []); field.required = true; } else if element.is("option", ns::DATA_FORMS) { if !field.is_list() { return Err(Error::ParseError("Option element found in non-list field.")); } - let label = get_attr!(element, "label", optional); - let mut value = None; - for child2 in element.children() { - if child2.is("value", ns::DATA_FORMS) { - if value.is_some() { - return Err(Error::ParseError("More than one value element in option element")); - } - value = Some(child2.text()); - } else { - return Err(Error::ParseError("Non-value element in option element")); - } - } - let value = value.ok_or(Error::ParseError("No value element in option element"))?; - field.options.push(Option_ { - label: label, - value: value, - }); + let option = Option_::try_from(element.clone())?; + field.options.push(option); } else if element.is("media", ns::MEDIA_ELEMENT) { let media_element = MediaElement::try_from(element.clone())?; field.media.push(media_element); } else { - return Err(Error::ParseError("Field child isn’t a value or media element.")); + return Err(Error::ParseError("Field child isn’t a value, option or media element.")); } } Ok(field) @@ -166,9 +168,8 @@ impl TryFrom for DataForm { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("x", ns::DATA_FORMS) { - return Err(Error::ParseError("This is not a data form element.")); - } + check_self!(elem, "x", ns::DATA_FORMS); + check_no_unknown_attributes!(elem, "x", ["type"]); let type_ = get_attr!(elem, "type", required); let mut form = DataForm { type_: type_, @@ -182,23 +183,15 @@ impl TryFrom for DataForm { if form.title.is_some() { return Err(Error::ParseError("More than one title in form element.")); } - for _ in child.children() { - return Err(Error::ParseError("Title element must not have any child.")); - } - for _ in child.attrs() { - return Err(Error::ParseError("Title element must not have any attribute.")); - } + check_no_children!(child, "title"); + check_no_unknown_attributes!(child, "title", []); form.title = Some(child.text()); } else if child.is("instructions", ns::DATA_FORMS) { if form.instructions.is_some() { return Err(Error::ParseError("More than one instructions in form element.")); } - for _ in child.children() { - return Err(Error::ParseError("instructions element must not have any child.")); - } - for _ in child.attrs() { - return Err(Error::ParseError("instructions element must not have any attribute.")); - } + check_no_children!(child, "instructions"); + check_no_unknown_attributes!(child, "instructions", []); form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { let field = Field::try_from(child.clone())?; From 5f6f6a5e910c55b7fd315862547e277149f15198 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 19:00:42 +0100 Subject: [PATCH 0485/1020] disco: Use the new helper macros to simplify parsing. --- src/disco.rs | 77 +++++++++++++--------------------------------------- 1 file changed, 19 insertions(+), 58 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index e67e7973a6b73d38a2c2e37afc2ed27bbd9f96b8..3e9d8ce1fe2a0313d1d57febf1a803788cf92168 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -30,17 +30,9 @@ impl TryFrom for DiscoInfoQuery { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::DISCO_INFO) { - return Err(Error::ParseError("This is not a disco#info element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in disco#info.")); - } - for (attr, _) in elem.attrs() { - if attr != "node" { - return Err(Error::ParseError("Unknown attribute in disco#info.")); - } - } + check_self!(elem, "query", ns::DISCO_INFO); + check_no_children!(elem, "query"); + check_no_unknown_attributes!(elem, "query", ["node"]); Ok(DiscoInfoQuery { node: get_attr!(elem, "node", optional), }) @@ -67,17 +59,9 @@ impl TryFrom for Feature { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("feature", ns::DISCO_INFO) { - return Err(Error::ParseError("This is not a disco#info feature element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in disco#info feature element.")); - } - for (attr, _) in elem.attrs() { - if attr != "var" { - return Err(Error::ParseError("Unknown attribute in disco#info feature element.")); - } - } + check_self!(elem, "feature", ns::DISCO_INFO, "disco#info feature"); + check_no_children!(elem, "disco#info feature"); + check_no_unknown_attributes!(elem, "disco#info feature", ["var"]); Ok(Feature { var: get_attr!(elem, "var", required) }) @@ -113,9 +97,9 @@ impl TryFrom for Identity { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("identity", ns::DISCO_INFO) { - return Err(Error::ParseError("This is not a disco#info identity element.")); - } + check_self!(elem, "identity", ns::DISCO_INFO, "disco#info identity"); + check_no_children!(elem, "disco#info identity"); + check_no_unknown_attributes!(elem, "disco#info identity", ["category", "type", "xml:lang", "name"]); let category = get_attr!(elem, "category", required); if category == "" { @@ -171,9 +155,8 @@ impl TryFrom for DiscoInfoResult { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::DISCO_INFO) { - return Err(Error::ParseError("This is not a disco#info element.")); - } + check_self!(elem, "query", ns::DISCO_INFO, "disco#info result"); + check_no_unknown_attributes!(elem, "disco#info result", ["node"]); let mut result = DiscoInfoResult { node: get_attr!(elem, "node", optional), @@ -243,17 +226,9 @@ impl TryFrom for DiscoItemsQuery { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::DISCO_ITEMS) { - return Err(Error::ParseError("This is not a disco#items element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in disco#items.")); - } - for (attr, _) in elem.attrs() { - if attr != "node" { - return Err(Error::ParseError("Unknown attribute in disco#items.")); - } - } + check_self!(elem, "query", ns::DISCO_ITEMS, "disco#items query"); + check_no_children!(elem, "disco#items query"); + check_no_unknown_attributes!(elem, "disco#items query", ["node"]); Ok(DiscoItemsQuery { node: get_attr!(elem, "node", optional), }) @@ -284,17 +259,9 @@ impl TryFrom for Item { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("item", ns::DISCO_ITEMS) { - return Err(Error::ParseError("This is not an item element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in item element.")); - } - for (attr, _) in elem.attrs() { - if attr != "jid" && attr != "node" && attr != "name" { - return Err(Error::ParseError("Unknown attribute in item element.")); - } - } + check_self!(elem, "item", ns::DISCO_ITEMS); + check_no_children!(elem, "item"); + check_no_unknown_attributes!(elem, "item", ["jid", "node", "name"]); Ok(Item { jid: get_attr!(elem, "jid", required), node: get_attr!(elem, "node", optional), @@ -332,14 +299,8 @@ impl TryFrom for DiscoItemsResult { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::DISCO_ITEMS) { - return Err(Error::ParseError("This is not a disco#items element.")); - } - for (attr, _) in elem.attrs() { - if attr != "node" { - return Err(Error::ParseError("Unknown attribute in disco#items.")); - } - } + check_self!(elem, "query", ns::DISCO_ITEMS, "disco#items query"); + check_no_unknown_attributes!(elem, "disco#items query", ["node"]); let mut items: Vec = vec!(); for child in elem.children() { From e1477f146b463a6729c398d3d5a496c4d5b1e0cf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 19:27:29 +0100 Subject: [PATCH 0486/1020] pubsub::event: Use the new helper macros to simplify parsing. --- src/pubsub/event.rs | 59 ++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index d055476d5b66ce4a034f1b8d6178bb6579915eda..12f1f774973e0ebc170140c57d832ddee21f594c 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -25,6 +25,26 @@ pub struct Item { publisher: Option, } +impl TryFrom for Item { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "item", ns::PUBSUB_EVENT); + check_no_unknown_attributes!(elem, "item", ["id", "node", "publisher"]); + let mut payloads = elem.children().cloned().collect::>(); + let payload = payloads.pop(); + if !payloads.is_empty() { + return Err(Error::ParseError("More than a single payload in item element.")); + } + Ok(Item { + payload, + id: get_attr!(elem, "id", optional), + node: get_attr!(elem, "node", optional), + publisher: get_attr!(elem, "publisher", optional), + }) + } +} + impl From for Element { fn from(item: Item) -> Element { Element::builder("item") @@ -92,32 +112,15 @@ fn parse_items(elem: Element, node: String) -> Result { Some(false) => (), Some(true) => return Err(Error::ParseError("Mix of item and retract in items element.")), } - let mut payloads = child.children().cloned().collect::>(); - let payload = payloads.pop(); - if !payloads.is_empty() { - return Err(Error::ParseError("More than a single payload in item element.")); - } - let item = Item { - payload, - id: get_attr!(child, "id", optional), - node: get_attr!(child, "node", optional), - publisher: get_attr!(child, "publisher", optional), - }; - items.push(item); + items.push(Item::try_from(child.clone())?); } else if child.is("retract", ns::PUBSUB_EVENT) { match is_retract { None => is_retract = Some(true), Some(true) => (), Some(false) => return Err(Error::ParseError("Mix of item and retract in items element.")), } - for _ in child.children() { - return Err(Error::ParseError("Unknown child in retract element.")); - } - for (attr, _) in child.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in retract element.")); - } - } + check_no_children!(child, "retract"); + check_no_unknown_attributes!(child, "retract", ["id"]); let id = get_attr!(child, "id", required); retracts.push(id); } else { @@ -135,21 +138,11 @@ impl TryFrom for PubSubEvent { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("event", ns::PUBSUB_EVENT) { - return Err(Error::ParseError("This is not an event element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in event element.")); - } + check_self!(elem, "event", ns::PUBSUB_EVENT); + check_no_unknown_attributes!(elem, "event", []); + let mut payload = None; for child in elem.children() { - /* - for (attr, _) in child.attrs() { - if attr != "node" { - return Err(Error::ParseError("Unknown attribute in items element.")); - } - } - */ let node = get_attr!(child, "node", required); if child.is("configuration", ns::PUBSUB_EVENT) { let mut payloads = child.children().cloned().collect::>(); From a8cfc8e62e230557bba65a500c50475c7adc8c81 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 10 Oct 2017 19:45:08 +0100 Subject: [PATCH 0487/1020] pubsub::event: Add more type safety. --- src/pubsub/event.rs | 61 ++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 12f1f774973e0ebc170140c57d832ddee21f594c..9f8b1e4c199020f70cbdc6ace3cfdde620fc137c 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -17,11 +17,15 @@ use ns; use data_forms::DataForm; +generate_id!(NodeName); +generate_id!(ItemId); +generate_id!(SubscriptionId); + #[derive(Debug, Clone)] pub struct Item { payload: Option, - id: Option, - node: Option, + node: Option, + id: Option, publisher: Option, } @@ -71,37 +75,37 @@ pub enum PubSubEvent { }, */ Configuration { - node: String, + node: NodeName, form: Option, }, Delete { - node: String, + node: NodeName, redirect: Option, }, EmptyItems { - node: String, + node: NodeName, }, PublishedItems { - node: String, + node: NodeName, items: Vec, }, RetractedItems { - node: String, - items: Vec, + node: NodeName, + items: Vec, }, Purge { - node: String, + node: NodeName, }, Subscription { - node: String, + node: NodeName, expiry: Option>, jid: Option, - subid: Option, + subid: Option, subscription: Option, }, } -fn parse_items(elem: Element, node: String) -> Result { +fn parse_items(elem: Element, node: NodeName) -> Result { let mut is_retract = None; let mut items = vec!(); let mut retracts = vec!(); @@ -234,7 +238,12 @@ impl From for Element { Element::builder("items") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append(items) + .append(items.into_iter().map(|id| { + Element::builder("retract") + .ns(ns::PUBSUB_EVENT) + .attr("id", id) + .build() + }).collect::>()) .build() }, PubSubEvent::Purge { node } => { @@ -272,7 +281,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let event = PubSubEvent::try_from(elem).unwrap(); match event { - PubSubEvent::EmptyItems { node } => assert_eq!(node, String::from("coucou")), + PubSubEvent::EmptyItems { node } => assert_eq!(node, NodeName(String::from("coucou"))), _ => panic!(), } } @@ -283,9 +292,9 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::PublishedItems { node, items } => { - assert_eq!(node, String::from("coucou")); - assert_eq!(items[0].id, Some(String::from("test"))); - assert_eq!(items[0].node, Some(String::from("huh?"))); + assert_eq!(node, NodeName(String::from("coucou"))); + assert_eq!(items[0].id, Some(ItemId(String::from("test")))); + assert_eq!(items[0].node, Some(NodeName(String::from("huh?")))); assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap())); assert_eq!(items[0].payload, None); }, @@ -299,7 +308,7 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::PublishedItems { node, items } => { - assert_eq!(node, String::from("something")); + assert_eq!(node, NodeName(String::from("something"))); assert_eq!(items[0].id, None); assert_eq!(items[0].node, None); assert_eq!(items[0].publisher, None); @@ -318,9 +327,9 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::RetractedItems { node, items } => { - assert_eq!(node, String::from("something")); - assert_eq!(items[0], String::from("coucou")); - assert_eq!(items[1], String::from("test")); + assert_eq!(node, NodeName(String::from("something"))); + assert_eq!(items[0], ItemId(String::from("coucou"))); + assert_eq!(items[1], ItemId(String::from("test"))); }, _ => panic!(), } @@ -332,7 +341,7 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::Delete { node, redirect } => { - assert_eq!(node, String::from("coucou")); + assert_eq!(node, NodeName(String::from("coucou"))); assert_eq!(redirect, Some(String::from("hello"))); }, _ => panic!(), @@ -345,7 +354,7 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::Purge { node } => { - assert_eq!(node, String::from("coucou")); + assert_eq!(node, NodeName(String::from("coucou"))); }, _ => panic!(), } @@ -357,7 +366,7 @@ mod tests { let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::Configuration { node, form: _ } => { - assert_eq!(node, String::from("coucou")); + assert_eq!(node, NodeName(String::from("coucou"))); //assert_eq!(form.type_, Result_); }, _ => panic!(), @@ -401,8 +410,8 @@ mod tests { let event = PubSubEvent::try_from(elem.clone()).unwrap(); match event.clone() { PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => { - assert_eq!(node, String::from("princely_musings")); - assert_eq!(subid, Some(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3"))); + assert_eq!(node, NodeName(String::from("princely_musings"))); + assert_eq!(subid, Some(SubscriptionId(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3")))); assert_eq!(subscription, Some(Subscription::Subscribed)); assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap())); assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap())); From 1892e1ca042b77aafdb96149e15cd614f1656512 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 29 Oct 2017 00:36:36 +0100 Subject: [PATCH 0488/1020] disco: Check for children ordering in disco#info. --- src/disco.rs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 3e9d8ce1fe2a0313d1d57febf1a803788cf92168..2c3189f06f42d542537d80024c1923d33aacfe78 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -164,15 +164,26 @@ impl TryFrom for DiscoInfoResult { features: vec!(), extensions: vec!(), }; + let mut parsing_identities_done = false; + let mut parsing_features_done = false; for child in elem.children() { - if child.is("feature", ns::DISCO_INFO) { - let feature = Feature::try_from(child.clone())?; - result.features.push(feature); - } else if child.is("identity", ns::DISCO_INFO) { + if child.is("identity", ns::DISCO_INFO) { + if parsing_identities_done { + return Err(Error::ParseError("Identity found after features or data forms in disco#info.")); + } let identity = Identity::try_from(child.clone())?; result.identities.push(identity); + } else if child.is("feature", ns::DISCO_INFO) { + parsing_identities_done = true; + if parsing_features_done { + return Err(Error::ParseError("Feature found after data forms in disco#info.")); + } + let feature = Feature::try_from(child.clone())?; + result.features.push(feature); } else if child.is("x", ns::DATA_FORMS) { + parsing_identities_done = true; + parsing_features_done = true; let data_form = DataForm::try_from(child.clone())?; if data_form.type_ != DataFormType::Result_ { return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")); @@ -441,6 +452,22 @@ mod tests { _ => panic!(), }; assert_eq!(message, "disco#info feature not present in disco#info."); + + let elem: Element = "".parse().unwrap(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Identity found after features or data forms in disco#info."); + + let elem: Element = "coucou".parse().unwrap(); + let error = DiscoInfoResult::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Feature found after data forms in disco#info."); } #[test] From f85b451fcf5dbcab6bd4c53b7ca93344a69f5e80 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 15:47:38 +0000 Subject: [PATCH 0489/1020] Add a new check_no_attributes macro, to avoid the empty list. --- src/chatstates.rs | 2 +- src/data_forms.rs | 8 ++++---- src/lib.rs | 8 +++++++- src/pubsub/event.rs | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index 995cd11e0a11ccb42f63f79689b0e85634c33c69..e226f21d3bb72cc620bb3b7485fad57d89602986 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -40,7 +40,7 @@ impl TryFrom for ChatState { fn try_from(elem: Element) -> Result { check_ns_only!(elem, "chatstate", ns::CHATSTATES); check_no_children!(elem, "chatstate"); - check_no_unknown_attributes!(elem, "chatstate", []); + check_no_attributes!(elem, "chatstate"); Ok(match elem.name() { "active" => ChatState::Active, "composing" => ChatState::Composing, diff --git a/src/data_forms.rs b/src/data_forms.rs index e3a68f4680079b7069a89befb8de7ed4b16fca7b..a54be9c2e7a3139915be92b22140c9b2eb58672e 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -105,14 +105,14 @@ impl TryFrom for Field { for element in elem.children() { if element.is("value", ns::DATA_FORMS) { check_no_children!(element, "value"); - check_no_unknown_attributes!(element, "value", []); + check_no_attributes!(element, "value"); field.values.push(element.text()); } else if element.is("required", ns::DATA_FORMS) { if field.required { return Err(Error::ParseError("More than one required element.")); } check_no_children!(element, "required"); - check_no_unknown_attributes!(element, "required", []); + check_no_attributes!(element, "required"); field.required = true; } else if element.is("option", ns::DATA_FORMS) { if !field.is_list() { @@ -184,14 +184,14 @@ impl TryFrom for DataForm { return Err(Error::ParseError("More than one title in form element.")); } check_no_children!(child, "title"); - check_no_unknown_attributes!(child, "title", []); + check_no_attributes!(child, "title"); form.title = Some(child.text()); } else if child.is("instructions", ns::DATA_FORMS) { if form.instructions.is_some() { return Err(Error::ParseError("More than one instructions in form element.")); } check_no_children!(child, "instructions"); - check_no_unknown_attributes!(child, "instructions", []); + check_no_attributes!(child, "instructions"); form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { let field = Field::try_from(child.clone())?; diff --git a/src/lib.rs b/src/lib.rs index 5b52da0564111cd31c848590aa8ab549ab98ee6a..fee722cc0a00df8bbfed51591176c8c8f7062646 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,6 +154,12 @@ macro_rules! check_no_children { ); } +macro_rules! check_no_attributes { + ($elem:ident, $name:tt) => ( + check_no_unknown_attributes!($elem, $name, []); + ); +} + macro_rules! check_no_unknown_attributes { ($elem:ident, $name:tt, [$($attr:tt),*]) => ( for (_attr, _) in $elem.attrs() { @@ -182,7 +188,7 @@ macro_rules! generate_empty_element { fn try_from(elem: Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); - check_no_unknown_attributes!(elem, $name, []); + check_no_attributes!(elem, $name); Ok($elem) } } diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 9f8b1e4c199020f70cbdc6ace3cfdde620fc137c..74b59ae6cdf32baf92705de5a9f5796d5fd3f511 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -143,7 +143,7 @@ impl TryFrom for PubSubEvent { fn try_from(elem: Element) -> Result { check_self!(elem, "event", ns::PUBSUB_EVENT); - check_no_unknown_attributes!(elem, "event", []); + check_no_attributes!(elem, "event"); let mut payload = None; for child in elem.children() { From b7b3340c9f8e58f774b3cbb49d6e42d27c7a71bb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 15:48:11 +0000 Subject: [PATCH 0490/1020] Add a blocking command parser (XEP-0191). --- src/blocking.rs | 165 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 5 ++ 3 files changed, 173 insertions(+) create mode 100644 src/blocking.rs diff --git a/src/blocking.rs b/src/blocking.rs new file mode 100644 index 0000000000000000000000000000000000000000..084844e67b732d4bd9c41f530ef7eab76ee1f574 --- /dev/null +++ b/src/blocking.rs @@ -0,0 +1,165 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; + +use jid::Jid; +use minidom::Element; + +use error::Error; + +use ns; + +generate_empty_element!(BlocklistRequest, "blocklist", ns::BLOCKING); + +fn get_children_items(elem: Element) -> Result, Error> { + let mut items = vec!(); + for child in elem.children() { + check_self!(child, "item", ns::BLOCKING); + check_no_unknown_attributes!(child, "item", ["jid"]); + check_no_children!(child, "item"); + items.push(get_attr!(child, "jid", required)); + } + Ok(items) +} + +macro_rules! generate_blocking_element { + ($elem:ident, $name:tt) => ( + #[derive(Debug, Clone)] + pub struct $elem { + pub items: Vec, + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, ns::BLOCKING); + check_no_attributes!(elem, $name); + Ok($elem { + items: get_children_items(elem)?, + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns(ns::BLOCKING) + .append(elem.items.into_iter().map(|jid| { + Element::builder("item") + .ns(ns::BLOCKING) + .attr("jid", jid) + .build() + }).collect::>()) + .build() + } + } + ); +} + +generate_blocking_element!(BlocklistResult, "blocklist"); +generate_blocking_element!(Block, "block"); +generate_blocking_element!(Unblock, "unblock"); + +generate_empty_element!(Blocked, "blocked", ns::BLOCKING_ERRORS); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let request_elem = elem.clone(); + BlocklistRequest::try_from(request_elem).unwrap(); + + let result_elem = elem.clone(); + let result = BlocklistResult::try_from(result_elem).unwrap(); + assert_eq!(result.items, vec!()); + + let elem: Element = "".parse().unwrap(); + let block = Block::try_from(elem).unwrap(); + assert_eq!(block.items, vec!()); + + let elem: Element = "".parse().unwrap(); + let unblock = Unblock::try_from(elem).unwrap(); + assert_eq!(unblock.items, vec!()); + } + + #[test] + fn test_items() { + let elem: Element = "".parse().unwrap(); + let two_items = vec!( + Jid { + node: Some(String::from("coucou")), + domain: String::from("coucou"), + resource: None, + }, + Jid { + node: None, + domain: String::from("domain"), + resource: None, + }, + ); + + let request_elem = elem.clone(); + let error = BlocklistRequest::try_from(request_elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in blocklist element."); + + let result_elem = elem.clone(); + let result = BlocklistResult::try_from(result_elem).unwrap(); + assert_eq!(result.items, two_items); + + let elem: Element = "".parse().unwrap(); + let block = Block::try_from(elem).unwrap(); + assert_eq!(block.items, two_items); + + let elem: Element = "".parse().unwrap(); + let unblock = Unblock::try_from(elem).unwrap(); + assert_eq!(unblock.items, two_items); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let request_elem = elem.clone(); + let error = BlocklistRequest::try_from(request_elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in blocklist element."); + + let result_elem = elem.clone(); + let error = BlocklistResult::try_from(result_elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in blocklist element."); + + let elem: Element = "".parse().unwrap(); + let error = Block::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in block element."); + + let elem: Element = "".parse().unwrap(); + let error = Unblock::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in unblock element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index fee722cc0a00df8bbfed51591176c8c8f7062646..7eba6799152f8e2e59c371fb60d5a8bab0b672b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -301,6 +301,9 @@ pub mod jingle; /// XEP-0184: Message Delivery Receipts pub mod receipts; +/// XEP-0191: Blocking Command +pub mod blocking; + /// XEP-0199: XMPP Ping pub mod ping; diff --git a/src/ns.rs b/src/ns.rs index 22e5f1566d4a3c06c325aa9f77e6b7ec7a11e1d0..045957d5b6125648eb2905fdfcb2e850ba292b73 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -62,6 +62,11 @@ pub const JINGLE: &str = "urn:xmpp:jingle:1"; /// XEP-0184: Message Delivery Receipts pub const RECEIPTS: &str = "urn:xmpp:receipts"; +/// XEP-0191: Blocking Command +pub const BLOCKING: &str = "urn:xmpp:blocking"; +/// XEP-0191: Blocking Command +pub const BLOCKING_ERRORS: &str = "urn:xmpp:blocking:errors"; + /// XEP-0199: XMPP Ping pub const PING: &str = "urn:xmpp:ping"; From 0200ced3e5eca4e8898f7952efdf603ffa2adb2a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 16:09:28 +0000 Subject: [PATCH 0491/1020] jingle_ft: Finish implementation of received. --- src/jingle_ft.rs | 83 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 876aae37a76e740ab1547ea340c9eb413815820b..97ad5cd4707aacc169754f8dfbff6fafdef3ee06 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -10,6 +10,7 @@ use std::collections::BTreeMap; use std::str::FromStr; use hashes::Hash; +use jingle::Creator; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use chrono::{DateTime, FixedOffset}; @@ -58,12 +59,6 @@ pub struct Description { pub file: File, } -#[derive(Debug, Clone)] -pub enum Creator { - Initiator, - Responder, -} - #[derive(Debug, Clone)] pub struct Checksum { pub name: String, @@ -77,17 +72,27 @@ pub struct Received { pub creator: Creator, } -impl IntoElements for Received { - fn into_elements(self, emitter: &mut ElementEmitter) { - let elem = Element::builder("received") - .ns(ns::JINGLE_FT) - .attr("name", self.name) - .attr("creator", match self.creator { - Creator::Initiator => "initiator", - Creator::Responder => "responder", - }) - .build(); - emitter.append_child(elem); +impl TryFrom for Received { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "received", ns::JINGLE_FT); + check_no_children!(elem, "received"); + check_no_unknown_attributes!(elem, "received", ["name", "creator"]); + Ok(Received { + name: get_attr!(elem, "name", required), + creator: get_attr!(elem, "creator", required), + }) + } +} + +impl From for Element { + fn from(received: Received) -> Element { + Element::builder("received") + .ns(ns::JINGLE_FT) + .attr("name", received.name) + .attr("creator", received.creator) + .build() } } @@ -327,4 +332,48 @@ mod tests { }; assert_eq!(message, "Desc element present twice for the same xml:lang."); } + + #[test] + fn test_received() { + let elem: Element = "".parse().unwrap(); + let received = Received::try_from(elem).unwrap(); + assert_eq!(received.name, String::from("coucou")); + assert_eq!(received.creator, Creator::Initiator); + let elem2 = Element::from(received.clone()); + let received2 = Received::try_from(elem2).unwrap(); + assert_eq!(received2.name, String::from("coucou")); + assert_eq!(received2.creator, Creator::Initiator); + + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in received element."); + + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in received element."); + + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'name' missing."); + + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown value for 'creator' attribute."); + } } From db9ef3ef305cedb2e2de6bf83f1487cc505d9a3e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 16:11:09 +0000 Subject: [PATCH 0492/1020] =?UTF-8?q?jingle=5Fft:=20Use=20jingle=E2=80=99s?= =?UTF-8?q?=20ContentId=20type=20to=20identify=20content=20names.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jingle_ft.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 97ad5cd4707aacc169754f8dfbff6fafdef3ee06..3558d3022b790791ddbe00bacf0df52801a50162 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -10,7 +10,7 @@ use std::collections::BTreeMap; use std::str::FromStr; use hashes::Hash; -use jingle::Creator; +use jingle::{Creator, ContentId}; use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use chrono::{DateTime, FixedOffset}; @@ -61,14 +61,14 @@ pub struct Description { #[derive(Debug, Clone)] pub struct Checksum { - pub name: String, + pub name: ContentId, pub creator: Creator, pub file: File, } #[derive(Debug, Clone)] pub struct Received { - pub name: String, + pub name: ContentId, pub creator: Creator, } @@ -337,11 +337,11 @@ mod tests { fn test_received() { let elem: Element = "".parse().unwrap(); let received = Received::try_from(elem).unwrap(); - assert_eq!(received.name, String::from("coucou")); + assert_eq!(received.name, ContentId(String::from("coucou"))); assert_eq!(received.creator, Creator::Initiator); let elem2 = Element::from(received.clone()); let received2 = Received::try_from(elem2).unwrap(); - assert_eq!(received2.name, String::from("coucou")); + assert_eq!(received2.name, ContentId(String::from("coucou"))); assert_eq!(received2.creator, Creator::Initiator); let elem: Element = "".parse().unwrap(); From cf31506580c8d1fd646e7a09d0d70cae144c24d7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 16:25:01 +0000 Subject: [PATCH 0493/1020] jingle_ft: Split Description::try_from into File::try_from. --- src/jingle_ft.rs | 245 ++++++++++++++++++++++++----------------------- 1 file changed, 125 insertions(+), 120 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 3558d3022b790791ddbe00bacf0df52801a50162..5cc739c878327aa84e903bf1efcf983cc30a9772 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -54,134 +54,76 @@ pub struct File { pub hashes: Vec, } -#[derive(Debug, Clone)] -pub struct Description { - pub file: File, -} - -#[derive(Debug, Clone)] -pub struct Checksum { - pub name: ContentId, - pub creator: Creator, - pub file: File, -} - -#[derive(Debug, Clone)] -pub struct Received { - pub name: ContentId, - pub creator: Creator, -} - -impl TryFrom for Received { +impl TryFrom for File { type Err = Error; - fn try_from(elem: Element) -> Result { - check_self!(elem, "received", ns::JINGLE_FT); - check_no_children!(elem, "received"); - check_no_unknown_attributes!(elem, "received", ["name", "creator"]); - Ok(Received { - name: get_attr!(elem, "name", required), - creator: get_attr!(elem, "creator", required), - }) - } -} - -impl From for Element { - fn from(received: Received) -> Element { - Element::builder("received") - .ns(ns::JINGLE_FT) - .attr("name", received.name) - .attr("creator", received.creator) - .build() - } -} - -impl TryFrom for Description { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("description", ns::JINGLE_FT) { - return Err(Error::ParseError("This is not a JingleFT description element.")); - } - if elem.children().count() != 1 { - return Err(Error::ParseError("JingleFT description element must have exactly one child.")); - } + fn try_from(elem: Element) -> Result { + check_self!(elem, "file", ns::JINGLE_FT); + check_no_attributes!(elem, "file"); + + let mut file = File { + date: None, + media_type: None, + name: None, + descs: BTreeMap::new(), + size: None, + range: None, + hashes: vec!(), + }; - let mut date = None; - let mut media_type = None; - let mut name = None; - let mut descs = BTreeMap::new(); - let mut size = None; - let mut range = None; - let mut hashes = vec!(); - for description_payload in elem.children() { - if !description_payload.is("file", ns::JINGLE_FT) { - return Err(Error::ParseError("Unknown element in JingleFT description.")); - } - for file_payload in description_payload.children() { - if file_payload.is("date", ns::JINGLE_FT) { - if date.is_some() { - return Err(Error::ParseError("File must not have more than one date.")); - } - date = Some(file_payload.text().parse()?); - } else if file_payload.is("media-type", ns::JINGLE_FT) { - if media_type.is_some() { - return Err(Error::ParseError("File must not have more than one media-type.")); - } - media_type = Some(file_payload.text()); - } else if file_payload.is("name", ns::JINGLE_FT) { - if name.is_some() { - return Err(Error::ParseError("File must not have more than one name.")); - } - name = Some(file_payload.text()); - } else if file_payload.is("desc", ns::JINGLE_FT) { - let lang = get_attr!(file_payload, "xml:lang", default); - let desc = Desc(file_payload.text()); - if descs.insert(lang, desc).is_some() { - return Err(Error::ParseError("Desc element present twice for the same xml:lang.")); - } - } else if file_payload.is("size", ns::JINGLE_FT) { - if size.is_some() { - return Err(Error::ParseError("File must not have more than one size.")); - } - size = Some(file_payload.text().parse()?); - } else if file_payload.is("range", ns::JINGLE_FT) { - if range.is_some() { - return Err(Error::ParseError("File must not have more than one range.")); - } - let offset = get_attr!(file_payload, "offset", default); - let length = get_attr!(file_payload, "length", optional); - let mut range_hashes = vec!(); - for hash_element in file_payload.children() { - if !hash_element.is("hash", ns::HASHES) { - return Err(Error::ParseError("Unknown element in JingleFT range.")); - } - range_hashes.push(Hash::try_from(hash_element.clone())?); + for child in elem.children() { + if child.is("date", ns::JINGLE_FT) { + if file.date.is_some() { + return Err(Error::ParseError("File must not have more than one date.")); + } + file.date = Some(child.text().parse()?); + } else if child.is("media-type", ns::JINGLE_FT) { + if file.media_type.is_some() { + return Err(Error::ParseError("File must not have more than one media-type.")); + } + file.media_type = Some(child.text()); + } else if child.is("name", ns::JINGLE_FT) { + if file.name.is_some() { + return Err(Error::ParseError("File must not have more than one name.")); + } + file.name = Some(child.text()); + } else if child.is("desc", ns::JINGLE_FT) { + let lang = get_attr!(child, "xml:lang", default); + let desc = Desc(child.text()); + if file.descs.insert(lang, desc).is_some() { + return Err(Error::ParseError("Desc element present twice for the same xml:lang.")); + } + } else if child.is("size", ns::JINGLE_FT) { + if file.size.is_some() { + return Err(Error::ParseError("File must not have more than one size.")); + } + file.size = Some(child.text().parse()?); + } else if child.is("range", ns::JINGLE_FT) { + if file.range.is_some() { + return Err(Error::ParseError("File must not have more than one range.")); + } + let offset = get_attr!(child, "offset", default); + let length = get_attr!(child, "length", optional); + let mut range_hashes = vec!(); + for hash_element in child.children() { + if !hash_element.is("hash", ns::HASHES) { + return Err(Error::ParseError("Unknown element in JingleFT range.")); } - range = Some(Range { - offset: offset, - length: length, - hashes: range_hashes, - }); - } else if file_payload.is("hash", ns::HASHES) { - hashes.push(Hash::try_from(file_payload.clone())?); - } else { - return Err(Error::ParseError("Unknown element in JingleFT file.")); + range_hashes.push(Hash::try_from(hash_element.clone())?); } + file.range = Some(Range { + offset: offset, + length: length, + hashes: range_hashes, + }); + } else if child.is("hash", ns::HASHES) { + file.hashes.push(Hash::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown element in JingleFT file.")); } } - Ok(Description { - file: File { - date: date, - media_type: media_type, - name: name, - descs: descs, - size: size, - range: range, - hashes: hashes, - }, - }) + Ok(file) } } @@ -233,6 +175,32 @@ impl From for Element { root } } +#[derive(Debug, Clone)] +pub struct Description { + pub file: File, +} + +impl TryFrom for Description { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "description", ns::JINGLE_FT, "JingleFT description"); + check_no_attributes!(elem, "JingleFT description"); + let mut file = None; + for child in elem.children() { + if file.is_some() { + return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + } + file = Some(File::try_from(child.clone())?); + } + if file.is_none() { + return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + } + Ok(Description { + file: file.unwrap(), + }) + } +} impl From for Element { fn from(description: Description) -> Element { @@ -244,6 +212,43 @@ impl From for Element { } } +#[derive(Debug, Clone)] +pub struct Checksum { + pub name: ContentId, + pub creator: Creator, + pub file: File, +} + +#[derive(Debug, Clone)] +pub struct Received { + pub name: ContentId, + pub creator: Creator, +} + +impl TryFrom for Received { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "received", ns::JINGLE_FT); + check_no_children!(elem, "received"); + check_no_unknown_attributes!(elem, "received", ["name", "creator"]); + Ok(Received { + name: get_attr!(elem, "name", required), + creator: get_attr!(elem, "creator", required), + }) + } +} + +impl From for Element { + fn from(received: Received) -> Element { + Element::builder("received") + .ns(ns::JINGLE_FT) + .attr("name", received.name) + .attr("creator", received.creator) + .build() + } +} + #[cfg(test)] mod tests { use super::*; From 667ae4c40f52f798a3b7c7a8d3ea60a0e7d78cb1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 16:41:22 +0000 Subject: [PATCH 0494/1020] jingle_ft: Implement checksum parsers. --- src/jingle_ft.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 5cc739c878327aa84e903bf1efcf983cc30a9772..3ee4e4a9d7975997e2b8f1874c14b0c47908446d 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -219,6 +219,41 @@ pub struct Checksum { pub file: File, } +impl TryFrom for Checksum { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "checksum", ns::JINGLE_FT); + check_no_unknown_attributes!(elem, "checksum", ["name", "creator"]); + let mut file = None; + for child in elem.children() { + if file.is_some() { + return Err(Error::ParseError("JingleFT checksum element must have exactly one child.")); + } + file = Some(File::try_from(child.clone())?); + } + if file.is_none() { + return Err(Error::ParseError("JingleFT checksum element must have exactly one child.")); + } + Ok(Checksum { + name: get_attr!(elem, "name", required), + creator: get_attr!(elem, "creator", required), + file: file.unwrap(), + }) + } +} + +impl From for Element { + fn from(checksum: Checksum) -> Element { + Element::builder("checksum") + .ns(ns::JINGLE_FT) + .attr("name", checksum.name) + .attr("creator", checksum.creator) + .append(checksum.file) + .build() + } +} + #[derive(Debug, Clone)] pub struct Received { pub name: ContentId, @@ -381,4 +416,51 @@ mod tests { }; assert_eq!(message, "Unknown value for 'creator' attribute."); } + + #[test] + fn test_checksum() { + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let hash = vec!(195, 73, 156, 39, 41, 115, 10, 127, 128, 126, 251, 134, 118, 169, 45, 203, 111, 138, 63, 143); + let checksum = Checksum::try_from(elem).unwrap(); + assert_eq!(checksum.name, ContentId(String::from("coucou"))); + assert_eq!(checksum.creator, Creator::Initiator); + assert_eq!(checksum.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() })); + let elem2 = Element::from(checksum); + let checksum2 = Checksum::try_from(elem2).unwrap(); + assert_eq!(checksum2.name, ContentId(String::from("coucou"))); + assert_eq!(checksum2.creator, Creator::Initiator); + assert_eq!(checksum2.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() })); + + let elem: Element = "".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "This is not a file element."); + + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in checksum element."); + + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'name' missing."); + + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown value for 'creator' attribute."); + } } From 97abc37f1a0a78bdcf77d9a416a05ed2f547a817 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:02:24 +0000 Subject: [PATCH 0495/1020] jingle_ft: Improve parsing. --- src/jingle_ft.rs | 81 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 3ee4e4a9d7975997e2b8f1874c14b0c47908446d..7e13351f8a96de92f578241d0197d8e624aeaa95 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -12,7 +12,7 @@ use std::str::FromStr; use hashes::Hash; use jingle::{Creator, ContentId}; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use chrono::{DateTime, FixedOffset}; use error::Error; @@ -25,17 +25,32 @@ pub struct Range { pub hashes: Vec, } -impl IntoElements for Range { - fn into_elements(self, emitter: &mut ElementEmitter) { - let mut elem = Element::builder("range") - .ns(ns::JINGLE_FT) - .attr("offset", if self.offset == 0 { None } else { Some(self.offset) }) - .attr("length", self.length) - .build(); - for hash in self.hashes { - elem.append_child(hash.into()); +impl TryFrom for Range { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "range", ns::JINGLE_FT); + check_no_unknown_attributes!(elem, "range", ["offset", "length"]); + let mut hashes = vec!(); + for child in elem.children() { + hashes.push(Hash::try_from(child.clone())?); } - emitter.append_child(elem); + Ok(Range { + offset: get_attr!(elem, "offset", default), + length: get_attr!(elem, "length", optional), + hashes: hashes, + }) + } +} + +impl From for Element { + fn from(range: Range) -> Element { + Element::builder("range") + .ns(ns::JINGLE_FT) + .attr("offset", if range.offset == 0 { None } else { Some(range.offset) }) + .attr("length", range.length) + .append(range.hashes) + .build() } } @@ -102,20 +117,7 @@ impl TryFrom for File { if file.range.is_some() { return Err(Error::ParseError("File must not have more than one range.")); } - let offset = get_attr!(child, "offset", default); - let length = get_attr!(child, "length", optional); - let mut range_hashes = vec!(); - for hash_element in child.children() { - if !hash_element.is("hash", ns::HASHES) { - return Err(Error::ParseError("Unknown element in JingleFT range.")); - } - range_hashes.push(Hash::try_from(hash_element.clone())?); - } - file.range = Some(Range { - offset: offset, - length: length, - hashes: range_hashes, - }); + file.range = Some(Range::try_from(child.clone())?); } else if child.is("hash", ns::HASHES) { file.hashes.push(Hash::try_from(child.clone())?); } else { @@ -463,4 +465,33 @@ mod tests { }; assert_eq!(message, "Unknown value for 'creator' attribute."); } + + #[test] + fn test_range() { + let elem: Element = "".parse().unwrap(); + let range = Range::try_from(elem).unwrap(); + assert_eq!(range.offset, 0); + assert_eq!(range.length, None); + assert_eq!(range.hashes, vec!()); + + let elem: Element = "kHp5RSzW/h7Gm1etSf90Mr5PC/k=".parse().unwrap(); + let hashes = vec!(Hash { algo: Algo::Sha_1, hash: vec!(144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79, 11, 249) }); + let range = Range::try_from(elem).unwrap(); + assert_eq!(range.offset, 2048); + assert_eq!(range.length, Some(1024)); + assert_eq!(range.hashes, hashes); + let elem2 = Element::from(range); + let range2 = Range::try_from(elem2).unwrap(); + assert_eq!(range2.offset, 2048); + assert_eq!(range2.length, Some(1024)); + assert_eq!(range2.hashes, hashes); + + let elem: Element = "".parse().unwrap(); + let error = Range::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in range element."); + } } From 7fa889f46eab947914388749feec85f29065d5e9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:17:06 +0000 Subject: [PATCH 0496/1020] jingle: Import the disposition attribute values. --- src/jingle.rs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 9d142612e5b74a15606dd8d525556d4a706fe580..c9737b03fd3fd58349b8f5bca5ce901e7b0f738c 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -43,16 +43,27 @@ generate_attribute!(Senders, "senders", { Responder => "responder", }, Default = Both); -generate_id!(ContentId); - -// TODO: the list of values is defined, use an enum! -generate_id!(Disposition); +// From https://www.iana.org/assignments/cont-disp/cont-disp.xhtml +generate_attribute!(Disposition, "disposition", { + Inline => "inline", + Attachment => "attachment", + FormData => "form-data", + Signal => "signal", + Alert => "alert", + Icon => "icon", + Render => "render", + RecipientListHistory => "recipient-list-history", + Session => "session", + Aib => "aib", + EarlySession => "early-session", + RecipientList => "recipient-list", + Notification => "notification", + ByReference => "by-reference", + InfoPackage => "info-package", + RecordingSession => "recording-session", +}, Default = Session); -impl Default for Disposition { - fn default() -> Disposition { - Disposition(String::from("session")) - } -} +generate_id!(ContentId); #[derive(Debug, Clone)] pub struct Content { @@ -353,7 +364,7 @@ mod tests { assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, ContentId(String::from("coucou"))); assert_eq!(jingle.contents[0].senders, Senders::Both); - assert_eq!(jingle.contents[0].disposition, Disposition(String::from("session"))); + assert_eq!(jingle.contents[0].disposition, Disposition::Session); let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); @@ -361,7 +372,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); - assert_eq!(jingle.contents[0].disposition, Disposition(String::from("early-session"))); + assert_eq!(jingle.contents[0].disposition, Disposition::EarlySession); } #[test] From b5e41a70742730de5a03bb594f8346260fdf0164 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:55:27 +0000 Subject: [PATCH 0497/1020] Fix the name in create_empty_element. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 7eba6799152f8e2e59c371fb60d5a8bab0b672b4..ccc84f022575287748020885e952882d986750be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,7 +195,7 @@ macro_rules! generate_empty_element { impl From<$elem> for Element { fn from(_: $elem) -> Element { - Element::builder("attention") + Element::builder($name) .ns($ns) .build() } From ed53f452c37164d3235bf9c24f96cf62f51e94e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:57:49 +0000 Subject: [PATCH 0498/1020] ping: Add a serialise test. --- src/ping.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ping.rs b/src/ping.rs index d4044f477d8cd534dbe7ce68356cbe81e010126a..fa696d08bbc472cff36cc40a83d83eb98f3af2d7 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -25,6 +25,13 @@ mod tests { Ping::try_from(elem).unwrap(); } + #[test] + fn test_serialise() { + let elem1 = Element::from(Ping); + let elem2: Element = "".parse().unwrap(); + assert_eq!(elem1, elem2); + } + #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); From 929bd577f328bf5fa3866ff56195732038e6b626 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 17:58:11 +0000 Subject: [PATCH 0499/1020] Add a generate_element_with_only_attributes macro, and use it wherever it makes sense. --- src/eme.rs | 39 ++++------------------- src/ibb.rs | 72 +++++------------------------------------- src/jingle_ft.rs | 33 +++---------------- src/jingle_ibb.rs | 40 +++-------------------- src/jingle_s5b.rs | 31 +++++------------- src/lib.rs | 40 +++++++++++++++++++++++ src/message_correct.rs | 28 ++-------------- src/muc/user.rs | 39 +++-------------------- src/receipts.rs | 65 +++----------------------------------- src/stanza_id.rs | 70 +++++----------------------------------- 10 files changed, 92 insertions(+), 365 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index ad4b437f8098a5cc18551277f7b03ce4eebf4210..540b50730df1036c450f80478b44fd77fa1738ca 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -13,39 +13,14 @@ use error::Error; use ns; /// Structure representing an `` element. -#[derive(Debug, Clone)] -pub struct ExplicitMessageEncryption { - /// Namespace of the encryption scheme used. - pub namespace: String, +generate_element_with_only_attributes!(ExplicitMessageEncryption, "encryption", ns::EME, [ + // Namespace of the encryption scheme used. + namespace: String = "namespace" => required, - /// User-friendly name for the encryption scheme, should be `None` for OTR, - /// legacy OpenPGP and OX. - pub name: Option, -} - -impl TryFrom for ExplicitMessageEncryption { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "encryption", ns::EME); - check_no_children!(elem, "encryption"); - check_no_unknown_attributes!(elem, "encryption", ["namespace", "name"]); - Ok(ExplicitMessageEncryption { - namespace: get_attr!(elem, "namespace", required), - name: get_attr!(elem, "name", optional), - }) - } -} - -impl From for Element { - fn from(eme: ExplicitMessageEncryption) -> Element { - Element::builder("encryption") - .ns(ns::EME) - .attr("namespace", eme.namespace) - .attr("name", eme.name) - .build() - } -} + // User-friendly name for the encryption scheme, should be `None` for OTR, + // legacy OpenPGP and OX. + name: Option = "name" => optional, +]); #[cfg(test)] mod tests { diff --git a/src/ibb.rs b/src/ibb.rs index 2c01e1c741aa9785c5a308471f5bbfea2ed9e9d5..7d7f3e73fa501e4a7ffd63e1586bac52ba8532ee 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -19,41 +19,11 @@ generate_attribute!(Stanza, "stanza", { Message => "message", }, Default = Iq); -#[derive(Debug, Clone)] -pub struct Open { - pub block_size: u16, - pub sid: String, - pub stanza: Stanza, -} - -impl TryFrom for Open { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("open", ns::IBB) { - return Err(Error::ParseError("This is not an open element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in open element.")); - } - Ok(Open { - block_size: get_attr!(elem, "block-size", required), - sid: get_attr!(elem, "sid", required), - stanza: get_attr!(elem, "stanza", default), - }) - } -} - -impl From for Element { - fn from(open: Open) -> Element { - Element::builder("open") - .ns(ns::IBB) - .attr("block-size", open.block_size) - .attr("sid", open.sid) - .attr("stanza", open.stanza) - .build() - } -} +generate_element_with_only_attributes!(Open, "open", ns::IBB, [ + block_size: u16 = "block-size" => required, + sid: String = "sid" => required, + stanza: Stanza = "stanza" => default, +]); #[derive(Debug, Clone)] pub struct Data { @@ -91,35 +61,9 @@ impl From for Element { } } -#[derive(Debug, Clone)] -pub struct Close { - pub sid: String, -} - -impl TryFrom for Close { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("close", ns::IBB) { - return Err(Error::ParseError("This is not a close element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in close element.")); - } - Ok(Close { - sid: get_attr!(elem, "sid", required), - }) - } -} - -impl From for Element { - fn from(close: Close) -> Element { - Element::builder("close") - .ns(ns::IBB) - .attr("sid", close.sid) - .build() - } -} +generate_element_with_only_attributes!(Close, "close", ns::IBB, [ + sid: String = "sid" => required, +]); #[cfg(test)] mod tests { diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 7e13351f8a96de92f578241d0197d8e624aeaa95..15a21c67efa16c70df390cb3774b4d3d505a59de 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -256,35 +256,10 @@ impl From for Element { } } -#[derive(Debug, Clone)] -pub struct Received { - pub name: ContentId, - pub creator: Creator, -} - -impl TryFrom for Received { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "received", ns::JINGLE_FT); - check_no_children!(elem, "received"); - check_no_unknown_attributes!(elem, "received", ["name", "creator"]); - Ok(Received { - name: get_attr!(elem, "name", required), - creator: get_attr!(elem, "creator", required), - }) - } -} - -impl From for Element { - fn from(received: Received) -> Element { - Element::builder("received") - .ns(ns::JINGLE_FT) - .attr("name", received.name) - .attr("creator", received.creator) - .build() - } -} +generate_element_with_only_attributes!(Received, "received", ns::JINGLE_FT, [ + name: ContentId = "name" => required, + creator: Creator = "creator" => required, +]); #[cfg(test)] mod tests { diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index ed1c6d189ec5c5662aa4aab3682bd58cde9223a9..632540ed2cc909d22c3625a5e621a1c6565ab498 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -17,41 +17,11 @@ use ibb::Stanza; generate_id!(StreamId); -#[derive(Debug, Clone)] -pub struct Transport { - pub block_size: u16, - pub sid: StreamId, - pub stanza: Stanza, -} - -impl TryFrom for Transport { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("transport", ns::JINGLE_IBB) { - return Err(Error::ParseError("This is not an JingleIBB element.")) - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in JingleIBB element.")); - } - Ok(Transport { - block_size: get_attr!(elem, "block-size", required), - sid: get_attr!(elem, "sid", required), - stanza: get_attr!(elem, "stanza", default), - }) - } -} - -impl From for Element { - fn from(transport: Transport) -> Element { - Element::builder("transport") - .ns(ns::JINGLE_IBB) - .attr("block-size", transport.block_size) - .attr("sid", transport.sid) - .attr("stanza", transport.stanza) - .build() - } -} +generate_element_with_only_attributes!(Transport, "transport", ns::JINGLE_IBB, [ + block_size: u16 = "block-size" => required, + sid: StreamId = "sid" => required, + stanza: Stanza = "stanza" => default, +]); #[cfg(test)] mod tests { diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 6f8bb902106ebcdbdd1dedae5f00fda9c57d53da..f1396c5df4d1d1c1fdf032fa18c324a17cba229e 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -30,29 +30,14 @@ generate_id!(CandidateId); generate_id!(StreamId); -#[derive(Debug, Clone)] -pub struct Candidate { - pub cid: CandidateId, - pub host: String, - pub jid: Jid, - pub port: Option, - pub priority: u32, - pub type_: Type, -} - -impl From for Element { - fn from(candidate: Candidate) -> Element { - Element::builder("candidate") - .ns(ns::JINGLE_S5B) - .attr("cid", candidate.cid) - .attr("host", candidate.host) - .attr("jid", String::from(candidate.jid)) - .attr("port", candidate.port) - .attr("priority", candidate.priority) - .attr("type", candidate.type_) - .build() - } -} +generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ + cid: CandidateId = "cid" => required, + host: String = "host" => required, + jid: Jid = "jid" => required, + port: Option = "port" => optional, + priority: u32 = "priority" => required, + type_: Type = "type" => default, +]); #[derive(Debug, Clone)] pub enum TransportPayload { diff --git a/src/lib.rs b/src/lib.rs index ccc84f022575287748020885e952882d986750be..5e767fc20acc13ebb29e75c3cc458985324065e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -203,6 +203,46 @@ macro_rules! generate_empty_element { ); } +macro_rules! generate_element_with_only_attributes { + ($elem:ident, $name:tt, $ns:expr, [$($attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + generate_element_with_only_attributes!($elem, $name, $ns, [$($attr: $attr_type = $attr_name => $attr_action),*]); + ); + ($elem:ident, $name:tt, $ns:expr, [$($attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + #[derive(Debug, Clone)] + pub struct $elem { + $( + pub $attr: $attr_type + ),* + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action) + ),* + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + $( + .attr($attr_name, elem.$attr) + )* + .build() + } + } + ); +} + macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/message_correct.rs b/src/message_correct.rs index d8200ae19b07401e21a548a6e0b0018e7e4efa1e..26cdcf99596b8c79d8403dabbe108f46e8800d02 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -12,31 +12,9 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct Replace { - pub id: String, -} - -impl TryFrom for Replace { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "replace", ns::MESSAGE_CORRECT); - check_no_children!(elem, "replace"); - check_no_unknown_attributes!(elem, "replace", ["id"]); - let id = get_attr!(elem, "id", required); - Ok(Replace { id }) - } -} - -impl From for Element { - fn from(replace: Replace) -> Element { - Element::builder("replace") - .ns(ns::MESSAGE_CORRECT) - .attr("id", replace.id) - .build() - } -} +generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, [ + id: String = "id" => required, +]); #[cfg(test)] mod tests { diff --git a/src/muc/user.rs b/src/muc/user.rs index 322525cbe2a3cadbe0a292f23f401947d8c15b3f..fb30967993d0d417959a373a168f9a98232590fd 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -192,38 +192,9 @@ impl From for Element { } } -#[derive(Debug, Clone, PartialEq)] -pub struct Continue { - thread: Option, -} - -impl TryFrom for Continue { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("continue", ns::MUC_USER) { - return Err(Error::ParseError("This is not a continue element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in continue element.")); - } - for (attr, _) in elem.attrs() { - if attr != "thread" { - return Err(Error::ParseError("Unknown attribute in continue element.")); - } - } - Ok(Continue { thread: get_attr!(elem, "thread", optional) }) - } -} - -impl From for Element { - fn from(cont: Continue) -> Element { - Element::builder("continue") - .ns(ns::MUC_USER) - .attr("thread", cont.thread) - .build() - } -} +generate_element_with_only_attributes!(Continue, "continue", ns::MUC_USER, [ + thread: Option = "thread" => optional, +]); #[derive(Debug, Clone, PartialEq)] pub struct Reason(String); @@ -576,7 +547,7 @@ mod tests { thread='foo'/> ".parse().unwrap(); let continue_ = Continue::try_from(elem).unwrap(); - assert_eq!(continue_, Continue { thread: Some("foo".to_owned()) }); + assert_eq!(continue_.thread, Some("foo".to_owned())); } #[test] @@ -725,7 +696,7 @@ mod tests { let item = Item::try_from(elem).unwrap(); let continue_1 = Continue { thread: Some("foobar".to_owned()) }; match item { - Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2, continue_1), + Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2.thread, continue_1.thread), _ => panic!(), } } diff --git a/src/receipts.rs b/src/receipts.rs index 6ccec7eae1f500062175bfdc776eb5e3ac178106..470a46474dde9241e8fc74570fa186429aa0ace9 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -12,68 +12,11 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct Request; +generate_empty_element!(Request, "request", ns::RECEIPTS); -impl TryFrom for Request { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("request", ns::RECEIPTS) { - return Err(Error::ParseError("This is not a request element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in request element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in request element.")); - } - Ok(Request) - } -} - -impl From for Element { - fn from(_: Request) -> Element { - Element::builder("request") - .ns(ns::RECEIPTS) - .build() - } -} - -#[derive(Debug, Clone)] -pub struct Received { - pub id: Option, -} - -impl TryFrom for Received { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("received", ns::RECEIPTS) { - return Err(Error::ParseError("This is not a received element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in received element.")); - } - for (attr, _) in elem.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in received element.")); - } - } - Ok(Received { - id: get_attr!(elem, "id", optional), - }) - } -} - -impl From for Element { - fn from(received: Received) -> Element { - Element::builder("received") - .ns(ns::RECEIPTS) - .attr("id", received.id) - .build() - } -} +generate_element_with_only_attributes!(Received, "received", ns::RECEIPTS, [ + id: Option = "id" => optional, +]); #[cfg(test)] mod tests { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index feeeadbe0328d00411e776d557227ed9dc7439c4..6e65e012b885d13e0b1458b3687f710743a21746 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -13,68 +13,14 @@ use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct StanzaId { - pub id: String, - pub by: Jid, -} - -impl TryFrom for StanzaId { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("stanza-id", ns::SID) { - return Err(Error::ParseError("This is not a stanza-id element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in stanza-id element.")); - } - Ok(StanzaId { - id: get_attr!(elem, "id", required), - by: get_attr!(elem, "by", required), - }) - } -} - -impl From for Element { - fn from(stanza_id: StanzaId) -> Element { - Element::builder("stanza-id") - .ns(ns::SID) - .attr("id", stanza_id.id) - .attr("by", stanza_id.by) - .build() - } -} - -#[derive(Debug, Clone)] -pub struct OriginId { - pub id: String, -} - -impl TryFrom for OriginId { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("origin-id", ns::SID) { - return Err(Error::ParseError("This is not an origin-id element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in origin-id element.")); - } - Ok(OriginId { - id: get_attr!(elem, "id", required), - }) - } -} - -impl From for Element { - fn from(origin_id: OriginId) -> Element { - Element::builder("origin-id") - .ns(ns::SID) - .attr("id", origin_id.id) - .build() - } -} +generate_element_with_only_attributes!(StanzaId, "stanza-id", ns::SID, [ + id: String = "id" => required, + by: Jid = "by" => required, +]); + +generate_element_with_only_attributes!(OriginId, "origin-id", ns::SID, [ + id: String = "id" => required, +]); #[cfg(test)] mod tests { From 61badccf8b07328f2cb0074718c4a69bebbd5f73 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 18:37:32 +0000 Subject: [PATCH 0500/1020] Add meta support to macros. --- src/lib.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5e767fc20acc13ebb29e75c3cc458985324065e6..2a63302cf100c0adc4697ca0cace696d17ebc0b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -174,11 +174,8 @@ macro_rules! check_no_unknown_attributes { } macro_rules! generate_empty_element { - ($elem:ident, $name:tt, $ns:expr) => ( - // TODO: Find a better way to concatenate doc. - #[doc="Structure representing a "] - #[doc=$name] - #[doc=" element."] + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => ( + $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem; @@ -204,13 +201,15 @@ macro_rules! generate_empty_element { } macro_rules! generate_element_with_only_attributes { - ($elem:ident, $name:tt, $ns:expr, [$($attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element_with_only_attributes!($elem, $name, $ns, [$($attr: $attr_type = $attr_name => $attr_action),*]); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]); ); - ($elem:ident, $name:tt, $ns:expr, [$($attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { $( + $(#[$attr_meta])* pub $attr: $attr_type ),* } From 649286d59e701c5d836f3daa3a9d71ba52413be2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 18:49:22 +0000 Subject: [PATCH 0501/1020] disco: Use generate_element_with_only_attributes. --- src/disco.rs | 129 ++++++++------------------------------------------- 1 file changed, 19 insertions(+), 110 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 2c3189f06f42d542537d80024c1923d33aacfe78..9964cd8c9e625e8592b3ba4587db60c3ac6a0ae3 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -16,66 +16,23 @@ use ns; use data_forms::{DataForm, DataFormType}; +generate_element_with_only_attributes!( /// Structure representing a `` element. /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -#[derive(Debug, Clone)] -pub struct DiscoInfoQuery { +DiscoInfoQuery, "query", ns::DISCO_INFO, [ /// Node on which we are doing the discovery. - pub node: Option, -} - -impl TryFrom for DiscoInfoQuery { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::DISCO_INFO); - check_no_children!(elem, "query"); - check_no_unknown_attributes!(elem, "query", ["node"]); - Ok(DiscoInfoQuery { - node: get_attr!(elem, "node", optional), - }) - } -} - -impl From for Element { - fn from(disco: DiscoInfoQuery) -> Element { - Element::builder("query") - .ns(ns::DISCO_INFO) - .attr("node", disco.node) - .build() - } -} + node: Option = "node" => optional, +]); +generate_element_with_only_attributes!( /// Structure representing a `` element. -#[derive(Debug, Clone, PartialEq)] -pub struct Feature { +#[derive(PartialEq)] +Feature, "feature", ns::DISCO_INFO, [ /// Namespace of the feature we want to represent. - pub var: String, -} - -impl TryFrom for Feature { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "feature", ns::DISCO_INFO, "disco#info feature"); - check_no_children!(elem, "disco#info feature"); - check_no_unknown_attributes!(elem, "disco#info feature", ["var"]); - Ok(Feature { - var: get_attr!(elem, "var", required) - }) - } -} - -impl From for Element { - fn from(feature: Feature) -> Element { - Element::builder("feature") - .ns(ns::DISCO_INFO) - .attr("var", feature.var) - .build() - } -} + var: String = "var" => required, +]); /// Structure representing an `` element. #[derive(Debug, Clone)] @@ -223,74 +180,26 @@ impl From for Element { } } +generate_element_with_only_attributes!( /// Structure representing a `` element. /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -#[derive(Debug, Clone)] -pub struct DiscoItemsQuery { +DiscoItemsQuery, "query", ns::DISCO_ITEMS, [ /// Node on which we are doing the discovery. - pub node: Option, -} - -impl TryFrom for DiscoItemsQuery { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::DISCO_ITEMS, "disco#items query"); - check_no_children!(elem, "disco#items query"); - check_no_unknown_attributes!(elem, "disco#items query", ["node"]); - Ok(DiscoItemsQuery { - node: get_attr!(elem, "node", optional), - }) - } -} - -impl From for Element { - fn from(disco: DiscoItemsQuery) -> Element { - Element::builder("query") - .ns(ns::DISCO_ITEMS) - .attr("node", disco.node) - .build() - } -} + node: Option = "node" => optional, +]); +generate_element_with_only_attributes!( /// Structure representing an `` element. -#[derive(Debug, Clone)] -pub struct Item { +Item, "item", ns::DISCO_ITEMS, [ /// JID of the entity pointed by this item. - pub jid: Jid, + jid: Jid = "jid" => required, /// Node of the entity pointed by this item. - pub node: Option, + node: Option = "node" => optional, /// Name of the entity pointed by this item. - pub name: Option, -} - -impl TryFrom for Item { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "item", ns::DISCO_ITEMS); - check_no_children!(elem, "item"); - check_no_unknown_attributes!(elem, "item", ["jid", "node", "name"]); - Ok(Item { - jid: get_attr!(elem, "jid", required), - node: get_attr!(elem, "node", optional), - name: get_attr!(elem, "name", optional), - }) - } -} - -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::DISCO_ITEMS) - .attr("jid", item.jid) - .attr("node", item.node) - .attr("name", item.name) - .build() - } -} + name: Option = "name" => optional, +]); /// Structure representing a `` element. From 47c9263b8685688373e6338b322133c7d1a0bcec Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 18:53:51 +0000 Subject: [PATCH 0502/1020] blocking: Merge get_children_items into the try_from. --- src/blocking.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/blocking.rs b/src/blocking.rs index 084844e67b732d4bd9c41f530ef7eab76ee1f574..997e24111ff536f32396a4befd46ffb2e6dbe850 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -15,17 +15,6 @@ use ns; generate_empty_element!(BlocklistRequest, "blocklist", ns::BLOCKING); -fn get_children_items(elem: Element) -> Result, Error> { - let mut items = vec!(); - for child in elem.children() { - check_self!(child, "item", ns::BLOCKING); - check_no_unknown_attributes!(child, "item", ["jid"]); - check_no_children!(child, "item"); - items.push(get_attr!(child, "jid", required)); - } - Ok(items) -} - macro_rules! generate_blocking_element { ($elem:ident, $name:tt) => ( #[derive(Debug, Clone)] @@ -39,9 +28,14 @@ macro_rules! generate_blocking_element { fn try_from(elem: Element) -> Result<$elem, Error> { check_self!(elem, $name, ns::BLOCKING); check_no_attributes!(elem, $name); - Ok($elem { - items: get_children_items(elem)?, - }) + let mut items = vec!(); + for child in elem.children() { + check_self!(child, "item", ns::BLOCKING); + check_no_unknown_attributes!(child, "item", ["jid"]); + check_no_children!(child, "item"); + items.push(get_attr!(child, "jid", required)); + } + Ok($elem { items }) } } From 82fcf3bad5f614cb3e014c3208bab442c019d551 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 19:07:49 +0000 Subject: [PATCH 0503/1020] ecaps2: Simplify parsing. --- src/ecaps2.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 8166e5b0f3e525f4019c7628fd4d9f24ce0c3200..347d1dac8e71074fcee18191804a54f6fab64aad 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -30,17 +30,11 @@ impl TryFrom for ECaps2 { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("c", ns::ECAPS2) { - return Err(Error::ParseError("This is not an ecaps2 element.")); - } + check_self!(elem, "c", ns::ECAPS2, "ecaps2"); + check_no_attributes!(elem, "ecaps2"); let mut hashes = vec!(); for child in elem.children() { - if child.is("hash", ns::HASHES) { - let hash = Hash::try_from(child.clone())?; - hashes.push(hash); - } else { - return Err(Error::ParseError("Unknown child in ecaps2 element.")); - } + hashes.push(Hash::try_from(child.clone())?); } Ok(ECaps2 { hashes: hashes, @@ -52,9 +46,7 @@ impl From for Element { fn from(ecaps2: ECaps2) -> Element { Element::builder("c") .ns(ns::ECAPS2) - .append(ecaps2.hashes.into_iter() - .map(Element::from) - .collect::>()) + .append(ecaps2.hashes) .build() } } @@ -207,7 +199,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in ecaps2 element."); + assert_eq!(message, "This is not a hash element."); } #[test] From 5cdb2d77ab27b0b496a8506f2d7f19f451b909d1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 19:41:45 +0000 Subject: [PATCH 0504/1020] Split out DateTime parsing into its own module (implement XEP-0082). --- src/date.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++ src/delay.rs | 22 +++------ src/idle.rs | 36 +++----------- src/jingle_ft.rs | 8 +-- src/lib.rs | 3 ++ src/pubsub/event.rs | 6 +-- 6 files changed, 139 insertions(+), 52 deletions(-) create mode 100644 src/date.rs diff --git a/src/date.rs b/src/date.rs new file mode 100644 index 0000000000000000000000000000000000000000..a344eb58d97dc4ad297de5fd471811c8594fd2f9 --- /dev/null +++ b/src/date.rs @@ -0,0 +1,116 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::str::FromStr; + +use minidom::{IntoAttributeValue, IntoElements, ElementEmitter}; +use chrono::{DateTime as ChronoDateTime, FixedOffset}; + +use error::Error; + +/// Implements the DateTime profile of XEP-0082, which represents a +/// non-recurring moment in time, with an accuracy of seconds or fraction of +/// seconds, and includes a timezone. +#[derive(Debug, Clone, PartialEq)] +pub struct DateTime(ChronoDateTime); + +impl FromStr for DateTime { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(DateTime(ChronoDateTime::parse_from_rfc3339(s)?)) + } +} + +impl IntoAttributeValue for DateTime { + fn into_attribute_value(self) -> Option { + Some(self.0.to_rfc3339()) + } +} + +impl IntoElements for DateTime { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_text_node(self.0.to_rfc3339()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use chrono::{Datelike, Timelike}; + use std::error::Error as StdError; + + #[test] + fn test_simple() { + let date: DateTime = "2002-09-10T23:08:25Z".parse().unwrap(); + assert_eq!(date.0.year(), 2002); + assert_eq!(date.0.month(), 9); + assert_eq!(date.0.day(), 10); + assert_eq!(date.0.hour(), 23); + assert_eq!(date.0.minute(), 08); + assert_eq!(date.0.second(), 25); + assert_eq!(date.0.nanosecond(), 0); + assert_eq!(date.0.timezone(), FixedOffset::east(0)); + } + + #[test] + fn test_invalid_date() { + // There is no thirteenth month. + let error = DateTime::from_str("2017-13-01T12:23:34Z").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input is out of range"); + + // Timezone ≥24:00 aren’t allowed. + let error = DateTime::from_str("2017-05-27T12:11:02+25:00").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input is out of range"); + + // Timezone without the : separator aren’t allowed. + let error = DateTime::from_str("2017-05-27T12:11:02+0100").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // No seconds, error message could be improved. + let error = DateTime::from_str("2017-05-27T12:11+01:00").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // TODO: maybe we’ll want to support this one, as per XEP-0082 §4. + let error = DateTime::from_str("20170527T12:11:02+01:00").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "input contains invalid characters"); + + // No timezone. + let error = DateTime::from_str("2017-05-27T12:11:02").unwrap_err(); + let message = match error { + Error::ChronoParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message.description(), "premature end of input"); + } + + #[test] + fn test_serialise() { + let date = DateTime(ChronoDateTime::parse_from_rfc3339("2017-05-21T20:19:55+01:00").unwrap()); + let attr = date.into_attribute_value(); + assert_eq!(attr, Some(String::from("2017-05-21T20:19:55+01:00"))); + } +} diff --git a/src/delay.rs b/src/delay.rs index 97c10ad76423132acb7d86782cccb0e4bd02f819..53462792680991e7575a084a596a8e23af2f91dd 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use minidom::Element; -use chrono::{DateTime, FixedOffset}; +use date::DateTime; use error::Error; use jid::Jid; @@ -17,7 +17,7 @@ use ns; #[derive(Debug, Clone)] pub struct Delay { pub from: Option, - pub stamp: DateTime, + pub stamp: DateTime, pub data: Option, } @@ -32,7 +32,7 @@ impl TryFrom for Delay { return Err(Error::ParseError("Unknown child in delay element.")); } let from = get_attr!(elem, "from", optional); - let stamp = get_attr!(elem, "stamp", required, stamp, DateTime::parse_from_rfc3339(stamp)?); + let stamp = get_attr!(elem, "stamp", required); let data = match elem.text().as_ref() { "" => None, text => Some(text.to_owned()), @@ -50,7 +50,7 @@ impl From for Element { Element::builder("delay") .ns(ns::DELAY) .attr("from", delay.from) - .attr("stamp", delay.stamp.to_rfc3339()) + .attr("stamp", delay.stamp) .append(delay.data) .build() } @@ -60,21 +60,13 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; - use chrono::{Datelike, Timelike}; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); let delay = Delay::try_from(elem).unwrap(); assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap())); - assert_eq!(delay.stamp.year(), 2002); - assert_eq!(delay.stamp.month(), 9); - assert_eq!(delay.stamp.day(), 10); - assert_eq!(delay.stamp.hour(), 23); - assert_eq!(delay.stamp.minute(), 08); - assert_eq!(delay.stamp.second(), 25); - assert_eq!(delay.stamp.nanosecond(), 0); - assert_eq!(delay.stamp.timezone(), FixedOffset::east(0)); + assert_eq!(delay.stamp, DateTime::from_str("2002-09-10T23:08:25Z").unwrap()); assert_eq!(delay.data, None); } @@ -105,7 +97,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let delay = Delay { from: None, - stamp: DateTime::parse_from_rfc3339("2002-09-10T23:08:25Z").unwrap(), + stamp: DateTime::from_str("2002-09-10T23:08:25Z").unwrap(), data: None, }; let elem2 = delay.into(); @@ -117,7 +109,7 @@ mod tests { let elem: Element = "Reason".parse().unwrap(); let delay = Delay { from: Some(Jid::from_str("juliet@example.org").unwrap()), - stamp: DateTime::parse_from_rfc3339("2002-09-10T23:08:25Z").unwrap(), + stamp: DateTime::from_str("2002-09-10T23:08:25Z").unwrap(), data: Some(String::from("Reason")), }; let elem2 = delay.into(); diff --git a/src/idle.rs b/src/idle.rs index 51c203934056755d7a07bc771aea6837ced92d63..e4b09924580777f39746e0d25a743a24279fdfb0 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -7,44 +7,20 @@ use try_from::TryFrom; use minidom::Element; -use chrono::{DateTime, FixedOffset}; +use date::DateTime; use error::Error; use ns; -#[derive(Debug, Clone)] -pub struct Idle { - pub since: DateTime, -} - -impl TryFrom for Idle { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("idle", ns::IDLE) { - return Err(Error::ParseError("This is not an idle element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in idle element.")); - } - let since = get_attr!(elem, "since", required, since, DateTime::parse_from_rfc3339(since)?); - Ok(Idle { since: since }) - } -} - -impl From for Element { - fn from(idle: Idle) -> Element { - Element::builder("idle") - .ns(ns::IDLE) - .attr("since", idle.since.to_rfc3339()) - .build() - } -} +generate_element_with_only_attributes!(Idle, "idle", ns::IDLE, [ + since: DateTime = "since" => required, +]); #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; use std::error::Error as StdError; #[test] @@ -135,7 +111,7 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let idle = Idle { since: DateTime::parse_from_rfc3339("2017-05-21T20:19:55+01:00").unwrap() }; + let idle = Idle { since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap() }; let elem2 = idle.into(); assert_eq!(elem, elem2); } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 15a21c67efa16c70df390cb3774b4d3d505a59de..fda8dbeffd0819f9150e00bb63c96c5269cc01a8 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -11,9 +11,9 @@ use std::str::FromStr; use hashes::Hash; use jingle::{Creator, ContentId}; +use date::DateTime; use minidom::{Element, IntoAttributeValue}; -use chrono::{DateTime, FixedOffset}; use error::Error; use ns; @@ -60,7 +60,7 @@ generate_id!(Desc); #[derive(Debug, Clone)] pub struct File { - pub date: Option>, + pub date: Option, pub media_type: Option, pub name: Option, pub descs: BTreeMap, @@ -137,7 +137,7 @@ impl From for Element { if let Some(date) = file.date { root.append_child(Element::builder("date") .ns(ns::JINGLE_FT) - .append(date.to_rfc3339()) + .append(date) .build()); } if let Some(media_type) = file.media_type { @@ -285,7 +285,7 @@ mod tests { assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.descs, BTreeMap::new()); - assert_eq!(desc.file.date, Some(DateTime::parse_from_rfc3339("2015-07-26T21:46:00+01:00").unwrap())); + assert_eq!(desc.file.date, DateTime::from_str("2015-07-26T21:46:00+01:00").ok()); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); diff --git a/src/lib.rs b/src/lib.rs index 2a63302cf100c0adc4697ca0cace696d17ebc0b2..68302afe8f6a8ed199aa0cbe7af1a617d8335cc9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -325,6 +325,9 @@ pub mod pubsub; /// XEP-0077: In-Band Registration pub mod ibr; +/// XEP-0082: XMPP Date and Time Profiles +pub mod date; + /// XEP-0085: Chat State Notifications pub mod chatstates; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 74b59ae6cdf32baf92705de5a9f5796d5fd3f511..cbeb6ef759b519fd07b082a92f555b3b28838329 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; use jid::Jid; -use chrono::{DateTime, FixedOffset}; +use date::DateTime; use error::Error; @@ -98,7 +98,7 @@ pub enum PubSubEvent { }, Subscription { node: NodeName, - expiry: Option>, + expiry: Option, jid: Option, subid: Option, subscription: Option, @@ -256,7 +256,7 @@ impl From for Element { Element::builder("subscription") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .attr("expiry", expiry.map(|expiry| expiry.to_rfc3339())) + .attr("expiry", expiry) .attr("jid", jid) .attr("subid", subid) .attr("subscription", subscription) From 2ffa3dc1d9efb6da99cc2f5c7b93c063887bec40 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 20:24:41 +0000 Subject: [PATCH 0505/1020] optionally implement minidom::IntoElements --- src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 30ba7f7a03ce71dce5861600c2849e53e008be9b..f0280d99aab1fa1670c06031411e0795686118ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -363,7 +363,7 @@ impl Jid { extern crate minidom; #[cfg(feature = "minidom")] -use minidom::IntoAttributeValue; +use minidom::{IntoAttributeValue, IntoElements, ElementEmitter}; #[cfg(feature = "minidom")] impl IntoAttributeValue for Jid { @@ -372,6 +372,13 @@ impl IntoAttributeValue for Jid { } } +#[cfg(feature = "minidom")] +impl IntoElements for Jid { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_text_node(String::from(self)) + } +} + #[cfg(test)] mod tests { use super::*; From 5185c0bb9ef22bdc25e80d5e739aa6e55dd91ff1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 20:24:47 +0000 Subject: [PATCH 0506/1020] bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 57783567112121287eb378667f1216ce37bfdd1c..e87ebc91d8e10a5b153fddc56ef26cd2a4d4baea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.3.0" +version = "0.3.1" authors = ["lumi ", "Emmanuel Gil Peyrot "] description = "A crate which provides a Jid struct for Jabber IDs." homepage = "https://gitlab.com/lumi/jid-rs" From 6eba0e7d8738137e29e2b206474ca50b18287359 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 20:35:25 +0000 Subject: [PATCH 0507/1020] Simplify check_self macro. --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 68302afe8f6a8ed199aa0cbe7af1a617d8335cc9..db57ce2698cc459f1d381889d6672d749bff18c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,9 +127,7 @@ macro_rules! generate_attribute { macro_rules! check_self { ($elem:ident, $name:tt, $ns:expr) => ( - if !$elem.is($name, $ns) { - return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); - } + check_self!($elem, $name, $ns, $name); ); ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( if !$elem.is($name, $ns) { From df069ac6c6346381e9ba267b5b6e0cd2c90e6fed Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 21:10:04 +0000 Subject: [PATCH 0508/1020] mam: Simplify parsing. --- src/mam.rs | 118 ++++++++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/mam.rs b/src/mam.rs index 65299b700be715c8efe2d0ee7685fff300fd759c..4a2872b45eac20a90ea7e80480fc45b4b53702fe 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -56,9 +56,8 @@ impl TryFrom for Query { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::MAM) { - return Err(Error::ParseError("This is not a query element.")); - } + check_self!(elem, "query", ns::MAM); + check_no_unknown_attributes!(elem, "query", ["queryid", "node"]); let mut form = None; let mut set = None; for child in elem.children() { @@ -80,9 +79,8 @@ impl TryFrom for Result_ { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("result", ns::MAM) { - return Err(Error::ParseError("This is not a result element.")); - } + check_self!(elem, "result", ns::MAM); + check_no_unknown_attributes!(elem, "result", ["queryid", "id"]); let mut forwarded = None; for child in elem.children() { if child.is("forwarded", ns::FORWARD) { @@ -106,9 +104,8 @@ impl TryFrom for Fin { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("fin", ns::MAM) { - return Err(Error::ParseError("This is not a fin element.")); - } + check_self!(elem, "fin", ns::MAM); + check_no_unknown_attributes!(elem, "fin", ["complete"]); let mut set = None; for child in elem.children() { if child.is("set", ns::RSM) { @@ -132,9 +129,8 @@ impl TryFrom for Prefs { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("prefs", ns::MAM) { - return Err(Error::ParseError("This is not a prefs element.")); - } + check_self!(elem, "prefs", ns::MAM); + check_no_unknown_attributes!(elem, "prefs", ["default"]); let mut always = vec!(); let mut never = vec!(); for child in elem.children() { @@ -167,66 +163,57 @@ impl From for Element { .ns(ns::MAM) .attr("queryid", query.queryid) .attr("node", query.node) - //.append(query.form.map(Element::from)) - .append(query.set.map(Element::from)) + //.append(query.form) + .append(query.set) .build() } } impl From for Element { fn from(result: Result_) -> Element { - let mut elem = Element::builder("result") - .ns(ns::MAM) - .attr("queryid", result.queryid) - .attr("id", result.id) - .build(); - elem.append_child(result.forwarded.into()); - elem + Element::builder("result") + .ns(ns::MAM) + .attr("queryid", result.queryid) + .attr("id", result.id) + .append(result.forwarded) + .build() } } impl From for Element { fn from(fin: Fin) -> Element { - let mut elem = Element::builder("fin") - .ns(ns::MAM) - .attr("complete", if fin.complete { Some("true") } else { None }) - .build(); - elem.append_child(fin.set.into()); - elem + Element::builder("fin") + .ns(ns::MAM) + .attr("complete", if fin.complete { Some("true") } else { None }) + .append(fin.set) + .build() + } +} + +fn serialise_jid_list(name: &str, jids: Vec) -> Option { + if jids.is_empty() { + None + } else { + Some(Element::builder(name) + .ns(ns::MAM) + .append(jids.into_iter() + .map(|jid| Element::builder("jid") + .ns(ns::MAM) + .append(String::from(jid)) + .build()) + .collect::>()) + .build()) } } impl From for Element { fn from(prefs: Prefs) -> Element { - let mut elem = Element::builder("prefs") - .ns(ns::MAM) - .attr("default", prefs.default_) - .build(); - if !prefs.always.is_empty() { - let mut always = Element::builder("always") - .ns(ns::RSM) - .build(); - for jid in prefs.always { - always.append_child(Element::builder("jid") - .ns(ns::RSM) - .append(String::from(jid)) - .build()); - } - elem.append_child(always); - } - if !prefs.never.is_empty() { - let mut never = Element::builder("never") - .ns(ns::RSM) - .build(); - for jid in prefs.never { - never.append_child(Element::builder("jid") - .ns(ns::RSM) - .append(String::from(jid)) - .build()); - } - elem.append_child(never); - } - elem + Element::builder("prefs") + .ns(ns::MAM) + .attr("default", prefs.default_) + .append(serialise_jid_list("always", prefs.always)) + .append(serialise_jid_list("never", prefs.never)) + .build() } } @@ -320,7 +307,9 @@ mod tests { #[test] fn test_prefs_get() { let elem: Element = "".parse().unwrap(); - Prefs::try_from(elem).unwrap(); + let prefs = Prefs::try_from(elem).unwrap(); + assert_eq!(prefs.always, vec!()); + assert_eq!(prefs.never, vec!()); let elem: Element = r#" @@ -328,7 +317,9 @@ mod tests { "#.parse().unwrap(); - Prefs::try_from(elem).unwrap(); + let prefs = Prefs::try_from(elem).unwrap(); + assert_eq!(prefs.always, vec!()); + assert_eq!(prefs.never, vec!()); } #[test] @@ -343,7 +334,16 @@ mod tests { "#.parse().unwrap(); - Prefs::try_from(elem).unwrap(); + let prefs = Prefs::try_from(elem).unwrap(); + assert_eq!(prefs.always, vec!(Jid::from_str("romeo@montague.lit").unwrap())); + assert_eq!(prefs.never, vec!(Jid::from_str("montague@montague.lit").unwrap())); + + let elem2 = Element::from(prefs.clone()); + println!("{:?}", elem2); + let prefs2 = Prefs::try_from(elem2).unwrap(); + assert_eq!(prefs.default_, prefs2.default_); + assert_eq!(prefs.always, prefs2.always); + assert_eq!(prefs.never, prefs2.never); } #[test] From bd9aa01ad54c9a320fc84fd3cba1329304be8e9c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 21:17:24 +0000 Subject: [PATCH 0509/1020] Split macros into their own module. --- src/lib.rs | 251 +------------------------------------------------ src/macros.rs | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+), 248 deletions(-) create mode 100644 src/macros.rs diff --git a/src/lib.rs b/src/lib.rs index db57ce2698cc459f1d381889d6672d749bff18c0..4a40ae119f69218f7f0f7a3a20a677c5377eac71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,258 +33,13 @@ extern crate blake2; extern crate chrono; extern crate try_from; -macro_rules! get_attr { - ($elem:ident, $attr:tt, $type:tt) => ( - get_attr!($elem, $attr, $type, value, value.parse()?) - ); - ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( - match $elem.attr($attr) { - Some($value) => Some($func), - None => None, - } - ); - ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( - match $elem.attr($attr) { - Some($value) => $func, - None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), - } - ); - ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( - match $elem.attr($attr) { - Some($value) => $func, - None => Default::default(), - } - ); -} - -macro_rules! generate_attribute { - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => ( - generate_attribute!($elem, $name, {$($a => $b),+}); - ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => ( - generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default); - ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => ( - #[derive(Debug, Clone, PartialEq)] - pub enum $elem { - $( - #[doc=$b] - #[doc="value for this attribute."] - $a - ),+ - } - impl FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { - Ok(match s { - $($b => $elem::$a),+, - _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), - }) - } - } - impl IntoAttributeValue for $elem { - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - $($elem::$a => $b),+ - })) - } - } - ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => ( - #[derive(Debug, Clone, PartialEq)] - pub enum $elem { - $( - #[doc=$b] - #[doc="value for this attribute."] - $a - ),+ - } - impl FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { - Ok(match s { - $($b => $elem::$a),+, - _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), - }) - } - } - impl IntoAttributeValue for $elem { - #[allow(unreachable_patterns)] - fn into_attribute_value(self) -> Option { - Some(String::from(match self { - $elem::$default => return None, - $($elem::$a => $b),+ - })) - } - } - impl Default for $elem { - fn default() -> $elem { - $elem::$default - } - } - ); -} - -macro_rules! check_self { - ($elem:ident, $name:tt, $ns:expr) => ( - check_self!($elem, $name, $ns, $name); - ); - ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( - if !$elem.is($name, $ns) { - return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); - } - ); -} - -macro_rules! check_ns_only { - ($elem:ident, $name:tt, $ns:expr) => ( - if !$elem.has_ns($ns) { - return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); - } - ); -} - -macro_rules! check_no_children { - ($elem:ident, $name:tt) => ( - for _ in $elem.children() { - return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); - } - ); -} - -macro_rules! check_no_attributes { - ($elem:ident, $name:tt) => ( - check_no_unknown_attributes!($elem, $name, []); - ); -} - -macro_rules! check_no_unknown_attributes { - ($elem:ident, $name:tt, [$($attr:tt),*]) => ( - for (_attr, _) in $elem.attrs() { - $( - if _attr == $attr { - continue; - } - )* - return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); - } - ); -} - -macro_rules! generate_empty_element { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem; - - impl TryFrom for $elem { - type Err = Error; - - fn try_from(elem: Element) -> Result<$elem, Error> { - check_self!(elem, $name, $ns); - check_no_children!(elem, $name); - check_no_attributes!(elem, $name); - Ok($elem) - } - } - - impl From<$elem> for Element { - fn from(_: $elem) -> Element { - Element::builder($name) - .ns($ns) - .build() - } - } - ); -} - -macro_rules! generate_element_with_only_attributes { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]); - ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem { - $( - $(#[$attr_meta])* - pub $attr: $attr_type - ),* - } - - impl TryFrom for $elem { - type Err = Error; - - fn try_from(elem: Element) -> Result<$elem, Error> { - check_self!(elem, $name, $ns); - check_no_children!(elem, $name); - check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - Ok($elem { - $( - $attr: get_attr!(elem, $attr_name, $attr_action) - ),* - }) - } - } - - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) - .ns($ns) - $( - .attr($attr_name, elem.$attr) - )* - .build() - } - } - ); -} - -macro_rules! generate_id { - ($elem:ident) => ( - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $elem(pub String); - impl FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { - // TODO: add a way to parse that differently when needed. - Ok($elem(String::from(s))) - } - } - impl IntoAttributeValue for $elem { - fn into_attribute_value(self) -> Option { - Some(self.0) - } - } - ); -} - -macro_rules! generate_elem_id { - ($elem:ident, $name:tt, $ns:expr) => ( - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $elem(pub String); - impl FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { - // TODO: add a way to parse that differently when needed. - Ok($elem(String::from(s))) - } - } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) - .ns($ns) - .append(elem.0) - .build() - } - } - ); -} - /// Error type returned by every parser on failure. pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; +/// Helper macros to parse and serialise more easily. +#[macro_use] +pub mod macros; #[cfg(test)] /// Namespace-aware comparison for tests diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000000000000000000000000000000000000..97f0adcdc684aa774aac7dedd433e739d766d40f --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,253 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +macro_rules! get_attr { + ($elem:ident, $attr:tt, $type:tt) => ( + get_attr!($elem, $attr, $type, value, value.parse()?) + ); + ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => Some($func), + None => None, + } + ); + ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => $func, + None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), + } + ); + ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some($value) => $func, + None => Default::default(), + } + ); +} + +macro_rules! generate_attribute { + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => ( + generate_attribute!($elem, $name, {$($a => $b),+}); + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => ( + generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default); + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => ( + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $( + #[doc=$b] + #[doc="value for this attribute."] + $a + ),+ + } + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + Ok(match s { + $($b => $elem::$a),+, + _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + $($elem::$a => $b),+ + })) + } + } + ); + ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => ( + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $( + #[doc=$b] + #[doc="value for this attribute."] + $a + ),+ + } + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + Ok(match s { + $($b => $elem::$a),+, + _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl IntoAttributeValue for $elem { + #[allow(unreachable_patterns)] + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + $elem::$default => return None, + $($elem::$a => $b),+ + })) + } + } + impl Default for $elem { + fn default() -> $elem { + $elem::$default + } + } + ); +} + +macro_rules! check_self { + ($elem:ident, $name:tt, $ns:expr) => ( + check_self!($elem, $name, $ns, $name); + ); + ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( + if !$elem.is($name, $ns) { + return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); + } + ); +} + +macro_rules! check_ns_only { + ($elem:ident, $name:tt, $ns:expr) => ( + if !$elem.has_ns($ns) { + return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); + } + ); +} + +macro_rules! check_no_children { + ($elem:ident, $name:tt) => ( + for _ in $elem.children() { + return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); + } + ); +} + +macro_rules! check_no_attributes { + ($elem:ident, $name:tt) => ( + check_no_unknown_attributes!($elem, $name, []); + ); +} + +macro_rules! check_no_unknown_attributes { + ($elem:ident, $name:tt, [$($attr:tt),*]) => ( + for (_attr, _) in $elem.attrs() { + $( + if _attr == $attr { + continue; + } + )* + return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + } + ); +} + +macro_rules! generate_empty_element { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem; + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_attributes!(elem, $name); + Ok($elem) + } + } + + impl From<$elem> for Element { + fn from(_: $elem) -> Element { + Element::builder($name) + .ns($ns) + .build() + } + } + ); +} + +macro_rules! generate_element_with_only_attributes { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem { + $( + $(#[$attr_meta])* + pub $attr: $attr_type + ),* + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action) + ),* + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + $( + .attr($attr_name, elem.$attr) + )* + .build() + } + } + ); +} + +macro_rules! generate_id { + ($elem:ident) => ( + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $elem(pub String); + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + // TODO: add a way to parse that differently when needed. + Ok($elem(String::from(s))) + } + } + impl IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + Some(self.0) + } + } + ); +} + +macro_rules! generate_elem_id { + ($elem:ident, $name:tt, $ns:expr) => ( + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $elem(pub String); + impl FromStr for $elem { + type Err = Error; + fn from_str(s: &str) -> Result<$elem, Error> { + // TODO: add a way to parse that differently when needed. + Ok($elem(String::from(s))) + } + } + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + .append(elem.0) + .build() + } + } + ); +} From 655fc9ae682bc2b9730a622f3b9cb9586a3f0816 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 21:35:07 +0000 Subject: [PATCH 0510/1020] Cargo.toml: Bump base64 to 0.7.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9db6539d876dba3d09a6c8dc03ea3a1294647394..3a447e9add42647eafb6027e1589748f88d2c73e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MPL-2.0" [dependencies] minidom = "0.6.2" jid = { version = "0.3.0", features = ["minidom"] } -base64 = "0.6.0" +base64 = "0.7.0" digest = "0.6.0" sha-1 = "0.4.0" sha2 = "0.6.0" From fb27b6225c1e28c2acaceaab44ef2be91e54809b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 21:44:43 +0000 Subject: [PATCH 0511/1020] jingle_ft: Correctly insert in . --- src/jingle_ft.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index fda8dbeffd0819f9150e00bb63c96c5269cc01a8..3339e015553ffc80c34b9b77ad66ea431db0dc11 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -166,10 +166,7 @@ impl From for Element { .build()); } if let Some(range) = file.range { - root.append_child(Element::builder("range") - .ns(ns::JINGLE_FT) - .append(range) - .build()); + root.append_child(range.into()); } for hash in file.hashes { root.append_child(hash.into()); From 054123477693dba9aa72add5121f64855e0ff3d3 Mon Sep 17 00:00:00 2001 From: lumi Date: Tue, 31 Oct 2017 22:51:29 +0100 Subject: [PATCH 0512/1020] add a changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..55a39f96f1aba2cdb8fed727f47e4ecca2a4fcf2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +Version 0.3.1, released 10-31-2017: + * Additions + - Link Mauve added a minidom::IntoElements implementation on Jid behind the "minidom" feature. ( https://gitlab.com/lumi/jid-rs/merge_requests/9 ) From 7490799acd43526dc56d518b2bcd1f35decf78cd Mon Sep 17 00:00:00 2001 From: lumi Date: Tue, 31 Oct 2017 22:53:10 +0100 Subject: [PATCH 0513/1020] fixing the date format in the change log --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55a39f96f1aba2cdb8fed727f47e4ecca2a4fcf2..63a6cc0743f99dd40ba9e0ef1fcb1e38e30112f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,3 @@ -Version 0.3.1, released 10-31-2017: +Version 0.3.1, released 2017-10-31: * Additions - Link Mauve added a minidom::IntoElements implementation on Jid behind the "minidom" feature. ( https://gitlab.com/lumi/jid-rs/merge_requests/9 ) From 4875f15201f687d0a0e22e735b35507fb49d5108 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:01:52 +0000 Subject: [PATCH 0514/1020] media_element: Parse URI separately from MediaElement. --- src/media_element.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/media_element.rs b/src/media_element.rs index b2e5857d449630ab36956cd4b2bcc46f736b558f..478dbb3117603a5dd4b0a5d7c169930f78fff114 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -18,6 +18,24 @@ pub struct URI { pub uri: String, } +impl TryFrom for URI { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "uri", ns::MEDIA_ELEMENT); + check_no_unknown_attributes!(elem, "uri", ["type"]); + check_no_children!(elem, "uri"); + let uri = elem.text().trim().to_owned(); + if uri == "" { + return Err(Error::ParseError("URI missing in uri.")); + } + Ok(URI { + type_: get_attr!(elem, "type", required), + uri: uri, + }) + } +} + impl From for Element { fn from(uri: URI) -> Element { Element::builder("uri") @@ -48,14 +66,9 @@ impl TryFrom for MediaElement { height: get_attr!(elem, "height", optional), uris: vec!(), }; - for uri in elem.children() { - if uri.is("uri", ns::MEDIA_ELEMENT) { - let type_ = get_attr!(uri, "type", required); - let text = uri.text().trim().to_owned(); - if text == "" { - return Err(Error::ParseError("URI missing in uri.")); - } - media.uris.push(URI { type_: type_, uri: text }); + for child in elem.children() { + if child.is("uri", ns::MEDIA_ELEMENT) { + media.uris.push(URI::try_from(child.clone())?); } else { return Err(Error::ParseError("Unknown child in media element.")); } From 6e48d0f65a424c60348ba8bae0c4f7e554f5dae7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:04:13 +0000 Subject: [PATCH 0515/1020] media_element: Also check for unknown attributes. --- src/media_element.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/media_element.rs b/src/media_element.rs index 478dbb3117603a5dd4b0a5d7c169930f78fff114..b700eec1af6e5fd512a5055f5472493ea636ca01 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -57,9 +57,8 @@ impl TryFrom for MediaElement { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("media", ns::MEDIA_ELEMENT) { - return Err(Error::ParseError("This is not a media element.")); - } + check_self!(elem, "media", ns::MEDIA_ELEMENT); + check_no_unknown_attributes!(elem, "media", ["width", "height"]); let mut media = MediaElement { width: get_attr!(elem, "width", optional), From 86eb65b8291a31877379a8dfc480885e20dbe2aa Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:21:27 +0000 Subject: [PATCH 0516/1020] mam: Bump jid to 0.3.1, to serialise it directly to a text node. --- Cargo.toml | 2 +- src/mam.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3a447e9add42647eafb6027e1589748f88d2c73e..cd55dfbf7f894e03f8ba0c87fd1e6da1e275d21c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ license = "MPL-2.0" [dependencies] minidom = "0.6.2" -jid = { version = "0.3.0", features = ["minidom"] } +jid = { version = "0.3.1", features = ["minidom"] } base64 = "0.7.0" digest = "0.6.0" sha-1 = "0.4.0" diff --git a/src/mam.rs b/src/mam.rs index 4a2872b45eac20a90ea7e80480fc45b4b53702fe..82158a40e01db7fc082cba3b8dbadc14f636fb5e 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -199,7 +199,7 @@ fn serialise_jid_list(name: &str, jids: Vec) -> Option { .append(jids.into_iter() .map(|jid| Element::builder("jid") .ns(ns::MAM) - .append(String::from(jid)) + .append(jid) .build()) .collect::>()) .build()) From 7950fe8f5222c5afe443ad4d1e15e88738d991d5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:21:50 +0000 Subject: [PATCH 0517/1020] rsm: Simplify serialisation. --- src/rsm.rs | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/src/rsm.rs b/src/rsm.rs index 97e7abc6182852a55f2ec36f2d732e3452d6e191..17642986488e1688e81b956688c8d0f5cb453b6e 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -88,34 +88,22 @@ impl TryFrom for Set { impl From for Element { fn from(set: Set) -> Element { - let mut elem = Element::builder("set") - .ns(ns::RSM) - .build(); - if set.after.is_some() { - elem.append_child(Element::builder("after").ns(ns::RSM).append(set.after).build()); - } - if set.before.is_some() { - elem.append_child(Element::builder("before").ns(ns::RSM).append(set.before).build()); - } - if let Some(count) = set.count { - elem.append_child(Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build()); - } - if set.first.is_some() { - elem.append_child(Element::builder("first") - .ns(ns::RSM) - .attr("index", set.first_index) - .append(set.first).build()); - } - if let Some(index) = set.index { - elem.append_child(Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build()); - } - if set.last.is_some() { - elem.append_child(Element::builder("last").ns(ns::RSM).append(set.last).build()); - } - if let Some(max) = set.max { - elem.append_child(Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build()); - } - elem + let first = set.first.clone() + .map(|first| Element::builder("first") + .ns(ns::RSM) + .attr("index", set.first_index) + .append(first) + .build()); + Element::builder("set") + .ns(ns::RSM) + .append(set.after.map(|after| Element::builder("after").ns(ns::RSM).append(after).build())) + .append(set.before.map(|before| Element::builder("before").ns(ns::RSM).append(before).build())) + .append(set.count.map(|count| Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build())) + .append(first) + .append(set.index.map(|index| Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build())) + .append(set.last.map(|last| Element::builder("last").ns(ns::RSM).append(last).build())) + .append(set.max.map(|max| Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build())) + .build() } } From 35258482ba6738f8cf02af254eb58d822028dfe8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:27:13 +0000 Subject: [PATCH 0518/1020] delay: Simplify parsing and add more checks. --- src/delay.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 53462792680991e7575a084a596a8e23af2f91dd..e95ed4c2f81791880fe29ba048be15ba3d4195e2 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -25,21 +25,16 @@ impl TryFrom for Delay { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("delay", ns::DELAY) { - return Err(Error::ParseError("This is not a delay element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in delay element.")); - } - let from = get_attr!(elem, "from", optional); - let stamp = get_attr!(elem, "stamp", required); + check_self!(elem, "delay", ns::DELAY); + check_no_children!(elem, "delay"); + check_no_unknown_attributes!(elem, "delay", ["from", "stamp"]); let data = match elem.text().as_ref() { "" => None, text => Some(text.to_owned()), }; Ok(Delay { - from: from, - stamp: stamp, + from: get_attr!(elem, "from", optional), + stamp: get_attr!(elem, "stamp", required), data: data, }) } From 1e39a4cdd7f0e4fe9753e917e318139865eb0291 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 22:31:57 +0000 Subject: [PATCH 0519/1020] jingle_s5b: Actually use the Candidate parser. --- src/jingle_s5b.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index f1396c5df4d1d1c1fdf032fa18c324a17cba229e..09b3dc56ea7bd6b6c3db874929b8740024497bcc 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -74,14 +74,7 @@ impl TryFrom for Transport { Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")), None => vec!(), }; - candidates.push(Candidate { - cid: get_attr!(child, "cid", required), - host: get_attr!(child, "host", required), - jid: get_attr!(child, "jid", required), - port: get_attr!(child, "port", optional), - priority: get_attr!(child, "priority", required), - type_: get_attr!(child, "type", default), - }); + candidates.push(Candidate::try_from(child.clone())?); TransportPayload::Candidates(candidates) } else if child.is("activated", ns::JINGLE_S5B) { if payload.is_some() { From 4fb909cfd1e5d77278132b76d1bff62ed34afe07 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 31 Oct 2017 23:33:34 +0000 Subject: [PATCH 0520/1020] data_forms: Simplify serialisation using .map(). --- src/data_forms.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index a54be9c2e7a3139915be92b22140c9b2eb58672e..9b173b8aee979c6cf3f2f9b64889e01ed4a3e1ef 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -218,8 +218,8 @@ impl From for Element { Element::builder("x") .ns(ns::DATA_FORMS) .attr("type", form.type_) - .append(if form.title.is_some() { Some(Element::builder("title").ns(ns::DATA_FORMS).append(form.title)) } else { None }) - .append(if form.instructions.is_some() { Some(Element::builder("instructions").ns(ns::DATA_FORMS).append(form.instructions)) } else { None }) + .append(form.title.map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title))) + .append(form.instructions.map(|text| Element::builder("instructions").ns(ns::DATA_FORMS).append(text))) .append(form.fields) .build() } From 8aa51bd04742916f097130e945b19bc1c9e97dd8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 1 Nov 2017 00:24:23 +0000 Subject: [PATCH 0521/1020] eme: Restore docstrings. --- src/eme.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index 540b50730df1036c450f80478b44fd77fa1738ca..5d6e7111ffabfd5060d2b752dd75a65df6ce78e8 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -12,13 +12,14 @@ use error::Error; use ns; +generate_element_with_only_attributes!( /// Structure representing an `` element. -generate_element_with_only_attributes!(ExplicitMessageEncryption, "encryption", ns::EME, [ - // Namespace of the encryption scheme used. +ExplicitMessageEncryption, "encryption", ns::EME, [ + /// Namespace of the encryption scheme used. namespace: String = "namespace" => required, - // User-friendly name for the encryption scheme, should be `None` for OTR, - // legacy OpenPGP and OX. + /// User-friendly name for the encryption scheme, should be `None` for OTR, + /// legacy OpenPGP and OX. name: Option = "name" => optional, ]); From df76bc147a6511d3df9bdf5e072d200292e29eb4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 12 Nov 2017 19:58:07 +0000 Subject: [PATCH 0522/1020] fix clippy warnings --- examples/articles.rs | 4 ++-- src/element.rs | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/articles.rs b/examples/articles.rs index 6db61d86d078032baf8ed6057770c2db0bab89ae..29b1e513464b36a0b094d0babd282c151ab24316 100644 --- a/examples/articles.rs +++ b/examples/articles.rs @@ -2,7 +2,7 @@ extern crate minidom; use minidom::Element; -const DATA: &'static str = r#" +const DATA: &str = r#"
10 Terrible Bugs You Would NEVER Believe Happened @@ -17,7 +17,7 @@ const DATA: &'static str = r#"
"#; -const ARTICLE_NS: &'static str = "article"; +const ARTICLE_NS: &str = "article"; #[derive(Debug)] pub struct Article { diff --git a/src/element.rs b/src/element.rs index 13426449a646d7463f10e129be03c17fa664047a..e398d40f74d035817dcf79b9ffcf15b78fdb08bd 100644 --- a/src/element.rs +++ b/src/element.rs @@ -290,7 +290,7 @@ impl Element { let e = reader.read_event(&mut buf)?; match e { Event::Empty(ref e) | Event::Start(ref e) => { - break build_element(&reader, e)?; + break build_element(reader, e)?; }, Event::Eof => { bail!(ErrorKind::EndOfDocument); @@ -304,12 +304,12 @@ impl Element { loop { match reader.read_event(&mut buf)? { Event::Empty(ref e) => { - let elem = build_element(&reader, e)?; + let elem = build_element(reader, e)?; // Since there is no Event::End after, directly append it to the current node stack.last_mut().unwrap().append_child(elem); }, Event::Start(ref e) => { - let elem = build_element(&reader, e)?; + let elem = build_element(reader, e)?; stack.push(elem); }, Event::End(ref e) => { @@ -348,20 +348,20 @@ impl Element { /// Like `write_to()` but without the `` prelude pub fn write_to_inner(&self, writer: &mut W) -> Result<()> { - let name = match &self.prefix { - &None => Cow::Borrowed(&self.name), - &Some(ref prefix) => Cow::Owned(format!("{}:{}", prefix, self.name)), + let name = match self.prefix { + None => Cow::Borrowed(&self.name), + Some(ref prefix) => Cow::Owned(format!("{}:{}", prefix, self.name)), }; write!(writer, "<{}", name)?; for (prefix, ns) in self.namespaces.declared_ns() { - match prefix { - &None => { + match *prefix { + None => { write!(writer, " xmlns=\"")?; write_escaped(writer, ns)?; write!(writer, "\"")?; }, - &Some(ref prefix) => { + Some(ref prefix) => { write!(writer, " xmlns:{}=\"", prefix)?; write_escaped(writer, ns)?; write!(writer, "\"")?; @@ -496,7 +496,7 @@ impl Element { /// assert_eq!(child.name(), "new"); /// ``` pub fn append_child(&mut self, child: Element) -> &mut Element { - child.namespaces.set_parent(self.namespaces.clone()); + child.namespaces.set_parent(Rc::clone(&self.namespaces)); self.children.push(Node::Element(child)); if let Node::Element(ref mut cld) = *self.children.last_mut().unwrap() { @@ -639,12 +639,12 @@ fn build_element(reader: &EventReader, event: &BytesStart) -> Res Ok((key, value)) }) .filter(|o| { - match o { - &Ok((ref key, ref value)) if key == "xmlns" => { + match *o { + Ok((ref key, ref value)) if key == "xmlns" => { namespaces.insert(None, value.to_owned()); false }, - &Ok((ref key, ref value)) if key.starts_with("xmlns:") => { + Ok((ref key, ref value)) if key.starts_with("xmlns:") => { namespaces.insert(Some(key[6..].to_owned()), value.to_owned()); false }, @@ -799,7 +799,7 @@ impl ElementBuilder { // Propagate namespaces for node in &element.children { if let Node::Element(ref e) = *node { - e.namespaces.set_parent(element.namespaces.clone()); + e.namespaces.set_parent(Rc::clone(&element.namespaces)); } } From c1ff291c11b78a153298201d893f630e5e185a37 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 15 Nov 2017 18:37:28 +0000 Subject: [PATCH 0523/1020] Update all crypto crates to their latest release; un-break blake2b. --- Cargo.toml | 10 +++++----- src/caps.rs | 2 +- src/ecaps2.rs | 10 ++-------- src/lib.rs | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cd55dfbf7f894e03f8ba0c87fd1e6da1e275d21c..4cf04e7430fb0e36ab5b639b7a40f804a7b5ec0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,11 @@ license = "MPL-2.0" minidom = "0.6.2" jid = { version = "0.3.1", features = ["minidom"] } base64 = "0.7.0" -digest = "0.6.0" -sha-1 = "0.4.0" -sha2 = "0.6.0" -sha3 = "0.6.0" -blake2 = "0.6.1" +digest = "0.7.1" +sha-1 = "0.7.0" +sha2 = "0.7.0" +sha3 = "0.7.0" +blake2 = "0.7.0" chrono = "0.4.0" try_from = "0.2.2" diff --git a/src/caps.rs b/src/caps.rs index 2b986795daa7ab7b9c243fdd6e1a0654353d27bb..27d511ca1ea6054482e23e5eeeb6465ec4d40369 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -16,7 +16,7 @@ use ns; use base64; use digest::Digest; -use sha_1::Sha1; +use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; //use blake2::Blake2b; diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 347d1dac8e71074fcee18191804a54f6fab64aad..b4c407c29c8bb89c0d4ee39222fbc68ff2d76530 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -15,11 +15,10 @@ use error::Error; use ns; use base64; -use digest::Digest; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -//use blake2::Blake2b; -//use digest::{Digest, VariableOutput}; +use blake2::Blake2b; +use digest::{Digest, VariableOutput}; #[derive(Debug, Clone)] pub struct ECaps2 { @@ -144,9 +143,6 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { let hash = hasher.result(); get_hash_vec(hash.as_slice()) }, - Algo::Blake2b_256 - | Algo::Blake2b_512 => panic!("See https://github.com/RustCrypto/hashes/issues/34"), - /* Algo::Blake2b_256 => { let mut hasher = Blake2b::default(); hasher.input(data); @@ -161,7 +157,6 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { let hash = hasher.variable_result(&mut buf).unwrap(); get_hash_vec(hash) }, - */ Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, @@ -452,7 +447,6 @@ mod tests { } #[test] - #[ignore] fn test_blake2b_512() { let hash = ecaps2::hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); let known_hash: Vec = vec!( diff --git a/src/lib.rs b/src/lib.rs index 4a40ae119f69218f7f0f7a3a20a677c5377eac71..e5b5439c1c9f04b0926a4225624ec48ad2a61078 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ extern crate minidom; extern crate jid; extern crate base64; extern crate digest; -extern crate sha_1; +extern crate sha1; extern crate sha2; extern crate sha3; extern crate blake2; From 702c4d2932adf188be9a0273ae85c4e2586e9cd6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 15 Nov 2017 23:15:12 +0000 Subject: [PATCH 0524/1020] ecaps2: Use the digest() facility to simplify hashing. --- src/ecaps2.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index b4c407c29c8bb89c0d4ee39222fbc68ff2d76530..587418b88c0dc7831670247c1ecebebe4dad9abd 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -120,27 +120,19 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { Ok(Hash { hash: match algo { Algo::Sha_256 => { - let mut hasher = Sha256::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha256::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha_512 => { - let mut hasher = Sha512::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha512::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha3_256 => { - let mut hasher = Sha3_256::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha3_256::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha3_512 => { - let mut hasher = Sha3_512::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha3_512::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Blake2b_256 => { From 9c9ffe70a75d6f5a165a397bc1e4c2918a3ab9d1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 15 Nov 2017 23:16:37 +0000 Subject: [PATCH 0525/1020] caps: Do the same fixes, unbreak blake2 and simplify code. --- src/caps.rs | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index 27d511ca1ea6054482e23e5eeeb6465ec4d40369..05f84e1e3a393cab83893b217252a59d8cb4f179 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -15,12 +15,11 @@ use error::Error; use ns; use base64; -use digest::Digest; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -//use blake2::Blake2b; -//use digest::{Digest, VariableOutput}; +use blake2::Blake2b; +use digest::{Digest, VariableOutput}; #[derive(Debug, Clone)] pub struct Caps { @@ -141,38 +140,25 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { Ok(Hash { hash: match algo { Algo::Sha_1 => { - let mut hasher = Sha1::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha1::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha_256 => { - let mut hasher = Sha256::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha256::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha_512 => { - let mut hasher = Sha512::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha512::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha3_256 => { - let mut hasher = Sha3_256::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha3_256::digest(data); get_hash_vec(hash.as_slice()) }, Algo::Sha3_512 => { - let mut hasher = Sha3_512::default(); - hasher.input(data); - let hash = hasher.result(); + let hash = Sha3_512::digest(data); get_hash_vec(hash.as_slice()) }, - Algo::Blake2b_256 - | Algo::Blake2b_512 => panic!("See https://github.com/RustCrypto/hashes/issues/34"), - /* Algo::Blake2b_256 => { let mut hasher = Blake2b::default(); hasher.input(data); @@ -187,7 +173,6 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { let hash = hasher.variable_result(&mut buf).unwrap(); get_hash_vec(hash) }, - */ Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, algo: algo, From 77e150c63d81c448088e43bb1208bb013aa05f47 Mon Sep 17 00:00:00 2001 From: Rust Cambridge Mob Date: Thu, 16 Nov 2017 20:17:11 +0000 Subject: [PATCH 0526/1020] Implement macro for elements containing text --- src/ibb.rs | 45 +++++++++++++++------------------------------ src/macros.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/ibb.rs b/src/ibb.rs index 7d7f3e73fa501e4a7ffd63e1586bac52ba8532ee..cc2d89c3c11fba3eb704ee87da6ed11c674a5f76 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -25,42 +25,27 @@ generate_element_with_only_attributes!(Open, "open", ns::IBB, [ stanza: Stanza = "stanza" => default, ]); -#[derive(Debug, Clone)] -pub struct Data { - pub seq: u16, - pub sid: String, - pub data: Vec, -} +/// Codec wrapping base64 encode/decode +struct Base64; -impl TryFrom for Data { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("data", ns::IBB) { - return Err(Error::ParseError("This is not a data element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in data element.")); - } - Ok(Data { - seq: get_attr!(elem, "seq", required), - sid: get_attr!(elem, "sid", required), - data: base64::decode(&elem.text())?, - }) +impl Base64 { + fn decode(s: &str) -> Result, Error> { + Ok(base64::decode(s)?) } -} -impl From for Element { - fn from(data: Data) -> Element { - Element::builder("data") - .ns(ns::IBB) - .attr("seq", data.seq) - .attr("sid", data.sid) - .append(base64::encode(&data.data)) - .build() + fn encode(b: &Vec) -> String { + base64::encode(b) } } +generate_element_with_text!(Data, "data", ns::IBB, + [ + seq: u16 = "seq" => required, + sid: String = "sid" => required + ], + data: Base64> +); + generate_element_with_only_attributes!(Close, "close", ns::IBB, [ sid: String = "sid" => required, ]); diff --git a/src/macros.rs b/src/macros.rs index 97f0adcdc684aa774aac7dedd433e739d766d40f..155767ac0bb5b5169bae421d31426a1c61c553f5 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -251,3 +251,45 @@ macro_rules! generate_elem_id { } ); } + +macro_rules! generate_element_with_text { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], $text_ident:ident: $codec:ident < $text_type:ty >) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem { + $( + $(#[$attr_meta])* + pub $attr: $attr_type + ),*, + pub $text_ident: $text_type, + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action) + ),*, + $text_ident: $codec::decode(&elem.text())?, + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + $( + .attr($attr_name, elem.$attr) + )* + .append($codec::encode(&elem.$text_ident)) + .build() + } + } + ); +} From 3e37beffe25307a31815a2c7ea21c82f2a9d1ad8 Mon Sep 17 00:00:00 2001 From: Rust Cambridge Mob Date: Thu, 16 Nov 2017 21:00:01 +0000 Subject: [PATCH 0527/1020] Implement macro for elements containing children --- src/disco.rs | 62 +++++++++++++-------------------------------------- src/macros.rs | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 46 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 9964cd8c9e625e8592b3ba4587db60c3ac6a0ae3..b4efeee9d3ad9ed1db0b43a6010547478498f91f 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -201,52 +201,22 @@ Item, "item", ns::DISCO_ITEMS, [ name: Option = "name" => optional, ]); -/// Structure representing a `` element. -/// -/// It should only be used in an ``, as it can only -/// represent the result, and not a request. -#[derive(Debug, Clone)] -pub struct DiscoItemsResult { - /// Node on which we have done this discovery. - pub node: Option, - - /// List of items pointed by this entity. - pub items: Vec, -} - -impl TryFrom for DiscoItemsResult { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::DISCO_ITEMS, "disco#items query"); - check_no_unknown_attributes!(elem, "disco#items query", ["node"]); - - let mut items: Vec = vec!(); - for child in elem.children() { - if child.is("item", ns::DISCO_ITEMS) { - items.push(Item::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown element in disco#items.")); - } - } - - Ok(DiscoItemsResult { - node: get_attr!(elem, "node", optional), - items: items, - }) - } -} - -impl From for Element { - fn from(disco: DiscoItemsResult) -> Element { - Element::builder("query") - .ns(ns::DISCO_ITEMS) - .attr("node", disco.node) - .append(disco.items) - .build() - } -} +generate_element_with_children!( + /// Structure representing a `` element. + /// + /// It should only be used in an ``, as it can only + /// represent the result, and not a request. + DiscoItemsResult, "query", ns::DISCO_ITEMS, + attributes: [ + /// Node on which we have done this discovery. + node: Option = "node" => optional + ], + children: [ + /// List of items pointed by this entity. + items: Vec = "item" => Item + ] +); #[cfg(test)] mod tests { diff --git a/src/macros.rs b/src/macros.rs index 155767ac0bb5b5169bae421d31426a1c61c553f5..136b32f97ce4b1fdd7667b5e53eb8e6d417fe49b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -293,3 +293,55 @@ macro_rules! generate_element_with_text { } ); } + +macro_rules! generate_element_with_children { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = $child_name:tt => $child_constructor:ident),+]) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem { + $( + $(#[$attr_meta])* + pub $attr: $attr_type + ),*, + $( + $(#[$child_meta])* + pub $child_ident: Vec<$child_type> + ),* + } + + impl TryFrom for $elem { + type Err = Error; + + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + let mut parsed_children = vec!(); + for child in elem.children() { + $( + let parsed_child = $child_constructor::try_from(child.clone())?; + parsed_children.push(parsed_child); + )* + } + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action) + ),*, + $( + $child_ident: parsed_children + )* + }) + } + } + + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + $( + .attr($attr_name, elem.$attr) + )* + .build() + } + } + ); +} From ad17c877f5c5fe82d3457d2fd129a9be01a69ed0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:00:47 +0000 Subject: [PATCH 0528/1020] Move Base64 codec into a helper module. --- src/helpers.rs | 21 +++++++++++++++++++++ src/ibb.rs | 15 +-------------- src/lib.rs | 2 ++ 3 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 src/helpers.rs diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..e12000ac9e85540b88839c0cf60b2ea425aab4c5 --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,21 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use base64; +use error::Error; + +/// Codec wrapping base64 encode/decode +pub struct Base64; + +impl Base64 { + pub fn decode(s: &str) -> Result, Error> { + Ok(base64::decode(s)?) + } + + pub fn encode(b: &Vec) -> String { + base64::encode(b) + } +} diff --git a/src/ibb.rs b/src/ibb.rs index cc2d89c3c11fba3eb704ee87da6ed11c674a5f76..d7717a7b7105cc23258c0d0f36faec4af7b4b089 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -8,11 +8,11 @@ use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; -use base64; use error::Error; use ns; +use helpers::Base64; generate_attribute!(Stanza, "stanza", { Iq => "iq", @@ -25,19 +25,6 @@ generate_element_with_only_attributes!(Open, "open", ns::IBB, [ stanza: Stanza = "stanza" => default, ]); -/// Codec wrapping base64 encode/decode -struct Base64; - -impl Base64 { - fn decode(s: &str) -> Result, Error> { - Ok(base64::decode(s)?) - } - - fn encode(b: &Vec) -> String { - base64::encode(b) - } -} - generate_element_with_text!(Data, "data", ns::IBB, [ seq: u16 = "seq" => required, diff --git a/src/lib.rs b/src/lib.rs index e5b5439c1c9f04b0926a4225624ec48ad2a61078..2bc12e05e40ff2e0ba15a3053fb48f483204ff97 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,8 @@ extern crate try_from; pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; +/// Various helpers. +pub mod helpers; /// Helper macros to parse and serialise more easily. #[macro_use] pub mod macros; From 3f57edfc277c81d1d83b7f0447b33163dda95ef2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 15:52:06 +0000 Subject: [PATCH 0529/1020] hashes: Use the new helpers. --- src/hashes.rs | 49 ++++++++++--------------------------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index bcaf94ac1e874ca641239c74a8987711364bd500..535f99c536b5ebb4d4a0232cc0eb4af2a0350a88 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -12,8 +12,7 @@ use minidom::{Element, IntoAttributeValue}; use error::Error; use ns; - -use base64; +use helpers::Base64; #[allow(non_camel_case_types)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -68,47 +67,19 @@ impl IntoAttributeValue for Algo { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Hash { - pub algo: Algo, - pub hash: Vec, -} - -impl TryFrom for Hash { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("hash", ns::HASHES) { - return Err(Error::ParseError("This is not a hash element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in hash element.")); - } - let algo = get_attr!(elem, "algo", required); - let hash = match elem.text().as_ref() { - "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), - text => base64::decode(text)?, - }; - Ok(Hash { - algo: algo, - hash: hash, - }) - } -} - -impl From for Element { - fn from(hash: Hash) -> Element { - Element::builder("hash") - .ns(ns::HASHES) - .attr("algo", hash.algo) - .append(base64::encode(&hash.hash)) - .build() - } -} +generate_element_with_text!( + #[derive(PartialEq)] + Hash, "hash", ns::HASHES, + [ + algo: Algo = "algo" => required + ], + hash: Base64> +); #[cfg(test)] mod tests { use super::*; + use base64; #[test] fn test_simple() { From cb6eb55686c07200c2bac2a2c98b87a5fda075d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:06:35 +0000 Subject: [PATCH 0530/1020] macros: Uniformise trailing commas handling. --- src/macros.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 136b32f97ce4b1fdd7667b5e53eb8e6d417fe49b..2d02a0330d0f7cf16c76abdd9542d0397ad40f6c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -179,8 +179,8 @@ macro_rules! generate_element_with_only_attributes { pub struct $elem { $( $(#[$attr_meta])* - pub $attr: $attr_type - ),* + pub $attr: $attr_type, + )* } impl TryFrom for $elem { @@ -192,8 +192,8 @@ macro_rules! generate_element_with_only_attributes { check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); Ok($elem { $( - $attr: get_attr!(elem, $attr_name, $attr_action) - ),* + $attr: get_attr!(elem, $attr_name, $attr_action), + )* }) } } @@ -259,8 +259,8 @@ macro_rules! generate_element_with_text { pub struct $elem { $( $(#[$attr_meta])* - pub $attr: $attr_type - ),*, + pub $attr: $attr_type, + )* pub $text_ident: $text_type, } @@ -273,8 +273,8 @@ macro_rules! generate_element_with_text { check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); Ok($elem { $( - $attr: get_attr!(elem, $attr_name, $attr_action) - ),*, + $attr: get_attr!(elem, $attr_name, $attr_action), + )* $text_ident: $codec::decode(&elem.text())?, }) } @@ -301,12 +301,12 @@ macro_rules! generate_element_with_children { pub struct $elem { $( $(#[$attr_meta])* - pub $attr: $attr_type - ),*, + pub $attr: $attr_type, + )* $( $(#[$child_meta])* - pub $child_ident: Vec<$child_type> - ),* + pub $child_ident: Vec<$child_type>, + )* } impl TryFrom for $elem { @@ -324,10 +324,10 @@ macro_rules! generate_element_with_children { } Ok($elem { $( - $attr: get_attr!(elem, $attr_name, $attr_action) - ),*, + $attr: get_attr!(elem, $attr_name, $attr_action), + )* $( - $child_ident: parsed_children + $child_ident: parsed_children, )* }) } From 32e373be03d78449a3ee76929ff19514ee6a43f4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:19:04 +0000 Subject: [PATCH 0531/1020] helpers: Add a plain text codec. --- src/helpers.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index e12000ac9e85540b88839c0cf60b2ea425aab4c5..a457081db560b7f5018ff91fcbffa0eb776e5c26 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -7,6 +7,24 @@ use base64; use error::Error; +/// Codec for plain text content. +pub struct PlainText; + +impl PlainText { + pub fn decode(s: &str) -> Result, Error> { + Ok(match s { + "" => None, + text => Some(text.to_owned()), + }) + } + + pub fn encode(string: &Option) -> Option { + string.as_ref().map(|text| { + text.to_owned() + }) + } +} + /// Codec wrapping base64 encode/decode pub struct Base64; @@ -15,7 +33,7 @@ impl Base64 { Ok(base64::decode(s)?) } - pub fn encode(b: &Vec) -> String { - base64::encode(b) + pub fn encode(b: &Vec) -> Option { + Some(base64::encode(b)) } } From 682bb34b0bf76f1664337e5d1dd639dfca0c86fe Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:19:24 +0000 Subject: [PATCH 0532/1020] delay: Use the new helper macro. --- src/delay.rs | 44 ++++++++------------------------------------ 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index e95ed4c2f81791880fe29ba048be15ba3d4195e2..6904ca838edc35b415f8ad6218d16f9887476b7b 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -13,43 +13,15 @@ use error::Error; use jid::Jid; use ns; +use helpers::PlainText; -#[derive(Debug, Clone)] -pub struct Delay { - pub from: Option, - pub stamp: DateTime, - pub data: Option, -} - -impl TryFrom for Delay { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "delay", ns::DELAY); - check_no_children!(elem, "delay"); - check_no_unknown_attributes!(elem, "delay", ["from", "stamp"]); - let data = match elem.text().as_ref() { - "" => None, - text => Some(text.to_owned()), - }; - Ok(Delay { - from: get_attr!(elem, "from", optional), - stamp: get_attr!(elem, "stamp", required), - data: data, - }) - } -} - -impl From for Element { - fn from(delay: Delay) -> Element { - Element::builder("delay") - .ns(ns::DELAY) - .attr("from", delay.from) - .attr("stamp", delay.stamp) - .append(delay.data) - .build() - } -} +generate_element_with_text!(Delay, "delay", ns::DELAY, + [ + from: Option = "from" => optional, + stamp: DateTime = "stamp" => required + ], + data: PlainText> +); #[cfg(test)] mod tests { From d78a0e6daebdcfce7bc32b02d1a218189cac5686 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:30:53 +0000 Subject: [PATCH 0533/1020] helpers, disco: Add missing serialisation, and add a test. --- src/disco.rs | 2 ++ src/macros.rs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index b4efeee9d3ad9ed1db0b43a6010547478498f91f..72dc934a499d1e34af95f83720388811084352e0 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -377,6 +377,8 @@ mod tests { fn test_answers_items_result() { let elem: Element = "".parse().unwrap(); let query = DiscoItemsResult::try_from(elem).unwrap(); + let elem2 = Element::from(query); + let query = DiscoItemsResult::try_from(elem2).unwrap(); assert_eq!(query.items.len(), 2); assert_eq!(query.items[0].jid, Jid::from_str("component").unwrap()); assert_eq!(query.items[0].node, None); diff --git a/src/macros.rs b/src/macros.rs index 2d02a0330d0f7cf16c76abdd9542d0397ad40f6c..9177009dcf00129e31fe331a03cfc850a83e1277 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -340,6 +340,9 @@ macro_rules! generate_element_with_children { $( .attr($attr_name, elem.$attr) )* + $( + .append(elem.$child_ident) + )* .build() } } From 0ead24a04189754f08b64a261cb5d7fed9126654 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:32:18 +0000 Subject: [PATCH 0534/1020] helpers, disco: Parse children based on their name and namespace. --- src/disco.rs | 2 +- src/macros.rs | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 72dc934a499d1e34af95f83720388811084352e0..c48c863c8ce963c6f6bf8d6cb3ebebdd60587b6e 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -214,7 +214,7 @@ generate_element_with_children!( ], children: [ /// List of items pointed by this entity. - items: Vec = "item" => Item + items: Vec = ("item", ns::DISCO_ITEMS) => Item ] ); diff --git a/src/macros.rs b/src/macros.rs index 9177009dcf00129e31fe331a03cfc850a83e1277..b1d9374cceb4598144021fc3319e30f675993045 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -295,7 +295,7 @@ macro_rules! generate_element_with_text { } macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = $child_name:tt => $child_constructor:ident),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:expr) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -318,9 +318,13 @@ macro_rules! generate_element_with_children { let mut parsed_children = vec!(); for child in elem.children() { $( - let parsed_child = $child_constructor::try_from(child.clone())?; - parsed_children.push(parsed_child); + if child.is($child_name, $child_ns) { + let parsed_child = $child_constructor::try_from(child.clone())?; + parsed_children.push(parsed_child); + continue; + } )* + return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( From 988b6a61601d27bf3fe4bf3b9847647be30e35d4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:33:08 +0000 Subject: [PATCH 0535/1020] jingle_ft: Parse using the new helper. --- src/jingle_ft.rs | 46 +++++++++++----------------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 3339e015553ffc80c34b9b77ad66ea431db0dc11..53730a328c756c521838adb4911bf173649e1d01 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -18,41 +18,17 @@ use minidom::{Element, IntoAttributeValue}; use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub struct Range { - pub offset: u64, - pub length: Option, - pub hashes: Vec, -} - -impl TryFrom for Range { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "range", ns::JINGLE_FT); - check_no_unknown_attributes!(elem, "range", ["offset", "length"]); - let mut hashes = vec!(); - for child in elem.children() { - hashes.push(Hash::try_from(child.clone())?); - } - Ok(Range { - offset: get_attr!(elem, "offset", default), - length: get_attr!(elem, "length", optional), - hashes: hashes, - }) - } -} - -impl From for Element { - fn from(range: Range) -> Element { - Element::builder("range") - .ns(ns::JINGLE_FT) - .attr("offset", if range.offset == 0 { None } else { Some(range.offset) }) - .attr("length", range.length) - .append(range.hashes) - .build() - } -} +generate_element_with_children!( + #[derive(PartialEq)] + Range, "range", ns::JINGLE_FT, + attributes: [ + offset: u64 = "offset" => default, + length: Option = "length" => optional + ], + children: [ + hashes: Vec = ("hash", ns::HASHES) => Hash + ] +); type Lang = String; From e4cfc1b8671cdbc75e75beef4f3ed9b719105e1f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:37:04 +0000 Subject: [PATCH 0536/1020] jingle_ft: Simplify serialisation. --- src/jingle_ft.rs | 54 +++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 53730a328c756c521838adb4911bf173649e1d01..bf584380d40b5c30510bafd78542c62c44a47df1 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -108,46 +108,45 @@ impl TryFrom for File { impl From for Element { fn from(file: File) -> Element { let mut root = Element::builder("file") - .ns(ns::JINGLE_FT) - .build(); + .ns(ns::JINGLE_FT); if let Some(date) = file.date { - root.append_child(Element::builder("date") - .ns(ns::JINGLE_FT) - .append(date) - .build()); + root = root.append(Element::builder("date") + .ns(ns::JINGLE_FT) + .append(date) + .build()); } if let Some(media_type) = file.media_type { - root.append_child(Element::builder("media-type") - .ns(ns::JINGLE_FT) - .append(media_type) - .build()); + root = root.append(Element::builder("media-type") + .ns(ns::JINGLE_FT) + .append(media_type) + .build()); } if let Some(name) = file.name { - root.append_child(Element::builder("name") - .ns(ns::JINGLE_FT) - .append(name) - .build()); + root = root.append(Element::builder("name") + .ns(ns::JINGLE_FT) + .append(name) + .build()); } for (lang, desc) in file.descs.into_iter() { - root.append_child(Element::builder("desc") - .ns(ns::JINGLE_FT) - .attr("xml:lang", lang) - .append(desc.0) - .build()); + root = root.append(Element::builder("desc") + .ns(ns::JINGLE_FT) + .attr("xml:lang", lang) + .append(desc.0) + .build()); } if let Some(size) = file.size { - root.append_child(Element::builder("size") - .ns(ns::JINGLE_FT) - .append(format!("{}", size)) - .build()); + root = root.append(Element::builder("size") + .ns(ns::JINGLE_FT) + .append(format!("{}", size)) + .build()); } if let Some(range) = file.range { - root.append_child(range.into()); + root = root.append(range); } for hash in file.hashes { - root.append_child(hash.into()); + root = root.append(hash); } - root + root.build() } } #[derive(Debug, Clone)] @@ -179,10 +178,9 @@ impl TryFrom for Description { impl From for Element { fn from(description: Description) -> Element { - let file: Element = description.file.into(); Element::builder("description") .ns(ns::JINGLE_FT) - .append(file) + .append(description.file) .build() } } From cec581baa02e3b13505ef4a01b35de33c11fd47a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 23 Nov 2017 16:45:05 +0000 Subject: [PATCH 0537/1020] roster: Parse query using the new helpers. --- src/roster.rs | 71 +++++++++++++-------------------------------------- 1 file changed, 18 insertions(+), 53 deletions(-) diff --git a/src/roster.rs b/src/roster.rs index 38b183f1d29b6c4dda1a1fb7a750c14e05612724..cb57ed3fde0d0c0847cff03cbcb7fe5a071b3cf5 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -82,57 +82,22 @@ impl From for Element { } } -/// The contact list of the user. -#[derive(Debug, Clone)] -pub struct Roster { - /// Version of the contact list. - /// - /// This is an opaque string that should only be sent back to the server on - /// a new connection, if this client is storing the contact list between - /// connections. - pub ver: Option, - - /// List of the contacts of the user. - pub items: Vec, -} - -impl TryFrom for Roster { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::ROSTER) { - return Err(Error::ParseError("This is not a roster element.")); - } - for (attr, _) in elem.attrs() { - if attr != "ver" { - return Err(Error::ParseError("Unknown attribute in roster element.")); - } - } - - let mut roster = Roster { - ver: get_attr!(elem, "ver", optional), - items: vec!(), - }; - for child in elem.children() { - if !child.is("item", ns::ROSTER) { - return Err(Error::ParseError("Unknown element in roster element.")); - } - let item = Item::try_from(child.clone())?; - roster.items.push(item); - } - Ok(roster) - } -} - -impl From for Element { - fn from(roster: Roster) -> Element { - Element::builder("query") - .ns(ns::ROSTER) - .attr("ver", roster.ver) - .append(roster.items) - .build() - } -} +generate_element_with_children!( + /// The contact list of the user. + Roster, "query", ns::ROSTER, + attributes: [ + /// Version of the contact list. + /// + /// This is an opaque string that should only be sent back to the server on + /// a new connection, if this client is storing the contact list between + /// connections. + ver: Option = "ver" => optional + ], + children: [ + /// List of the contacts of the user. + items: Vec = ("item", ns::ROSTER) => Item + ] +); #[cfg(test)] mod tests { @@ -256,7 +221,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown element in roster element."); + assert_eq!(message, "Unknown child in query element."); let elem: Element = "".parse().unwrap(); let error = Roster::try_from(elem).unwrap_err(); @@ -264,7 +229,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown attribute in roster element."); + assert_eq!(message, "Unknown attribute in query element."); } #[test] From 2661259e9aaac6f2bef98a5c3528c18e8d20edfc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 04:27:35 +0000 Subject: [PATCH 0538/1020] chatstates: Generate ChatState automatically. --- src/chatstates.rs | 60 ++++++++++++----------------------------------- src/macros.rs | 36 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/src/chatstates.rs b/src/chatstates.rs index e226f21d3bb72cc620bb3b7485fad57d89602986..6858c1147a012896e0afe467e7d31beabcd1c814 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -14,56 +14,26 @@ use error::Error; use ns; -/// Enum representing chatstate elements part of the -/// `http://jabber.org/protocol/chatstates` namespace. -#[derive(Debug, Clone)] -pub enum ChatState { - /// `` - Active, +generate_element_enum!( + /// Enum representing chatstate elements part of the + /// `http://jabber.org/protocol/chatstates` namespace. + ChatState, "chatstate", ns::CHATSTATES, { + /// `` + Active => "active", - /// `` - Composing, + /// `` + Composing => "composing", - /// `` - Gone, + /// `` + Gone => "gone", - /// `` - Inactive, + /// `` + Inactive => "inactive", - /// `` - Paused, -} - -impl TryFrom for ChatState { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_ns_only!(elem, "chatstate", ns::CHATSTATES); - check_no_children!(elem, "chatstate"); - check_no_attributes!(elem, "chatstate"); - Ok(match elem.name() { - "active" => ChatState::Active, - "composing" => ChatState::Composing, - "gone" => ChatState::Gone, - "inactive" => ChatState::Inactive, - "paused" => ChatState::Paused, - _ => return Err(Error::ParseError("This is not a chatstate element.")), - }) - } -} - -impl From for Element { - fn from(chatstate: ChatState) -> Element { - Element::builder(match chatstate { - ChatState::Active => "active", - ChatState::Composing => "composing", - ChatState::Gone => "gone", - ChatState::Inactive => "inactive", - ChatState::Paused => "paused", - }).ns(ns::CHATSTATES) - .build() + /// `` + Paused => "paused", } -} +); #[cfg(test)] mod tests { diff --git a/src/macros.rs b/src/macros.rs index b1d9374cceb4598144021fc3319e30f675993045..1569470a12ffa4bdece6b75c8859c347518d5408 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -96,6 +96,42 @@ macro_rules! generate_attribute { ); } +macro_rules! generate_element_enum { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( + generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+}); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $( + $(#[$enum_meta])* + $enum + ),+ + } + impl TryFrom for $elem { + type Err = Error; + fn try_from(elem: Element) -> Result<$elem, Error> { + check_ns_only!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_attributes!(elem, $name); + Ok(match elem.name() { + $($enum_name => $elem::$enum,)+ + _ => return Err(Error::ParseError(concat!("This is not a ", $name, " element."))), + }) + } + } + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder(match elem { + $($elem::$enum => $enum_name,)+ + }).ns($ns) + .build() + } + } + ); +} + macro_rules! check_self { ($elem:ident, $name:tt, $ns:expr) => ( check_self!($elem, $name, $ns, $name); From ef04d2e52441e2e86053e955de63b982ee8d0bee Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 04:30:33 +0000 Subject: [PATCH 0539/1020] stanza_error: Generate DefinedCondition automatically. --- src/stanza_error.rs | 115 ++++++++++---------------------------------- 1 file changed, 26 insertions(+), 89 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index bbc31623bcf98827e438b2470255887de90c1096..4548864cf4e59116ee4c6c63b0e17f86ef78e8a0 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -8,7 +8,7 @@ use try_from::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, IntoAttributeValue}; use error::Error; use jid::Jid; @@ -22,93 +22,30 @@ generate_attribute!(ErrorType, "type", { Wait => "wait", }); -#[derive(Debug, Clone, PartialEq)] -pub enum DefinedCondition { - BadRequest, - Conflict, - FeatureNotImplemented, - Forbidden, - Gone, - InternalServerError, - ItemNotFound, - JidMalformed, - NotAcceptable, - NotAllowed, - NotAuthorized, - PolicyViolation, - RecipientUnavailable, - Redirect, - RegistrationRequired, - RemoteServerNotFound, - RemoteServerTimeout, - ResourceConstraint, - ServiceUnavailable, - SubscriptionRequired, - UndefinedCondition, - UnexpectedRequest, -} - -impl FromStr for DefinedCondition { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "bad-request" => DefinedCondition::BadRequest, - "conflict" => DefinedCondition::Conflict, - "feature-not-implemented" => DefinedCondition::FeatureNotImplemented, - "forbidden" => DefinedCondition::Forbidden, - "gone" => DefinedCondition::Gone, - "internal-server-error" => DefinedCondition::InternalServerError, - "item-not-found" => DefinedCondition::ItemNotFound, - "jid-malformed" => DefinedCondition::JidMalformed, - "not-acceptable" => DefinedCondition::NotAcceptable, - "not-allowed" => DefinedCondition::NotAllowed, - "not-authorized" => DefinedCondition::NotAuthorized, - "policy-violation" => DefinedCondition::PolicyViolation, - "recipient-unavailable" => DefinedCondition::RecipientUnavailable, - "redirect" => DefinedCondition::Redirect, - "registration-required" => DefinedCondition::RegistrationRequired, - "remote-server-not-found" => DefinedCondition::RemoteServerNotFound, - "remote-server-timeout" => DefinedCondition::RemoteServerTimeout, - "resource-constraint" => DefinedCondition::ResourceConstraint, - "service-unavailable" => DefinedCondition::ServiceUnavailable, - "subscription-required" => DefinedCondition::SubscriptionRequired, - "undefined-condition" => DefinedCondition::UndefinedCondition, - "unexpected-request" => DefinedCondition::UnexpectedRequest, - - _ => return Err(Error::ParseError("Unknown defined-condition.")), - }) - } -} - -impl IntoElements for DefinedCondition { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(Element::builder(match self { - DefinedCondition::BadRequest => "bad-request", - DefinedCondition::Conflict => "conflict", - DefinedCondition::FeatureNotImplemented => "feature-not-implemented", - DefinedCondition::Forbidden => "forbidden", - DefinedCondition::Gone => "gone", - DefinedCondition::InternalServerError => "internal-server-error", - DefinedCondition::ItemNotFound => "item-not-found", - DefinedCondition::JidMalformed => "jid-malformed", - DefinedCondition::NotAcceptable => "not-acceptable", - DefinedCondition::NotAllowed => "not-allowed", - DefinedCondition::NotAuthorized => "not-authorized", - DefinedCondition::PolicyViolation => "policy-violation", - DefinedCondition::RecipientUnavailable => "recipient-unavailable", - DefinedCondition::Redirect => "redirect", - DefinedCondition::RegistrationRequired => "registration-required", - DefinedCondition::RemoteServerNotFound => "remote-server-not-found", - DefinedCondition::RemoteServerTimeout => "remote-server-timeout", - DefinedCondition::ResourceConstraint => "resource-constraint", - DefinedCondition::ServiceUnavailable => "service-unavailable", - DefinedCondition::SubscriptionRequired => "subscription-required", - DefinedCondition::UndefinedCondition => "undefined-condition", - DefinedCondition::UnexpectedRequest => "unexpected-request", - }).ns(ns::XMPP_STANZAS).build()); - } -} +generate_element_enum!(DefinedCondition, "condition", ns::XMPP_STANZAS, { + BadRequest => "bad-request", + Conflict => "conflict", + FeatureNotImplemented => "feature-not-implemented", + Forbidden => "forbidden", + Gone => "gone", + InternalServerError => "internal-server-error", + ItemNotFound => "item-not-found", + JidMalformed => "jid-malformed", + NotAcceptable => "not-acceptable", + NotAllowed => "not-allowed", + NotAuthorized => "not-authorized", + PolicyViolation => "policy-violation", + RecipientUnavailable => "recipient-unavailable", + Redirect => "redirect", + RegistrationRequired => "registration-required", + RemoteServerNotFound => "remote-server-not-found", + RemoteServerTimeout => "remote-server-timeout", + ResourceConstraint => "resource-constraint", + ServiceUnavailable => "service-unavailable", + SubscriptionRequired => "subscription-required", + UndefinedCondition => "undefined-condition", + UnexpectedRequest => "unexpected-request", +}); pub type Lang = String; @@ -151,7 +88,7 @@ impl TryFrom for StanzaError { for _ in child.children() { return Err(Error::ParseError("Unknown element in defined-condition.")); } - let condition = DefinedCondition::from_str(child.name())?; + let condition = DefinedCondition::try_from(child.clone())?; defined_condition = Some(condition); } else { if other.is_some() { From 6533395ec3d8d5adb9a40b036f0d45f3f4cbce79 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 04:46:08 +0000 Subject: [PATCH 0540/1020] media_element: Simplify parsing of URI. --- src/helpers.rs | 16 ++++++++++++++++ src/media_element.rs | 42 ++++++++---------------------------------- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index a457081db560b7f5018ff91fcbffa0eb776e5c26..4db2d2c1c729512d6772a9bd2a075b0024ed28b5 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -25,6 +25,22 @@ impl PlainText { } } +/// Codec for trimmed plain text content. +pub struct TrimmedPlainText; + +impl TrimmedPlainText { + pub fn decode(s: &str) -> Result { + Ok(match s.trim() { + "" => return Err(Error::ParseError("URI missing in uri.")), + text => text.to_owned(), + }) + } + + pub fn encode(string: &String) -> String { + string.to_owned() + } +} + /// Codec wrapping base64 encode/decode pub struct Base64; diff --git a/src/media_element.rs b/src/media_element.rs index b700eec1af6e5fd512a5055f5472493ea636ca01..0ef386a4f8d9c33b6347849e6f9fe592ac7b2008 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -11,40 +11,14 @@ use minidom::Element; use error::Error; use ns; - -#[derive(Debug, Clone)] -pub struct URI { - pub type_: String, - pub uri: String, -} - -impl TryFrom for URI { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "uri", ns::MEDIA_ELEMENT); - check_no_unknown_attributes!(elem, "uri", ["type"]); - check_no_children!(elem, "uri"); - let uri = elem.text().trim().to_owned(); - if uri == "" { - return Err(Error::ParseError("URI missing in uri.")); - } - Ok(URI { - type_: get_attr!(elem, "type", required), - uri: uri, - }) - } -} - -impl From for Element { - fn from(uri: URI) -> Element { - Element::builder("uri") - .ns(ns::MEDIA_ELEMENT) - .attr("type", uri.type_) - .append(uri.uri) - .build() - } -} +use helpers::TrimmedPlainText; + +generate_element_with_text!(URI, "uri", ns::MEDIA_ELEMENT, + [ + type_: String = "type" => required + ], + uri: TrimmedPlainText +); #[derive(Debug, Clone)] pub struct MediaElement { From 80a2f425e2fbab76d97619e9d89ec18777f0571c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 04:48:31 +0000 Subject: [PATCH 0541/1020] media_element: Simplify parsing of MediaElement. --- src/media_element.rs | 49 ++++++++------------------------------------ 1 file changed, 9 insertions(+), 40 deletions(-) diff --git a/src/media_element.rs b/src/media_element.rs index 0ef386a4f8d9c33b6347849e6f9fe592ac7b2008..db91c44f44128c6d20bf0b5a9f47929f61486c74 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -20,46 +20,15 @@ generate_element_with_text!(URI, "uri", ns::MEDIA_ELEMENT, uri: TrimmedPlainText ); -#[derive(Debug, Clone)] -pub struct MediaElement { - pub width: Option, - pub height: Option, - pub uris: Vec, -} - -impl TryFrom for MediaElement { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "media", ns::MEDIA_ELEMENT); - check_no_unknown_attributes!(elem, "media", ["width", "height"]); - - let mut media = MediaElement { - width: get_attr!(elem, "width", optional), - height: get_attr!(elem, "height", optional), - uris: vec!(), - }; - for child in elem.children() { - if child.is("uri", ns::MEDIA_ELEMENT) { - media.uris.push(URI::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in media element.")); - } - } - Ok(media) - } -} - -impl From for Element { - fn from(media: MediaElement) -> Element { - Element::builder("media") - .ns(ns::MEDIA_ELEMENT) - .attr("width", media.width) - .attr("height", media.height) - .append(media.uris) - .build() - } -} +generate_element_with_children!(MediaElement, "media", ns::MEDIA_ELEMENT, + attributes: [ + width: Option = "width" => optional, + height: Option = "height" => optional + ], + children: [ + uris: Vec = ("uri", ns::MEDIA_ELEMENT) => URI + ] +); #[cfg(test)] mod tests { From 42a3e425337c9b0b3bee78f43c03486825106650 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 05:09:25 +0000 Subject: [PATCH 0542/1020] roster: Simplify parsing of Item. --- src/macros.rs | 17 ++++++++++++ src/roster.rs | 74 +++++++++++++-------------------------------------- 2 files changed, 35 insertions(+), 56 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 1569470a12ffa4bdece6b75c8859c347518d5408..b084207aaff47a84960b5e7eb36041f07748f372 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -8,6 +8,13 @@ macro_rules! get_attr { ($elem:ident, $attr:tt, $type:tt) => ( get_attr!($elem, $attr, $type, value, value.parse()?) ); + ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => ( + match $elem.attr($attr) { + Some("") => None, + Some($value) => Some($func), + None => None, + } + ); ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( match $elem.attr($attr) { Some($value) => Some($func), @@ -277,6 +284,16 @@ macro_rules! generate_elem_id { Ok($elem(String::from(s))) } } + impl TryFrom for $elem { + type Err = Error; + fn try_from(elem: Element) -> Result<$elem, Error> { + check_self!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_attributes!(elem, $name); + // TODO: add a way to parse that differently when needed. + Ok($elem(elem.text())) + } + } impl From<$elem> for Element { fn from(elem: $elem) -> Element { Element::builder($name) diff --git a/src/roster.rs b/src/roster.rs index cb57ed3fde0d0c0847cff03cbcb7fe5a071b3cf5..cb78e4ef5a2f1c60f5b00bba931b913ecd9fdc44 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -23,64 +23,26 @@ generate_attribute!(Subscription, "subscription", { Remove => "remove", }, Default = None); -/// Contact from the user’s contact list. -#[derive(Debug, Clone, PartialEq)] -pub struct Item { - /// JID of this contact. - pub jid: Jid, - - /// Name of this contact. - pub name: Option, - - /// Subscription status of this contact. - pub subscription: Subscription, - - /// Groups this contact is part of. - pub groups: Vec, -} - -impl TryFrom for Item { - type Err = Error; +generate_element_with_children!( + /// Contact from the user’s contact list. + #[derive(PartialEq)] + Item, "item", ns::ROSTER, + attributes: [ + /// JID of this contact. + jid: Jid = "jid" => required, - fn try_from(elem: Element) -> Result { - if !elem.is("item", ns::ROSTER) { - return Err(Error::ParseError("This is not a roster item element.")); - } + /// Name of this contact. + name: Option = "name" => optional_empty, - let mut item = Item { - jid: get_attr!(elem, "jid", required), - name: get_attr!(elem, "name", optional).and_then(|name| if name == "" { None } else { Some(name) }), - subscription: get_attr!(elem, "subscription", default), - groups: vec!(), - }; - for child in elem.children() { - if !child.is("group", ns::ROSTER) { - return Err(Error::ParseError("Unknown element in roster item element.")); - } - for _ in child.children() { - return Err(Error::ParseError("Roster item group can’t have children.")); - } - for _ in child.attrs() { - return Err(Error::ParseError("Roster item group can’t have attributes.")); - } - let group = Group(child.text()); - item.groups.push(group); - } - Ok(item) - } -} + /// Subscription status of this contact. + subscription: Subscription = "subscription" => default + ], -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::ROSTER) - .attr("jid", item.jid) - .attr("name", item.name) - .attr("subscription", item.subscription) - .append(item.groups) - .build() - } -} + children: [ + /// Groups this contact is part of. + groups: Vec = ("group", ns::ROSTER) => Group + ] +); generate_element_with_children!( /// The contact list of the user. @@ -258,6 +220,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown element in roster item element."); + assert_eq!(message, "Unknown child in item element."); } } From 5a2f4859cffed6ff00cd1f30676a30bbc590fdf2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 05:20:36 +0000 Subject: [PATCH 0543/1020] Add a mood parser and serialiser. --- src/lib.rs | 3 + src/mood.rs | 284 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 3 + 3 files changed, 290 insertions(+) create mode 100644 src/mood.rs diff --git a/src/lib.rs b/src/lib.rs index 2bc12e05e40ff2e0ba15a3053fb48f483204ff97..415572c0f4092d811e57ae196363a9714a532da1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,9 @@ pub mod chatstates; /// XEP-0092: Software Version pub mod version; +/// XEP-0107: User Mood +pub mod mood; + /// XEP-0115: Entity Capabilities pub mod caps; diff --git a/src/mood.rs b/src/mood.rs new file mode 100644 index 0000000000000000000000000000000000000000..c83915be62d0d68c595c3352c76bf4bcc802f73d --- /dev/null +++ b/src/mood.rs @@ -0,0 +1,284 @@ +// Copyright (c) 2017 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#![deny(missing_docs)] + +use try_from::TryFrom; + +use minidom::Element; + +use error::Error; + +use ns; + +generate_element_enum!( + /// Enum representing all of the possible values of the XEP-0107 moods. + MoodEnum, "mood", ns::MOOD, { + /// Impressed with fear or apprehension; in fear; apprehensive. + Afraid => "afraid", + + /// Astonished; confounded with fear, surprise or wonder. + Amazed => "amazed", + + /// Inclined to love; having a propensity to love, or to sexual enjoyment; loving, fond, affectionate, passionate, lustful, sexual, etc. + Amorous => "amorous", + + /// Displaying or feeling anger, i.e., a strong feeling of displeasure, hostility or antagonism towards someone or something, usually combined with an urge to harm. + Angry => "angry", + + /// To be disturbed or irritated, especially by continued or repeated acts. + Annoyed => "annoyed", + + /// Full of anxiety or disquietude; greatly concerned or solicitous, esp. respecting something future or unknown; being in painful suspense. + Anxious => "anxious", + + /// To be stimulated in one's feelings, especially to be sexually stimulated. + Aroused => "aroused", + + /// Feeling shame or guilt. + Ashamed => "ashamed", + + /// Suffering from boredom; uninterested, without attention. + Bored => "bored", + + /// Strong in the face of fear; courageous. + Brave => "brave", + + /// Peaceful, quiet. + Calm => "calm", + + /// Taking care or caution; tentative. + Cautious => "cautious", + + /// Feeling the sensation of coldness, especially to the point of discomfort. + Cold => "cold", + + /// Feeling very sure of or positive about something, especially about one's own capabilities. + Confident => "confident", + + /// Chaotic, jumbled or muddled. + Confused => "confused", + + /// Feeling introspective or thoughtful. + Contemplative => "contemplative", + + /// Pleased at the satisfaction of a want or desire; satisfied. + Contented => "contented", + + /// Grouchy, irritable; easily upset. + Cranky => "cranky", + + /// Feeling out of control; feeling overly excited or enthusiastic. + Crazy => "crazy", + + /// Feeling original, expressive, or imaginative. + Creative => "creative", + + /// Inquisitive; tending to ask questions, investigate, or explore. + Curious => "curious", + + /// Feeling sad and dispirited. + Dejected => "dejected", + + /// Severely despondent and unhappy. + Depressed => "depressed", + + /// Defeated of expectation or hope; let down. + Disappointed => "disappointed", + + /// Filled with disgust; irritated and out of patience. + Disgusted => "disgusted", + + /// Feeling a sudden or complete loss of courage in the face of trouble or danger. + Dismayed => "dismayed", + + /// Having one's attention diverted; preoccupied. + Distracted => "distracted", + + /// Having a feeling of shameful discomfort. + Embarrassed => "embarrassed", + + /// Feeling pain by the excellence or good fortune of another. + Envious => "envious", + + /// Having great enthusiasm. + Excited => "excited", + + /// In the mood for flirting. + Flirtatious => "flirtatious", + + /// Suffering from frustration; dissatisfied, agitated, or discontented because one is unable to perform an action or fulfill a desire. + Frustrated => "frustrated", + + /// Feeling appreciation or thanks. + Grateful => "grateful", + + /// Feeling very sad about something, especially something lost; mournful; sorrowful. + Grieving => "grieving", + + /// Unhappy and irritable. + Grumpy => "grumpy", + + /// Feeling responsible for wrongdoing; feeling blameworthy. + Guilty => "guilty", + + /// Experiencing the effect of favourable fortune; having the feeling arising from the consciousness of well-being or of enjoyment; enjoying good of any kind, as peace, tranquillity, comfort; contented; joyous. + Happy => "happy", + + /// Having a positive feeling, belief, or expectation that something wished for can or will happen. + Hopeful => "hopeful", + + /// Feeling the sensation of heat, especially to the point of discomfort. + Hot => "hot", + + /// Having or showing a modest or low estimate of one's own importance; feeling lowered in dignity or importance. + Humbled => "humbled", + + /// Feeling deprived of dignity or self-respect. + Humiliated => "humiliated", + + /// Having a physical need for food. + Hungry => "hungry", + + /// Wounded, injured, or pained, whether physically or emotionally. + Hurt => "hurt", + + /// Favourably affected by something or someone. + Impressed => "impressed", + + /// Feeling amazement at something or someone; or feeling a combination of fear and reverence. + InAwe => "in_awe", + + /// Feeling strong affection, care, liking, or attraction.. + InLove => "in_love", + + /// Showing anger or indignation, especially at something unjust or wrong. + Indignant => "indignant", + + /// Showing great attention to something or someone; having or showing interest. + Interested => "interested", + + /// Under the influence of alcohol; drunk. + Intoxicated => "intoxicated", + + /// Feeling as if one cannot be defeated, overcome or denied. + Invincible => "invincible", + + /// Fearful of being replaced in position or affection. + Jealous => "jealous", + + /// Feeling isolated, empty, or abandoned. + Lonely => "lonely", + + /// Unable to find one's way, either physically or emotionally. + Lost => "lost", + + /// Feeling as if one will be favored by luck. + Lucky => "lucky", + + /// Causing or intending to cause intentional harm; bearing ill will towards another; cruel; malicious. + Mean => "mean", + + /// Given to sudden or frequent changes of mind or feeling; temperamental. + Moody => "moody", + + /// Easily agitated or alarmed; apprehensive or anxious. + Nervous => "nervous", + + /// Not having a strong mood or emotional state. + Neutral => "neutral", + + /// Feeling emotionally hurt, displeased, or insulted. + Offended => "offended", + + /// Feeling resentful anger caused by an extremely violent or vicious attack, or by an offensive, immoral, or indecent act. + Outraged => "outraged", + + /// Interested in play; fun, recreational, unserious, lighthearted; joking, silly. + Playful => "playful", + + /// Feeling a sense of one's own worth or accomplishment. + Proud => "proud", + + /// Having an easy-going mood; not stressed; calm. + Relaxed => "relaxed", + + /// Feeling uplifted because of the removal of stress or discomfort. + Relieved => "relieved", + + /// Feeling regret or sadness for doing something wrong. + Remorseful => "remorseful", + + /// Without rest; unable to be still or quiet; uneasy; continually moving. + Restless => "restless", + + /// Feeling sorrow; sorrowful, mournful. + Sad => "sad", + + /// Mocking and ironical. + Sarcastic => "sarcastic", + + /// Pleased at the fulfillment of a need or desire. + Satisfied => "satisfied", + + /// Without humor or expression of happiness; grave in manner or disposition; earnest; thoughtful; solemn. + Serious => "serious", + + /// Surprised, startled, confused, or taken aback. + Shocked => "shocked", + + /// Feeling easily frightened or scared; timid; reserved or coy. + Shy => "shy", + + /// Feeling in poor health; ill. + Sick => "sick", + + /// Feeling the need for sleep. + Sleepy => "sleepy", + + /// Acting without planning; natural; impulsive. + Spontaneous => "spontaneous", + + /// Suffering emotional pressure. + Stressed => "stressed", + + /// Capable of producing great physical force; or, emotionally forceful, able, determined, unyielding. + Strong => "strong", + + /// Experiencing a feeling caused by something unexpected. + Surprised => "surprised", + + /// Showing appreciation or gratitude. + Thankful => "thankful", + + /// Feeling the need to drink. + Thirsty => "thirsty", + + /// In need of rest or sleep. + Tired => "tired", + + /// [Feeling any emotion not defined here.] + Undefined => "undefined", + + /// Lacking in force or ability, either physical or emotional. + Weak => "weak", + + /// Thinking about unpleasant things that have happened or that might happen; feeling afraid and unhappy. + Worried => "worried", + } +); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let mood = MoodEnum::try_from(elem).unwrap(); + assert_eq!(mood, MoodEnum::Happy); + } +} diff --git a/src/ns.rs b/src/ns.rs index 045957d5b6125648eb2905fdfcb2e850ba292b73..b8a10bca58e0eca69cc60c3445c392dace9b7662 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -50,6 +50,9 @@ pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; /// XEP-0092: Software Version pub const VERSION: &str = "jabber:iq:version"; +/// XEP-0107: User Mood +pub const MOOD: &str = "http://jabber.org/protocol/mood"; + /// XEP-0114: Jabber Component Protocol pub const COMPONENT_ACCEPT: &str = "jabber:component:accept"; From 32f427a73c4aa24d07ccf9d13beefd92e0d36038 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 05:24:12 +0000 Subject: [PATCH 0544/1020] muc/user: Generate Reason automatically. --- src/muc/user.rs | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/muc/user.rs b/src/muc/user.rs index fb30967993d0d417959a373a168f9a98232590fd..6c25209fb12eb2219913d04905729d5a802a5c0b 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -196,34 +196,7 @@ generate_element_with_only_attributes!(Continue, "continue", ns::MUC_USER, [ thread: Option = "thread" => optional, ]); -#[derive(Debug, Clone, PartialEq)] -pub struct Reason(String); - -impl TryFrom for Reason { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("reason", ns::MUC_USER) { - return Err(Error::ParseError("This is not a reason element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in reason element.")); - } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in reason element.")); - } - Ok(Reason(elem.text())) - } -} - -impl From for Element { - fn from(reason: Reason) -> Element { - Element::builder("reason") - .ns(ns::MUC_USER) - .append(reason.0) - .build() - } -} +generate_elem_id!(Reason, "reason", ns::MUC_USER); generate_attribute!(Affiliation, "affiliation", { Owner => "owner", From 75625c497c78b5e7a91c345634414fea5e90ecfe Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 05:44:58 +0000 Subject: [PATCH 0545/1020] muc/user: Simplify Status with a new macro. --- src/macros.rs | 38 ++++++++++++++++ src/muc/user.rs | 112 +++++++++--------------------------------------- 2 files changed, 59 insertions(+), 91 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index b084207aaff47a84960b5e7eb36041f07748f372..8fb573637cede8dd4df0e94d68a0967f41b4488e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -139,6 +139,44 @@ macro_rules! generate_element_enum { ); } +macro_rules! generate_attribute_enum { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( + generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+}); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $( + $(#[$enum_meta])* + $enum + ),+ + } + impl TryFrom for $elem { + type Err = Error; + fn try_from(elem: Element) -> Result<$elem, Error> { + check_ns_only!(elem, $name, $ns); + check_no_children!(elem, $name); + check_no_unknown_attributes!(elem, $name, [$attr]); + Ok(match get_attr!(elem, $attr, required) { + $($enum_name => $elem::$enum,)+ + _ => return Err(Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), + }) + } + } + impl From<$elem> for Element { + fn from(elem: $elem) -> Element { + Element::builder($name) + .ns($ns) + .attr($attr, match elem { + $($elem::$enum => $enum_name,)+ + }) + .build() + } + } + ); +} + macro_rules! check_self { ($elem:ident, $name:tt, $ns:expr) => ( check_self!($elem, $name, $ns, $name); diff --git a/src/muc/user.rs b/src/muc/user.rs index 6c25209fb12eb2219913d04905729d5a802a5c0b..deff9452b84a512dcebc9b28650e1d1e6734b260 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -16,131 +16,61 @@ use error::Error; use ns; -#[derive(Debug, Clone, PartialEq)] -pub enum Status { +generate_attribute_enum!(Status, "status", ns::MUC_USER, "code", { /// Status: 100 - NonAnonymousRoom, + NonAnonymousRoom => 100, /// Status: 101 - AffiliationChange, + AffiliationChange => 101, /// Status: 102 - ConfigShowsUnavailableMembers, + ConfigShowsUnavailableMembers => 102, /// Status: 103 - ConfigHidesUnavailableMembers, + ConfigHidesUnavailableMembers => 103, /// Status: 104 - ConfigNonPrivacyRelated, + ConfigNonPrivacyRelated => 104, /// Status: 110 - SelfPresence, + SelfPresence => 110, /// Status: 170 - ConfigRoomLoggingEnabled, + ConfigRoomLoggingEnabled => 170, /// Status: 171 - ConfigRoomLoggingDisabled, + ConfigRoomLoggingDisabled => 171, /// Status: 172 - ConfigRoomNonAnonymous, + ConfigRoomNonAnonymous => 172, /// Status: 173 - ConfigRoomSemiAnonymous, + ConfigRoomSemiAnonymous => 173, /// Status: 201 - RoomHasBeenCreated, + RoomHasBeenCreated => 201, /// Status: 210 - AssignedNick, + AssignedNick => 210, /// Status: 301 - Banned, + Banned => 301, /// Status: 303 - NewNick, + NewNick => 303, /// Status: 307 - Kicked, + Kicked => 307, /// Status: 321 - RemovalFromRoom, + RemovalFromRoom => 321, /// Status: 322 - ConfigMembersOnly, + ConfigMembersOnly => 322, /// Status: 332 - ServiceShutdown, -} - -impl TryFrom for Status { - type Err = Error; - - fn try_from(elem: Element) -> Result { - if !elem.is("status", ns::MUC_USER) { - return Err(Error::ParseError("This is not a status element.")); - } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in status element.")); - } - for (attr, _) in elem.attrs() { - if attr != "code" { - return Err(Error::ParseError("Unknown attribute in status element.")); - } - } - let code = get_attr!(elem, "code", required); - - Ok(match code { - 100 => Status::NonAnonymousRoom, - 101 => Status::AffiliationChange, - 102 => Status::ConfigShowsUnavailableMembers, - 103 => Status::ConfigHidesUnavailableMembers, - 104 => Status::ConfigNonPrivacyRelated, - 110 => Status::SelfPresence, - 170 => Status::ConfigRoomLoggingEnabled, - 171 => Status::ConfigRoomLoggingDisabled, - 172 => Status::ConfigRoomNonAnonymous, - 173 => Status::ConfigRoomSemiAnonymous, - 201 => Status::RoomHasBeenCreated, - 210 => Status::AssignedNick, - 301 => Status::Banned, - 303 => Status::NewNick, - 307 => Status::Kicked, - 321 => Status::RemovalFromRoom, - 322 => Status::ConfigMembersOnly, - 332 => Status::ServiceShutdown, - _ => return Err(Error::ParseError("Invalid status code.")), - }) - } -} - -impl From for Element { - fn from(status: Status) -> Element { - Element::builder("status") - .ns(ns::MUC_USER) - .attr("code", match status { - Status::NonAnonymousRoom => 100, - Status::AffiliationChange => 101, - Status::ConfigShowsUnavailableMembers => 102, - Status::ConfigHidesUnavailableMembers => 103, - Status::ConfigNonPrivacyRelated => 104, - Status::SelfPresence => 110, - Status::ConfigRoomLoggingEnabled => 170, - Status::ConfigRoomLoggingDisabled => 171, - Status::ConfigRoomNonAnonymous => 172, - Status::ConfigRoomSemiAnonymous => 173, - Status::RoomHasBeenCreated => 201, - Status::AssignedNick => 210, - Status::Banned => 301, - Status::NewNick => 303, - Status::Kicked => 307, - Status::RemovalFromRoom => 321, - Status::ConfigMembersOnly => 322, - Status::ServiceShutdown => 332, - }) - .build() - } -} + ServiceShutdown => 332, +}); /// Optional element used in elements inside presence stanzas of type /// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking @@ -434,7 +364,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Invalid status code."); + assert_eq!(message, "Invalid status code value."); } #[test] From 93c7ea69143e30075323fdb9c6c6d4f47938c925 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 24 Nov 2017 05:50:24 +0000 Subject: [PATCH 0546/1020] muc/user: Improve documentation of Status. --- src/muc/user.rs | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/muc/user.rs b/src/muc/user.rs index deff9452b84a512dcebc9b28650e1d1e6734b260..d09c73c4d6b6d01be32301383003f6cdfa454997 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -16,59 +16,65 @@ use error::Error; use ns; -generate_attribute_enum!(Status, "status", ns::MUC_USER, "code", { - /// Status: 100 +generate_attribute_enum!( +/// Lists all of the possible status codes used in MUC presences. +Status, "status", ns::MUC_USER, "code", { + /// Inform user that any occupant is allowed to see the user's full JID NonAnonymousRoom => 100, - /// Status: 101 + /// Inform user that his or her affiliation changed while not in the room AffiliationChange => 101, - /// Status: 102 + /// Inform occupants that room now shows unavailable members ConfigShowsUnavailableMembers => 102, - /// Status: 103 + /// Inform occupants that room now does not show unavailable members ConfigHidesUnavailableMembers => 103, - /// Status: 104 + /// Inform occupants that a non-privacy-related room configuration change has occurred ConfigNonPrivacyRelated => 104, - /// Status: 110 + /// Inform user that presence refers to itself SelfPresence => 110, - /// Status: 170 + /// Inform occupants that room logging is now enabled ConfigRoomLoggingEnabled => 170, - /// Status: 171 + /// Inform occupants that room logging is now disabled ConfigRoomLoggingDisabled => 171, - /// Status: 172 + /// Inform occupants that the room is now non-anonymous ConfigRoomNonAnonymous => 172, - /// Status: 173 + /// Inform occupants that the room is now semi-anonymous ConfigRoomSemiAnonymous => 173, - /// Status: 201 + /// Inform user that a new room has been created RoomHasBeenCreated => 201, - /// Status: 210 + /// Inform user that service has assigned or modified occupant's roomnick AssignedNick => 210, - /// Status: 301 + /// Inform user that he or she has been banned from the room Banned => 301, - /// Status: 303 + /// Inform all occupants of new room nickname NewNick => 303, - /// Status: 307 + /// Inform user that he or she has been kicked from the room Kicked => 307, - /// Status: 321 + /// Inform user that he or she is being removed from the room + /// because of an affiliation change RemovalFromRoom => 321, - /// Status: 322 + /// Inform user that he or she is being removed from the room + /// because the room has been changed to members-only and the + /// user is not a member ConfigMembersOnly => 322, - /// Status: 332 + /// Inform user that he or she is being removed from the room + /// because the MUC service is being shut down ServiceShutdown => 332, }); From 8f0549b0ddbb825d12ba54bdd6db675cbd27777d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Dec 2017 16:03:19 +0100 Subject: [PATCH 0547/1020] update dependencies, and bump version --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b9fa61ad49721e7ad916477d1e11d507def0cab1..e04d8b04cb2b8cb05a37ebd40c353480c4bb984e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.6.2" +version = "0.7.0" authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel ", "Astro "] description = "A small, simple DOM implementation on top of quick-xml" homepage = "https://gitlab.com/lumi/minidom-rs" @@ -14,5 +14,5 @@ license = "MIT" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -quick-xml = "0.9.0" -error-chain = "0.10.0" +quick-xml = "0.10.0" +error-chain = "0.11.0" From a733ea5fb8593bedafd56fd664627bb7236ea17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 27 Dec 2017 16:42:07 +0100 Subject: [PATCH 0548/1020] Update minidom dep to 0.7.0 --- Cargo.toml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e87ebc91d8e10a5b153fddc56ef26cd2a4d4baea..11ae97242c92b159b8e38f1d9dc63eecca5e68a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,11 @@ [package] name = "jid" -version = "0.3.1" -authors = ["lumi ", "Emmanuel Gil Peyrot "] +version = "0.4.0" +authors = [ + "lumi ", + "Emmanuel Gil Peyrot ", + "Maxime “pep” Buquet ", +] description = "A crate which provides a Jid struct for Jabber IDs." homepage = "https://gitlab.com/lumi/jid-rs" repository = "https://gitlab.com/lumi/jid-rs" @@ -14,4 +18,4 @@ license = "LGPL-3.0+" gitlab = { repository = "lumi/jid-rs" } [dependencies] -minidom = { version = "0.6.1", optional = true } +minidom = { version = "0.7", optional = true } From 023ba2b9f0dfc1dc9a96e84d7c820e91caea7580 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Dec 2017 16:52:28 +0100 Subject: [PATCH 0549/1020] Cargo.toml: Bump minidom and jid. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4cf04e7430fb0e36ab5b639b7a40f804a7b5ec0c..713b00a0c12885fb81d6226e4626ef8ef9e71274 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,8 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = "0.6.2" -jid = { version = "0.3.1", features = ["minidom"] } +minidom = { version = "0.7.0" } +jid = { version = "0.4.0", features = ["minidom"] } base64 = "0.7.0" digest = "0.7.1" sha-1 = "0.7.0" From 5563449c07def12b8d9f2394b38db367ba01106b Mon Sep 17 00:00:00 2001 From: lumi Date: Wed, 27 Dec 2017 17:16:59 +0100 Subject: [PATCH 0550/1020] change Cargo.toml to reflect the repository transfer --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 11ae97242c92b159b8e38f1d9dc63eecca5e68a4..a7ba1a0c3ee4547b00247bb32b4720e9921b1982 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,15 +7,15 @@ authors = [ "Maxime “pep” Buquet ", ] description = "A crate which provides a Jid struct for Jabber IDs." -homepage = "https://gitlab.com/lumi/jid-rs" -repository = "https://gitlab.com/lumi/jid-rs" +homepage = "https://gitlab.com/xmpp-rs/jid-rs" +repository = "https://gitlab.com/xmpp-rs/jid-rs" documentation = "https://docs.rs/jid" readme = "README.md" keywords = ["xmpp", "jid"] license = "LGPL-3.0+" [badges] -gitlab = { repository = "lumi/jid-rs" } +gitlab = { repository = "xmpp-rs/jid-rs" } [dependencies] minidom = { version = "0.7", optional = true } From 4392446189f1ab771fe37ee682d686a70f2da8f7 Mon Sep 17 00:00:00 2001 From: lumi Date: Wed, 27 Dec 2017 17:23:28 +0100 Subject: [PATCH 0551/1020] update the change log --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63a6cc0743f99dd40ba9e0ef1fcb1e38e30112f8..49f7daac9c5edb5ff4e2f531409a74f3b391768e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +Version 0.4.0, released 2017-12-27: + * Updates + - Maxime Buquet has updated the optional `minidom` dependency. + - The repository has been transferred to xmpp-rs/jid-rs. + Version 0.3.1, released 2017-10-31: * Additions - Link Mauve added a minidom::IntoElements implementation on Jid behind the "minidom" feature. ( https://gitlab.com/lumi/jid-rs/merge_requests/9 ) From efdda7404c6f397f5fcfa609189556215c9a5a78 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Dec 2017 17:32:43 +0100 Subject: [PATCH 0552/1020] ChangeLog: Add imminent vesion 0.9.0. --- ChangeLog | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ChangeLog b/ChangeLog index 3f86f90e3c06d3db4fee2a1f2d365a0201e208af..e07a50a5f26905ffd09212fe1bac79e16b074459 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +Version 0.9.0: +2017-10-31 Emmanuel Gil Peyrot + * New parsers/serialisers: + - Blocking Command (XEP-0191) has been added. + - Date and Time Profiles (XEP-0082) has been added, replacing + ad-hoc use of chrono in various places. + - User Mood (XEP-0107) has been added. + * Breaking changes: + - Fix subscription="none" not being the default. + - Add more type safety to pubsub#event. + - Reuse Jingle’s ContentId type in JingleFT. + - Import the disposition attribute values in Jingle. + * Improvements: + - Refactor a good part of the code using macros. + - Simplify the parsing code wherever it makes sense. + - Check for children ordering in disco#info result. + - Finish implementation of , and + in JingleFT. + - Correctly serialise , and test it. + Version 0.8.0: 2017-08-27 Emmanuel Gil Peyrot * New parsers/serialisers: From 9cd34901002d6405f7746ce11f7e34c757f5fe02 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Dec 2017 17:35:29 +0100 Subject: [PATCH 0553/1020] Cargo.toml: Bump base64 version. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 713b00a0c12885fb81d6226e4626ef8ef9e71274..f27dae874c44e99509ee92af3d80e1f35db7dc74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MPL-2.0" [dependencies] minidom = { version = "0.7.0" } jid = { version = "0.4.0", features = ["minidom"] } -base64 = "0.7.0" +base64 = "0.9.0" digest = "0.7.1" sha-1 = "0.7.0" sha2 = "0.7.0" From bf116941ece0b42bd1f439ff3c85736a6451d5bf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Dec 2017 17:35:36 +0100 Subject: [PATCH 0554/1020] Release version 0.9.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f27dae874c44e99509ee92af3d80e1f35db7dc74..1f1ab4bfa0e770ed2d4594d391fdd4f3373ca7ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.8.0" +version = "0.9.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 1c3a701d2e5847586cdadaebc18f6482c721878a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 30 Dec 2017 16:48:07 +0100 Subject: [PATCH 0556/1020] element: Fix element name comparison in from_reader quick-xml's Events seem to always return prefixed names, and the from_reader function, when comparing name of the pop-ed element, and received event element, was using the unprefixed name to compare. --- src/element.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/element.rs b/src/element.rs index e398d40f74d035817dcf79b9ffcf15b78fdb08bd..4494d411782521ae73127c94142c53179dcd1c97 100644 --- a/src/element.rs +++ b/src/element.rs @@ -191,6 +191,11 @@ impl Element { &self.name } + /// Returns a reference to the prefix of this element. + pub fn prefix(&self) -> Option { + self.prefix.clone() + } + /// Returns a reference to the namespace of this element, if it has one, else `None`. pub fn ns(&self) -> Option { self.namespaces.get(&self.prefix) @@ -318,7 +323,11 @@ impl Element { } let elem = stack.pop().unwrap(); if let Some(to) = stack.last_mut() { - if elem.name().as_bytes() != e.name() { + let name = match elem.prefix() { + Some(ref prefix) => format!("{}:", prefix.clone()), + None => String::from(""), + } + elem.name(); + if name.as_bytes() != e.name() { bail!(ErrorKind::InvalidElementClosed); } to.append_child(elem); From 7d009923b71e3c30185e7872ca59bfe292ca2375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 30 Dec 2017 16:54:03 +0100 Subject: [PATCH 0557/1020] Update author list --- Cargo.toml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e04d8b04cb2b8cb05a37ebd40c353480c4bb984e..9a29a616c03789fa943afccb27fdac14c6b5a4f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,13 @@ [package] name = "minidom" version = "0.7.0" -authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel ", "Astro "] +authors = [ + "lumi ", + "Emmanuel Gil Peyrot ", + "Bastien Orivel ", + "Astro ", + "Maxime “pep” Buquet ", +] description = "A small, simple DOM implementation on top of quick-xml" homepage = "https://gitlab.com/lumi/minidom-rs" repository = "https://gitlab.com/lumi/minidom-rs" From e250e9cf2c9e261ce0011713b1881c44dcde882a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 30 Dec 2017 17:24:54 +0100 Subject: [PATCH 0558/1020] Add doctest for prefix fn --- src/element.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/element.rs b/src/element.rs index 4494d411782521ae73127c94142c53179dcd1c97..b2cde3a3243535cbbc810e6c6ba1bc141cc99790 100644 --- a/src/element.rs +++ b/src/element.rs @@ -192,6 +192,17 @@ impl Element { } /// Returns a reference to the prefix of this element. + /// + /// # Examples + /// ```rust + /// use minidom::Element; + /// + /// let elem = Element::builder("prefix:name") + /// .build(); + /// + /// assert_eq!(elem.name(), "name"); + /// assert_eq!(elem.prefix(), Some("prefix".to_owned())); + /// ``` pub fn prefix(&self) -> Option { self.prefix.clone() } From 9c498044ee15ee0afc8df7a7c8d32f5d49aea073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 31 Dec 2017 07:19:24 +0100 Subject: [PATCH 0559/1020] element: Add tests for Element::from_reader --- src/element.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/element.rs b/src/element.rs index b2cde3a3243535cbbc810e6c6ba1bc141cc99790..e363cbb6c674bb64b387aec7cd37d5746d8aeefb 100644 --- a/src/element.rs +++ b/src/element.rs @@ -843,3 +843,46 @@ fn test_element_new() { assert_eq!(elem.attr("name"), Some("value")); assert_eq!(elem.attr("inexistent"), None); } + +#[test] +fn test_from_reader_simple() { + let xml = ""; + let mut reader = EventReader::from_str(xml); + let elem = Element::from_reader(&mut reader); + + let elem2 = Element::builder("foo").build(); + + assert_eq!(elem.unwrap(), elem2); +} + +#[test] +fn test_from_reader_nested() { + let xml = ""; + let mut reader = EventReader::from_str(xml); + let elem = Element::from_reader(&mut reader); + + let nested = Element::builder("bar") + .attr("baz", "qxx") + .build(); + let elem2 = Element::builder("foo") + .append(nested) + .build(); + + assert_eq!(elem.unwrap(), elem2); +} + +#[test] +fn test_from_reader_with_prefix() { + let xml = ""; + let mut reader = EventReader::from_str(xml); + let elem = Element::from_reader(&mut reader); + + let nested = Element::builder("prefix:bar") + .attr("baz", "qxx") + .build(); + let elem2 = Element::builder("foo") + .append(nested) + .build(); + + assert_eq!(elem.unwrap(), elem2); +} From db70487a31e290ea04f44010f7b765406e271ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 31 Dec 2017 15:24:28 +0000 Subject: [PATCH 0560/1020] Change prefix fn to return Option<&str> --- src/element.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/element.rs b/src/element.rs index e363cbb6c674bb64b387aec7cd37d5746d8aeefb..c96424b65c945d900785cf5a0a563d337988eda1 100644 --- a/src/element.rs +++ b/src/element.rs @@ -201,10 +201,10 @@ impl Element { /// .build(); /// /// assert_eq!(elem.name(), "name"); - /// assert_eq!(elem.prefix(), Some("prefix".to_owned())); + /// assert_eq!(elem.prefix(), Some("prefix")); /// ``` - pub fn prefix(&self) -> Option { - self.prefix.clone() + pub fn prefix(&self) -> Option<&str> { + self.prefix.as_ref().map(String::as_ref) } /// Returns a reference to the namespace of this element, if it has one, else `None`. @@ -335,7 +335,7 @@ impl Element { let elem = stack.pop().unwrap(); if let Some(to) = stack.last_mut() { let name = match elem.prefix() { - Some(ref prefix) => format!("{}:", prefix.clone()), + Some(ref prefix) => format!("{}:", prefix), None => String::from(""), } + elem.name(); if name.as_bytes() != e.name() { From 9959abbb256ba048e4f5e3e144cc13894a4a8bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 31 Dec 2017 16:31:10 +0000 Subject: [PATCH 0561/1020] Be explicit about quick-xml Events we are handling --- src/element.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/element.rs b/src/element.rs index e398d40f74d035817dcf79b9ffcf15b78fdb08bd..7d88d2d92c7386c567d75fab5205036998f3c636 100644 --- a/src/element.rs +++ b/src/element.rs @@ -295,7 +295,13 @@ impl Element { Event::Eof => { bail!(ErrorKind::EndOfDocument); }, - _ => (), // TODO: may need more errors + Event::Text { .. } | + Event::End { .. } | + Event::Comment { .. } | + Event::CData { .. } | + Event::Decl { .. } | + Event::PI { .. } | + Event::DocType { .. } => (), // TODO: may need more errors } }; @@ -334,7 +340,10 @@ impl Element { Event::Eof => { break; }, - _ => (), // TODO: may need to implement more + Event::Comment { .. } | + Event::Decl { .. } | + Event::PI { .. } | + Event::DocType { .. } => (), } } Ok(stack.pop().unwrap()) From cdb2cb8d861d91885efa9d39231c3d39a1400fe0 Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 1 Jan 2018 14:01:03 +0100 Subject: [PATCH 0562/1020] try not to allocate memory when comparing ending tags with starting tags --- src/element.rs | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/element.rs b/src/element.rs index 2dd08adddaea50b0e51fd07be7f26f13b9703808..cf9ee35cf42064c3c14d025a751985f0444ec377 100644 --- a/src/element.rs +++ b/src/element.rs @@ -340,12 +340,34 @@ impl Element { } let elem = stack.pop().unwrap(); if let Some(to) = stack.last_mut() { - let name = match elem.prefix() { - Some(ref prefix) => format!("{}:", prefix), - None => String::from(""), - } + elem.name(); - if name.as_bytes() != e.name() { - bail!(ErrorKind::InvalidElementClosed); + // TODO: check whether this is correct, we are comparing &[u8]s, not &strs + let elem_name = e.name(); + let mut split_iter = elem_name.splitn(2, |u| *u == 0x3A); + let possible_prefix = split_iter.next().unwrap(); // Can't be empty. + match split_iter.next() { + Some(name) => { + match elem.prefix() { + Some(prefix) => { + if possible_prefix != prefix.as_bytes() { + bail!(ErrorKind::InvalidElementClosed); + } + }, + None => { + bail!(ErrorKind::InvalidElementClosed); + }, + } + if name != elem.name().as_bytes() { + bail!(ErrorKind::InvalidElementClosed); + } + }, + None => { + if elem.prefix().is_some() { + bail!(ErrorKind::InvalidElementClosed); + } + if possible_prefix != elem.name().as_bytes() { + bail!(ErrorKind::InvalidElementClosed); + } + }, } to.append_child(elem); } From 118bbb54621a7023e326c6318e8b8737cb50993a Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 1 Jan 2018 14:28:17 +0100 Subject: [PATCH 0563/1020] add the spectest.xml from https://gitlab.com/lumi/minidom-rs/issues/8 --- src/element.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/element.rs b/src/element.rs index cf9ee35cf42064c3c14d025a751985f0444ec377..1f8a19039ea930bb8004f00e4984db9b544038b4 100644 --- a/src/element.rs +++ b/src/element.rs @@ -917,3 +917,14 @@ fn test_from_reader_with_prefix() { assert_eq!(elem.unwrap(), elem2); } + +#[test] +fn parses_spectest_xml() { // From: https://gitlab.com/lumi/minidom-rs/issues/8 + let xml = r#" + + + + "#; + let mut reader = EventReader::from_str(xml); + let _ = Element::from_reader(&mut reader); +} From d079f0503af7f1193488c77e1d661f1014b893b7 Mon Sep 17 00:00:00 2001 From: lumi Date: Mon, 1 Jan 2018 14:34:24 +0100 Subject: [PATCH 0564/1020] make sure that my tests actually work... --- src/element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/element.rs b/src/element.rs index 1f8a19039ea930bb8004f00e4984db9b544038b4..d126aa1da18be2edd2797652caa68aada1844cb8 100644 --- a/src/element.rs +++ b/src/element.rs @@ -926,5 +926,5 @@ fn parses_spectest_xml() { // From: https://gitlab.com/lumi/minidom-rs/issues/8 "#; let mut reader = EventReader::from_str(xml); - let _ = Element::from_reader(&mut reader); + let _ = Element::from_reader(&mut reader).unwrap(); } From be3279a999b4008536319663a0e60ffda3e9433d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 1 Jan 2018 13:39:32 +0000 Subject: [PATCH 0565/1020] Update docker image used in CI --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0f54ef57dff651c4da0e8d1054489abc4653019e..c02e04ce55fee669db09a49bfa08f4aea2d5cfbc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: "scorpil/rust:nightly" +image: "pitkley/rust:nightly" before_script: - apt-get update -yqq From 5e6990bef9ec7a5e815e6f9562cdffea0fd24112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 1 Jan 2018 13:41:52 +0000 Subject: [PATCH 0566/1020] Update docker image used in CI --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cb292343052f44cf4efaa6f5bd686defc527ae8a..de843e5964b80dfc95c3fd7a9cb5aa061c1b993b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,6 @@ -image: "scorpil/rust:stable" +image: "pitkley/rust:stable" test:cargo: script: - rustc --version && cargo --version - - cargo test --verbose --jobs 1 --release \ No newline at end of file + - cargo test --verbose --jobs 1 --release From f456600efd9b5c2bf537ff37525cc737e83c3676 Mon Sep 17 00:00:00 2001 From: Yue Liu Date: Fri, 5 Jan 2018 20:20:12 -0800 Subject: [PATCH 0567/1020] Support comment and write to quick-xml Writer --- src/element.rs | 128 ++++++++++++++++++++++++++++++++++++------------- src/tests.rs | 33 ++++++++++++- 2 files changed, 125 insertions(+), 36 deletions(-) diff --git a/src/element.rs b/src/element.rs index d126aa1da18be2edd2797652caa68aada1844cb8..3cd3e824cd5eecec06d60a904779370ef6cd3383 100644 --- a/src/element.rs +++ b/src/element.rs @@ -10,7 +10,8 @@ use std::borrow::Cow; use error::{Error, ErrorKind, Result}; use quick_xml::reader::Reader as EventReader; -use quick_xml::events::{Event, BytesStart}; +use quick_xml::writer::Writer as EventWriter; +use quick_xml::events::{Event, BytesStart, BytesEnd, BytesText, BytesDecl}; use std::io::BufRead; @@ -21,20 +22,50 @@ use std::slice; use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; use namespace_set::NamespaceSet; -/// Escape XML text -pub fn write_escaped(writer: &mut W, input: &str) -> Result<()> { - for c in input.chars() { - match c { - '&' => write!(writer, "&")?, - '<' => write!(writer, "<")?, - '>' => write!(writer, ">")?, - '\'' => write!(writer, "'")?, - '"' => write!(writer, """)?, - _ => write!(writer, "{}", c)?, +/// helper function to escape a `&[u8]` and replace all +/// xml special characters (<, >, &, ', ") with their corresponding +/// xml escaped value. +pub fn escape(raw: &[u8]) -> Cow<[u8]> { + let mut escapes: Vec<(usize, &'static [u8])> = Vec::new(); + let mut bytes = raw.iter(); + fn to_escape(b: u8) -> bool { + match b { + b'<' | b'>' | b'\'' | b'&' | b'"' => true, + _ => false, } } - Ok(()) + let mut loc = 0; + while let Some(i) = bytes.position(|&b| to_escape(b)) { + loc += i; + match raw[loc] { + b'<' => escapes.push((loc, b"<")), + b'>' => escapes.push((loc, b">")), + b'\'' => escapes.push((loc, b"'")), + b'&' => escapes.push((loc, b"&")), + b'"' => escapes.push((loc, b""")), + _ => unreachable!("Only '<', '>','\', '&' and '\"' are escaped"), + } + loc += 1; + } + + if escapes.is_empty() { + Cow::Borrowed(raw) + } else { + let len = raw.len(); + let mut v = Vec::with_capacity(len); + let mut start = 0; + for (i, r) in escapes { + v.extend_from_slice(&raw[start..i]); + v.extend_from_slice(r); + start = i + 1; + } + + if start < len { + v.extend_from_slice(&raw[start..]); + } + Cow::Owned(v) + } } /// A node in an element tree. @@ -44,6 +75,8 @@ pub enum Node { Element(Element), /// A text node. Text(String), + /// A comment node. + Comment(String), } impl Node { @@ -64,6 +97,7 @@ impl Node { match *self { Node::Element(ref e) => Some(e), Node::Text(_) => None, + Node::Comment(_) => None, } } @@ -84,14 +118,22 @@ impl Node { match *self { Node::Element(_) => None, Node::Text(ref s) => Some(s), + Node::Comment(_) => None, } } - fn write_to_inner(&self, writer: &mut W) -> Result<()>{ + fn write_to_inner(&self, writer: &mut EventWriter) -> Result<()>{ match *self { Node::Element(ref elmt) => elmt.write_to_inner(writer)?, - Node::Text(ref s) => write_escaped(writer, s)?, - } + Node::Text(ref s) => { + writer.write_event(Event::Text(BytesText::from_str(s)))?; + () + }, + Node::Comment(ref s) => { + writer.write_event(Event::Comment(BytesText::from_str(s)))?; + () + }, + }; Ok(()) } @@ -382,7 +424,13 @@ impl Element { Event::Eof => { break; }, - Event::Comment { .. } | + Event::Comment(s) => { + let comment = reader.decode(&s).into_owned(); + if comment != "" { + let current_elem = stack.last_mut().unwrap(); + current_elem.append_comment_node(comment); + } + }, Event::Decl { .. } | Event::PI { .. } | Event::DocType { .. } => (), @@ -393,51 +441,48 @@ impl Element { /// Output a document to a `Writer`. pub fn write_to(&self, writer: &mut W) -> Result<()> { - write!(writer, "")?; + self.to_writer(&mut EventWriter::new(writer)) + } + + /// Output the document to quick-xml `Writer` + pub fn to_writer(&self, writer: &mut EventWriter) -> Result<()> { + writer.write_event(Event::Decl(BytesDecl::new(b"1.0", Some(b"utf-8"), None)))?; self.write_to_inner(writer) } /// Like `write_to()` but without the `` prelude - pub fn write_to_inner(&self, writer: &mut W) -> Result<()> { + pub fn write_to_inner(&self, writer: &mut EventWriter) -> Result<()> { let name = match self.prefix { None => Cow::Borrowed(&self.name), Some(ref prefix) => Cow::Owned(format!("{}:{}", prefix, self.name)), }; - write!(writer, "<{}", name)?; + let mut start = BytesStart::borrowed(name.as_bytes(), name.len()); for (prefix, ns) in self.namespaces.declared_ns() { match *prefix { - None => { - write!(writer, " xmlns=\"")?; - write_escaped(writer, ns)?; - write!(writer, "\"")?; - }, + None => start.push_attribute(("xmlns", ns.as_ref())), Some(ref prefix) => { - write!(writer, " xmlns:{}=\"", prefix)?; - write_escaped(writer, ns)?; - write!(writer, "\"")?; + let key = format!("xmlns:{}", prefix); + start.push_attribute((key.as_bytes(), ns.as_bytes())) }, } } - for (key, value) in &self.attributes { - write!(writer, " {}=\"", key)?; - write_escaped(writer, value)?; - write!(writer, "\"")?; + start.push_attribute((key.as_bytes(), escape(value.as_bytes()).as_ref())); } if self.children.is_empty() { - write!(writer, " />")?; + writer.write_event(Event::Empty(start))?; return Ok(()) } - write!(writer, ">")?; + writer.write_event(Event::Start(start))?; for child in &self.children { child.write_to_inner(writer)?; } - write!(writer, "", name)?; + writer.write_event(Event::End(BytesEnd::borrowed(name.as_bytes())))?; Ok(()) } @@ -576,6 +621,21 @@ impl Element { self.children.push(Node::Text(child.into())); } + /// Appends a comment node to an `Element`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Element; + /// + /// let mut elem = Element::bare("node"); + /// + /// elem.append_comment_node("comment"); + /// ``` + pub fn append_comment_node>(&mut self, child: S) { + self.children.push(Node::Comment(child.into())); + } + /// Appends a node to an `Element`. /// /// # Examples diff --git a/src/tests.rs b/src/tests.rs index e787715830823ef66285348a0f67ae63ad6a0ad8..59ef0fedcc00725fb725d1d922a1fb88e9944b17 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -4,7 +4,7 @@ use quick_xml::reader::Reader; use element::Element; -const TEST_STRING: &'static str = r#"meownya"#; +const TEST_STRING: &'static str = r#"meownya"#; fn build_test_tree() -> Element { let mut root = Element::builder("root") @@ -27,6 +27,19 @@ fn build_test_tree() -> Element { root } +const COMMENT_TEST_STRING: &'static str = r#""#; + +fn build_comment_test_tree() -> Element { + let mut root = Element::builder("root").build(); + root.append_comment_node("This is a child."); + let mut child = Element::builder("child").attr("attr", "val").build(); + child.append_comment_node("This is a grandchild."); + let grand_child = Element::builder("grandchild").build(); + child.append_child(grand_child); + root.append_child(child); + root +} + #[test] fn reader_works() { let mut reader = Reader::from_str(TEST_STRING); @@ -53,7 +66,7 @@ fn writer_escapes_attributes() { root.write_to(&mut writer).unwrap(); } assert_eq!(String::from_utf8(writer).unwrap(), - r#""# + r#""# ); } @@ -197,3 +210,19 @@ fn namespace_inherited_prefixed2() { assert_eq!(child.name(), "message"); assert_eq!(child.ns(), Some("jabber:client".to_owned())); } + +#[test] +fn read_comments() { + let mut reader = Reader::from_str(COMMENT_TEST_STRING); + assert_eq!(Element::from_reader(&mut reader).unwrap(), build_comment_test_tree()); +} + +#[test] +fn write_comments() { + let root = build_comment_test_tree(); + let mut writer = Vec::new(); + { + root.write_to(&mut writer).unwrap(); + } + assert_eq!(String::from_utf8(writer).unwrap(), COMMENT_TEST_STRING); +} From 055caa8e24bc64da170e956c929200ccae5ada20 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 18 Feb 2018 19:41:03 +0100 Subject: [PATCH 0568/1020] add tests for some XML errors --- src/tests.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/tests.rs b/src/tests.rs index e787715830823ef66285348a0f67ae63ad6a0ad8..0db96b70af99f9843d8a769f6eeb5b9170de823e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -197,3 +197,24 @@ fn namespace_inherited_prefixed2() { assert_eq!(child.name(), "message"); assert_eq!(child.ns(), Some("jabber:client".to_owned())); } + +#[test] +fn xml_error() { + match "
".parse::() { + Err(::error::Error(::error::ErrorKind::XmlError(_), _)) => (), + err => panic!("No or wrong error: {:?}", err) + } + + match "() { + Err(::error::Error(::error::ErrorKind::XmlError(_), _)) => (), + err => panic!("No or wrong error: {:?}", err) + } +} + +#[test] +fn invalid_element_error() { + match "".parse::() { + Err(::error::Error(::error::ErrorKind::InvalidElement, _)) => (), + err => panic!("No or wrong error: {:?}", err) + } +} From 236582b24ea098afd7c78703d01dff1cac8f7891 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 18 Feb 2018 19:54:09 +0100 Subject: [PATCH 0569/1020] update quick_xml and migrate from error-chain to failure --- Cargo.toml | 5 ++-- src/element.rs | 16 ++++++------ src/error.rs | 71 +++++++++++++++++++++++++++++++------------------- src/lib.rs | 5 ++-- src/tests.rs | 6 ++--- 5 files changed, 61 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9a29a616c03789fa943afccb27fdac14c6b5a4f6..a4ea886e164dc8eed4fa6f664e8affed8808aa95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,5 +20,6 @@ license = "MIT" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -quick-xml = "0.10.0" -error-chain = "0.11.0" +quick-xml = "0.11.0" +failure = "0.1.1" +failure_derive = "0.1.1" diff --git a/src/element.rs b/src/element.rs index d126aa1da18be2edd2797652caa68aada1844cb8..0f3fa9d1bd53e2586b7ac0838292b52979d737c8 100644 --- a/src/element.rs +++ b/src/element.rs @@ -7,7 +7,7 @@ use std::str; use std::rc::Rc; use std::borrow::Cow; -use error::{Error, ErrorKind, Result}; +use error::{Error, Result}; use quick_xml::reader::Reader as EventReader; use quick_xml::events::{Event, BytesStart}; @@ -309,7 +309,7 @@ impl Element { break build_element(reader, e)?; }, Event::Eof => { - bail!(ErrorKind::EndOfDocument); + return Err(Error::EndOfDocument); }, Event::Text { .. } | Event::End { .. } | @@ -349,23 +349,23 @@ impl Element { match elem.prefix() { Some(prefix) => { if possible_prefix != prefix.as_bytes() { - bail!(ErrorKind::InvalidElementClosed); + return Err(Error::InvalidElementClosed); } }, None => { - bail!(ErrorKind::InvalidElementClosed); + return Err(Error::InvalidElementClosed); }, } if name != elem.name().as_bytes() { - bail!(ErrorKind::InvalidElementClosed); + return Err(Error::InvalidElementClosed); } }, None => { if elem.prefix().is_some() { - bail!(ErrorKind::InvalidElementClosed); + return Err(Error::InvalidElementClosed); } if possible_prefix != elem.name().as_bytes() { - bail!(ErrorKind::InvalidElementClosed); + return Err(Error::InvalidElementClosed); } }, } @@ -676,7 +676,7 @@ fn split_element_name>(s: S) -> Result<(Option, String)> { match name_parts.len() { 2 => Ok((Some(name_parts[0].to_owned()), name_parts[1].to_owned())), 1 => Ok((None, name_parts[0].to_owned())), - _ => bail!(ErrorKind::InvalidElement), + _ => Err(Error::InvalidElement), } } diff --git a/src/error.rs b/src/error.rs index 4079365fa4d028181d140a6d3954421ca2764764..48f334403830eb1b89d06ed26f09c05ec2917ba8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,34 +2,51 @@ use std::convert::From; -error_chain! { - foreign_links { - XmlError(::quick_xml::errors::Error) - /// An error from quick_xml. - ; - Utf8Error(::std::str::Utf8Error) - /// An UTF-8 conversion error. - ; - IoError(::std::io::Error) - /// An I/O error, from std::io. - ; +/// Our main error type. +#[derive(Debug, Fail)] +pub enum Error { + /// An error from quick_xml. + #[fail(display = "XML error: {}", _0)] + XmlError(#[cause] ::quick_xml::errors::Error), + + /// An UTF-8 conversion error. + #[fail(display = "UTF-8 error: {}", _0)] + Utf8Error(#[cause] ::std::str::Utf8Error), + + /// An I/O error, from std::io. + #[fail(display = "IO error: {}", _0)] + IoError(#[cause] ::std::io::Error), + + /// An error which is returned when the end of the document was reached prematurely. + #[fail(display = "the end of the document has been reached prematurely")] + EndOfDocument, + + /// An error which is returned when an element is closed when it shouldn't be + #[fail(display = "the XML is invalid, an element was wrongly closed")] + InvalidElementClosed, + + /// An error which is returned when an elemet's name contains more than one colon + #[fail(display = "the XML element is invalid")] + InvalidElement, +} + +impl From<::quick_xml::errors::Error> for Error { + fn from(err: ::quick_xml::errors::Error) -> Error { + Error::XmlError(err) + } +} + +impl From<::std::str::Utf8Error> for Error { + fn from(err: ::std::str::Utf8Error) -> Error { + Error::Utf8Error(err) } +} - errors { - /// An error which is returned when the end of the document was reached prematurely. - EndOfDocument { - description("the end of the document has been reached prematurely") - display("the end of the document has been reached prematurely") - } - /// An error which is returned when an element is closed when it shouldn't be - InvalidElementClosed { - description("The XML is invalid, an element was wrongly closed") - display("the XML is invalid, an element was wrongly closed") - } - /// An error which is returned when an elemet's name contains more than one colon - InvalidElement { - description("The XML element is invalid") - display("the XML element is invalid") - } +impl From<::std::io::Error> for Error { + fn from(err: ::std::io::Error) -> Error { + Error::IoError(err) } } + +/// Our simplified Result type. +pub type Result = ::std::result::Result; diff --git a/src/lib.rs b/src/lib.rs index b11fe0603ddf40c566a5af23675f0a5adab88b50..8cf4ab7e5b70ab0104e5b6e67a7b529b2ae4e3b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,7 +65,8 @@ //! ``` extern crate quick_xml; -#[macro_use] extern crate error_chain; +extern crate failure; +#[macro_use] extern crate failure_derive; pub mod error; pub mod element; @@ -74,6 +75,6 @@ mod namespace_set; #[cfg(test)] mod tests; -pub use error::{Error, ErrorKind, Result, ResultExt}; +pub use error::{Error, Result}; pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder}; pub use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; diff --git a/src/tests.rs b/src/tests.rs index 0db96b70af99f9843d8a769f6eeb5b9170de823e..7d6c523e5e397686ceeb008a603ac02288a7192c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -201,12 +201,12 @@ fn namespace_inherited_prefixed2() { #[test] fn xml_error() { match "
".parse::() { - Err(::error::Error(::error::ErrorKind::XmlError(_), _)) => (), + Err(::error::Error::XmlError(_)) => (), err => panic!("No or wrong error: {:?}", err) } match "() { - Err(::error::Error(::error::ErrorKind::XmlError(_), _)) => (), + Err(::error::Error::XmlError(_)) => (), err => panic!("No or wrong error: {:?}", err) } } @@ -214,7 +214,7 @@ fn xml_error() { #[test] fn invalid_element_error() { match "".parse::() { - Err(::error::Error(::error::ErrorKind::InvalidElement, _)) => (), + Err(::error::Error::InvalidElement) => (), err => panic!("No or wrong error: {:?}", err) } } From 0eba989122a6df1d0d5c75b6707249b9e6425c44 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 18 Feb 2018 20:56:40 +0100 Subject: [PATCH 0570/1020] bump version to 0.8.0 and add CHANGELOG.md entry --- CHANGELOG.md | 3 +++ Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 369ef8ad62d81000184f2cf47b2e713ac4d80326..2e1c16ab0eb36d62b31c9d6bb77a42fbd145a565 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +Version 0.8.0, released 2018-02-18: + * Additions + - Link Mauve replaced error\_chain with failure ( https://gitlab.com/lumi/minidom-rs/merge_requests/27 ) Version 0.6.2, released 2017-08-27: * Additions - Link Mauve added an implementation of IntoElements for all Into ( https://gitlab.com/lumi/minidom-rs/merge_requests/19 ) diff --git a/Cargo.toml b/Cargo.toml index a4ea886e164dc8eed4fa6f664e8affed8808aa95..a05552ca908f56a7a7e14a5c14ff7d58a89ecbee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.7.0" +version = "0.8.0" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From 00f7d545aa7a4e5ae6aa4c545dc6ef754729b5a7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 18 Feb 2018 21:36:36 +0100 Subject: [PATCH 0571/1020] add tests for errors --- src/lib.rs | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f0280d99aab1fa1670c06031411e0795686118ef..ebdfa80d55c2ec94df3cde5dcf3fe0372d2e77b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,8 +13,13 @@ use std::str::FromStr; /// An error that signifies that a `Jid` cannot be parsed from a string. #[derive(Debug, Clone, PartialEq, Eq)] pub enum JidParseError { - /// Happens when there is no domain. (really, only happens when the string is empty) + /// Happens when there is no domain, that is either the string is empty, + /// starts with a /, or contains the @/ sequence. NoDomain, + /// Happens when the node is empty, that is the string starts with a @. + EmptyNode, + /// Happens when the resource is empty, that is the string ends with a /. + EmptyResource, } /// A struct representing a Jabber ID. @@ -86,11 +91,17 @@ impl FromStr for Jid { ParserState::Node => { match c { '@' => { + if buf == "" { + return Err(JidParseError::EmptyNode); + } state = ParserState::Domain; node = Some(buf.clone()); // TODO: performance tweaks, do not need to copy it buf.clear(); }, '/' => { + if buf == "" { + return Err(JidParseError::NoDomain); + } state = ParserState::Resource; domain = Some(buf.clone()); // TODO: performance tweaks buf.clear(); @@ -103,6 +114,9 @@ impl FromStr for Jid { ParserState::Domain => { match c { '/' => { + if buf == "" { + return Err(JidParseError::NoDomain); + } state = ParserState::Resource; domain = Some(buf.clone()); // TODO: performance tweaks buf.clear(); @@ -129,6 +143,8 @@ impl FromStr for Jid { resource = Some(buf); }, } + } else if let ParserState::Resource = state { + return Err(JidParseError::EmptyResource); } Ok(Jid { node: node, @@ -401,6 +417,34 @@ mod tests { assert_eq!(String::from(Jid::full("a", "b", "c")), String::from("a@b/c")); } + #[test] + fn invalid() { + match Jid::from_str("") { + Err(JidParseError::NoDomain) => (), + err => panic!("Invalid error: {:?}", err) + } + + match Jid::from_str("a@/c") { + Err(JidParseError::NoDomain) => (), + err => panic!("Invalid error: {:?}", err) + } + + match Jid::from_str("/c") { + Err(JidParseError::NoDomain) => (), + err => panic!("Invalid error: {:?}", err) + } + + match Jid::from_str("@b") { + Err(JidParseError::EmptyNode) => (), + err => panic!("Invalid error: {:?}", err) + } + + match Jid::from_str("b/") { + Err(JidParseError::EmptyResource) => (), + err => panic!("Invalid error: {:?}", err) + } + } + #[cfg(feature = "minidom")] #[test] fn minidom() { From c1fbfd26324ea5a46bf52c483cb18f6b535f9a70 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 18 Feb 2018 21:37:01 +0100 Subject: [PATCH 0572/1020] bump minidom dependency --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a7ba1a0c3ee4547b00247bb32b4720e9921b1982..84eed9924f8dbcd2864d6b442d29f68559d0cee8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,4 @@ license = "LGPL-3.0+" gitlab = { repository = "xmpp-rs/jid-rs" } [dependencies] -minidom = { version = "0.7", optional = true } +minidom = { version = "0.8.0", optional = true } From 98ad44b5118acc7b75e82f1d5e7db0de2cd24462 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 18 Feb 2018 21:38:07 +0100 Subject: [PATCH 0573/1020] release version 0.5.0 --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49f7daac9c5edb5ff4e2f531409a74f3b391768e..ad2dbb10d2e9b5b3b015b96362a4bd2e8f6c8efd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +Version 0.5.0, released 2018-02-18: + * Updates + - Link Mauve has updated the optional `minidom` dependency. + - Link Mauve has added tests for invalid JIDs, which adds more error cases. + Version 0.4.0, released 2017-12-27: * Updates - Maxime Buquet has updated the optional `minidom` dependency. diff --git a/Cargo.toml b/Cargo.toml index 84eed9924f8dbcd2864d6b442d29f68559d0cee8..cc9916d4eef4a1266f51b3344b3a6735be75a227 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.4.0" +version = "0.5.0" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From e4dfa218b6c71320d4c4d2eee9ee8622d078447e Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 18 Feb 2018 21:50:32 +0100 Subject: [PATCH 0574/1020] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e1c16ab0eb36d62b31c9d6bb77a42fbd145a565..f2f831b7e80b5da7efe337813fd2a73dcf5d0885 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ Version 0.8.0, released 2018-02-18: * Additions - Link Mauve replaced error\_chain with failure ( https://gitlab.com/lumi/minidom-rs/merge_requests/27 ) + - Yue Liu added support for writing comments and made the writing methods use quick-xml's EventWriter ( https://gitlab.com/lumi/minidom-rs/merge_requests/26 ) Version 0.6.2, released 2017-08-27: * Additions - Link Mauve added an implementation of IntoElements for all Into ( https://gitlab.com/lumi/minidom-rs/merge_requests/19 ) From 208e280067f075daf1d061b78992fa8611342386 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 20 Feb 2018 16:20:45 +0100 Subject: [PATCH 0575/1020] Add a WebSocket parser. --- src/lib.rs | 3 +++ src/ns.rs | 3 +++ src/websocket.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 src/websocket.rs diff --git a/src/lib.rs b/src/lib.rs index 415572c0f4092d811e57ae196363a9714a532da1..36b4b3e73fe1d91036a5a4926a9e77c7a8934ad8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,9 @@ pub mod stanza_error; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod roster; +/// RFC 7395: An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket +pub mod websocket; + /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/ns.rs b/src/ns.rs index b8a10bca58e0eca69cc60c3445c392dace9b7662..dcc3946c95b276ca17d3aba8c55061a48e7ac144 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -13,6 +13,9 @@ pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub const ROSTER: &str = "jabber:iq:roster"; +/// RFC 7395: An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket +pub const WEBSOCKET: &str = "urn:ietf:params:xml:ns:xmpp-framing"; + /// XEP-0004: Data Forms pub const DATA_FORMS: &str = "jabber:x:data"; diff --git a/src/websocket.rs b/src/websocket.rs new file mode 100644 index 0000000000000000000000000000000000000000..163ce3ef4ae91b2699a90b60c1d069f7df470584 --- /dev/null +++ b/src/websocket.rs @@ -0,0 +1,70 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; + +use minidom::Element; +use jid::Jid; +use error::Error; +use ns; + +generate_element_with_only_attributes!(Open, "open", ns::WEBSOCKET, [ + from: Option = "from" => optional, + to: Option = "to" => optional, + id: Option = "id" => optional, + version: Option = "version" => optional, + xml_lang: Option = "xml:lang" => optional, +]); + +impl Open { + pub fn new(to: Jid) -> Open { + Open { + from: None, + to: Some(to), + id: None, + version: Some(String::from("1.0")), + xml_lang: None, + } + } + + pub fn with_from(mut self, from: Jid) -> Open { + self.from = Some(from); + self + } + + pub fn with_id(mut self, id: String) -> Open { + self.id = Some(id); + self + } + + pub fn with_lang(mut self, xml_lang: String) -> Open { + self.xml_lang = Some(xml_lang); + self + } + + pub fn is_version(&self, version: &str) -> bool { + match self.version { + None => false, + Some(ref self_version) => self_version == &String::from(version), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let open = Open::try_from(elem).unwrap(); + assert_eq!(open.from, None); + assert_eq!(open.to, None); + assert_eq!(open.id, None); + assert_eq!(open.version, None); + assert_eq!(open.xml_lang, None); + } +} From fab99c90044dcbdc25e1c31b7597e1575554e0b5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 20 Feb 2018 16:53:42 +0100 Subject: [PATCH 0576/1020] error: Remove std::error::Error implementation as it conflicts with failure. --- Cargo.toml | 4 ++-- src/error.rs | 16 ---------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1f1ab4bfa0e770ed2d4594d391fdd4f3373ca7ce..12239020669fdd3d71380f6231b0dae51a9fa3d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,8 @@ categories = ["parsing", "network-programming"] license = "MPL-2.0" [dependencies] -minidom = { version = "0.7.0" } -jid = { version = "0.4.0", features = ["minidom"] } +minidom = { version = "0.8.0" } +jid = { version = "0.5.0", features = ["minidom"] } base64 = "0.9.0" digest = "0.7.1" sha-1 = "0.7.0" diff --git a/src/error.rs b/src/error.rs index f8d7372b8710384c93b9a56162f693e600f34b01..912ad02543557d337188b9e865003030216a6fc5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,7 +9,6 @@ use std::io; use std::num; use std::string; use std::fmt; -use std::error; use base64; use minidom; @@ -43,21 +42,6 @@ impl fmt::Display for Error { } } -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::ParseError(s) => s, - Error::IoError(ref e) => e.description(), - Error::XMLError(ref e) => e.description(), - Error::Base64Error(ref e) => e.description(), - Error::ParseIntError(ref e) => e.description(), - Error::ParseStringError(ref e) => e.description(), - Error::JidParseError(_) => "JID parse error", - Error::ChronoParseError(ref e) => e.description(), - } - } -} - impl From for Error { fn from(err: io::Error) -> Error { Error::IoError(err) From cfaebb4a91c4ac090e8f761a874eb81e8d09cf17 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 20 Feb 2018 17:01:12 +0100 Subject: [PATCH 0577/1020] Add a SASL parser. --- src/lib.rs | 2 ++ src/macros.rs | 5 ++++- src/ns.rs | 2 ++ src/sasl.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/sasl.rs diff --git a/src/lib.rs b/src/lib.rs index 36b4b3e73fe1d91036a5a4926a9e77c7a8934ad8..3f3df913b0aeb917e22944651ed9d0e841d99957 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,8 @@ pub mod presence; pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod sasl; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod roster; diff --git a/src/macros.rs b/src/macros.rs index 8fb573637cede8dd4df0e94d68a0967f41b4488e..24041cb05fdb1da057aa29fa37cce2d7fa6503c2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -344,7 +344,10 @@ macro_rules! generate_elem_id { } macro_rules! generate_element_with_text { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], $text_ident:ident: $codec:ident < $text_type:ty >) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $text_ident:ident: $codec:ident < $text_type:ty >) => ( + generate_element_with_text!($(#[$meta])* $elem, $name, $ns, [], $text_ident: $codec<$text_type>); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], $text_ident:ident: $codec:ident < $text_type:ty >) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { diff --git a/src/ns.rs b/src/ns.rs index dcc3946c95b276ca17d3aba8c55061a48e7ac144..f17e602bcfb8e19d73ff0a48721ab8b198179c22 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -9,6 +9,8 @@ pub const JABBER_CLIENT: &str = "jabber:client"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub const SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub const ROSTER: &str = "jabber:iq:roster"; diff --git a/src/sasl.rs b/src/sasl.rs new file mode 100644 index 0000000000000000000000000000000000000000..a0ab15bb7d12c3c7edabaee59f9f289d4a8f6df5 --- /dev/null +++ b/src/sasl.rs @@ -0,0 +1,53 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; +use std::str::FromStr; + +use minidom::{Element, IntoAttributeValue}; + +use error::Error; + +use ns; +use helpers::Base64; + +generate_attribute!(Mechanism, "mechanism", { + Plain => "PLAIN", + ScramSha1 => "SCRAM-SHA-1", + Anonymous => "ANONYMOUS", +}); + +generate_element_with_text!(Auth, "auth", ns::SASL, + [ + mechanism: Mechanism = "mechanism" => required + ], + data: Base64> +); + +generate_element_with_text!(Challenge, "challenge", ns::SASL, + data: Base64> +); + +generate_element_with_text!(Response, "response", ns::SASL, + data: Base64> +); + +generate_element_with_text!(Success, "success", ns::SASL, + data: Base64> +); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let auth = Auth::try_from(elem).unwrap(); + assert_eq!(auth.mechanism, Mechanism::Plain); + assert!(auth.data.is_empty()); + } +} From 7c0975dd5dcac94deb269fb9bbbd00d7e8de16d7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 20 Feb 2018 17:43:19 +0100 Subject: [PATCH 0578/1020] Add a Bind parser. --- src/bind.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ src/ns.rs | 2 ++ 3 files changed, 97 insertions(+) create mode 100644 src/bind.rs diff --git a/src/bind.rs b/src/bind.rs new file mode 100644 index 0000000000000000000000000000000000000000..6974ce508d26011028821084be378dd92712776b --- /dev/null +++ b/src/bind.rs @@ -0,0 +1,93 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::str::FromStr; +use try_from::TryFrom; + +use minidom::Element; + +use error::Error; +use jid::Jid; +use ns; + +#[derive(Debug, Clone)] +pub struct Bind { + pub resource: Option, + pub jid: Option, +} + +impl Bind { + pub fn new(resource: Option) -> Bind { + Bind { + resource, + jid: None, + } + } +} + +impl TryFrom for Bind { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "bind", ns::BIND); + check_no_attributes!(elem, "bind"); + + let mut bind = Bind { + resource: None, + jid: None, + }; + let mut already_set = false; + for child in elem.children() { + if already_set { + return Err(Error::ParseError("Bind can only have one child.")); + } + if child.is("resource", ns::BIND) { + check_no_children!(child, "resource"); + bind.resource = Some(child.text()); + already_set = true; + } else if child.is("jid", ns::BIND) { + check_no_children!(child, "jid"); + bind.jid = Some(Jid::from_str(&child.text())?); + already_set = true; + } else { + return Err(Error::ParseError("Unknown element in bind.")); + } + } + + Ok(bind) + } +} + +impl From for Element { + fn from(bind: Bind) -> Element { + Element::builder("bind") + .ns(ns::BIND) + .append(bind.resource.map(|resource| + Element::builder("resource") + .ns(ns::BIND) + .append(resource) + .build())) + .append(bind.jid.map(|jid| + Element::builder("jid") + .ns(ns::BIND) + .append(jid) + .build())) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let bind = Bind::try_from(elem).unwrap(); + assert_eq!(bind.resource, None); + assert_eq!(bind.jid, None); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3f3df913b0aeb917e22944651ed9d0e841d99957..b7afeac60fd233240b306ed633cb8eec0089bd41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,8 @@ pub mod iq; pub mod stanza_error; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod sasl; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod bind; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod roster; diff --git a/src/ns.rs b/src/ns.rs index f17e602bcfb8e19d73ff0a48721ab8b198179c22..b09c9d7e4e1f058896309a14f927f24ab293a135 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -11,6 +11,8 @@ pub const JABBER_CLIENT: &str = "jabber:client"; pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub const BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind"; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub const ROSTER: &str = "jabber:iq:roster"; From 6ac5bb99dbe1d3392d621e34432e2993bdba2e26 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 22 Feb 2018 19:56:56 +0100 Subject: [PATCH 0579/1020] happy_eyeballs: fix two mut warnings --- src/happy_eyeballs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 806472c5d602b4b32e082841a997f7b8634cc0ed..f2cd1a62d6e18deb7337d301a6617c20cde06ed6 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -45,7 +45,7 @@ impl Future for Connecter { type Error = String; fn poll(&mut self) -> Poll { - match self.lookup.as_mut().map(|mut lookup| lookup.poll()) { + match self.lookup.as_mut().map(|lookup| lookup.poll()) { None | Some(Ok(Async::NotReady)) => (), Some(Ok(Async::Ready(found_srvs))) => { self.lookup = None; @@ -60,7 +60,7 @@ impl Future for Connecter { return Err(format!("{}", e)), } - match self.srvs.as_mut().map(|mut srv| srv.poll()) { + match self.srvs.as_mut().map(|srv| srv.poll()) { None | Some(Ok(Async::NotReady)) => (), Some(Ok(Async::Ready(None))) => self.srvs = None, From 772c6b56ee4e487b8f162f0f80e735a27dce4df2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 22 Feb 2018 20:05:41 +0100 Subject: [PATCH 0580/1020] update dependencies --- Cargo.toml | 9 +++++---- src/component/auth.rs | 2 +- src/lib.rs | 3 ++- src/xmpp_codec.rs | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e8386b53b9da8fbce9e9dc85dc012950365ebda8..b664112a7b3a326927c4c32c861c74c5102f53f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,15 @@ tokio-io = "*" bytes = "0.4.4" xml5ever = "*" tendril = "*" -minidom = "0.6.1" +minidom = "0.8.0" native-tls = "*" tokio-tls = "*" sasl = "*" rustc-serialize = "*" -jid = { version = "0.3.0", features = ["minidom"] } +jid = { version = "0.5.0", features = ["minidom"] } domain = "0.2.1" -xmpp-parsers = "0.8.0" +xmpp-parsers = "0.10.0" idna = "*" try_from = "0.2.2" -sha-1 = "0.4.1" +sha-1 = "0.7.0" +quick-xml = "*" diff --git a/src/component/auth.rs b/src/component/auth.rs index 729fd3c12652d182cf94f7ca43bdbc43720a9636..bd1c9aa7000e444bbf73faa13ffd00a21ae5a283 100644 --- a/src/component/auth.rs +++ b/src/component/auth.rs @@ -2,7 +2,7 @@ use std::mem::replace; use futures::{Future, Poll, Async, sink, Sink, Stream}; use tokio_io::{AsyncRead, AsyncWrite}; use minidom::Element; -use sha_1::{Sha1, Digest}; +use sha1::{Sha1, Digest}; use xmpp_codec::Packet; use xmpp_stream::XMPPStream; diff --git a/src/lib.rs b/src/lib.rs index ddb4f9a3b1bd2704c32775ba5d73da3e3223f4d2..9401cc46c5c2226f660b3bf4050a365e1427686e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ extern crate tokio_io; extern crate bytes; extern crate xml5ever; extern crate tendril; +extern crate quick_xml; extern crate minidom; extern crate native_tls; extern crate tokio_tls; @@ -12,7 +13,7 @@ extern crate rustc_serialize as serialize; extern crate jid; extern crate domain; extern crate idna; -extern crate sha_1; +extern crate sha1; pub mod xmpp_codec; pub mod xmpp_stream; diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 5e340a9fb5177f18c37667b4fd071bc1a64b865d..97fe2a80d429a8642455becce6b02f9f052fc90f 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -13,6 +13,7 @@ use minidom::Element; use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind}; use xml5ever::interface::Attribute; use bytes::{BytesMut, BufMut}; +use quick_xml::writer::Writer as EventWriter; // const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/"; @@ -278,7 +279,7 @@ impl Encoder for XMPPCodec { .map_err(|e| Error::new(ErrorKind::InvalidInput, e)) }, Packet::Stanza(stanza) => { - stanza.write_to_inner(&mut WriteBytes::new(dst)) + stanza.write_to_inner(&mut EventWriter::new(WriteBytes::new(dst))) .and_then(|_| { println!(">> {:?}", dst); Ok(()) From a484150e4b4cce303d19fcbbc7c33d92e2e25c4b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 23 Feb 2018 12:38:40 +0100 Subject: [PATCH 0581/1020] bind: Switch to an enum, only three options are possible. --- src/bind.rs | 57 ++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/bind.rs b/src/bind.rs index 6974ce508d26011028821084be378dd92712776b..3e76dba01dc39d25c4db8e2d2b910deb2ff0f58f 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -13,17 +13,18 @@ use error::Error; use jid::Jid; use ns; -#[derive(Debug, Clone)] -pub struct Bind { - pub resource: Option, - pub jid: Option, +#[derive(Debug, Clone, PartialEq)] +pub enum Bind { + None, + Resource(String), + Jid(Jid), } impl Bind { pub fn new(resource: Option) -> Bind { - Bind { - resource, - jid: None, + match resource { + None => Bind::None, + Some(resource) => Bind::Resource(resource), } } } @@ -35,23 +36,17 @@ impl TryFrom for Bind { check_self!(elem, "bind", ns::BIND); check_no_attributes!(elem, "bind"); - let mut bind = Bind { - resource: None, - jid: None, - }; - let mut already_set = false; + let mut bind = Bind::None; for child in elem.children() { - if already_set { + if bind != Bind::None { return Err(Error::ParseError("Bind can only have one child.")); } if child.is("resource", ns::BIND) { check_no_children!(child, "resource"); - bind.resource = Some(child.text()); - already_set = true; + bind = Bind::Resource(child.text()); } else if child.is("jid", ns::BIND) { check_no_children!(child, "jid"); - bind.jid = Some(Jid::from_str(&child.text())?); - already_set = true; + bind = Bind::Jid(Jid::from_str(&child.text())?); } else { return Err(Error::ParseError("Unknown element in bind.")); } @@ -65,16 +60,21 @@ impl From for Element { fn from(bind: Bind) -> Element { Element::builder("bind") .ns(ns::BIND) - .append(bind.resource.map(|resource| - Element::builder("resource") - .ns(ns::BIND) - .append(resource) - .build())) - .append(bind.jid.map(|jid| - Element::builder("jid") - .ns(ns::BIND) - .append(jid) - .build())) + .append(match bind { + Bind::None => vec!(), + Bind::Resource(resource) => vec!( + Element::builder("resource") + .ns(ns::BIND) + .append(resource) + .build() + ), + Bind::Jid(jid) => vec!( + Element::builder("jid") + .ns(ns::BIND) + .append(jid) + .build() + ), + }) .build() } } @@ -87,7 +87,6 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let bind = Bind::try_from(elem).unwrap(); - assert_eq!(bind.resource, None); - assert_eq!(bind.jid, None); + assert_eq!(bind, Bind::None); } } From 3de29e1e81897ad0c76ece45e66afb11a6a2de27 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 10:01:35 +0100 Subject: [PATCH 0582/1020] lib, ns, error, macros: Update the copyright year. --- src/error.rs | 2 +- src/lib.rs | 2 +- src/macros.rs | 2 +- src/ns.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index 912ad02543557d337188b9e865003030216a6fc5..562e656a2990cdafdf6cf285dbf859431ad3c3b9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017-2018 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/lib.rs b/src/lib.rs index b7afeac60fd233240b306ed633cb8eec0089bd41..5fde6358ef1b2297b15311d606a5cc0df165ff13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ //! [`TryFrom`]: ../try_from/trait.TryFrom.html //! [`Element`]: ../minidom/element/struct.Element.html -// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017-2018 Emmanuel Gil Peyrot // Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public diff --git a/src/macros.rs b/src/macros.rs index 24041cb05fdb1da057aa29fa37cce2d7fa6503c2..3eded4587433fb4151c369aebd4f170f1322789c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017-2018 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/ns.rs b/src/ns.rs index b09c9d7e4e1f058896309a14f927f24ab293a135..9f905e60805a49fecd5691f2b365fd5964200da8 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot +// Copyright (c) 2017-2018 Emmanuel Gil Peyrot // Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public From 395b64e64440d1fa908c972168f9803c2494d580 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 10:08:30 +0100 Subject: [PATCH 0583/1020] Add a parser. --- src/lib.rs | 2 ++ src/ns.rs | 2 ++ src/stream.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 src/stream.rs diff --git a/src/lib.rs b/src/lib.rs index 5fde6358ef1b2297b15311d606a5cc0df165ff13..7d9091b67a18ffe07c3082690591268329080f6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,8 @@ pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod stream; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod sasl; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod bind; diff --git a/src/ns.rs b/src/ns.rs index 9f905e60805a49fecd5691f2b365fd5964200da8..af77fe32b67aa7252d885cf4747e387ff295d2cc 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -10,6 +10,8 @@ pub const JABBER_CLIENT: &str = "jabber:client"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const XMPP_STANZAS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub const STREAM: &str = "http://etherx.jabber.org/streams"; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub const BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind"; diff --git a/src/stream.rs b/src/stream.rs new file mode 100644 index 0000000000000000000000000000000000000000..83335ae2377aa3fd51670595d56a274b4937ac46 --- /dev/null +++ b/src/stream.rs @@ -0,0 +1,70 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; + +use minidom::Element; +use jid::Jid; +use error::Error; +use ns; + +generate_element_with_only_attributes!(Stream, "stream", ns::STREAM, [ + from: Option = "from" => optional, + to: Option = "to" => optional, + id: Option = "id" => optional, + version: Option = "version" => optional, + xml_lang: Option = "xml:lang" => optional, +]); + +impl Stream { + pub fn new(to: Jid) -> Stream { + Stream { + from: None, + to: Some(to), + id: None, + version: Some(String::from("1.0")), + xml_lang: None, + } + } + + pub fn with_from(mut self, from: Jid) -> Stream { + self.from = Some(from); + self + } + + pub fn with_id(mut self, id: String) -> Stream { + self.id = Some(id); + self + } + + pub fn with_lang(mut self, xml_lang: String) -> Stream { + self.xml_lang = Some(xml_lang); + self + } + + pub fn is_version(&self, version: &str) -> bool { + match self.version { + None => false, + Some(ref self_version) => self_version == &String::from(version), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let stream = Stream::try_from(elem).unwrap(); + assert_eq!(stream.from, Some(Jid::domain("some-server.example"))); + assert_eq!(stream.to, None); + assert_eq!(stream.id, Some(String::from("abc"))); + assert_eq!(stream.version, Some(String::from("1.0"))); + assert_eq!(stream.xml_lang, Some(String::from("en"))); + } +} From 011308b4b4554dbd8d47e210e64735c4f3d260fa Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 10:57:01 +0100 Subject: [PATCH 0584/1020] Add a component handshake parser. --- src/component.rs | 32 ++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ 3 files changed, 38 insertions(+) create mode 100644 src/component.rs diff --git a/src/component.rs b/src/component.rs new file mode 100644 index 0000000000000000000000000000000000000000..9c54118dd05d65eaf466d78be60123387ab9854b --- /dev/null +++ b/src/component.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; + +use minidom::Element; +use error::Error; +use helpers::Base64; +use ns; + +generate_element_with_text!(Handshake, "handshake", ns::COMPONENT, + data: Base64> +); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let handshake = Handshake::try_from(elem).unwrap(); + assert!(handshake.data.is_empty()); + + let elem: Element = "AAAA".parse().unwrap(); + let handshake = Handshake::try_from(elem).unwrap(); + assert_eq!(handshake.data, b"\0\0\0"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 7d9091b67a18ffe07c3082690591268329080f6d..e420e233e6389dcc7a06d6183c5f4703bfb924b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,6 +101,9 @@ pub mod version; /// XEP-0107: User Mood pub mod mood; +/// XEP-0114: Jabber Component Protocol +pub mod component; + /// XEP-0115: Entity Capabilities pub mod caps; diff --git a/src/ns.rs b/src/ns.rs index af77fe32b67aa7252d885cf4747e387ff295d2cc..2227886271a88847628fb82edda6b36faa428163 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -65,6 +65,9 @@ pub const MOOD: &str = "http://jabber.org/protocol/mood"; /// XEP-0114: Jabber Component Protocol pub const COMPONENT_ACCEPT: &str = "jabber:component:accept"; +/// XEP-0114: Jabber Component Protocol +pub const COMPONENT: &str = "jabber:component:accept"; + /// XEP-0115: Entity Capabilities pub const CAPS: &str = "http://jabber.org/protocol/caps"; From 56b465751890f7c53b0d972210338cba6528584c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 16:24:53 +0100 Subject: [PATCH 0585/1020] simplify Debug and Display implementations --- src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ebdfa80d55c2ec94df3cde5dcf3fe0372d2e77b8..0f2289f5a56b6fb15c4ea94dcef73e4324cb7e8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,15 +57,13 @@ impl From for String { impl fmt::Debug for Jid { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "JID({})", self)?; - Ok(()) + write!(fmt, "JID({})", self) } } impl fmt::Display for Jid { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - fmt.write_str(String::from(self.clone()).as_ref())?; - Ok(()) + fmt.write_str(String::from(self.clone()).as_ref()) } } From 87d59181cb6f0d557ae221792d20320a823063f0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 16:25:05 +0100 Subject: [PATCH 0586/1020] remove redundant test --- src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0f2289f5a56b6fb15c4ea94dcef73e4324cb7e8e..6f2641e21ccd831e19c85472b60c1c10ccb9cafb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -404,9 +404,6 @@ mod tests { assert_eq!(Jid::from_str("a@b.c/d"), Ok(Jid::full("a", "b.c", "d"))); assert_eq!(Jid::from_str("a@b.c"), Ok(Jid::bare("a", "b.c"))); assert_eq!(Jid::from_str("b.c"), Ok(Jid::domain("b.c"))); - - assert_eq!(Jid::from_str(""), Err(JidParseError::NoDomain)); - assert_eq!(Jid::from_str("a/b@c"), Ok(Jid::domain_with_resource("a", "b@c"))); } From fd4a51377942cf5020d0bcb7290ce4c4af73dbef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 16:25:59 +0100 Subject: [PATCH 0587/1020] implement Fail on JidParseError --- Cargo.toml | 2 ++ src/lib.rs | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cc9916d4eef4a1266f51b3344b3a6735be75a227..e9fcb30c59c8d30160a09348a1b8b6acd8b28510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,6 @@ license = "LGPL-3.0+" gitlab = { repository = "xmpp-rs/jid-rs" } [dependencies] +failure = "0.1.1" +failure_derive = "0.1.1" minidom = { version = "0.8.0", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 6f2641e21ccd831e19c85472b60c1c10ccb9cafb..fb04453f54df2aec939dcc65985e5de3b5e60c07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,9 @@ //! //! For usage, check the documentation on the `Jid` struct. +extern crate failure; +#[macro_use] extern crate failure_derive; + use std::fmt; use std::convert::Into; @@ -11,14 +14,19 @@ use std::convert::Into; use std::str::FromStr; /// An error that signifies that a `Jid` cannot be parsed from a string. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Fail)] pub enum JidParseError { /// Happens when there is no domain, that is either the string is empty, /// starts with a /, or contains the @/ sequence. + #[fail(display = "no domain found in this JID")] NoDomain, + /// Happens when the node is empty, that is the string starts with a @. + #[fail(display = "nodepart empty despite the presence of a @")] EmptyNode, + /// Happens when the resource is empty, that is the string ends with a /. + #[fail(display = "resource empty despite the presence of a /")] EmptyResource, } From c45d1bf5ca5422686ef85a5b96c18cf312286550 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 16:26:44 +0100 Subject: [PATCH 0588/1020] simplify tests for invalid JIDs --- src/lib.rs | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fb04453f54df2aec939dcc65985e5de3b5e60c07..293c4a0ccfc04c97fbb17de4bfa8bb3b3656cbe6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -421,31 +421,12 @@ mod tests { } #[test] - fn invalid() { - match Jid::from_str("") { - Err(JidParseError::NoDomain) => (), - err => panic!("Invalid error: {:?}", err) - } - - match Jid::from_str("a@/c") { - Err(JidParseError::NoDomain) => (), - err => panic!("Invalid error: {:?}", err) - } - - match Jid::from_str("/c") { - Err(JidParseError::NoDomain) => (), - err => panic!("Invalid error: {:?}", err) - } - - match Jid::from_str("@b") { - Err(JidParseError::EmptyNode) => (), - err => panic!("Invalid error: {:?}", err) - } - - match Jid::from_str("b/") { - Err(JidParseError::EmptyResource) => (), - err => panic!("Invalid error: {:?}", err) - } + fn invalid_jids() { + assert_eq!(Jid::from_str(""), Err(JidParseError::NoDomain)); + assert_eq!(Jid::from_str("/c"), Err(JidParseError::NoDomain)); + assert_eq!(Jid::from_str("a@/c"), Err(JidParseError::NoDomain)); + assert_eq!(Jid::from_str("@b"), Err(JidParseError::EmptyNode)); + assert_eq!(Jid::from_str("b/"), Err(JidParseError::EmptyResource)); } #[cfg(feature = "minidom")] From 06afb5afede5053ee04fcb9e3abc0496d06ad348 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 16:27:59 +0100 Subject: [PATCH 0589/1020] release version 0.5.1 --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad2dbb10d2e9b5b3b015b96362a4bd2e8f6c8efd..f1dfd03b4523b2ab0330b90a1f8e0c44fff4b53d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +Version 0.5.1, released 2018-03-01: + * Updates + - Link Mauve implemented failure::Fail on JidParseError. + - Link Mauve simplified the code a bit. + Version 0.5.0, released 2018-02-18: * Updates - Link Mauve has updated the optional `minidom` dependency. diff --git a/Cargo.toml b/Cargo.toml index e9fcb30c59c8d30160a09348a1b8b6acd8b28510..8a5c09c83a46d2a231b9991089efa106d31717f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.5.0" +version = "0.5.1" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From 0a057cdfef4549b986f1b531ecc6fa84ce91f025 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 17:31:49 +0100 Subject: [PATCH 0590/1020] =?UTF-8?q?component:=20Fix=20handshake=20conten?= =?UTF-8?q?t,=20it=E2=80=99s=20hex,=20not=20base64!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/component.rs b/src/component.rs index 9c54118dd05d65eaf466d78be60123387ab9854b..f9ac62fda1f2f6a914ce9b856f6299021538ef49 100644 --- a/src/component.rs +++ b/src/component.rs @@ -8,11 +8,11 @@ use try_from::TryFrom; use minidom::Element; use error::Error; -use helpers::Base64; +use helpers::PlainText; use ns; generate_element_with_text!(Handshake, "handshake", ns::COMPONENT, - data: Base64> + data: PlainText> ); #[cfg(test)] @@ -23,10 +23,10 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let handshake = Handshake::try_from(elem).unwrap(); - assert!(handshake.data.is_empty()); + assert_eq!(handshake.data, None); - let elem: Element = "AAAA".parse().unwrap(); + let elem: Element = "Coucou".parse().unwrap(); let handshake = Handshake::try_from(elem).unwrap(); - assert_eq!(handshake.data, b"\0\0\0"); + assert_eq!(handshake.data, Some(String::from("Coucou"))); } } From d83624c8a49918f72e9c5c527355961506859668 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Mar 2018 17:32:50 +0100 Subject: [PATCH 0591/1020] component: Add constructors. --- src/component.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/component.rs b/src/component.rs index f9ac62fda1f2f6a914ce9b856f6299021538ef49..0f42e50ae67b08872579cb0554f7f0a1b66ec594 100644 --- a/src/component.rs +++ b/src/component.rs @@ -11,10 +11,30 @@ use error::Error; use helpers::PlainText; use ns; +use sha1::Sha1; +use digest::Digest; + generate_element_with_text!(Handshake, "handshake", ns::COMPONENT, data: PlainText> ); +impl Handshake { + pub fn new() -> Handshake { + Handshake { + data: None, + } + } + + pub fn from_password_and_stream_id(password: &str, stream_id: &str) -> Handshake { + let input = String::from(stream_id) + password; + let hash = Sha1::digest(input.as_bytes()); + let content = format!("{:x}", hash); + Handshake { + data: Some(content), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -29,4 +49,13 @@ mod tests { let handshake = Handshake::try_from(elem).unwrap(); assert_eq!(handshake.data, Some(String::from("Coucou"))); } + + #[test] + fn test_constructors() { + let handshake = Handshake::new(); + assert_eq!(handshake.data, None); + + let handshake = Handshake::from_password_and_stream_id("123456", "sid"); + assert_eq!(handshake.data, Some(String::from("9accec263ab84a43c6037ccf7cd48cb1d3f6df8e"))); + } } From 96f223b7e3b0ce21fe1a023f49527fa221bd4176 Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Tue, 10 Apr 2018 21:01:18 -0400 Subject: [PATCH 0592/1020] bump version to 0.9.0 and upgrade quick-xml to 0.12.1 --- CHANGELOG.md | 3 +++ Cargo.toml | 4 ++-- src/element.rs | 8 ++++---- src/error.rs | 6 +++--- src/tests.rs | 2 +- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2f831b7e80b5da7efe337813fd2a73dcf5d0885..d3fff023eabb6e81b577e0e92e1c9de4fcc5fe24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +Version 0.9.0, released 2018-04-10: + * Small changes + - Upgrade quick_xml to 0.12.1 Version 0.8.0, released 2018-02-18: * Additions - Link Mauve replaced error\_chain with failure ( https://gitlab.com/lumi/minidom-rs/merge_requests/27 ) diff --git a/Cargo.toml b/Cargo.toml index a05552ca908f56a7a7e14a5c14ff7d58a89ecbee..de970054f5c74f05b6359d5a990e109ccbab9c79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.8.0" +version = "0.9.0" authors = [ "lumi ", "Emmanuel Gil Peyrot ", @@ -20,6 +20,6 @@ license = "MIT" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -quick-xml = "0.11.0" +quick-xml = "0.12.1" failure = "0.1.1" failure_derive = "0.1.1" diff --git a/src/element.rs b/src/element.rs index 81e3fffc8a156f01ff1793b4831c5a1b28abebbe..d8a4a98a4762dbd52c8c7a60dd353fa777adb30e 100644 --- a/src/element.rs +++ b/src/element.rs @@ -9,8 +9,8 @@ use std::borrow::Cow; use error::{Error, Result}; -use quick_xml::reader::Reader as EventReader; -use quick_xml::writer::Writer as EventWriter; +use quick_xml::Reader as EventReader; +use quick_xml::Writer as EventWriter; use quick_xml::events::{Event, BytesStart, BytesEnd, BytesText, BytesDecl}; use std::io::BufRead; @@ -126,11 +126,11 @@ impl Node { match *self { Node::Element(ref elmt) => elmt.write_to_inner(writer)?, Node::Text(ref s) => { - writer.write_event(Event::Text(BytesText::from_str(s)))?; + writer.write_event(Event::Text(BytesText::from_plain_str(s)))?; () }, Node::Comment(ref s) => { - writer.write_event(Event::Comment(BytesText::from_str(s)))?; + writer.write_event(Event::Comment(BytesText::from_plain_str(s)))?; () }, }; diff --git a/src/error.rs b/src/error.rs index 48f334403830eb1b89d06ed26f09c05ec2917ba8..b2ba473cd88c987cef13a6c334f6a0a2dada33b2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,7 +7,7 @@ use std::convert::From; pub enum Error { /// An error from quick_xml. #[fail(display = "XML error: {}", _0)] - XmlError(#[cause] ::quick_xml::errors::Error), + XmlError(#[cause] ::quick_xml::Error), /// An UTF-8 conversion error. #[fail(display = "UTF-8 error: {}", _0)] @@ -30,8 +30,8 @@ pub enum Error { InvalidElement, } -impl From<::quick_xml::errors::Error> for Error { - fn from(err: ::quick_xml::errors::Error) -> Error { +impl From<::quick_xml::Error> for Error { + fn from(err: ::quick_xml::Error) -> Error { Error::XmlError(err) } } diff --git a/src/tests.rs b/src/tests.rs index b57a2037322952dbbadf5034f43d498341bb0fbf..2acc97052ecc63c81761d0447dc9ae2c8eb6e0d0 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,6 +1,6 @@ use std::iter::Iterator; -use quick_xml::reader::Reader; +use quick_xml::Reader; use element::Element; From c762a03c39875f481db04e2bacbba6f3a398f793 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 4 May 2018 19:10:04 +0200 Subject: [PATCH 0593/1020] hashes: Add two constructors. --- src/hashes.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/hashes.rs b/src/hashes.rs index 535f99c536b5ebb4d4a0232cc0eb4af2a0350a88..e414b91a4fb100ac86a172553810af0fbe27865f 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -13,6 +13,7 @@ use error::Error; use ns; use helpers::Base64; +use base64; #[allow(non_camel_case_types)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -76,10 +77,22 @@ generate_element_with_text!( hash: Base64> ); +impl Hash { + pub fn new(algo: Algo, hash: Vec) -> Hash { + Hash { + algo, + hash, + } + } + + pub fn from_base64(algo: Algo, hash: &str) -> Result { + Ok(Hash::new(algo, base64::decode(hash)?)) + } +} + #[cfg(test)] mod tests { use super::*; - use base64; #[test] fn test_simple() { From a1e95ffb2e4b2cbef73f35e4299f9f04a87be152 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 4 May 2018 19:10:29 +0200 Subject: [PATCH 0594/1020] jingle: Add constructors and setters. --- src/jingle.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/jingle.rs b/src/jingle.rs index c9737b03fd3fd58349b8f5bca5ce901e7b0f738c..ae313766a2b0ec59a61c94fb8e0f44dbaabbcc91 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -76,6 +76,45 @@ pub struct Content { pub security: Option, } +impl Content { + pub fn new(creator: Creator, name: ContentId) -> Content { + Content { + creator, + name, + disposition: Disposition::Session, + senders: Senders::Both, + description: None, + transport: None, + security: None, + } + } + + pub fn with_disposition(mut self, disposition: Disposition) -> Content { + self.disposition = disposition; + self + } + + pub fn with_senders(mut self, senders: Senders) -> Content { + self.senders = senders; + self + } + + pub fn with_description(mut self, description: Element) -> Content { + self.description = Some(description); + self + } + + pub fn with_transport(mut self, transport: Element) -> Content { + self.transport = Some(transport); + self + } + + pub fn with_security(mut self, security: Element) -> Content { + self.security = Some(security); + self + } +} + impl TryFrom for Content { type Err = Error; @@ -267,6 +306,40 @@ pub struct Jingle { pub other: Vec, } +impl Jingle { + pub fn new(action: Action, sid: SessionId) -> Jingle { + Jingle { + action: action, + sid: sid, + initiator: None, + responder: None, + contents: Vec::new(), + reason: None, + other: Vec::new(), + } + } + + pub fn with_initiator(mut self, initiator: Jid) -> Jingle { + self.initiator = Some(initiator); + self + } + + pub fn with_responder(mut self, responder: Jid) -> Jingle { + self.responder = Some(responder); + self + } + + pub fn add_content(mut self, content: Content) -> Jingle { + self.contents.push(content); + self + } + + pub fn set_reason(mut self, content: Content) -> Jingle { + self.contents.push(content); + self + } +} + impl TryFrom for Jingle { type Err = Error; From 927df2fdb12fbe1e711f73f8a9a154f8717a84e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 4 May 2018 19:10:45 +0200 Subject: [PATCH 0595/1020] jingle_ft: Add constructors and setters. --- src/jingle_ft.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index bf584380d40b5c30510bafd78542c62c44a47df1..c78e1d45bf4776c1af567e61256849dfeab92732 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -19,7 +19,7 @@ use error::Error; use ns; generate_element_with_children!( - #[derive(PartialEq)] + #[derive(PartialEq, Default)] Range, "range", ns::JINGLE_FT, attributes: [ offset: u64 = "offset" => default, @@ -30,6 +30,12 @@ generate_element_with_children!( ] ); +impl Range { + pub fn new() -> Range { + Default::default() + } +} + type Lang = String; generate_id!(Desc); @@ -45,6 +51,60 @@ pub struct File { pub hashes: Vec, } +impl File { + pub fn new() -> File { + File { + date: None, + media_type: None, + name: None, + descs: BTreeMap::new(), + size: None, + range: None, + hashes: Vec::new(), + } + } + + pub fn with_date(mut self, date: DateTime) -> File { + self.date = Some(date); + self + } + + pub fn with_date_str(mut self, date: &str) -> Result { + self.date = Some(DateTime::from_str(date)?); + Ok(self) + } + + pub fn with_media_type(mut self, media_type: String) -> File { + self.media_type = Some(media_type); + self + } + + pub fn with_name(mut self, name: String) -> File { + self.name = Some(name); + self + } + + pub fn add_desc(mut self, lang: &str, desc: Desc) -> File { + self.descs.insert(Lang::from(lang), desc); + self + } + + pub fn with_size(mut self, size: u64) -> File { + self.size = Some(size); + self + } + + pub fn with_range(mut self, range: Range) -> File { + self.range = Some(range); + self + } + + pub fn add_hash(mut self, hash: Hash) -> File { + self.hashes.push(hash); + self + } +} + impl TryFrom for File { type Err = Error; From 3310f297047a7a5d244d21c090167e1a5bb37314 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 4 May 2018 19:11:03 +0200 Subject: [PATCH 0596/1020] jingle_s5b: Add constructors and setters. --- src/jingle_s5b.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 09b3dc56ea7bd6b6c3db874929b8740024497bcc..4233d2d9dccd14bb739ca4b8e1468f98632fbd14 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -39,6 +39,29 @@ generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ type_: Type = "type" => default, ]); +impl Candidate { + pub fn new(cid: CandidateId, host: String, jid: Jid, priority: u32) -> Candidate { + Candidate { + cid, + host, + jid, + priority, + port: Default::default(), + type_: Default::default(), + } + } + + pub fn with_port(mut self, port: u16) -> Candidate { + self.port = Some(port); + self + } + + pub fn with_type(mut self, type_: Type) -> Candidate { + self.type_ = type_; + self + } +} + #[derive(Debug, Clone)] pub enum TransportPayload { Activated(String), @@ -57,6 +80,32 @@ pub struct Transport { pub payload: TransportPayload, } +impl Transport { + pub fn new(sid: StreamId) -> Transport { + Transport { + sid, + dstaddr: None, + mode: Default::default(), + payload: TransportPayload::None, + } + } + + pub fn with_dstaddr(mut self, dstaddr: String) -> Transport { + self.dstaddr = Some(dstaddr); + self + } + + pub fn with_mode(mut self, mode: Mode) -> Transport { + self.mode = mode; + self + } + + pub fn with_payload(mut self, payload: TransportPayload) -> Transport { + self.payload = payload; + self + } +} + impl TryFrom for Transport { type Err = Error; From 53e23cbf654f6d9d1bc51f69d2168e513c807944 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 4 May 2018 21:19:40 +0200 Subject: [PATCH 0597/1020] jingle_s5b: Make Candidate.host an IpAddr instead of a String. --- src/error.rs | 9 +++++++++ src/jingle_s5b.rs | 9 +++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index 562e656a2990cdafdf6cf285dbf859431ad3c3b9..dbfbd8af082de97e44cae6f93570b97ad3cbe662 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ use std::io; use std::num; use std::string; use std::fmt; +use std::net; use base64; use minidom; @@ -23,6 +24,7 @@ pub enum Error { Base64Error(base64::DecodeError), ParseIntError(num::ParseIntError), ParseStringError(string::ParseError), + ParseAddrError(net::AddrParseError), JidParseError(jid::JidParseError), ChronoParseError(chrono::ParseError), } @@ -36,6 +38,7 @@ impl fmt::Display for Error { Error::Base64Error(ref e) => write!(fmt, "{}", e), Error::ParseIntError(ref e) => write!(fmt, "{}", e), Error::ParseStringError(ref e) => write!(fmt, "{}", e), + Error::ParseAddrError(ref e) => write!(fmt, "{}", e), Error::JidParseError(_) => write!(fmt, "JID parse error"), Error::ChronoParseError(ref e) => write!(fmt, "{}", e), } @@ -72,6 +75,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: net::AddrParseError) -> Error { + Error::ParseAddrError(err) + } +} + impl From for Error { fn from(err: jid::JidParseError) -> Error { Error::JidParseError(err) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 4233d2d9dccd14bb739ca4b8e1468f98632fbd14..c670124d24835b2ff59147f5ebf4919c96311101 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -6,6 +6,7 @@ use try_from::TryFrom; use std::str::FromStr; +use std::net::IpAddr; use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -32,7 +33,7 @@ generate_id!(StreamId); generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ cid: CandidateId = "cid" => required, - host: String = "host" => required, + host: IpAddr = "host" => required, jid: Jid = "jid" => required, port: Option = "port" => optional, priority: u32 = "priority" => required, @@ -40,7 +41,7 @@ generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ ]); impl Candidate { - pub fn new(cid: CandidateId, host: String, jid: Jid, priority: u32) -> Candidate { + pub fn new(cid: CandidateId, host: IpAddr, jid: Jid, priority: u32) -> Candidate { Candidate { cid, host, @@ -238,14 +239,14 @@ mod tests { #[test] fn test_serialise_candidate() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let transport = Transport { sid: StreamId(String::from("coucou")), dstaddr: None, mode: Mode::Tcp, payload: TransportPayload::Candidates(vec!(Candidate { cid: CandidateId(String::from("coucou")), - host: String::from("coucou"), + host: IpAddr::from_str("127.0.0.1").unwrap(), jid: Jid::from_str("coucou@coucou").unwrap(), port: None, priority: 0u32, From 6bafe35d2a3e14b04ba4df0f4e95e16ee10bd083 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 17:22:05 +0200 Subject: [PATCH 0598/1020] message: Add getters for the best body per language list. --- src/message.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/message.rs b/src/message.rs index add6d1733adbe62b43e116851867364599c13744..6bbab781931470792cee77a631e76ebf201ec7d1 100644 --- a/src/message.rs +++ b/src/message.rs @@ -146,6 +146,43 @@ impl Message { payloads: vec!(), } } + + fn get_best<'a, T>(map: &'a BTreeMap, preferred_langs: Vec<&str>) -> Option<(Lang, &'a T)> { + if map.is_empty() { + return None; + } + for lang in preferred_langs { + if map.contains_key(lang) { + return Some((Lang::from(lang), &map[lang])); + } + } + if map.contains_key("") { + return Some((Lang::new(), &map[""])); + } + map.iter().map(|(lang, body)| (lang.clone(), body)).next() + } + + /// Returns the best matching body from a list of languages. + /// + /// For instance, if a message contains both an xml:lang='de', an xml:lang='fr' and an English + /// body without an xml:lang attribute, and you pass ["fr", "en"] as your preferred languages, + /// `Some(("fr", the_second_body))` will be returned. + /// + /// If no body matches, an undefined body will be returned. + pub fn get_best_body(&self, preferred_langs: Vec<&str>) -> Option<(Lang, &Body)> { + Message::get_best::(&self.bodies, preferred_langs) + } + + /// Returns the best matching subject from a list of languages. + /// + /// For instance, if a message contains both an xml:lang='de', an xml:lang='fr' and an English + /// subject without an xml:lang attribute, and you pass ["fr", "en"] as your preferred + /// languages, `Some(("fr", the_second_subject))` will be returned. + /// + /// If no subject matches, an undefined subject will be returned. + pub fn get_best_subject(&self, preferred_langs: Vec<&str>) -> Option<(Lang, &Subject)> { + Message::get_best::(&self.subjects, preferred_langs) + } } impl TryFrom for Message { @@ -281,6 +318,12 @@ mod tests { let message = Message::try_from(elem).unwrap(); assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); + { + let (lang, body) = message.get_best_body(vec!("en")).unwrap(); + assert_eq!(lang, ""); + assert_eq!(body, &Body::from_str("Hello world!").unwrap()); + } + let elem2 = message.into(); assert!(elem1.compare_to(&elem2)); } @@ -307,10 +350,58 @@ mod tests { let message = Message::try_from(elem).unwrap(); assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); + { + let (lang, subject) = message.get_best_subject(vec!("en")).unwrap(); + assert_eq!(lang, ""); + assert_eq!(subject, &Subject::from_str("Hello world!").unwrap()); + } + let elem2 = message.into(); assert!(elem1.compare_to(&elem2)); } + #[test] + fn get_best_body() { + #[cfg(not(feature = "component"))] + let elem: Element = "Hallo Welt!Salut le monde !Hello world!".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "Hello world!".parse().unwrap(); + let message = Message::try_from(elem).unwrap(); + + // Tests basic feature. + { + let (lang, body) = message.get_best_body(vec!("fr")).unwrap(); + assert_eq!(lang, "fr"); + assert_eq!(body, &Body::from_str("Salut le monde !").unwrap()); + } + + // Tests order. + { + let (lang, body) = message.get_best_body(vec!("en", "de")).unwrap(); + assert_eq!(lang, "de"); + assert_eq!(body, &Body::from_str("Hallo Welt!").unwrap()); + } + + // Tests fallback. + { + let (lang, body) = message.get_best_body(vec!()).unwrap(); + assert_eq!(lang, ""); + assert_eq!(body, &Body::from_str("Hello world!").unwrap()); + } + + // Tests fallback. + { + let (lang, body) = message.get_best_body(vec!("ja")).unwrap(); + assert_eq!(lang, ""); + assert_eq!(body, &Body::from_str("Hello world!").unwrap()); + } + + let message = Message::new(None); + + // Tests without a body. + assert_eq!(message.get_best_body(vec!("ja")), None); + } + #[test] fn test_attention() { #[cfg(not(feature = "component"))] From c72f5819c7f468b026a73b463d198af4051dac3e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 17:42:07 +0200 Subject: [PATCH 0599/1020] message: Optimise slightly the get_best method. --- src/message.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/message.rs b/src/message.rs index 6bbab781931470792cee77a631e76ebf201ec7d1..ffd8f21915d76d4405b070f7591691709c03ca72 100644 --- a/src/message.rs +++ b/src/message.rs @@ -152,12 +152,12 @@ impl Message { return None; } for lang in preferred_langs { - if map.contains_key(lang) { - return Some((Lang::from(lang), &map[lang])); + if let Some(body) = map.get(lang) { + return Some((Lang::from(lang), body)); } } - if map.contains_key("") { - return Some((Lang::new(), &map[""])); + if let Some(body) = map.get("") { + return Some((Lang::new(), body)); } map.iter().map(|(lang, body)| (lang.clone(), body)).next() } From f2f8de773be619c054e5d8a7828cb0d923c2c962 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 17:42:49 +0200 Subject: [PATCH 0600/1020] message: Rename body into value, since this applies to subject too. --- src/message.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/message.rs b/src/message.rs index ffd8f21915d76d4405b070f7591691709c03ca72..dad3c486582c03f80c20d73086f82dc0a7ec782f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -152,14 +152,14 @@ impl Message { return None; } for lang in preferred_langs { - if let Some(body) = map.get(lang) { - return Some((Lang::from(lang), body)); + if let Some(value) = map.get(lang) { + return Some((Lang::from(lang), value)); } } - if let Some(body) = map.get("") { - return Some((Lang::new(), body)); + if let Some(value) = map.get("") { + return Some((Lang::new(), value)); } - map.iter().map(|(lang, body)| (lang.clone(), body)).next() + map.iter().map(|(lang, value)| (lang.clone(), value)).next() } /// Returns the best matching body from a list of languages. From e0438f9b887108e4dc437b83f9c81904f7a2b32a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 17:59:04 +0200 Subject: [PATCH 0601/1020] jingle_message, muc, presence, version: Always use the check_no_attributes macro. --- src/jingle_message.rs | 6 +----- src/muc/muc.rs | 5 +---- src/muc/user.rs | 17 +++-------------- src/presence.rs | 14 +++----------- src/version.rs | 4 +--- 5 files changed, 9 insertions(+), 37 deletions(-) diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 779af992d0b43f06b7f7b5104301a95966421cdd..159ce597adcbaeb3100d1febc17119c6c454a50a 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -28,11 +28,7 @@ pub enum JingleMI { } fn get_sid(elem: Element) -> Result { - for (attr, _) in elem.attrs() { - if attr != "id" { - return Err(Error::ParseError("Unknown attribute in Jingle message element.")); - } - } + check_no_unknown_attributes!(elem, "Jingle message", ["id"]); Ok(SessionId(get_attr!(elem, "id", required))) } diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 550cc61e6c603215cbfb94994570ce9eebf7fee7..9758b80071279443f6eec10c8d3e0c69c7cb51d9 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -25,6 +25,7 @@ impl TryFrom for Muc { if !elem.is("x", ns::MUC) { return Err(Error::ParseError("This is not an x element.")); } + check_no_attributes!(elem, "x"); let mut password = None; for child in elem.children() { @@ -35,10 +36,6 @@ impl TryFrom for Muc { } } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in x element.")); - } - Ok(Muc { password: password, }) diff --git a/src/muc/user.rs b/src/muc/user.rs index d09c73c4d6b6d01be32301383003f6cdfa454997..236ca8c87934e7bc352db230be12c5a88ec7dfe5 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -96,14 +96,10 @@ impl TryFrom for Actor { if !elem.is("actor", ns::MUC_USER) { return Err(Error::ParseError("This is not a actor element.")); } + check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); for _ in elem.children() { return Err(Error::ParseError("Unknown child in actor element.")); } - for (attr, _) in elem.attrs() { - if attr != "jid" && attr != "nick" { - return Err(Error::ParseError("Unknown attribute in actor element.")); - } - } let jid: Option = get_attr!(elem, "jid", optional); let nick = get_attr!(elem, "nick", optional); @@ -167,6 +163,7 @@ impl TryFrom for Item { if !elem.is("item", ns::MUC_USER) { return Err(Error::ParseError("This is not a item element.")); } + check_no_unknown_attributes!(elem, "item", ["affiliation", "jid", "nick", "role"]); let mut actor: Option = None; let mut continue_: Option = None; let mut reason: Option = None; @@ -181,12 +178,6 @@ impl TryFrom for Item { return Err(Error::ParseError("Unknown child in item element.")); } } - for (attr, _) in elem.attrs() { - if attr != "affiliation" && attr != "jid" && - attr != "nick" && attr != "role" { - return Err(Error::ParseError("Unknown attribute in item element.")); - } - } let affiliation: Affiliation = get_attr!(elem, "affiliation", required); let jid: Option = get_attr!(elem, "jid", optional); @@ -233,6 +224,7 @@ impl TryFrom for MucUser { if !elem.is("x", ns::MUC_USER) { return Err(Error::ParseError("This is not an x element.")); } + check_no_attributes!(elem, "x"); let mut status = vec!(); let mut items = vec!(); for child in elem.children() { @@ -244,9 +236,6 @@ impl TryFrom for MucUser { return Err(Error::ParseError("Unknown child in x element.")); } } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in x element.")); - } Ok(MucUser { status, items, diff --git a/src/presence.rs b/src/presence.rs index e48e68509dbc8c6f79673527b3fee31d9ecc01d1..d25a7b092a5e661c891176818b7022b10daed95d 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -268,22 +268,16 @@ impl TryFrom for Presence { if show.is_some() { return Err(Error::ParseError("More than one show element in a presence.")); } + check_no_attributes!(elem, "show"); for _ in elem.children() { return Err(Error::ParseError("Unknown child in show element.")); } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in show element.")); - } show = Some(Show::from_str(elem.text().as_ref())?); } else if elem.is("status", ns::DEFAULT_NS) { + check_no_unknown_attributes!(elem, "status", ["xml:lang"]); for _ in elem.children() { return Err(Error::ParseError("Unknown child in status element.")); } - for (attr, _) in elem.attrs() { - if attr != "xml:lang" { - return Err(Error::ParseError("Unknown attribute in status element.")); - } - } let lang = get_attr!(elem, "xml:lang", default); if presence.statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); @@ -292,12 +286,10 @@ impl TryFrom for Presence { if priority.is_some() { return Err(Error::ParseError("More than one priority element in a presence.")); } + check_no_attributes!(elem, "status"); for _ in elem.children() { return Err(Error::ParseError("Unknown child in priority element.")); } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown attribute in priority element.")); - } priority = Some(Priority::from_str(elem.text().as_ref())?); } else { presence.payloads.push(elem.clone()); diff --git a/src/version.rs b/src/version.rs index 5e7f0abf829d89e4a5b7ed0351657950a0a5aea8..e14bb422ed5a26c733d77d5c5b4fad5a917825ce 100644 --- a/src/version.rs +++ b/src/version.rs @@ -23,9 +23,7 @@ impl TryFrom for Version { if !elem.is("query", ns::VERSION) { return Err(Error::ParseError("This is not a version element.")); } - for _ in elem.attrs() { - return Err(Error::ParseError("Unknown child in version element.")); - } + check_no_attributes!(elem, "version"); let mut name = None; let mut version = None; let mut os = None; From 6bb466eea29cbab051bfd81456db5506cd818f2d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 18:31:11 +0200 Subject: [PATCH 0602/1020] Use check_self!() where it makes sense. --- src/forwarding.rs | 4 +- src/ibr.rs | 4 +- src/iq.rs | 4 +- src/jingle.rs | 12 ++---- src/jingle_s5b.rs | 95 ++++++++++++++++++++++----------------------- src/message.rs | 4 +- src/muc/muc.rs | 4 +- src/muc/user.rs | 12 ++---- src/presence.rs | 4 +- src/rsm.rs | 6 +-- src/stanza_error.rs | 4 +- src/version.rs | 4 +- 12 files changed, 62 insertions(+), 95 deletions(-) diff --git a/src/forwarding.rs b/src/forwarding.rs index 2348068b996e90ecc0f8fbc2902a6d508452ae19..8ab4ead2b534699cd1023f96bf4e3d3aa6453cfa 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -26,9 +26,7 @@ impl TryFrom for Forwarded { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("forwarded", ns::FORWARD) { - return Err(Error::ParseError("This is not a forwarded element.")); - } + check_self!(elem, "forwarded", ns::FORWARD); let mut delay = None; let mut stanza = None; for child in elem.children() { diff --git a/src/ibr.rs b/src/ibr.rs index 05281f333255b2d52b6ecdaf63c5284d116bb238..892c67d9d3fad57c659d4ca577d7b01f1a4cceff 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -29,9 +29,7 @@ impl TryFrom for Query { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::REGISTER) { - return Err(Error::ParseError("This is not an ibr element.")); - } + check_self!(elem, "query", ns::REGISTER, "IBR query"); let mut query = Query { registered: false, fields: HashMap::new(), diff --git a/src/iq.rs b/src/iq.rs index 65c13a2411a04894835b873d5199a1be5192e8f1..9a7f3b5933f3ecad76e2e0a97f6b2ea04bc432fa 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -207,9 +207,7 @@ impl TryFrom for Iq { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("iq", ns::DEFAULT_NS) { - return Err(Error::ParseError("This is not an iq element.")); - } + check_self!(root, "iq", ns::DEFAULT_NS); let from = get_attr!(root, "from", optional); let to = get_attr!(root, "to", optional); let id = get_attr!(root, "id", optional); diff --git a/src/jingle.rs b/src/jingle.rs index ae313766a2b0ec59a61c94fb8e0f44dbaabbcc91..08002bcf61ed5e4830a18f55f810126af2386818 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -119,9 +119,7 @@ impl TryFrom for Content { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("content", ns::JINGLE) { - return Err(Error::ParseError("This is not a content element.")); - } + check_self!(elem, "content", ns::JINGLE); let mut content = Content { creator: get_attr!(elem, "creator", required), @@ -252,9 +250,7 @@ impl TryFrom for ReasonElement { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("reason", ns::JINGLE) { - return Err(Error::ParseError("This is not a reason element.")); - } + check_self!(elem, "reason", ns::JINGLE); let mut reason = None; let mut text = None; for child in elem.children() { @@ -344,9 +340,7 @@ impl TryFrom for Jingle { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("jingle", ns::JINGLE) { - return Err(Error::ParseError("This is not a Jingle element.")); - } + check_self!(root, "jingle", ns::JINGLE, "Jingle"); let mut jingle = Jingle { action: get_attr!(root, "action", required), diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index c670124d24835b2ff59147f5ebf4919c96311101..558eb560e92e22cc0f9ab5ca17c25912faf42219 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -111,57 +111,54 @@ impl TryFrom for Transport { type Err = Error; fn try_from(elem: Element) -> Result { - if elem.is("transport", ns::JINGLE_S5B) { - let sid = get_attr!(elem, "sid", required); - let dstaddr = get_attr!(elem, "dstaddr", optional); - let mode = get_attr!(elem, "mode", default); + check_self!(elem, "transport", ns::JINGLE_S5B); + let sid = get_attr!(elem, "sid", required); + let dstaddr = get_attr!(elem, "dstaddr", optional); + let mode = get_attr!(elem, "mode", default); - let mut payload = None; - for child in elem.children() { - payload = Some(if child.is("candidate", ns::JINGLE_S5B) { - let mut candidates = match payload { - Some(TransportPayload::Candidates(candidates)) => candidates, - Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")), - None => vec!(), - }; - candidates.push(Candidate::try_from(child.clone())?); - TransportPayload::Candidates(candidates) - } else if child.is("activated", ns::JINGLE_S5B) { - if payload.is_some() { - return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); - } - let cid = get_attr!(child, "cid", required); - TransportPayload::Activated(cid) - } else if child.is("candidate-error", ns::JINGLE_S5B) { - if payload.is_some() { - return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element.")); - } - TransportPayload::CandidateError - } else if child.is("candidate-used", ns::JINGLE_S5B) { - if payload.is_some() { - return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); - } - let cid = get_attr!(child, "cid", required); - TransportPayload::CandidateUsed(cid) - } else if child.is("proxy-error", ns::JINGLE_S5B) { - if payload.is_some() { - return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element.")); - } - TransportPayload::ProxyError - } else { - return Err(Error::ParseError("Unknown child in JingleS5B transport element.")); - }); - } - let payload = payload.unwrap_or(TransportPayload::None); - Ok(Transport { - sid: sid, - dstaddr: dstaddr, - mode: mode, - payload: payload, - }) - } else { - Err(Error::ParseError("This is not an JingleS5B transport element.")) + let mut payload = None; + for child in elem.children() { + payload = Some(if child.is("candidate", ns::JINGLE_S5B) { + let mut candidates = match payload { + Some(TransportPayload::Candidates(candidates)) => candidates, + Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")), + None => vec!(), + }; + candidates.push(Candidate::try_from(child.clone())?); + TransportPayload::Candidates(candidates) + } else if child.is("activated", ns::JINGLE_S5B) { + if payload.is_some() { + return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); + } + let cid = get_attr!(child, "cid", required); + TransportPayload::Activated(cid) + } else if child.is("candidate-error", ns::JINGLE_S5B) { + if payload.is_some() { + return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element.")); + } + TransportPayload::CandidateError + } else if child.is("candidate-used", ns::JINGLE_S5B) { + if payload.is_some() { + return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); + } + let cid = get_attr!(child, "cid", required); + TransportPayload::CandidateUsed(cid) + } else if child.is("proxy-error", ns::JINGLE_S5B) { + if payload.is_some() { + return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element.")); + } + TransportPayload::ProxyError + } else { + return Err(Error::ParseError("Unknown child in JingleS5B transport element.")); + }); } + let payload = payload.unwrap_or(TransportPayload::None); + Ok(Transport { + sid: sid, + dstaddr: dstaddr, + mode: mode, + payload: payload, + }) } } diff --git a/src/message.rs b/src/message.rs index dad3c486582c03f80c20d73086f82dc0a7ec782f..c4c4a17fb23d5a82a5cb718e11db8341dfcc6bf6 100644 --- a/src/message.rs +++ b/src/message.rs @@ -189,9 +189,7 @@ impl TryFrom for Message { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("message", ns::DEFAULT_NS) { - return Err(Error::ParseError("This is not a message element.")); - } + check_self!(root, "message", ns::DEFAULT_NS); let from = get_attr!(root, "from", optional); let to = get_attr!(root, "to", optional); let id = get_attr!(root, "id", optional); diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 9758b80071279443f6eec10c8d3e0c69c7cb51d9..14a65302322f41ad0a10fce208893ccc63c269b0 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -22,9 +22,7 @@ impl TryFrom for Muc { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("x", ns::MUC) { - return Err(Error::ParseError("This is not an x element.")); - } + check_self!(elem, "x", ns::MUC); check_no_attributes!(elem, "x"); let mut password = None; diff --git a/src/muc/user.rs b/src/muc/user.rs index 236ca8c87934e7bc352db230be12c5a88ec7dfe5..a6614a63af1c4bd6ada54c41583394bc5102b109 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -93,9 +93,7 @@ impl TryFrom for Actor { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("actor", ns::MUC_USER) { - return Err(Error::ParseError("This is not a actor element.")); - } + check_self!(elem, "actor", ns::MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); for _ in elem.children() { return Err(Error::ParseError("Unknown child in actor element.")); @@ -160,9 +158,7 @@ impl TryFrom for Item { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("item", ns::MUC_USER) { - return Err(Error::ParseError("This is not a item element.")); - } + check_self!(elem, "item", ns::MUC_USER); check_no_unknown_attributes!(elem, "item", ["affiliation", "jid", "nick", "role"]); let mut actor: Option = None; let mut continue_: Option = None; @@ -221,9 +217,7 @@ impl TryFrom for MucUser { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("x", ns::MUC_USER) { - return Err(Error::ParseError("This is not an x element.")); - } + check_self!(elem, "x", ns::MUC_USER); check_no_attributes!(elem, "x"); let mut status = vec!(); let mut items = vec!(); diff --git a/src/presence.rs b/src/presence.rs index d25a7b092a5e661c891176818b7022b10daed95d..f4a4f11fb810a53147ee262031e156b6b5243c52 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -248,9 +248,7 @@ impl TryFrom for Presence { type Err = Error; fn try_from(root: Element) -> Result { - if !root.is("presence", ns::DEFAULT_NS) { - return Err(Error::ParseError("This is not a presence element.")); - } + check_self!(root, "presence", ns::DEFAULT_NS); let mut show = None; let mut priority = None; let mut presence = Presence { diff --git a/src/rsm.rs b/src/rsm.rs index 17642986488e1688e81b956688c8d0f5cb453b6e..6b9f54390f4f83fdbb368676b4dcd3534e8124c8 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -28,9 +28,7 @@ impl TryFrom for Set { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("set", ns::RSM) { - return Err(Error::ParseError("This is not a RSM element.")); - } + check_self!(elem, "set", ns::RSM, "RSM set"); let mut set = Set { after: None, before: None, @@ -136,7 +134,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "This is not a RSM element."); + assert_eq!(message, "This is not a RSM set element."); } #[test] diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 4548864cf4e59116ee4c6c63b0e17f86ef78e8a0..4977739d3238592180914f85939f616002844893 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -62,9 +62,7 @@ impl TryFrom for StanzaError { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("error", ns::DEFAULT_NS) { - return Err(Error::ParseError("This is not an error element.")); - } + check_self!(elem, "error", ns::DEFAULT_NS); let type_ = get_attr!(elem, "type", required); let by = get_attr!(elem, "by", optional); diff --git a/src/version.rs b/src/version.rs index e14bb422ed5a26c733d77d5c5b4fad5a917825ce..9521dc990b19267f17eac60b715c4121dd5ce403 100644 --- a/src/version.rs +++ b/src/version.rs @@ -20,9 +20,7 @@ impl TryFrom for Version { type Err = Error; fn try_from(elem: Element) -> Result { - if !elem.is("query", ns::VERSION) { - return Err(Error::ParseError("This is not a version element.")); - } + check_self!(elem, "query", ns::VERSION, "version"); check_no_attributes!(elem, "version"); let mut name = None; let mut version = None; From 84355f9e1dd7173133a0e2b9799499cc683352c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 May 2018 20:25:59 +0200 Subject: [PATCH 0603/1020] macros: Simplify generated code for check_no_attributes!(). --- src/macros.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 3eded4587433fb4151c369aebd4f170f1322789c..8fd057705355898475fdba3e6995e21b926b66e4 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -206,7 +206,9 @@ macro_rules! check_no_children { macro_rules! check_no_attributes { ($elem:ident, $name:tt) => ( - check_no_unknown_attributes!($elem, $name, []); + for _ in $elem.attrs() { + return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + } ); } From 292cdd059c0177cebad8f8e16d4392bf99319882 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:07:15 +0200 Subject: [PATCH 0604/1020] macros: Remove use requirement on try_from::TryFrom. --- src/attention.rs | 3 +-- src/chatstates.rs | 3 +-- src/component.rs | 3 +-- src/delay.rs | 3 +-- src/eme.rs | 3 +-- src/hashes.rs | 2 +- src/ibb.rs | 2 +- src/idle.rs | 3 +-- src/jingle_ibb.rs | 2 +- src/macros.rs | 14 +++++++------- src/media_element.rs | 3 +-- src/message_correct.rs | 3 +-- src/mood.rs | 3 +-- src/ping.rs | 3 +-- src/receipts.rs | 3 +-- src/roster.rs | 2 +- src/sasl.rs | 2 +- src/stanza_id.rs | 3 +-- src/stream.rs | 3 +-- src/websocket.rs | 3 +-- 20 files changed, 26 insertions(+), 40 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index da2e6f6494becee18aba3bea136b8e46cf9b19a8..9a7ed98c618124fd9e0688d0a6187cae15b5cb24 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -17,6 +15,7 @@ generate_empty_element!(Attention, "attention", ns::ATTENTION); #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/chatstates.rs b/src/chatstates.rs index 6858c1147a012896e0afe467e7d31beabcd1c814..83ac6f55b5b6973270429381e65802b24f7e2937 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -38,6 +36,7 @@ generate_element_enum!( #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/component.rs b/src/component.rs index 0f42e50ae67b08872579cb0554f7f0a1b66ec594..a93fc40ba9bfdbbca5453aa86ef4ea0c39020f33 100644 --- a/src/component.rs +++ b/src/component.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; use helpers::PlainText; @@ -38,6 +36,7 @@ impl Handshake { #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/delay.rs b/src/delay.rs index 6904ca838edc35b415f8ad6218d16f9887476b7b..8aba744ee5ce46b11a9c41e7919f63724cb06813 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use date::DateTime; @@ -26,6 +24,7 @@ generate_element_with_text!(Delay, "delay", ns::DELAY, #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use std::str::FromStr; #[test] diff --git a/src/eme.rs b/src/eme.rs index 5d6e7111ffabfd5060d2b752dd75a65df6ce78e8..9016123f8a5aee0e1e8a4d8fdfa57eb765dd2120 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -26,6 +24,7 @@ ExplicitMessageEncryption, "encryption", ns::EME, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/hashes.rs b/src/hashes.rs index e414b91a4fb100ac86a172553810af0fbe27865f..47843cea1ef89fba99c482780704a536aa02f007 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -93,6 +92,7 @@ impl Hash { #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/ibb.rs b/src/ibb.rs index d7717a7b7105cc23258c0d0f36faec4af7b4b089..2c0497cc7b32d6ec82729d403e18577575655ede 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -40,6 +39,7 @@ generate_element_with_only_attributes!(Close, "close", ns::IBB, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use std::error::Error as StdError; #[test] diff --git a/src/idle.rs b/src/idle.rs index e4b09924580777f39746e0d25a743a24279fdfb0..f2831ea1813c18909016c244a5c6504b91fef201 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use date::DateTime; @@ -20,6 +18,7 @@ generate_element_with_only_attributes!(Idle, "idle", ns::IDLE, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use std::str::FromStr; use std::error::Error as StdError; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 632540ed2cc909d22c3625a5e621a1c6565ab498..dca426681a0852258fa6f559ca1ddc91e0fb9f2d 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -26,6 +25,7 @@ generate_element_with_only_attributes!(Transport, "transport", ns::JINGLE_IBB, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use std::error::Error as StdError; #[test] diff --git a/src/macros.rs b/src/macros.rs index 8fd057705355898475fdba3e6995e21b926b66e4..5ea49210ed35ff48214f0e0b733e4a97fcec6015 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -116,7 +116,7 @@ macro_rules! generate_element_enum { $enum ),+ } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { check_ns_only!(elem, $name, $ns); @@ -152,7 +152,7 @@ macro_rules! generate_attribute_enum { $enum ),+ } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { check_ns_only!(elem, $name, $ns); @@ -231,7 +231,7 @@ macro_rules! generate_empty_element { #[derive(Debug, Clone)] pub struct $elem; - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { @@ -266,7 +266,7 @@ macro_rules! generate_element_with_only_attributes { )* } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { @@ -324,7 +324,7 @@ macro_rules! generate_elem_id { Ok($elem(String::from(s))) } } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); @@ -360,7 +360,7 @@ macro_rules! generate_element_with_text { pub $text_ident: $text_type, } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { @@ -405,7 +405,7 @@ macro_rules! generate_element_with_children { )* } - impl TryFrom for $elem { + impl ::try_from::TryFrom for $elem { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { diff --git a/src/media_element.rs b/src/media_element.rs index db91c44f44128c6d20bf0b5a9f47929f61486c74..b3e1aa5716c06dabc1c22cd7e8b00ad522de2014 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -33,6 +31,7 @@ generate_element_with_children!(MediaElement, "media", ns::MEDIA_ELEMENT, #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use data_forms::DataForm; use std::error::Error as StdError; diff --git a/src/message_correct.rs b/src/message_correct.rs index 26cdcf99596b8c79d8403dabbe108f46e8800d02..bfd33b86bd877c31948a789ebcf36c760a0882f0 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -19,6 +17,7 @@ generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/mood.rs b/src/mood.rs index c83915be62d0d68c595c3352c76bf4bcc802f73d..80f3147a18c18c7e7a840295ac85532e7d8f97d0 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -274,6 +272,7 @@ generate_element_enum!( #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/ping.rs b/src/ping.rs index fa696d08bbc472cff36cc40a83d83eb98f3af2d7..615580dfd548e98fe5b3505ac8428874e995b348 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,8 +5,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -18,6 +16,7 @@ generate_empty_element!(Ping, "ping", ns::PING); #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/receipts.rs b/src/receipts.rs index 470a46474dde9241e8fc74570fa186429aa0ace9..e9ec83265692f389285b5d8eaf6998e7c0da4d56 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use error::Error; @@ -21,6 +19,7 @@ generate_element_with_only_attributes!(Received, "received", ns::RECEIPTS, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/roster.rs b/src/roster.rs index cb78e4ef5a2f1c60f5b00bba931b913ecd9fdc44..358e4a461c47e5336007cc645b3ee19b331e5cb9 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -64,6 +63,7 @@ generate_element_with_children!( #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/sasl.rs b/src/sasl.rs index a0ab15bb7d12c3c7edabaee59f9f289d4a8f6df5..cb192804f094380d2734ff88ae4bd0d96f10e00a 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; @@ -42,6 +41,7 @@ generate_element_with_text!(Success, "success", ns::SASL, #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 6e65e012b885d13e0b1458b3687f710743a21746..3fbcfae857e19be0bf17d99f078f7e595762125a 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use jid::Jid; @@ -25,6 +23,7 @@ generate_element_with_only_attributes!(OriginId, "origin-id", ns::SID, [ #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; use std::str::FromStr; #[test] diff --git a/src/stream.rs b/src/stream.rs index 83335ae2377aa3fd51670595d56a274b4937ac46..168eee1fc25f5d34cd112d8caa9544f6487ef7dc 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use jid::Jid; use error::Error; @@ -56,6 +54,7 @@ impl Stream { #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { diff --git a/src/websocket.rs b/src/websocket.rs index 163ce3ef4ae91b2699a90b60c1d069f7df470584..e98d278dcaf978fd8fb825f9e90e88fe17fbe851 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use minidom::Element; use jid::Jid; use error::Error; @@ -56,6 +54,7 @@ impl Open { #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; #[test] fn test_simple() { From 040792c242fb8685eedfcf97c5b9ca71222f41ea Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:11:22 +0200 Subject: [PATCH 0605/1020] macros: Remove use requirement on std::str::FromStr. --- src/data_forms.rs | 1 - src/ibb.rs | 2 -- src/jingle_ft.rs | 2 +- src/jingle_ibb.rs | 2 -- src/jingle_s5b.rs | 2 +- src/macros.rs | 8 ++++---- src/mam.rs | 2 +- src/message.rs | 2 +- src/muc/user.rs | 1 - src/pubsub/event.rs | 1 - src/roster.rs | 3 +-- src/sasl.rs | 2 -- src/stanza_error.rs | 1 - 13 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 9b173b8aee979c6cf3f2f9b64889e01ed4a3e1ef..1e297409075bd04de6ea47a6c982aab36999a1d6 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; diff --git a/src/ibb.rs b/src/ibb.rs index 2c0497cc7b32d6ec82729d403e18577575655ede..d51881776157e5e9d83ad0fb2440d05d9896db0d 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; - use minidom::{Element, IntoAttributeValue}; use error::Error; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index c78e1d45bf4776c1af567e61256849dfeab92732..788ff22fa64ed1dbec4497379be9c0a15a1a09ab 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -5,9 +5,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; +use std::str::FromStr; use std::collections::BTreeMap; -use std::str::FromStr; use hashes::Hash; use jingle::{Creator, ContentId}; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index dca426681a0852258fa6f559ca1ddc91e0fb9f2d..2cf0fb446d963c219971b5c1ea3be7b4e5380eec 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; - use minidom::{Element, IntoAttributeValue}; use error::Error; diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 558eb560e92e22cc0f9ab5ca17c25912faf42219..7ef0bbd20d3e8a5f44723a2a7386023ac305264c 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use std::net::IpAddr; use minidom::{Element, IntoAttributeValue}; @@ -206,6 +205,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/macros.rs b/src/macros.rs index 5ea49210ed35ff48214f0e0b733e4a97fcec6015..0a056f8d110dc8b34d9689160b0f95f50f2e92a6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -51,7 +51,7 @@ macro_rules! generate_attribute { $a ),+ } - impl FromStr for $elem { + impl ::std::str::FromStr for $elem { type Err = Error; fn from_str(s: &str) -> Result<$elem, Error> { Ok(match s { @@ -77,7 +77,7 @@ macro_rules! generate_attribute { $a ),+ } - impl FromStr for $elem { + impl ::std::str::FromStr for $elem { type Err = Error; fn from_str(s: &str) -> Result<$elem, Error> { Ok(match s { @@ -298,7 +298,7 @@ macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); - impl FromStr for $elem { + impl ::std::str::FromStr for $elem { type Err = Error; fn from_str(s: &str) -> Result<$elem, Error> { // TODO: add a way to parse that differently when needed. @@ -317,7 +317,7 @@ macro_rules! generate_elem_id { ($elem:ident, $name:tt, $ns:expr) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); - impl FromStr for $elem { + impl ::std::str::FromStr for $elem { type Err = Error; fn from_str(s: &str) -> Result<$elem, Error> { // TODO: add a way to parse that differently when needed. diff --git a/src/mam.rs b/src/mam.rs index 82158a40e01db7fc082cba3b8dbadc14f636fb5e..115daf65b76d238ff12e0f4ccb0f0b4710fbb270 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -220,6 +219,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; #[test] fn test_query() { diff --git a/src/message.rs b/src/message.rs index c4c4a17fb23d5a82a5cb718e11db8341dfcc6bf6..36d09a7c4f6e7aba137529fde5dcad7328e988fe 100644 --- a/src/message.rs +++ b/src/message.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use std::collections::BTreeMap; use minidom::{Element, IntoAttributeValue}; @@ -278,6 +277,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/muc/user.rs b/src/muc/user.rs index a6614a63af1c4bd6ada54c41583394bc5102b109..1c3d2cc9bb2f9bda4badaca1072f8aa9890bdbaa 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -6,7 +6,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::{TryFrom, TryInto}; -use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index cbeb6ef759b519fd07b082a92f555b3b28838329..1d478cad7df1f76b8b736edc3eb76d37fa0b9a98 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; use jid::Jid; diff --git a/src/roster.rs b/src/roster.rs index 358e4a461c47e5336007cc645b3ee19b331e5cb9..54847e02679f2b7c54b2d22077c2f03010d7678a 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; - use minidom::{Element, IntoAttributeValue}; use jid::Jid; @@ -64,6 +62,7 @@ generate_element_with_children!( mod tests { use super::*; use try_from::TryFrom; + use std::str::FromStr; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/sasl.rs b/src/sasl.rs index cb192804f094380d2734ff88ae4bd0d96f10e00a..c53eba2f66a958668e2efe1e7f0c1c5fc771d69c 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; - use minidom::{Element, IntoAttributeValue}; use error::Error; diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 4977739d3238592180914f85939f616002844893..79386252e9d39593b45743c8980dcb2f6abcd5a9 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use try_from::TryFrom; -use std::str::FromStr; use std::collections::BTreeMap; use minidom::{Element, IntoAttributeValue}; From 0d4327eb42511fb9655764d845dc5a921a0851f9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:12:56 +0200 Subject: [PATCH 0606/1020] macros: Remove use requirement on minidom::IntoAttributeValue. --- src/data_forms.rs | 2 +- src/ibb.rs | 2 +- src/jingle.rs | 2 +- src/jingle_ft.rs | 2 +- src/jingle_ibb.rs | 2 +- src/jingle_s5b.rs | 2 +- src/macros.rs | 6 +++--- src/mam.rs | 2 +- src/message.rs | 2 +- src/muc/user.rs | 2 +- src/pubsub/event.rs | 2 +- src/roster.rs | 2 +- src/sasl.rs | 2 +- src/stanza_error.rs | 2 +- 14 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/data_forms.rs b/src/data_forms.rs index 1e297409075bd04de6ea47a6c982aab36999a1d6..74985edf0421c74184615d7bb248056828477d04 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -6,7 +6,7 @@ use try_from::TryFrom; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; use ns; diff --git a/src/ibb.rs b/src/ibb.rs index d51881776157e5e9d83ad0fb2440d05d9896db0d..3471715b9bcebe39bbb2052fdb21e19ba0184176 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; diff --git a/src/jingle.rs b/src/jingle.rs index 08002bcf61ed5e4830a18f55f810126af2386818..1b89dae7ca2d71eb85b3988dd8ae39076668cac5 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::str::FromStr; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; use error::Error; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 788ff22fa64ed1dbec4497379be9c0a15a1a09ab..e2539f9907c1424fd07f4e35c4bf935977201a91 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -13,7 +13,7 @@ use hashes::Hash; use jingle::{Creator, ContentId}; use date::DateTime; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; use ns; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 2cf0fb446d963c219971b5c1ea3be7b4e5380eec..2f6d0b1575a17bcb9238f86d79ae3dc557665161 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 7ef0bbd20d3e8a5f44723a2a7386023ac305264c..4e8ca817b65a129d0064a59cad6f7ed2d6c31842 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::net::IpAddr; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; use error::Error; diff --git a/src/macros.rs b/src/macros.rs index 0a056f8d110dc8b34d9689160b0f95f50f2e92a6..45b5c056df97a25f16452485d318e01f1333976d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -60,7 +60,7 @@ macro_rules! generate_attribute { }) } } - impl IntoAttributeValue for $elem { + impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { Some(String::from(match self { $($elem::$a => $b),+ @@ -86,7 +86,7 @@ macro_rules! generate_attribute { }) } } - impl IntoAttributeValue for $elem { + impl ::minidom::IntoAttributeValue for $elem { #[allow(unreachable_patterns)] fn into_attribute_value(self) -> Option { Some(String::from(match self { @@ -305,7 +305,7 @@ macro_rules! generate_id { Ok($elem(String::from(s))) } } - impl IntoAttributeValue for $elem { + impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { Some(self.0) } diff --git a/src/mam.rs b/src/mam.rs index 115daf65b76d238ff12e0f4ccb0f0b4710fbb270..6af739d9dea90ebaadbdc916e79c51f54d752d02 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -6,7 +6,7 @@ use try_from::TryFrom; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; use error::Error; diff --git a/src/message.rs b/src/message.rs index 36d09a7c4f6e7aba137529fde5dcad7328e988fe..72fcbde47663c08b3882abb6d2f76d7b7e7b53a6 100644 --- a/src/message.rs +++ b/src/message.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::collections::BTreeMap; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; diff --git a/src/muc/user.rs b/src/muc/user.rs index 1c3d2cc9bb2f9bda4badaca1072f8aa9890bdbaa..33b20d87cd05fac2e6bb1c433d867956662e8370 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -7,7 +7,7 @@ use try_from::{TryFrom, TryInto}; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 1d478cad7df1f76b8b736edc3eb76d37fa0b9a98..136489562ef3abf4606b0a25db85915d08cfc086 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -6,7 +6,7 @@ use try_from::TryFrom; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; use date::DateTime; diff --git a/src/roster.rs b/src/roster.rs index 54847e02679f2b7c54b2d22077c2f03010d7678a..96d39586af794304e1cc212dd2a8a4060e186cca 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use jid::Jid; use error::Error; diff --git a/src/sasl.rs b/src/sasl.rs index c53eba2f66a958668e2efe1e7f0c1c5fc771d69c..e7a226327db7958becfa79ec771a529382269b06 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 79386252e9d39593b45743c8980dcb2f6abcd5a9..5d26fa2598eb25eba48fb20a93cd3eb0bb349b6f 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -7,7 +7,7 @@ use try_from::TryFrom; use std::collections::BTreeMap; -use minidom::{Element, IntoAttributeValue}; +use minidom::Element; use error::Error; use jid::Jid; From 6f497027f5a851612dd0e3e65707684c1132b41e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:17:21 +0200 Subject: [PATCH 0607/1020] macros: Remove use requirement on minidom::Element. --- src/attention.rs | 3 +- src/chatstates.rs | 3 +- src/component.rs | 2 +- src/delay.rs | 2 +- src/eme.rs | 3 +- src/hashes.rs | 3 +- src/ibb.rs | 3 +- src/idle.rs | 2 +- src/jingle_ibb.rs | 3 +- src/macros.rs | 70 +++++++++++++++++++++--------------------- src/media_element.rs | 3 +- src/message_correct.rs | 3 +- src/mood.rs | 3 +- src/ping.rs | 3 +- src/receipts.rs | 3 +- src/roster.rs | 2 +- src/sasl.rs | 3 +- src/stanza_id.rs | 2 +- src/stream.rs | 2 +- src/websocket.rs | 2 +- 20 files changed, 55 insertions(+), 65 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 9a7ed98c618124fd9e0688d0a6187cae15b5cb24..d7521e9c758b8533b068c3647491f82b09a6c903 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -16,6 +14,7 @@ generate_empty_element!(Attention, "attention", ns::ATTENTION); mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/chatstates.rs b/src/chatstates.rs index 83ac6f55b5b6973270429381e65802b24f7e2937..33ce0a9c04f9206fafd058c19bfd5b9894d68378 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use minidom::Element; - use error::Error; use ns; @@ -37,6 +35,7 @@ generate_element_enum!( mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/component.rs b/src/component.rs index a93fc40ba9bfdbbca5453aa86ef4ea0c39020f33..778b9618ef15b6d7280dfb7ca231a0bf740440ca 100644 --- a/src/component.rs +++ b/src/component.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use error::Error; use helpers::PlainText; use ns; @@ -37,6 +36,7 @@ impl Handshake { mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/delay.rs b/src/delay.rs index 8aba744ee5ce46b11a9c41e7919f63724cb06813..5591c113dc45beed397ac90b7536e2fcada40e2d 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use date::DateTime; use error::Error; @@ -25,6 +24,7 @@ generate_element_with_text!(Delay, "delay", ns::DELAY, mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::str::FromStr; #[test] diff --git a/src/eme.rs b/src/eme.rs index 9016123f8a5aee0e1e8a4d8fdfa57eb765dd2120..b023a5abfbd165ac8536baf87aa0dfee5158dbbd 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -25,6 +23,7 @@ ExplicitMessageEncryption, "encryption", ns::EME, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/hashes.rs b/src/hashes.rs index 47843cea1ef89fba99c482780704a536aa02f007..472d4c471063e90fc8c2635613ea2b41e3490084 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -6,7 +6,7 @@ use std::str::FromStr; -use minidom::{Element, IntoAttributeValue}; +use minidom::IntoAttributeValue; use error::Error; @@ -93,6 +93,7 @@ impl Hash { mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/ibb.rs b/src/ibb.rs index 3471715b9bcebe39bbb2052fdb21e19ba0184176..555dfb2ee714527328d105f4288847e4e164ae94 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -38,6 +36,7 @@ generate_element_with_only_attributes!(Close, "close", ns::IBB, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::error::Error as StdError; #[test] diff --git a/src/idle.rs b/src/idle.rs index f2831ea1813c18909016c244a5c6504b91fef201..ea82ee31061a81df7e14c42373841501b5e46974 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use date::DateTime; use error::Error; @@ -19,6 +18,7 @@ generate_element_with_only_attributes!(Idle, "idle", ns::IDLE, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::str::FromStr; use std::error::Error as StdError; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 2f6d0b1575a17bcb9238f86d79ae3dc557665161..5cc0f25623d52853e1b079c859ffb530d3ec57dc 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -24,6 +22,7 @@ generate_element_with_only_attributes!(Transport, "transport", ns::JINGLE_IBB, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::error::Error as StdError; #[test] diff --git a/src/macros.rs b/src/macros.rs index 45b5c056df97a25f16452485d318e01f1333976d..752e5bb85db623ea47c4b53901a8e2fefe2f6ab0 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -116,9 +116,9 @@ macro_rules! generate_element_enum { $enum ),+ } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -128,9 +128,9 @@ macro_rules! generate_element_enum { }) } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder(match elem { + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder(match elem { $($elem::$enum => $enum_name,)+ }).ns($ns) .build() @@ -152,9 +152,9 @@ macro_rules! generate_attribute_enum { $enum ),+ } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); @@ -164,9 +164,9 @@ macro_rules! generate_attribute_enum { }) } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) .attr($attr, match elem { $($elem::$enum => $enum_name,)+ @@ -231,10 +231,10 @@ macro_rules! generate_empty_element { #[derive(Debug, Clone)] pub struct $elem; - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -242,9 +242,9 @@ macro_rules! generate_empty_element { } } - impl From<$elem> for Element { - fn from(_: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(_: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) .build() } @@ -266,10 +266,10 @@ macro_rules! generate_element_with_only_attributes { )* } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); @@ -281,9 +281,9 @@ macro_rules! generate_element_with_only_attributes { } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) $( .attr($attr_name, elem.$attr) @@ -324,9 +324,9 @@ macro_rules! generate_elem_id { Ok($elem(String::from(s))) } } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -334,9 +334,9 @@ macro_rules! generate_elem_id { Ok($elem(elem.text())) } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) .append(elem.0) .build() @@ -360,10 +360,10 @@ macro_rules! generate_element_with_text { pub $text_ident: $text_type, } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); @@ -376,9 +376,9 @@ macro_rules! generate_element_with_text { } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) $( .attr($attr_name, elem.$attr) @@ -405,10 +405,10 @@ macro_rules! generate_element_with_children { )* } - impl ::try_from::TryFrom for $elem { + impl ::try_from::TryFrom<::minidom::Element> for $elem { type Err = Error; - fn try_from(elem: Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); let mut parsed_children = vec!(); @@ -433,9 +433,9 @@ macro_rules! generate_element_with_children { } } - impl From<$elem> for Element { - fn from(elem: $elem) -> Element { - Element::builder($name) + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) .ns($ns) $( .attr($attr_name, elem.$attr) diff --git a/src/media_element.rs b/src/media_element.rs index b3e1aa5716c06dabc1c22cd7e8b00ad522de2014..a3d159a382c997daa954b70c4a05076f8cbbabc1 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -32,6 +30,7 @@ generate_element_with_children!(MediaElement, "media", ns::MEDIA_ELEMENT, mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use data_forms::DataForm; use std::error::Error as StdError; diff --git a/src/message_correct.rs b/src/message_correct.rs index bfd33b86bd877c31948a789ebcf36c760a0882f0..259eb88a3643ed3c50a0cfbb6198da78662c9434 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -18,6 +16,7 @@ generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/mood.rs b/src/mood.rs index 80f3147a18c18c7e7a840295ac85532e7d8f97d0..5ec89f600a5d306c665d9196e54a4290eb45b416 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use minidom::Element; - use error::Error; use ns; @@ -273,6 +271,7 @@ generate_element_enum!( mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/ping.rs b/src/ping.rs index 615580dfd548e98fe5b3505ac8428874e995b348..cacf3c355694ab90dfc14a80d2e90b687130ac2d 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,8 +5,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -17,6 +15,7 @@ generate_empty_element!(Ping, "ping", ns::PING); mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/receipts.rs b/src/receipts.rs index e9ec83265692f389285b5d8eaf6998e7c0da4d56..0c785c1cb63ec9a3838ad639b73749f031d08949 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -20,6 +18,7 @@ generate_element_with_only_attributes!(Received, "received", ns::RECEIPTS, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/roster.rs b/src/roster.rs index 96d39586af794304e1cc212dd2a8a4060e186cca..556cf11384aee058ba3b9f542175d83d3d6c208f 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use jid::Jid; use error::Error; @@ -62,6 +61,7 @@ generate_element_with_children!( mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::str::FromStr; use compare_elements::NamespaceAwareCompare; diff --git a/src/sasl.rs b/src/sasl.rs index e7a226327db7958becfa79ec771a529382269b06..23a56c43bdb64d96cc9f81fab0f4092593294d8b 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; - use error::Error; use ns; @@ -40,6 +38,7 @@ generate_element_with_text!(Success, "success", ns::SASL, mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 3fbcfae857e19be0bf17d99f078f7e595762125a..a02d8bf45c442de6f2d1a8f4a3992d25e82b9ca3 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use jid::Jid; use error::Error; @@ -24,6 +23,7 @@ generate_element_with_only_attributes!(OriginId, "origin-id", ns::SID, [ mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; use std::str::FromStr; #[test] diff --git a/src/stream.rs b/src/stream.rs index 168eee1fc25f5d34cd112d8caa9544f6487ef7dc..ee934116a58bdbaff7920677b95cccdb127c2bf5 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use jid::Jid; use error::Error; use ns; @@ -55,6 +54,7 @@ impl Stream { mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { diff --git a/src/websocket.rs b/src/websocket.rs index e98d278dcaf978fd8fb825f9e90e88fe17fbe851..4257278eb8f0eccece30258e64d555e4e38182e6 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::Element; use jid::Jid; use error::Error; use ns; @@ -55,6 +54,7 @@ impl Open { mod tests { use super::*; use try_from::TryFrom; + use minidom::Element; #[test] fn test_simple() { From d9aaa3e9ce756dae638718ebd30afda9b00e6555 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:21:39 +0200 Subject: [PATCH 0608/1020] macros: Remove use requirement on error::Error. --- src/attention.rs | 3 +- src/chatstates.rs | 3 +- src/component.rs | 1 - src/delay.rs | 2 +- src/eme.rs | 3 +- src/ibb.rs | 3 +- src/idle.rs | 3 +- src/jingle_ibb.rs | 3 +- src/macros.rs | 66 +++++++++++++++++++++--------------------- src/media_element.rs | 3 +- src/message_correct.rs | 3 +- src/mood.rs | 2 -- src/ping.rs | 3 +- src/receipts.rs | 2 -- src/roster.rs | 2 +- src/sasl.rs | 2 -- src/stanza_id.rs | 3 +- src/stream.rs | 1 - src/websocket.rs | 1 - 19 files changed, 45 insertions(+), 64 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index d7521e9c758b8533b068c3647491f82b09a6c903..02cc02f3ac3c6d5bb688c5b3c4b567096f47fda4 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; generate_empty_element!(Attention, "attention", ns::ATTENTION); @@ -15,6 +13,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; #[test] fn test_simple() { diff --git a/src/chatstates.rs b/src/chatstates.rs index 33ce0a9c04f9206fafd058c19bfd5b9894d68378..a328e23454b7fe3ef17ecc105752ca463c4085d6 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use error::Error; - use ns; generate_element_enum!( @@ -36,6 +34,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; #[test] fn test_simple() { diff --git a/src/component.rs b/src/component.rs index 778b9618ef15b6d7280dfb7ca231a0bf740440ca..7c74ff1a09219109a8107bdfe5c7c22c28fb3e22 100644 --- a/src/component.rs +++ b/src/component.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; use helpers::PlainText; use ns; diff --git a/src/delay.rs b/src/delay.rs index 5591c113dc45beed397ac90b7536e2fcada40e2d..84d12ad4be6db0c91fdef951ac4c61144a3ca91d 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -6,7 +6,6 @@ use date::DateTime; -use error::Error; use jid::Jid; use ns; @@ -25,6 +24,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::str::FromStr; #[test] diff --git a/src/eme.rs b/src/eme.rs index b023a5abfbd165ac8536baf87aa0dfee5158dbbd..de48c3b0aff6d67c42b5bab8ffbe8fe49ae92859 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; generate_element_with_only_attributes!( @@ -24,6 +22,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; #[test] fn test_simple() { diff --git a/src/ibb.rs b/src/ibb.rs index 555dfb2ee714527328d105f4288847e4e164ae94..60653da069e30ea9a09b358ab72454424dba43ed 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; use helpers::Base64; @@ -37,6 +35,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::error::Error as StdError; #[test] diff --git a/src/idle.rs b/src/idle.rs index ea82ee31061a81df7e14c42373841501b5e46974..a94a5ee63c207170f1339b7fa909c557b7d00a96 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -6,8 +6,6 @@ use date::DateTime; -use error::Error; - use ns; generate_element_with_only_attributes!(Idle, "idle", ns::IDLE, [ @@ -19,6 +17,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::str::FromStr; use std::error::Error as StdError; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 5cc0f25623d52853e1b079c859ffb530d3ec57dc..d96e9b910b27ef06644a4cd570022b74f674dc9f 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; use ibb::Stanza; @@ -23,6 +21,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::error::Error as StdError; #[test] diff --git a/src/macros.rs b/src/macros.rs index 752e5bb85db623ea47c4b53901a8e2fefe2f6ab0..54297b38d3559149313e2476001942d316ecb68a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -24,7 +24,7 @@ macro_rules! get_attr { ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( match $elem.attr($attr) { Some($value) => $func, - None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), + None => return Err(::error::Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), } ); ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( @@ -52,11 +52,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { + type Err = ::error::Error; + fn from_str(s: &str) -> Result<$elem, ::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -78,11 +78,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { + type Err = ::error::Error; + fn from_str(s: &str) -> Result<$elem, ::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -117,14 +117,14 @@ macro_rules! generate_element_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + type Err = ::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); Ok(match elem.name() { $($enum_name => $elem::$enum,)+ - _ => return Err(Error::ParseError(concat!("This is not a ", $name, " element."))), + _ => return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))), }) } } @@ -153,14 +153,14 @@ macro_rules! generate_attribute_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + type Err = ::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); Ok(match get_attr!(elem, $attr, required) { $($enum_name => $elem::$enum,)+ - _ => return Err(Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), + _ => return Err(::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), }) } } @@ -183,7 +183,7 @@ macro_rules! check_self { ); ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( if !$elem.is($name, $ns) { - return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); + return Err(::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); } ); } @@ -191,7 +191,7 @@ macro_rules! check_self { macro_rules! check_ns_only { ($elem:ident, $name:tt, $ns:expr) => ( if !$elem.has_ns($ns) { - return Err(Error::ParseError(concat!("This is not a ", $name, " element."))); + return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))); } ); } @@ -199,7 +199,7 @@ macro_rules! check_ns_only { macro_rules! check_no_children { ($elem:ident, $name:tt) => ( for _ in $elem.children() { - return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } ); } @@ -207,7 +207,7 @@ macro_rules! check_no_children { macro_rules! check_no_attributes { ($elem:ident, $name:tt) => ( for _ in $elem.attrs() { - return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } ); } @@ -220,7 +220,7 @@ macro_rules! check_no_unknown_attributes { continue; } )* - return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } ); } @@ -232,9 +232,9 @@ macro_rules! generate_empty_element { pub struct $elem; impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; + type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -267,9 +267,9 @@ macro_rules! generate_element_with_only_attributes { } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; + type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); @@ -299,8 +299,8 @@ macro_rules! generate_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { + type Err = ::error::Error; + fn from_str(s: &str) -> Result<$elem, ::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } @@ -318,15 +318,15 @@ macro_rules! generate_elem_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = Error; - fn from_str(s: &str) -> Result<$elem, Error> { + type Err = ::error::Error; + fn from_str(s: &str) -> Result<$elem, ::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + type Err = ::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -361,9 +361,9 @@ macro_rules! generate_element_with_text { } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; + type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); @@ -406,9 +406,9 @@ macro_rules! generate_element_with_children { } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = Error; + type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); let mut parsed_children = vec!(); @@ -420,7 +420,7 @@ macro_rules! generate_element_with_children { continue; } )* - return Err(Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( diff --git a/src/media_element.rs b/src/media_element.rs index a3d159a382c997daa954b70c4a05076f8cbbabc1..c9d6a129c1c3494f2c322914eb31eac151d14528 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; use helpers::TrimmedPlainText; @@ -31,6 +29,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use data_forms::DataForm; use std::error::Error as StdError; diff --git a/src/message_correct.rs b/src/message_correct.rs index 259eb88a3643ed3c50a0cfbb6198da78662c9434..edd9605f79cbc33b77eab37e971ae5940e4aa8bc 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, [ @@ -17,6 +15,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; #[test] fn test_simple() { diff --git a/src/mood.rs b/src/mood.rs index 5ec89f600a5d306c665d9196e54a4290eb45b416..ed8b19b64b6a62155853f5504dbf7d1861abce30 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -6,8 +6,6 @@ #![deny(missing_docs)] -use error::Error; - use ns; generate_element_enum!( diff --git a/src/ping.rs b/src/ping.rs index cacf3c355694ab90dfc14a80d2e90b687130ac2d..12368c10b59a996714590b5234cb5985e00b68fe 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,8 +5,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; generate_empty_element!(Ping, "ping", ns::PING); @@ -16,6 +14,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; #[test] fn test_simple() { diff --git a/src/receipts.rs b/src/receipts.rs index 0c785c1cb63ec9a3838ad639b73749f031d08949..564e8fc9e969c1b49f98dd269f597ce17e3bfdca 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; generate_empty_element!(Request, "request", ns::RECEIPTS); diff --git a/src/roster.rs b/src/roster.rs index 556cf11384aee058ba3b9f542175d83d3d6c208f..01011fded1ec93fc60b27a68fe5d2af58451b633 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -6,7 +6,6 @@ use jid::Jid; -use error::Error; use ns; generate_elem_id!(Group, "group", ns::ROSTER); @@ -62,6 +61,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::str::FromStr; use compare_elements::NamespaceAwareCompare; diff --git a/src/sasl.rs b/src/sasl.rs index 23a56c43bdb64d96cc9f81fab0f4092593294d8b..3cb84c78c1ff05734583c1fb966dfb05ce4ad26b 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use error::Error; - use ns; use helpers::Base64; diff --git a/src/stanza_id.rs b/src/stanza_id.rs index a02d8bf45c442de6f2d1a8f4a3992d25e82b9ca3..bad880029dfc6dcf65e11bcfa37d0d6f95708626 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -6,8 +6,6 @@ use jid::Jid; -use error::Error; - use ns; generate_element_with_only_attributes!(StanzaId, "stanza-id", ns::SID, [ @@ -24,6 +22,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use error::Error; use std::str::FromStr; #[test] diff --git a/src/stream.rs b/src/stream.rs index ee934116a58bdbaff7920677b95cccdb127c2bf5..9d9eff4625340cc86bcaf7614bc0f67cf7c07297 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; -use error::Error; use ns; generate_element_with_only_attributes!(Stream, "stream", ns::STREAM, [ diff --git a/src/websocket.rs b/src/websocket.rs index 4257278eb8f0eccece30258e64d555e4e38182e6..2402164807977d6100355576c8d680b23036e1e7 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; -use error::Error; use ns; generate_element_with_only_attributes!(Open, "open", ns::WEBSOCKET, [ From 93b018e5ac2fb10c59157c246ca2ae5a1e2bae26 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:30:28 +0200 Subject: [PATCH 0609/1020] macros: Remove use requirement on ns. --- src/attention.rs | 4 +--- src/bind.rs | 2 +- src/blocking.rs | 8 +++---- src/caps.rs | 2 +- src/chatstates.rs | 5 ++--- src/component.rs | 4 +--- src/data_forms.rs | 6 +++--- src/delay.rs | 3 +-- src/disco.rs | 16 +++++++------- src/ecaps2.rs | 2 +- src/eme.rs | 4 +--- src/forwarding.rs | 2 +- src/hashes.rs | 3 +-- src/ibb.rs | 7 +++--- src/ibr.rs | 2 +- src/idle.rs | 4 +--- src/iq.rs | 2 +- src/jingle.rs | 6 +++--- src/jingle_ft.rs | 12 +++++------ src/jingle_ibb.rs | 4 +--- src/jingle_s5b.rs | 4 ++-- src/macros.rs | 48 +++++++++++++++++++++--------------------- src/mam.rs | 8 +++---- src/media_element.rs | 7 +++--- src/message.rs | 8 +++---- src/message_correct.rs | 4 +--- src/mood.rs | 4 +--- src/muc/muc.rs | 2 +- src/muc/user.rs | 12 +++++------ src/ping.rs | 4 +--- src/presence.rs | 2 +- src/pubsub/event.rs | 4 ++-- src/receipts.rs | 7 +++--- src/roster.rs | 12 +++++------ src/rsm.rs | 2 +- src/sasl.rs | 9 ++++---- src/stanza_error.rs | 4 ++-- src/stanza_id.rs | 6 ++---- src/stream.rs | 3 +-- src/version.rs | 2 +- src/websocket.rs | 3 +-- 41 files changed, 112 insertions(+), 141 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 02cc02f3ac3c6d5bb688c5b3c4b567096f47fda4..20f79bae31bbe5b4f8b23daeccf656b61b9651f4 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,9 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; - -generate_empty_element!(Attention, "attention", ns::ATTENTION); +generate_empty_element!(Attention, "attention", ATTENTION); #[cfg(test)] mod tests { diff --git a/src/bind.rs b/src/bind.rs index 3e76dba01dc39d25c4db8e2d2b910deb2ff0f58f..4597636146584e53bc80c551e512eb20ced877f2 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -33,7 +33,7 @@ impl TryFrom for Bind { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "bind", ns::BIND); + check_self!(elem, "bind", BIND); check_no_attributes!(elem, "bind"); let mut bind = Bind::None; diff --git a/src/blocking.rs b/src/blocking.rs index 997e24111ff536f32396a4befd46ffb2e6dbe850..b24c9e49d82dea86edc29cdc7cd330f584bf2a5b 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -13,7 +13,7 @@ use error::Error; use ns; -generate_empty_element!(BlocklistRequest, "blocklist", ns::BLOCKING); +generate_empty_element!(BlocklistRequest, "blocklist", BLOCKING); macro_rules! generate_blocking_element { ($elem:ident, $name:tt) => ( @@ -26,11 +26,11 @@ macro_rules! generate_blocking_element { type Err = Error; fn try_from(elem: Element) -> Result<$elem, Error> { - check_self!(elem, $name, ns::BLOCKING); + check_self!(elem, $name, BLOCKING); check_no_attributes!(elem, $name); let mut items = vec!(); for child in elem.children() { - check_self!(child, "item", ns::BLOCKING); + check_self!(child, "item", BLOCKING); check_no_unknown_attributes!(child, "item", ["jid"]); check_no_children!(child, "item"); items.push(get_attr!(child, "jid", required)); @@ -59,7 +59,7 @@ generate_blocking_element!(BlocklistResult, "blocklist"); generate_blocking_element!(Block, "block"); generate_blocking_element!(Unblock, "unblock"); -generate_empty_element!(Blocked, "blocked", ns::BLOCKING_ERRORS); +generate_empty_element!(Blocked, "blocked", BLOCKING_ERRORS); #[cfg(test)] mod tests { diff --git a/src/caps.rs b/src/caps.rs index 05f84e1e3a393cab83893b217252a59d8cb4f179..cfafef7e3cbdfc469bcf46f64095062bf3e8f5a4 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -32,7 +32,7 @@ impl TryFrom for Caps { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "c", ns::CAPS, "caps"); + check_self!(elem, "c", CAPS, "caps"); check_no_children!(elem, "caps"); check_no_unknown_attributes!(elem, "caps", ["hash", "ver", "ext", "node"]); let ver: String = get_attr!(elem, "ver", required); diff --git a/src/chatstates.rs b/src/chatstates.rs index a328e23454b7fe3ef17ecc105752ca463c4085d6..d341975fae318176ae740f3b06b2f301b6d3a527 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -6,12 +6,10 @@ #![deny(missing_docs)] -use ns; - generate_element_enum!( /// Enum representing chatstate elements part of the /// `http://jabber.org/protocol/chatstates` namespace. - ChatState, "chatstate", ns::CHATSTATES, { + ChatState, "chatstate", CHATSTATES, { /// `` Active => "active", @@ -35,6 +33,7 @@ mod tests { use try_from::TryFrom; use minidom::Element; use error::Error; + use ns; #[test] fn test_simple() { diff --git a/src/component.rs b/src/component.rs index 7c74ff1a09219109a8107bdfe5c7c22c28fb3e22..6f86dc3cf78aff243f6095aac81f25293705c6c2 100644 --- a/src/component.rs +++ b/src/component.rs @@ -5,12 +5,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use helpers::PlainText; -use ns; - use sha1::Sha1; use digest::Digest; -generate_element_with_text!(Handshake, "handshake", ns::COMPONENT, +generate_element_with_text!(Handshake, "handshake", COMPONENT, data: PlainText> ); diff --git a/src/data_forms.rs b/src/data_forms.rs index 74985edf0421c74184615d7bb248056828477d04..3d8615de8f2fa7e67e119b3d5ecd24ff2b27d1ea 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -23,7 +23,7 @@ impl TryFrom for Option_ { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "option", ns::DATA_FORMS); + check_self!(elem, "option", DATA_FORMS); check_no_unknown_attributes!(elem, "option", ["label"]); let mut value = None; for child in elem.children() { @@ -90,7 +90,7 @@ impl TryFrom for Field { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "field", ns::DATA_FORMS); + check_self!(elem, "field", DATA_FORMS); check_no_unknown_attributes!(elem, "field", ["label", "type", "var"]); let mut field = Field { var: get_attr!(elem, "var", required), @@ -167,7 +167,7 @@ impl TryFrom for DataForm { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "x", ns::DATA_FORMS); + check_self!(elem, "x", DATA_FORMS); check_no_unknown_attributes!(elem, "x", ["type"]); let type_ = get_attr!(elem, "type", required); let mut form = DataForm { diff --git a/src/delay.rs b/src/delay.rs index 84d12ad4be6db0c91fdef951ac4c61144a3ca91d..4786365f800df4c4eabc96deb35e812b01d979fc 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -8,10 +8,9 @@ use date::DateTime; use jid::Jid; -use ns; use helpers::PlainText; -generate_element_with_text!(Delay, "delay", ns::DELAY, +generate_element_with_text!(Delay, "delay", DELAY, [ from: Option = "from" => optional, stamp: DateTime = "stamp" => required diff --git a/src/disco.rs b/src/disco.rs index c48c863c8ce963c6f6bf8d6cb3ebebdd60587b6e..136a50ad27a995f2773e84cddb7ead3c28e7b9d3 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -21,7 +21,7 @@ generate_element_with_only_attributes!( /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -DiscoInfoQuery, "query", ns::DISCO_INFO, [ +DiscoInfoQuery, "query", DISCO_INFO, [ /// Node on which we are doing the discovery. node: Option = "node" => optional, ]); @@ -29,7 +29,7 @@ DiscoInfoQuery, "query", ns::DISCO_INFO, [ generate_element_with_only_attributes!( /// Structure representing a `` element. #[derive(PartialEq)] -Feature, "feature", ns::DISCO_INFO, [ +Feature, "feature", DISCO_INFO, [ /// Namespace of the feature we want to represent. var: String = "var" => required, ]); @@ -54,7 +54,7 @@ impl TryFrom for Identity { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "identity", ns::DISCO_INFO, "disco#info identity"); + check_self!(elem, "identity", DISCO_INFO, "disco#info identity"); check_no_children!(elem, "disco#info identity"); check_no_unknown_attributes!(elem, "disco#info identity", ["category", "type", "xml:lang", "name"]); @@ -112,7 +112,7 @@ impl TryFrom for DiscoInfoResult { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::DISCO_INFO, "disco#info result"); + check_self!(elem, "query", DISCO_INFO, "disco#info result"); check_no_unknown_attributes!(elem, "disco#info result", ["node"]); let mut result = DiscoInfoResult { @@ -185,14 +185,14 @@ generate_element_with_only_attributes!( /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -DiscoItemsQuery, "query", ns::DISCO_ITEMS, [ +DiscoItemsQuery, "query", DISCO_ITEMS, [ /// Node on which we are doing the discovery. node: Option = "node" => optional, ]); generate_element_with_only_attributes!( /// Structure representing an `` element. -Item, "item", ns::DISCO_ITEMS, [ +Item, "item", DISCO_ITEMS, [ /// JID of the entity pointed by this item. jid: Jid = "jid" => required, /// Node of the entity pointed by this item. @@ -207,14 +207,14 @@ generate_element_with_children!( /// /// It should only be used in an ``, as it can only /// represent the result, and not a request. - DiscoItemsResult, "query", ns::DISCO_ITEMS, + DiscoItemsResult, "query", DISCO_ITEMS, attributes: [ /// Node on which we have done this discovery. node: Option = "node" => optional ], children: [ /// List of items pointed by this entity. - items: Vec = ("item", ns::DISCO_ITEMS) => Item + items: Vec = ("item", DISCO_ITEMS) => Item ] ); diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 587418b88c0dc7831670247c1ecebebe4dad9abd..8fcd0c9e539eb5bb2a7bcb74400dac9a4eb0f515 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -29,7 +29,7 @@ impl TryFrom for ECaps2 { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "c", ns::ECAPS2, "ecaps2"); + check_self!(elem, "c", ECAPS2, "ecaps2"); check_no_attributes!(elem, "ecaps2"); let mut hashes = vec!(); for child in elem.children() { diff --git a/src/eme.rs b/src/eme.rs index de48c3b0aff6d67c42b5bab8ffbe8fe49ae92859..ff3737b06d9b57c80c64fda8bf7c297987ddc8fb 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,11 +4,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; - generate_element_with_only_attributes!( /// Structure representing an `` element. -ExplicitMessageEncryption, "encryption", ns::EME, [ +ExplicitMessageEncryption, "encryption", EME, [ /// Namespace of the encryption scheme used. namespace: String = "namespace" => required, diff --git a/src/forwarding.rs b/src/forwarding.rs index 8ab4ead2b534699cd1023f96bf4e3d3aa6453cfa..70eead56dc176f238b01af977b70c80b309cb73c 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -26,7 +26,7 @@ impl TryFrom for Forwarded { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "forwarded", ns::FORWARD); + check_self!(elem, "forwarded", FORWARD); let mut delay = None; let mut stanza = None; for child in elem.children() { diff --git a/src/hashes.rs b/src/hashes.rs index 472d4c471063e90fc8c2635613ea2b41e3490084..06249ab288b6d592d2d99fd4ef92a6d2db6ee50b 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -10,7 +10,6 @@ use minidom::IntoAttributeValue; use error::Error; -use ns; use helpers::Base64; use base64; @@ -69,7 +68,7 @@ impl IntoAttributeValue for Algo { generate_element_with_text!( #[derive(PartialEq)] - Hash, "hash", ns::HASHES, + Hash, "hash", HASHES, [ algo: Algo = "algo" => required ], diff --git a/src/ibb.rs b/src/ibb.rs index 60653da069e30ea9a09b358ab72454424dba43ed..9c2cdccc5f3ea96394acd860baa825bd45748a84 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; use helpers::Base64; generate_attribute!(Stanza, "stanza", { @@ -12,13 +11,13 @@ generate_attribute!(Stanza, "stanza", { Message => "message", }, Default = Iq); -generate_element_with_only_attributes!(Open, "open", ns::IBB, [ +generate_element_with_only_attributes!(Open, "open", IBB, [ block_size: u16 = "block-size" => required, sid: String = "sid" => required, stanza: Stanza = "stanza" => default, ]); -generate_element_with_text!(Data, "data", ns::IBB, +generate_element_with_text!(Data, "data", IBB, [ seq: u16 = "seq" => required, sid: String = "sid" => required @@ -26,7 +25,7 @@ generate_element_with_text!(Data, "data", ns::IBB, data: Base64> ); -generate_element_with_only_attributes!(Close, "close", ns::IBB, [ +generate_element_with_only_attributes!(Close, "close", IBB, [ sid: String = "sid" => required, ]); diff --git a/src/ibr.rs b/src/ibr.rs index 892c67d9d3fad57c659d4ca577d7b01f1a4cceff..99cbcd57ba831381180551ab682691ee3f272d06 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -29,7 +29,7 @@ impl TryFrom for Query { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::REGISTER, "IBR query"); + check_self!(elem, "query", REGISTER, "IBR query"); let mut query = Query { registered: false, fields: HashMap::new(), diff --git a/src/idle.rs b/src/idle.rs index a94a5ee63c207170f1339b7fa909c557b7d00a96..1312efb96fd60200f5c16f4e587f468c8179bf1c 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -6,9 +6,7 @@ use date::DateTime; -use ns; - -generate_element_with_only_attributes!(Idle, "idle", ns::IDLE, [ +generate_element_with_only_attributes!(Idle, "idle", IDLE, [ since: DateTime = "since" => required, ]); diff --git a/src/iq.rs b/src/iq.rs index 9a7f3b5933f3ecad76e2e0a97f6b2ea04bc432fa..8726a9d5756b1af4f21a57b88c7729dbf825a660 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -207,7 +207,7 @@ impl TryFrom for Iq { type Err = Error; fn try_from(root: Element) -> Result { - check_self!(root, "iq", ns::DEFAULT_NS); + check_self!(root, "iq", DEFAULT_NS); let from = get_attr!(root, "from", optional); let to = get_attr!(root, "to", optional); let id = get_attr!(root, "id", optional); diff --git a/src/jingle.rs b/src/jingle.rs index 1b89dae7ca2d71eb85b3988dd8ae39076668cac5..8bca605474106efd464f3c61d14b016561cb8d77 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -119,7 +119,7 @@ impl TryFrom for Content { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "content", ns::JINGLE); + check_self!(elem, "content", JINGLE); let mut content = Content { creator: get_attr!(elem, "creator", required), @@ -250,7 +250,7 @@ impl TryFrom for ReasonElement { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "reason", ns::JINGLE); + check_self!(elem, "reason", JINGLE); let mut reason = None; let mut text = None; for child in elem.children() { @@ -340,7 +340,7 @@ impl TryFrom for Jingle { type Err = Error; fn try_from(root: Element) -> Result { - check_self!(root, "jingle", ns::JINGLE, "Jingle"); + check_self!(root, "jingle", JINGLE, "Jingle"); let mut jingle = Jingle { action: get_attr!(root, "action", required), diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index e2539f9907c1424fd07f4e35c4bf935977201a91..eb7e9169e0aeb71bfec2eb301b8da270d0f43fe9 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -20,13 +20,13 @@ use ns; generate_element_with_children!( #[derive(PartialEq, Default)] - Range, "range", ns::JINGLE_FT, + Range, "range", JINGLE_FT, attributes: [ offset: u64 = "offset" => default, length: Option = "length" => optional ], children: [ - hashes: Vec = ("hash", ns::HASHES) => Hash + hashes: Vec = ("hash", HASHES) => Hash ] ); @@ -109,7 +109,7 @@ impl TryFrom for File { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "file", ns::JINGLE_FT); + check_self!(elem, "file", JINGLE_FT); check_no_attributes!(elem, "file"); let mut file = File { @@ -218,7 +218,7 @@ impl TryFrom for Description { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "description", ns::JINGLE_FT, "JingleFT description"); + check_self!(elem, "description", JINGLE_FT, "JingleFT description"); check_no_attributes!(elem, "JingleFT description"); let mut file = None; for child in elem.children() { @@ -256,7 +256,7 @@ impl TryFrom for Checksum { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "checksum", ns::JINGLE_FT); + check_self!(elem, "checksum", JINGLE_FT); check_no_unknown_attributes!(elem, "checksum", ["name", "creator"]); let mut file = None; for child in elem.children() { @@ -287,7 +287,7 @@ impl From for Element { } } -generate_element_with_only_attributes!(Received, "received", ns::JINGLE_FT, [ +generate_element_with_only_attributes!(Received, "received", JINGLE_FT, [ name: ContentId = "name" => required, creator: Creator = "creator" => required, ]); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index d96e9b910b27ef06644a4cd570022b74f674dc9f..2e68e685b0b84e6ce4102621fa05e393198f95c0 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,13 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; - use ibb::Stanza; generate_id!(StreamId); -generate_element_with_only_attributes!(Transport, "transport", ns::JINGLE_IBB, [ +generate_element_with_only_attributes!(Transport, "transport", JINGLE_IBB, [ block_size: u16 = "block-size" => required, sid: StreamId = "sid" => required, stanza: Stanza = "stanza" => default, diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 4e8ca817b65a129d0064a59cad6f7ed2d6c31842..79949d73005f67d6dc38cbf9fe15d932755f7da9 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -30,7 +30,7 @@ generate_id!(CandidateId); generate_id!(StreamId); -generate_element_with_only_attributes!(Candidate, "candidate", ns::JINGLE_S5B, [ +generate_element_with_only_attributes!(Candidate, "candidate", JINGLE_S5B, [ cid: CandidateId = "cid" => required, host: IpAddr = "host" => required, jid: Jid = "jid" => required, @@ -110,7 +110,7 @@ impl TryFrom for Transport { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "transport", ns::JINGLE_S5B); + check_self!(elem, "transport", JINGLE_S5B); let sid = get_attr!(elem, "sid", required); let dstaddr = get_attr!(elem, "dstaddr", optional); let mode = get_attr!(elem, "mode", default); diff --git a/src/macros.rs b/src/macros.rs index 54297b38d3559149313e2476001942d316ecb68a..26a4b6334d8ededad88284b4d15b1d10e9669584 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -104,10 +104,10 @@ macro_rules! generate_attribute { } macro_rules! generate_element_enum { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+}); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { @@ -132,7 +132,7 @@ macro_rules! generate_element_enum { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder(match elem { $($elem::$enum => $enum_name,)+ - }).ns($ns) + }).ns(::ns::$ns) .build() } } @@ -140,10 +140,10 @@ macro_rules! generate_element_enum { } macro_rules! generate_attribute_enum { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => ( generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+}); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { @@ -167,7 +167,7 @@ macro_rules! generate_attribute_enum { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) .attr($attr, match elem { $($elem::$enum => $enum_name,)+ }) @@ -178,19 +178,19 @@ macro_rules! generate_attribute_enum { } macro_rules! check_self { - ($elem:ident, $name:tt, $ns:expr) => ( + ($elem:ident, $name:tt, $ns:ident) => ( check_self!($elem, $name, $ns, $name); ); - ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => ( - if !$elem.is($name, $ns) { + ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => ( + if !$elem.is($name, ::ns::$ns) { return Err(::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); } ); } macro_rules! check_ns_only { - ($elem:ident, $name:tt, $ns:expr) => ( - if !$elem.has_ns($ns) { + ($elem:ident, $name:tt, $ns:ident) => ( + if !$elem.has_ns(::ns::$ns) { return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))); } ); @@ -226,7 +226,7 @@ macro_rules! check_no_unknown_attributes { } macro_rules! generate_empty_element { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem; @@ -245,7 +245,7 @@ macro_rules! generate_empty_element { impl From<$elem> for ::minidom::Element { fn from(_: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) .build() } } @@ -253,10 +253,10 @@ macro_rules! generate_empty_element { } macro_rules! generate_element_with_only_attributes { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -284,7 +284,7 @@ macro_rules! generate_element_with_only_attributes { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) $( .attr($attr_name, elem.$attr) )* @@ -314,7 +314,7 @@ macro_rules! generate_id { } macro_rules! generate_elem_id { - ($elem:ident, $name:tt, $ns:expr) => ( + ($elem:ident, $name:tt, $ns:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { @@ -337,7 +337,7 @@ macro_rules! generate_elem_id { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) .append(elem.0) .build() } @@ -346,10 +346,10 @@ macro_rules! generate_elem_id { } macro_rules! generate_element_with_text { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $text_ident:ident: $codec:ident < $text_type:ty >) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $text_ident:ident: $codec:ident < $text_type:ty >) => ( generate_element_with_text!($(#[$meta])* $elem, $name, $ns, [], $text_ident: $codec<$text_type>); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], $text_ident:ident: $codec:ident < $text_type:ty >) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], $text_ident:ident: $codec:ident < $text_type:ty >) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -379,7 +379,7 @@ macro_rules! generate_element_with_text { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) $( .attr($attr_name, elem.$attr) )* @@ -391,7 +391,7 @@ macro_rules! generate_element_with_text { } macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:expr) => $child_constructor:ident),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -414,7 +414,7 @@ macro_rules! generate_element_with_children { let mut parsed_children = vec!(); for child in elem.children() { $( - if child.is($child_name, $child_ns) { + if child.is($child_name, ::ns::$child_ns) { let parsed_child = $child_constructor::try_from(child.clone())?; parsed_children.push(parsed_child); continue; @@ -436,7 +436,7 @@ macro_rules! generate_element_with_children { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns($ns) + .ns(::ns::$ns) $( .attr($attr_name, elem.$attr) )* diff --git a/src/mam.rs b/src/mam.rs index 6af739d9dea90ebaadbdc916e79c51f54d752d02..d9f8892902a386b140bbcd4d2b0cebe5dae1ddd9 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -55,7 +55,7 @@ impl TryFrom for Query { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::MAM); + check_self!(elem, "query", MAM); check_no_unknown_attributes!(elem, "query", ["queryid", "node"]); let mut form = None; let mut set = None; @@ -78,7 +78,7 @@ impl TryFrom for Result_ { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "result", ns::MAM); + check_self!(elem, "result", MAM); check_no_unknown_attributes!(elem, "result", ["queryid", "id"]); let mut forwarded = None; for child in elem.children() { @@ -103,7 +103,7 @@ impl TryFrom for Fin { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "fin", ns::MAM); + check_self!(elem, "fin", MAM); check_no_unknown_attributes!(elem, "fin", ["complete"]); let mut set = None; for child in elem.children() { @@ -128,7 +128,7 @@ impl TryFrom for Prefs { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "prefs", ns::MAM); + check_self!(elem, "prefs", MAM); check_no_unknown_attributes!(elem, "prefs", ["default"]); let mut always = vec!(); let mut never = vec!(); diff --git a/src/media_element.rs b/src/media_element.rs index c9d6a129c1c3494f2c322914eb31eac151d14528..43f0d7acd101e18f254e8c020a43396fe73911df 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,23 +4,22 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; use helpers::TrimmedPlainText; -generate_element_with_text!(URI, "uri", ns::MEDIA_ELEMENT, +generate_element_with_text!(URI, "uri", MEDIA_ELEMENT, [ type_: String = "type" => required ], uri: TrimmedPlainText ); -generate_element_with_children!(MediaElement, "media", ns::MEDIA_ELEMENT, +generate_element_with_children!(MediaElement, "media", MEDIA_ELEMENT, attributes: [ width: Option = "width" => optional, height: Option = "height" => optional ], children: [ - uris: Vec = ("uri", ns::MEDIA_ELEMENT) => URI + uris: Vec = ("uri", MEDIA_ELEMENT) => URI ] ); diff --git a/src/message.rs b/src/message.rs index 72fcbde47663c08b3882abb6d2f76d7b7e7b53a6..2fffdb902bd919eb8432a69015b0de6b09151654 100644 --- a/src/message.rs +++ b/src/message.rs @@ -115,9 +115,9 @@ generate_attribute!(MessageType, "type", { type Lang = String; -generate_elem_id!(Body, "body", ns::DEFAULT_NS); -generate_elem_id!(Subject, "subject", ns::DEFAULT_NS); -generate_elem_id!(Thread, "thread", ns::DEFAULT_NS); +generate_elem_id!(Body, "body", DEFAULT_NS); +generate_elem_id!(Subject, "subject", DEFAULT_NS); +generate_elem_id!(Thread, "thread", DEFAULT_NS); /// The main structure representing the `` stanza. #[derive(Debug, Clone)] @@ -188,7 +188,7 @@ impl TryFrom for Message { type Err = Error; fn try_from(root: Element) -> Result { - check_self!(root, "message", ns::DEFAULT_NS); + check_self!(root, "message", DEFAULT_NS); let from = get_attr!(root, "from", optional); let to = get_attr!(root, "to", optional); let id = get_attr!(root, "id", optional); diff --git a/src/message_correct.rs b/src/message_correct.rs index edd9605f79cbc33b77eab37e971ae5940e4aa8bc..3dbb86e49b5838bbfbf732bf376a8f2b0d772122 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,9 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; - -generate_element_with_only_attributes!(Replace, "replace", ns::MESSAGE_CORRECT, [ +generate_element_with_only_attributes!(Replace, "replace", MESSAGE_CORRECT, [ id: String = "id" => required, ]); diff --git a/src/mood.rs b/src/mood.rs index ed8b19b64b6a62155853f5504dbf7d1861abce30..400a91cdf64ce34bb9c7364936d1c263e8defaa6 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -6,11 +6,9 @@ #![deny(missing_docs)] -use ns; - generate_element_enum!( /// Enum representing all of the possible values of the XEP-0107 moods. - MoodEnum, "mood", ns::MOOD, { + MoodEnum, "mood", MOOD, { /// Impressed with fear or apprehension; in fear; apprehensive. Afraid => "afraid", diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 14a65302322f41ad0a10fce208893ccc63c269b0..4a6fda401064f946dcba19802e8bd63007f25d24 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -22,7 +22,7 @@ impl TryFrom for Muc { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "x", ns::MUC); + check_self!(elem, "x", MUC); check_no_attributes!(elem, "x"); let mut password = None; diff --git a/src/muc/user.rs b/src/muc/user.rs index 33b20d87cd05fac2e6bb1c433d867956662e8370..b1390dd69b572855fae9b2cd705e0e1e9ef998a3 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -17,7 +17,7 @@ use ns; generate_attribute_enum!( /// Lists all of the possible status codes used in MUC presences. -Status, "status", ns::MUC_USER, "code", { +Status, "status", MUC_USER, "code", { /// Inform user that any occupant is allowed to see the user's full JID NonAnonymousRoom => 100, @@ -92,7 +92,7 @@ impl TryFrom for Actor { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "actor", ns::MUC_USER); + check_self!(elem, "actor", MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); for _ in elem.children() { return Err(Error::ParseError("Unknown child in actor element.")); @@ -121,11 +121,11 @@ impl From for Element { } } -generate_element_with_only_attributes!(Continue, "continue", ns::MUC_USER, [ +generate_element_with_only_attributes!(Continue, "continue", MUC_USER, [ thread: Option = "thread" => optional, ]); -generate_elem_id!(Reason, "reason", ns::MUC_USER); +generate_elem_id!(Reason, "reason", MUC_USER); generate_attribute!(Affiliation, "affiliation", { Owner => "owner", @@ -157,7 +157,7 @@ impl TryFrom for Item { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "item", ns::MUC_USER); + check_self!(elem, "item", MUC_USER); check_no_unknown_attributes!(elem, "item", ["affiliation", "jid", "nick", "role"]); let mut actor: Option = None; let mut continue_: Option = None; @@ -216,7 +216,7 @@ impl TryFrom for MucUser { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "x", ns::MUC_USER); + check_self!(elem, "x", MUC_USER); check_no_attributes!(elem, "x"); let mut status = vec!(); let mut items = vec!(); diff --git a/src/ping.rs b/src/ping.rs index 12368c10b59a996714590b5234cb5985e00b68fe..8159ffe9384d2cfe5fb2d1af51b324c6dd935b48 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,9 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; - -generate_empty_element!(Ping, "ping", ns::PING); +generate_empty_element!(Ping, "ping", PING); #[cfg(test)] mod tests { diff --git a/src/presence.rs b/src/presence.rs index f4a4f11fb810a53147ee262031e156b6b5243c52..ecb587546d2432846c6fdd312867f11eaaa5a0f9 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -248,7 +248,7 @@ impl TryFrom for Presence { type Err = Error; fn try_from(root: Element) -> Result { - check_self!(root, "presence", ns::DEFAULT_NS); + check_self!(root, "presence", DEFAULT_NS); let mut show = None; let mut priority = None; let mut presence = Presence { diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 136489562ef3abf4606b0a25db85915d08cfc086..bd7a9351dc518a464c4b4dc161b4477c27a3d0e6 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -32,7 +32,7 @@ impl TryFrom for Item { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "item", ns::PUBSUB_EVENT); + check_self!(elem, "item", PUBSUB_EVENT); check_no_unknown_attributes!(elem, "item", ["id", "node", "publisher"]); let mut payloads = elem.children().cloned().collect::>(); let payload = payloads.pop(); @@ -141,7 +141,7 @@ impl TryFrom for PubSubEvent { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "event", ns::PUBSUB_EVENT); + check_self!(elem, "event", PUBSUB_EVENT); check_no_attributes!(elem, "event"); let mut payload = None; diff --git a/src/receipts.rs b/src/receipts.rs index 564e8fc9e969c1b49f98dd269f597ce17e3bfdca..dfaf54734a12f012208e5e2b95795ddb5b73410e 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,11 +4,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; +generate_empty_element!(Request, "request", RECEIPTS); -generate_empty_element!(Request, "request", ns::RECEIPTS); - -generate_element_with_only_attributes!(Received, "received", ns::RECEIPTS, [ +generate_element_with_only_attributes!(Received, "received", RECEIPTS, [ id: Option = "id" => optional, ]); @@ -17,6 +15,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; + use ns; #[test] fn test_simple() { diff --git a/src/roster.rs b/src/roster.rs index 01011fded1ec93fc60b27a68fe5d2af58451b633..2b62f8088c37c989465214484affb378392ac3b6 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -6,9 +6,7 @@ use jid::Jid; -use ns; - -generate_elem_id!(Group, "group", ns::ROSTER); +generate_elem_id!(Group, "group", ROSTER); generate_attribute!(Subscription, "subscription", { None => "none", @@ -21,7 +19,7 @@ generate_attribute!(Subscription, "subscription", { generate_element_with_children!( /// Contact from the user’s contact list. #[derive(PartialEq)] - Item, "item", ns::ROSTER, + Item, "item", ROSTER, attributes: [ /// JID of this contact. jid: Jid = "jid" => required, @@ -35,13 +33,13 @@ generate_element_with_children!( children: [ /// Groups this contact is part of. - groups: Vec = ("group", ns::ROSTER) => Group + groups: Vec = ("group", ROSTER) => Group ] ); generate_element_with_children!( /// The contact list of the user. - Roster, "query", ns::ROSTER, + Roster, "query", ROSTER, attributes: [ /// Version of the contact list. /// @@ -52,7 +50,7 @@ generate_element_with_children!( ], children: [ /// List of the contacts of the user. - items: Vec = ("item", ns::ROSTER) => Item + items: Vec = ("item", ROSTER) => Item ] ); diff --git a/src/rsm.rs b/src/rsm.rs index 6b9f54390f4f83fdbb368676b4dcd3534e8124c8..74484e853854295b7a0ee3c600fe19aeae25b5b7 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -28,7 +28,7 @@ impl TryFrom for Set { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "set", ns::RSM, "RSM set"); + check_self!(elem, "set", RSM, "RSM set"); let mut set = Set { after: None, before: None, diff --git a/src/sasl.rs b/src/sasl.rs index 3cb84c78c1ff05734583c1fb966dfb05ce4ad26b..98b4f93bf2a1a818c6503b2f55fbd6fa26046893 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ns; use helpers::Base64; generate_attribute!(Mechanism, "mechanism", { @@ -13,22 +12,22 @@ generate_attribute!(Mechanism, "mechanism", { Anonymous => "ANONYMOUS", }); -generate_element_with_text!(Auth, "auth", ns::SASL, +generate_element_with_text!(Auth, "auth", SASL, [ mechanism: Mechanism = "mechanism" => required ], data: Base64> ); -generate_element_with_text!(Challenge, "challenge", ns::SASL, +generate_element_with_text!(Challenge, "challenge", SASL, data: Base64> ); -generate_element_with_text!(Response, "response", ns::SASL, +generate_element_with_text!(Response, "response", SASL, data: Base64> ); -generate_element_with_text!(Success, "success", ns::SASL, +generate_element_with_text!(Success, "success", SASL, data: Base64> ); diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 5d26fa2598eb25eba48fb20a93cd3eb0bb349b6f..f2501498f8588d208b0643165d557528ac1742eb 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -21,7 +21,7 @@ generate_attribute!(ErrorType, "type", { Wait => "wait", }); -generate_element_enum!(DefinedCondition, "condition", ns::XMPP_STANZAS, { +generate_element_enum!(DefinedCondition, "condition", XMPP_STANZAS, { BadRequest => "bad-request", Conflict => "conflict", FeatureNotImplemented => "feature-not-implemented", @@ -61,7 +61,7 @@ impl TryFrom for StanzaError { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "error", ns::DEFAULT_NS); + check_self!(elem, "error", DEFAULT_NS); let type_ = get_attr!(elem, "type", required); let by = get_attr!(elem, "by", optional); diff --git a/src/stanza_id.rs b/src/stanza_id.rs index bad880029dfc6dcf65e11bcfa37d0d6f95708626..b321b46c8c19c4523131d65b37f4222a2113952f 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -6,14 +6,12 @@ use jid::Jid; -use ns; - -generate_element_with_only_attributes!(StanzaId, "stanza-id", ns::SID, [ +generate_element_with_only_attributes!(StanzaId, "stanza-id", SID, [ id: String = "id" => required, by: Jid = "by" => required, ]); -generate_element_with_only_attributes!(OriginId, "origin-id", ns::SID, [ +generate_element_with_only_attributes!(OriginId, "origin-id", SID, [ id: String = "id" => required, ]); diff --git a/src/stream.rs b/src/stream.rs index 9d9eff4625340cc86bcaf7614bc0f67cf7c07297..93a71c41d9a96249e93a19967bd16168f37f330b 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -5,9 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; -use ns; -generate_element_with_only_attributes!(Stream, "stream", ns::STREAM, [ +generate_element_with_only_attributes!(Stream, "stream", STREAM, [ from: Option = "from" => optional, to: Option = "to" => optional, id: Option = "id" => optional, diff --git a/src/version.rs b/src/version.rs index 9521dc990b19267f17eac60b715c4121dd5ce403..e01a40d308b8711690935153838e36763062d395 100644 --- a/src/version.rs +++ b/src/version.rs @@ -20,7 +20,7 @@ impl TryFrom for Version { type Err = Error; fn try_from(elem: Element) -> Result { - check_self!(elem, "query", ns::VERSION, "version"); + check_self!(elem, "query", VERSION, "version"); check_no_attributes!(elem, "version"); let mut name = None; let mut version = None; diff --git a/src/websocket.rs b/src/websocket.rs index 2402164807977d6100355576c8d680b23036e1e7..b5aa7903457af5d89ff46813471bd8ff1aa0a20a 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -5,9 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; -use ns; -generate_element_with_only_attributes!(Open, "open", ns::WEBSOCKET, [ +generate_element_with_only_attributes!(Open, "open", WEBSOCKET, [ from: Option = "from" => optional, to: Option = "to" => optional, id: Option = "id" => optional, From 3d495ccf416c8b61a3ee26a1af3aaa5f7c5ed75d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 16:33:47 +0200 Subject: [PATCH 0610/1020] Use check_no_children!() where it makes sense. --- src/jingle_message.rs | 4 +--- src/message.rs | 12 +++--------- src/muc/user.rs | 4 +--- src/presence.rs | 14 ++++---------- src/pubsub/event.rs | 8 ++------ src/stanza_error.rs | 8 ++------ 6 files changed, 13 insertions(+), 37 deletions(-) diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 159ce597adcbaeb3100d1febc17119c6c454a50a..268d93a4e6072636576a30bf74b293dd1149bbf5 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -33,9 +33,7 @@ fn get_sid(elem: Element) -> Result { } fn check_empty_and_get_sid(elem: Element) -> Result { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in Jingle message element.")); - } + check_no_children!(elem, "Jingle message"); get_sid(elem) } diff --git a/src/message.rs b/src/message.rs index 2fffdb902bd919eb8432a69015b0de6b09151654..f103ba8e5dd1bc5147e965064d01d31b689b7fd4 100644 --- a/src/message.rs +++ b/src/message.rs @@ -199,18 +199,14 @@ impl TryFrom for Message { let mut payloads = vec!(); for elem in root.children() { if elem.is("body", ns::DEFAULT_NS) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in body element.")); - } + check_no_children!(elem, "body"); let lang = get_attr!(elem, "xml:lang", default); let body = Body(elem.text()); if bodies.insert(lang, body).is_some() { return Err(Error::ParseError("Body element present twice for the same xml:lang.")); } } else if elem.is("subject", ns::DEFAULT_NS) { - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in subject element.")); - } + check_no_children!(elem, "subject"); let lang = get_attr!(elem, "xml:lang", default); let subject = Subject(elem.text()); if subjects.insert(lang, subject).is_some() { @@ -220,9 +216,7 @@ impl TryFrom for Message { if thread.is_some() { return Err(Error::ParseError("Thread element present twice.")); } - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in thread element.")); - } + check_no_children!(elem, "thread"); thread = Some(Thread(elem.text())); } else { payloads.push(elem.clone()) diff --git a/src/muc/user.rs b/src/muc/user.rs index b1390dd69b572855fae9b2cd705e0e1e9ef998a3..f53799cdfc7370e9c5147a157a25b9c1d825ba73 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -94,9 +94,7 @@ impl TryFrom for Actor { fn try_from(elem: Element) -> Result { check_self!(elem, "actor", MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in actor element.")); - } + check_no_children!(elem, "actor"); let jid: Option = get_attr!(elem, "jid", optional); let nick = get_attr!(elem, "nick", optional); diff --git a/src/presence.rs b/src/presence.rs index ecb587546d2432846c6fdd312867f11eaaa5a0f9..9eba1987595e63590610c0b16dd2a47e26ea8322 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -267,15 +267,11 @@ impl TryFrom for Presence { return Err(Error::ParseError("More than one show element in a presence.")); } check_no_attributes!(elem, "show"); - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in show element.")); - } + check_no_children!(elem, "show"); show = Some(Show::from_str(elem.text().as_ref())?); } else if elem.is("status", ns::DEFAULT_NS) { check_no_unknown_attributes!(elem, "status", ["xml:lang"]); - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in status element.")); - } + check_no_children!(elem, "status"); let lang = get_attr!(elem, "xml:lang", default); if presence.statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError("Status element present twice for the same xml:lang.")); @@ -284,10 +280,8 @@ impl TryFrom for Presence { if priority.is_some() { return Err(Error::ParseError("More than one priority element in a presence.")); } - check_no_attributes!(elem, "status"); - for _ in elem.children() { - return Err(Error::ParseError("Unknown child in priority element.")); - } + check_no_attributes!(elem, "priority"); + check_no_children!(elem, "priority"); priority = Some(Priority::from_str(elem.text().as_ref())?); } else { presence.payloads.push(elem.clone()); diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index bd7a9351dc518a464c4b4dc161b4477c27a3d0e6..8ac16cafd3eab5c7ce5b54d311ee8e4a258cb0a9 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -175,14 +175,10 @@ impl TryFrom for PubSubEvent { } else if child.is("items", ns::PUBSUB_EVENT) { payload = Some(parse_items(child.clone(), node)?); } else if child.is("purge", ns::PUBSUB_EVENT) { - for _ in child.children() { - return Err(Error::ParseError("Unknown child in purge element.")); - } + check_no_children!(child, "purge"); payload = Some(PubSubEvent::Purge { node }); } else if child.is("subscription", ns::PUBSUB_EVENT) { - for _ in child.children() { - return Err(Error::ParseError("Unknown child in purge element.")); - } + check_no_children!(child, "subscription"); payload = Some(PubSubEvent::Subscription { node: node, expiry: get_attr!(child, "expiry", optional), diff --git a/src/stanza_error.rs b/src/stanza_error.rs index f2501498f8588d208b0643165d557528ac1742eb..46496c910236a4f6e1b51545e4834f9be039dc6d 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -71,9 +71,7 @@ impl TryFrom for StanzaError { for child in elem.children() { if child.is("text", ns::XMPP_STANZAS) { - for _ in child.children() { - return Err(Error::ParseError("Unknown element in error text.")); - } + check_no_children!(child, "text"); let lang = get_attr!(elem, "xml:lang", default); if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); @@ -82,9 +80,7 @@ impl TryFrom for StanzaError { if defined_condition.is_some() { return Err(Error::ParseError("Error must not have more than one defined-condition.")); } - for _ in child.children() { - return Err(Error::ParseError("Unknown element in defined-condition.")); - } + check_no_children!(child, "defined-condition"); let condition = DefinedCondition::try_from(child.clone())?; defined_condition = Some(condition); } else { From 90063e5433a3861395ac588026e8222abe79709a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 17:32:15 +0200 Subject: [PATCH 0611/1020] mood: Add support for the element. --- src/macros.rs | 5 +++-- src/mood.rs | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 26a4b6334d8ededad88284b4d15b1d10e9669584..af7b7fcf6fdee0d0d715dd3e701fd2ab791f08f2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -314,7 +314,8 @@ macro_rules! generate_id { } macro_rules! generate_elem_id { - ($elem:ident, $name:tt, $ns:ident) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => ( + $(#[$meta])* #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { @@ -391,7 +392,7 @@ macro_rules! generate_element_with_text { } macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { diff --git a/src/mood.rs b/src/mood.rs index 400a91cdf64ce34bb9c7364936d1c263e8defaa6..b8a3ef9078ddaf40c09caf6d8c840ecdaaf16dc2 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -263,6 +263,11 @@ generate_element_enum!( } ); +generate_elem_id!( + /// Free-form text description of the mood. + Text, "text", MOOD +); + #[cfg(test)] mod tests { use super::*; @@ -275,4 +280,11 @@ mod tests { let mood = MoodEnum::try_from(elem).unwrap(); assert_eq!(mood, MoodEnum::Happy); } + + #[test] + fn test_text() { + let elem: Element = "Yay!".parse().unwrap(); + let text = Text::try_from(elem).unwrap(); + assert_eq!(text.0, String::from("Yay!")); + } } From b37a8da906ff16f56c6a23a93c7c04beab9e13e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 17:43:03 +0200 Subject: [PATCH 0612/1020] Add a User Nickname (XEP-0172) parser and serialiser. --- src/lib.rs | 3 +++ src/nick.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ns.rs | 3 +++ 3 files changed, 60 insertions(+) create mode 100644 src/nick.rs diff --git a/src/lib.rs b/src/lib.rs index e420e233e6389dcc7a06d6183c5f4703bfb924b6..a0b36c34e4d46b974328e375649777593981f02c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,6 +110,9 @@ pub mod caps; /// XEP-0166: Jingle pub mod jingle; +/// XEP-0172: User Nickname +pub mod nick; + /// XEP-0184: Message Delivery Receipts pub mod receipts; diff --git a/src/nick.rs b/src/nick.rs new file mode 100644 index 0000000000000000000000000000000000000000..9844e9318c8670319e3f032e9717c10d9ffbfade --- /dev/null +++ b/src/nick.rs @@ -0,0 +1,54 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_elem_id!( + /// Represents a global, memorable, friendly or informal name chosen by a user. + Nick, "nick", NICK +); + +#[cfg(test)] +mod tests { + use super::*; + use try_from::TryFrom; + use minidom::Element; + use error::Error; + + #[test] + fn test_simple() { + let elem: Element = "Link Mauve".parse().unwrap(); + let nick = Nick::try_from(elem).unwrap(); + assert_eq!(&nick.0, "Link Mauve"); + } + + #[test] + fn test_serialise() { + let elem1 = Element::from(Nick(String::from("Link Mauve"))); + let elem2: Element = "Link Mauve".parse().unwrap(); + assert_eq!(elem1, elem2); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = Nick::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in nick element."); + } + + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = Nick::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in nick element."); + } +} diff --git a/src/ns.rs b/src/ns.rs index 2227886271a88847628fb82edda6b36faa428163..1eb6e3015f45eb47c31cdc08cb6a26487090b28a 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -74,6 +74,9 @@ pub const CAPS: &str = "http://jabber.org/protocol/caps"; /// XEP-0166: Jingle pub const JINGLE: &str = "urn:xmpp:jingle:1"; +/// XEP-0172: User Nickname +pub const NICK: &str = "http://jabber.org/protocol/nick"; + /// XEP-0184: Message Delivery Receipts pub const RECEIPTS: &str = "urn:xmpp:receipts"; From 688ae145be042c722655c4e9c46bdb8442956afb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 17:49:25 +0200 Subject: [PATCH 0613/1020] pubsub: Move common attributes to the module. --- src/pubsub/event.rs | 4 +--- src/pubsub/mod.rs | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 8ac16cafd3eab5c7ce5b54d311ee8e4a258cb0a9..dea11ba446daf99dffad2ede856084326250fc07 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -16,9 +16,7 @@ use ns; use data_forms::DataForm; -generate_id!(NodeName); -generate_id!(ItemId); -generate_id!(SubscriptionId); +use pubsub::{NodeName, ItemId, SubscriptionId}; #[derive(Debug, Clone)] pub struct Item { diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index 9eb939442e863cabae522697569d5da6ac324f00..89b6f8ce951ca89cc8a697e794f1d19841efdf6c 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -7,3 +7,7 @@ pub mod event; pub use self::event::PubSubEvent; + +generate_id!(NodeName); +generate_id!(ItemId); +generate_id!(SubscriptionId); From d2dc77e4a3da7670b219beecba8056a380549aa6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 21:02:22 +0200 Subject: [PATCH 0614/1020] pubsub: Move Subscription to the module. --- src/pubsub/event.rs | 9 +-------- src/pubsub/mod.rs | 7 +++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index dea11ba446daf99dffad2ede856084326250fc07..4ac75cf2d4beb01211f8506857c36d6dace05a0b 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -16,7 +16,7 @@ use ns; use data_forms::DataForm; -use pubsub::{NodeName, ItemId, SubscriptionId}; +use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; #[derive(Debug, Clone)] pub struct Item { @@ -58,13 +58,6 @@ impl From for Element { } } -generate_attribute!(Subscription, "subscription", { - None => "none", - Pending => "pending", - Subscribed => "subscribed", - Unconfigured => "unconfigured", -}, Default = None); - #[derive(Debug, Clone)] pub enum PubSubEvent { /* diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index 89b6f8ce951ca89cc8a697e794f1d19841efdf6c..735fb9c699d05f7851d5f587907993e732d470b6 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -11,3 +11,10 @@ pub use self::event::PubSubEvent; generate_id!(NodeName); generate_id!(ItemId); generate_id!(SubscriptionId); + +generate_attribute!(Subscription, "subscription", { + None => "none", + Pending => "pending", + Subscribed => "subscribed", + Unconfigured => "unconfigured", +}, Default = None); From a3e35510cc8244c08e5e73e18e7ed4bc33187f30 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 21:03:24 +0200 Subject: [PATCH 0615/1020] macros: Always use the correct std::default::Default trait. --- src/macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index af7b7fcf6fdee0d0d715dd3e701fd2ab791f08f2..0a2648d567a36874f10fea060a2b98f0dca50413 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -30,7 +30,7 @@ macro_rules! get_attr { ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( match $elem.attr($attr) { Some($value) => $func, - None => Default::default(), + None => ::std::default::Default::default(), } ); } @@ -95,7 +95,7 @@ macro_rules! generate_attribute { })) } } - impl Default for $elem { + impl ::std::default::Default for $elem { fn default() -> $elem { $elem::$default } From fcdfc6d85adf984b88670e06137caf38ccd27b34 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 21:04:16 +0200 Subject: [PATCH 0616/1020] Add a PubSub module. --- src/macros.rs | 138 ++++++++++++ src/pubsub/mod.rs | 2 + src/pubsub/pubsub.rs | 490 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 630 insertions(+) create mode 100644 src/pubsub/pubsub.rs diff --git a/src/macros.rs b/src/macros.rs index 0a2648d567a36874f10fea060a2b98f0dca50413..81a7f82d32621cb01037ee1ed57d7c0d391393b2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -101,6 +101,38 @@ macro_rules! generate_attribute { } } ); + ($elem:ident, $name:tt, bool) => ( + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + /// True value, represented by either 'true' or '1'. + True, + /// False value, represented by either 'false' or '0'. + False, + } + impl ::std::str::FromStr for $elem { + type Err = ::error::Error; + fn from_str(s: &str) -> Result { + Ok(match s { + "true" | "1" => $elem::True, + "false" | "0" => $elem::False, + _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl ::minidom::IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + match self { + $elem::True => Some(String::from("true")), + $elem::False => None + } + } + } + impl ::std::default::Default for $elem { + fn default() -> $elem { + $elem::False + } + } + ); } macro_rules! generate_element_enum { @@ -392,6 +424,9 @@ macro_rules! generate_element_with_text { } macro_rules! generate_element_with_children { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] @@ -448,4 +483,107 @@ macro_rules! generate_element_with_children { } } ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor)); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor)); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem { + $( + $(#[$attr_meta])* + pub $attr: $attr_type, + )* + $(#[$child_meta])* + pub $child_ident: Option<$child_type>, + } + + impl ::try_from::TryFrom<::minidom::Element> for $elem { + type Err = ::error::Error; + + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + check_self!(elem, $name, $ns); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + let mut parsed_child = None; + for child in elem.children() { + if child.is($child_name, ::ns::$child_ns) { + parsed_child = Some($child_constructor::try_from(child.clone())?); + continue; + } + return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + } + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action), + )* + $child_ident: parsed_child, + }) + } + } + + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) + .ns(::ns::$ns) + $( + .attr($attr_name, elem.$attr) + )* + .append(elem.$child_ident) + .build() + } + } + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + $(#[$meta])* + #[derive(Debug, Clone)] + pub struct $elem { + $( + $(#[$attr_meta])* + pub $attr: $attr_type, + )* + $(#[$child_meta])* + pub $child_ident: $child_type, + } + + impl ::try_from::TryFrom<::minidom::Element> for $elem { + type Err = ::error::Error; + + fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + check_self!(elem, $name, $ns); + check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); + let mut parsed_child = None; + for child in elem.children() { + if child.is($child_name, ::ns::$child_ns) { + parsed_child = Some($child_constructor::try_from(child.clone())?); + continue; + } + return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + } + Ok($elem { + $( + $attr: get_attr!(elem, $attr_name, $attr_action), + )* + $child_ident: parsed_child.ok_or(::error::Error::ParseError(concat!("Missing child ", $child_name, " in ", $name, " element.")))?, + }) + } + } + + impl From<$elem> for ::minidom::Element { + fn from(elem: $elem) -> ::minidom::Element { + ::minidom::Element::builder($name) + .ns(::ns::$ns) + $( + .attr($attr_name, elem.$attr) + )* + .append(elem.$child_ident) + .build() + } + } + ); } diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index 735fb9c699d05f7851d5f587907993e732d470b6..e12d0c1aec0cc04e13085c8476f51945a417f6b8 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -5,8 +5,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. pub mod event; +pub mod pubsub; pub use self::event::PubSubEvent; +pub use self::pubsub::PubSub; generate_id!(NodeName); generate_id!(ItemId); diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs new file mode 100644 index 0000000000000000000000000000000000000000..ce1160d8904654a2d10d1ae703196eebbce2442c --- /dev/null +++ b/src/pubsub/pubsub.rs @@ -0,0 +1,490 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use try_from::TryFrom; + +use minidom::Element; +use jid::Jid; + +use error::Error; + +use ns; + +use data_forms::DataForm; + +use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; + +// TODO: a better solution would be to split this into a query and a result elements, like for +// XEP-0030. +generate_element_with_children!( + Affiliations, "affiliations", PUBSUB, + attributes: [ + node: Option = "node" => optional, + ], + children: [ + affiliations: Vec = ("affiliation", PUBSUB) => Affiliation + ] +); + +generate_attribute!( + AffiliationAttribute, "affiliation", { + Member => "member", + None => "none", + Outcast => "outcast", + Owner => "owner", + Publisher => "publisher", + PublishOnly => "publish-only", + } +); + +generate_element_with_only_attributes!( + Affiliation, "affiliation", PUBSUB, [ + node: NodeName = "node" => required, + affiliation: AffiliationAttribute = "affiliation" => required, + ] +); + +generate_element_with_children!( + Configure, "configure", PUBSUB, + child: ( + form: Option = ("x", DATA_FORMS) => DataForm + ) +); + +generate_element_with_only_attributes!( + Create, "create", PUBSUB, [ + node: Option = "node" => optional, + ] +); + +generate_element_with_only_attributes!( + Default, "default", PUBSUB, [ + node: Option = "node" => optional, + // TODO: do we really want to support collection nodes? + // type: String = "type" => optional, + ] +); + +generate_element_with_children!( + Items, "items", PUBSUB, + attributes: [ + // TODO: should be an xs:positiveInteger, that is, an unbounded int ≥ 1. + max_items: Option = "max_items" => optional, + node: NodeName = "node" => required, + subid: Option = "subid" => optional, + ], + children: [ + items: Vec = ("item", PUBSUB) => Item + ] +); + +#[derive(Debug, Clone)] +pub struct Item { + payload: Option, + id: Option, +} + +impl TryFrom for Item { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "item", PUBSUB); + check_no_unknown_attributes!(elem, "item", ["id"]); + let mut payloads = elem.children().cloned().collect::>(); + let payload = payloads.pop(); + if !payloads.is_empty() { + return Err(Error::ParseError("More than a single payload in item element.")); + } + Ok(Item { + payload, + id: get_attr!(elem, "id", optional), + }) + } +} + +impl From for Element { + fn from(item: Item) -> Element { + Element::builder("item") + .ns(ns::PUBSUB) + .attr("id", item.id) + .append(item.payload) + .build() + } +} + +generate_element_with_children!( + Options, "options", PUBSUB, + attributes: [ + jid: Jid = "jid" => required, + node: Option = "node" => optional, + subid: Option = "subid" => optional, + ], + child: ( + form: Option = ("x", DATA_FORMS) => DataForm + ) +); + +generate_element_with_children!( + Publish, "publish", PUBSUB, + attributes: [ + node: NodeName = "node" => required, + ], + children: [ + items: Vec = ("item", PUBSUB) => Item + ] +); + +generate_element_with_children!( + PublishOptions, "publish-options", PUBSUB, + child: ( + form: DataForm = ("x", DATA_FORMS) => DataForm + ) +); + +generate_attribute!(Notify, "notify", bool); + +generate_element_with_children!( + Retract, "retract", PUBSUB, + attributes: [ + node: NodeName = "node" => required, + notify: Notify = "notify" => default, + ], + children: [ + items: Vec = ("item", PUBSUB) => Item + ] +); + +#[derive(Debug, Clone)] +pub struct SubscribeOptions { + required: bool, +} + +impl TryFrom for SubscribeOptions { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "subscribe-options", PUBSUB); + check_no_attributes!(elem, "subscribe-options"); + let mut required = false; + for child in elem.children() { + if child.is("required", ns::PUBSUB) { + if required { + return Err(Error::ParseError("More than one required element in subscribe-options.")); + } + required = true; + } else { + return Err(Error::ParseError("Unknown child in subscribe-options element.")); + } + } + Ok(SubscribeOptions { required }) + } +} + +impl From for Element { + fn from(subscribe_options: SubscribeOptions) -> Element { + Element::builder("subscribe-options") + .ns(ns::PUBSUB) + .append(if subscribe_options.required { + vec!(Element::builder("required") + .ns(ns::PUBSUB) + .build()) + } else { + vec!() + }) + .build() + } +} + +generate_element_with_only_attributes!( + Subscribe, "subscribe", PUBSUB, [ + jid: Jid = "jid" => required, + node: Option = "node" => optional, + ] +); + +generate_element_with_children!( + Subscriptions, "subscriptions", PUBSUB, + attributes: [ + node: Option = "node" => optional, + ], + children: [ + subscription: Vec = ("subscription", PUBSUB) => SubscriptionElem + ] +); + +generate_element_with_children!( + SubscriptionElem, "subscription", PUBSUB, + attributes: [ + jid: Jid = "jid" => required, + node: Option = "node" => optional, + subid: Option = "subid" => optional, + subscription: Option = "subscription" => optional, + ], + child: ( + subscribe_options: Option = ("subscribe-options", PUBSUB) => SubscribeOptions + ) +); + +generate_element_with_only_attributes!( + Unsubscribe, "unsubscribe", PUBSUB, [ + jid: Jid = "jid" => required, + node: Option = "node" => optional, + subid: Option = "subid" => optional, + ] +); + +#[derive(Debug, Clone)] +pub enum PubSub { + Create { + create: Create, + configure: Option + }, + Publish { + publish: Publish, + publish_options: Option + }, + Affiliations(Affiliations), + Default(Default), + Items(Items), + Retract(Retract), + Subscription(SubscriptionElem), + Subscriptions(Subscriptions), + Unsubscribe(Unsubscribe), +} + +impl TryFrom for PubSub { + type Err = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "pubsub", PUBSUB); + check_no_attributes!(elem, "pubsub"); + + let mut payload = None; + for child in elem.children() { + if child.is("create", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("…")); + } + let create = Create::try_from(child.clone())?; + payload = Some(PubSub::Create { create, configure: None }); + } else { + return Err(Error::ParseError("Unknown child in pubsub element.")); + } + } + Ok(payload.ok_or(Error::ParseError("No payload in pubsub element."))?) + } +} + +impl From for Element { + fn from(pubsub: PubSub) -> Element { + Element::builder("pubsub") + .ns(ns::PUBSUB) + .append(match pubsub { + PubSub::Create { create, configure } => { + let mut elems = vec!(Element::from(create)); + if let Some(configure) = configure { + elems.push(Element::from(configure)); + } + elems + }, + PubSub::Publish { publish, publish_options } => { + let mut elems = vec!(Element::from(publish)); + if let Some(publish_options) = publish_options { + elems.push(Element::from(publish_options)); + } + elems + }, + PubSub::Affiliations(affiliations) => vec!(Element::from(affiliations)), + PubSub::Default(default) => vec!(Element::from(default)), + PubSub::Items(items) => vec!(Element::from(items)), + PubSub::Retract(retract) => vec!(Element::from(retract)), + PubSub::Subscription(subscription) => vec!(Element::from(subscription)), + PubSub::Subscriptions(subscriptions) => vec!(Element::from(subscriptions)), + PubSub::Unsubscribe(unsubscribe) => vec!(Element::from(unsubscribe)), + }) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use compare_elements::NamespaceAwareCompare; + + #[test] + fn invalid_empty_pubsub() { + let elem: Element = "".parse().unwrap(); + let error = PubSub::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No payload in pubsub element."); + /* + match pubsub { + PubSub::EmptyItems { node } => assert_eq!(node, NodeName(String::from("coucou"))), + _ => panic!(), + } + */ + } + + #[test] + fn publish_option() { + let elem: Element = "http://jabber.org/protocol/pubsub#publish-options".parse().unwrap(); + let publish_options = PublishOptions::try_from(elem).unwrap(); + assert_eq!(&publish_options.form.form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options"); + } + + #[test] + fn subscribe_options() { + let elem1: Element = "".parse().unwrap(); + let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap(); + assert_eq!(subscribe_options1.required, false); + + let elem2: Element = "".parse().unwrap(); + let subscribe_options2 = SubscribeOptions::try_from(elem2).unwrap(); + assert_eq!(subscribe_options2.required, true); + } + + /* + #[test] + fn test_simple_items() { + let elem: Element = "".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::PublishedItems { node, items } => { + assert_eq!(node, NodeName(String::from("coucou"))); + assert_eq!(items[0].id, Some(ItemId(String::from("test")))); + assert_eq!(items[0].node, Some(NodeName(String::from("huh?")))); + assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap())); + assert_eq!(items[0].payload, None); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_pep() { + let elem: Element = "".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::PublishedItems { node, items } => { + assert_eq!(node, NodeName(String::from("something"))); + assert_eq!(items[0].id, None); + assert_eq!(items[0].node, None); + assert_eq!(items[0].publisher, None); + match items[0].payload { + Some(ref elem) => assert!(elem.is("foreign", "example:namespace")), + _ => panic!(), + } + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_retract() { + let elem: Element = "".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::RetractedItems { node, items } => { + assert_eq!(node, NodeName(String::from("something"))); + assert_eq!(items[0], ItemId(String::from("coucou"))); + assert_eq!(items[1], ItemId(String::from("test"))); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_delete() { + let elem: Element = "".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::Delete { node, redirect } => { + assert_eq!(node, NodeName(String::from("coucou"))); + assert_eq!(redirect, Some(String::from("hello"))); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_purge() { + let elem: Element = "".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::Purge { node } => { + assert_eq!(node, NodeName(String::from("coucou"))); + }, + _ => panic!(), + } + } + + #[test] + fn test_simple_configure() { + let elem: Element = "http://jabber.org/protocol/pubsub#node_config".parse().unwrap(); + let event = PubSub::try_from(elem).unwrap(); + match event { + PubSub::Configuration { node, form: _ } => { + assert_eq!(node, NodeName(String::from("coucou"))); + //assert_eq!(form.type_, Result_); + }, + _ => panic!(), + } + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = PubSub::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in event element."); + } + + #[test] + fn test_invalid_attribute() { + let elem: Element = "".parse().unwrap(); + let error = PubSub::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in event element."); + } + + #[test] + fn test_ex221_subscription() { + let elem: Element = r#" + + + +"#.parse().unwrap(); + let event = PubSub::try_from(elem.clone()).unwrap(); + match event.clone() { + PubSub::Subscription { node, expiry, jid, subid, subscription } => { + assert_eq!(node, NodeName(String::from("princely_musings"))); + assert_eq!(subid, Some(SubscriptionId(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3")))); + assert_eq!(subscription, Some(Subscription::Subscribed)); + assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap())); + assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap())); + }, + _ => panic!(), + } + + let elem2: Element = event.into(); + assert!(elem.compare_to(&elem2)); + } + */ +} From 58a1d80b77cb2b1ed70575f700a1e788d1b4a964 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 14 May 2018 21:32:58 +0200 Subject: [PATCH 0617/1020] pubsub: Add forgotten toplevel parsing. --- src/pubsub/pubsub.rs | 275 ++++++++++++++++++++++--------------------- 1 file changed, 140 insertions(+), 135 deletions(-) diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index ce1160d8904654a2d10d1ae703196eebbce2442c..5ca62e9a4fb945e262a5d0ffda814fbaf2050be9 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -140,7 +140,7 @@ generate_element_with_children!( generate_element_with_children!( PublishOptions, "publish-options", PUBSUB, child: ( - form: DataForm = ("x", DATA_FORMS) => DataForm + form: Option = ("x", DATA_FORMS) => DataForm ) ); @@ -266,10 +266,78 @@ impl TryFrom for PubSub { for child in elem.children() { if child.is("create", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("…")); + return Err(Error::ParseError("Payload is already defined in pubsub element.")); } let create = Create::try_from(child.clone())?; payload = Some(PubSub::Create { create, configure: None }); + } else if child.is("configure", ns::PUBSUB) { + if let Some(PubSub::Create { create, configure }) = payload { + if configure.is_some() { + return Err(Error::ParseError("Configure is already defined in pubsub element.")); + } + let configure = Some(Configure::try_from(child.clone())?); + payload = Some(PubSub::Create { create, configure }); + } else { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + } else if child.is("publish", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let publish = Publish::try_from(child.clone())?; + payload = Some(PubSub::Publish { publish, publish_options: None }); + } else if child.is("publish-options", ns::PUBSUB) { + if let Some(PubSub::Publish { publish, publish_options }) = payload { + if publish_options.is_some() { + return Err(Error::ParseError("Publish-options are already defined in pubsub element.")); + } + let publish_options = Some(PublishOptions::try_from(child.clone())?); + payload = Some(PubSub::Publish { publish, publish_options }); + } else { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + } else if child.is("affiliations", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let affiliations = Affiliations::try_from(child.clone())?; + payload = Some(PubSub::Affiliations(affiliations)); + } else if child.is("default", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let default = Default::try_from(child.clone())?; + payload = Some(PubSub::Default(default)); + } else if child.is("items", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let items = Items::try_from(child.clone())?; + payload = Some(PubSub::Items(items)); + } else if child.is("retract", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let retract = Retract::try_from(child.clone())?; + payload = Some(PubSub::Retract(retract)); + } else if child.is("subscription", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let subscription = SubscriptionElem::try_from(child.clone())?; + payload = Some(PubSub::Subscription(subscription)); + } else if child.is("subscriptions", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let subscriptions = Subscriptions::try_from(child.clone())?; + payload = Some(PubSub::Subscriptions(subscriptions)); + } else if child.is("unsubscribe", ns::PUBSUB) { + if payload.is_some() { + return Err(Error::ParseError("Payload is already defined in pubsub element.")); + } + let unsubscribe = Unsubscribe::try_from(child.clone())?; + payload = Some(PubSub::Unsubscribe(unsubscribe)); } else { return Err(Error::ParseError("Unknown child in pubsub element.")); } @@ -315,176 +383,113 @@ mod tests { use compare_elements::NamespaceAwareCompare; #[test] - fn invalid_empty_pubsub() { - let elem: Element = "".parse().unwrap(); - let error = PubSub::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "No payload in pubsub element."); - /* - match pubsub { - PubSub::EmptyItems { node } => assert_eq!(node, NodeName(String::from("coucou"))), + fn create() { + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let pubsub = PubSub::try_from(elem).unwrap(); + match pubsub.clone() { + PubSub::Create { create, configure } => { + assert!(create.node.is_none()); + assert!(configure.is_none()); + } _ => panic!(), } - */ - } - - #[test] - fn publish_option() { - let elem: Element = "http://jabber.org/protocol/pubsub#publish-options".parse().unwrap(); - let publish_options = PublishOptions::try_from(elem).unwrap(); - assert_eq!(&publish_options.form.form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options"); - } - #[test] - fn subscribe_options() { - let elem1: Element = "".parse().unwrap(); - let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap(); - assert_eq!(subscribe_options1.required, false); + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); - let elem2: Element = "".parse().unwrap(); - let subscribe_options2 = SubscribeOptions::try_from(elem2).unwrap(); - assert_eq!(subscribe_options2.required, true); - } - - /* - #[test] - fn test_simple_items() { - let elem: Element = "".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::PublishedItems { node, items } => { - assert_eq!(node, NodeName(String::from("coucou"))); - assert_eq!(items[0].id, Some(ItemId(String::from("test")))); - assert_eq!(items[0].node, Some(NodeName(String::from("huh?")))); - assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap())); - assert_eq!(items[0].payload, None); - }, + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let pubsub = PubSub::try_from(elem).unwrap(); + match pubsub.clone() { + PubSub::Create { create, configure } => { + assert_eq!(&create.node.unwrap().0, "coucou"); + assert!(configure.is_none()); + } _ => panic!(), } - } - #[test] - fn test_simple_pep() { - let elem: Element = "".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::PublishedItems { node, items } => { - assert_eq!(node, NodeName(String::from("something"))); - assert_eq!(items[0].id, None); - assert_eq!(items[0].node, None); - assert_eq!(items[0].publisher, None); - match items[0].payload { - Some(ref elem) => assert!(elem.is("foreign", "example:namespace")), - _ => panic!(), - } - }, - _ => panic!(), - } + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); } #[test] - fn test_simple_retract() { - let elem: Element = "".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::RetractedItems { node, items } => { - assert_eq!(node, NodeName(String::from("something"))); - assert_eq!(items[0], ItemId(String::from("coucou"))); - assert_eq!(items[1], ItemId(String::from("test"))); - }, + fn create_and_configure() { + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let pubsub = PubSub::try_from(elem).unwrap(); + match pubsub.clone() { + PubSub::Create { create, configure } => { + assert!(create.node.is_none()); + assert!(configure.unwrap().form.is_none()); + } _ => panic!(), } - } - #[test] - fn test_simple_delete() { - let elem: Element = "".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::Delete { node, redirect } => { - assert_eq!(node, NodeName(String::from("coucou"))); - assert_eq!(redirect, Some(String::from("hello"))); - }, - _ => panic!(), - } + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); } #[test] - fn test_simple_purge() { - let elem: Element = "".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::Purge { node } => { - assert_eq!(node, NodeName(String::from("coucou"))); - }, + fn publish() { + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let pubsub = PubSub::try_from(elem).unwrap(); + match pubsub.clone() { + PubSub::Publish { publish, publish_options } => { + assert_eq!(&publish.node.0, "coucou"); + assert!(publish_options.is_none()); + } _ => panic!(), } + + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); } #[test] - fn test_simple_configure() { - let elem: Element = "http://jabber.org/protocol/pubsub#node_config".parse().unwrap(); - let event = PubSub::try_from(elem).unwrap(); - match event { - PubSub::Configuration { node, form: _ } => { - assert_eq!(node, NodeName(String::from("coucou"))); - //assert_eq!(form.type_, Result_); - }, + fn publish_with_publish_options() { + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let pubsub = PubSub::try_from(elem).unwrap(); + match pubsub.clone() { + PubSub::Publish { publish, publish_options } => { + assert_eq!(&publish.node.0, "coucou"); + assert!(publish_options.unwrap().form.is_none()); + } _ => panic!(), } + + let elem2 = Element::from(pubsub); + assert!(elem1.compare_to(&elem2)); } #[test] - fn test_invalid() { - let elem: Element = "".parse().unwrap(); + fn invalid_empty_pubsub() { + let elem: Element = "".parse().unwrap(); let error = PubSub::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in event element."); + assert_eq!(message, "No payload in pubsub element."); } #[test] - fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = PubSub::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in event element."); + fn publish_option() { + let elem: Element = "http://jabber.org/protocol/pubsub#publish-options".parse().unwrap(); + let publish_options = PublishOptions::try_from(elem).unwrap(); + assert_eq!(&publish_options.form.unwrap().form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options"); } #[test] - fn test_ex221_subscription() { - let elem: Element = r#" - - - -"#.parse().unwrap(); - let event = PubSub::try_from(elem.clone()).unwrap(); - match event.clone() { - PubSub::Subscription { node, expiry, jid, subid, subscription } => { - assert_eq!(node, NodeName(String::from("princely_musings"))); - assert_eq!(subid, Some(SubscriptionId(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3")))); - assert_eq!(subscription, Some(Subscription::Subscribed)); - assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap())); - assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap())); - }, - _ => panic!(), - } + fn subscribe_options() { + let elem1: Element = "".parse().unwrap(); + let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap(); + assert_eq!(subscribe_options1.required, false); - let elem2: Element = event.into(); - assert!(elem.compare_to(&elem2)); + let elem2: Element = "".parse().unwrap(); + let subscribe_options2 = SubscribeOptions::try_from(elem2).unwrap(); + assert_eq!(subscribe_options2.required, true); } - */ } From 71dc5ad6c80f059d4d7a002e1a904170b80bbfeb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 15 May 2018 00:18:15 +0200 Subject: [PATCH 0618/1020] pubsub: Document the new structs and their fields. --- src/macros.rs | 23 ++++----- src/pubsub/mod.rs | 3 ++ src/pubsub/pubsub.rs | 115 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 127 insertions(+), 14 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 81a7f82d32621cb01037ee1ed57d7c0d391393b2..20dff441d3c4cb90dbc2904eac674029c2d19a55 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -36,18 +36,18 @@ macro_rules! get_attr { } macro_rules! generate_attribute { - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => ( - generate_attribute!($elem, $name, {$($a => $b),+}); + ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}) => ( + generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+}); ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => ( - generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default); + ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}, Default = $default:ident) => ( + generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+}, Default = $default); ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}) => ( + $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( - #[doc=$b] - #[doc="value for this attribute."] + $(#[$a_meta])* $a ),+ } @@ -68,12 +68,12 @@ macro_rules! generate_attribute { } } ); - ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}, Default = $default:ident) => ( + $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( - #[doc=$b] - #[doc="value for this attribute."] + $(#[$a_meta])* $a ),+ } @@ -101,7 +101,8 @@ macro_rules! generate_attribute { } } ); - ($elem:ident, $name:tt, bool) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, bool) => ( + $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { /// True value, represented by either 'true' or '1'. diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index e12d0c1aec0cc04e13085c8476f51945a417f6b8..badf672996da99d58f915b7975f366e29b8f37b8 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -4,7 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +/// The `http://jabber.org/protocol/pubsub#event` protocol. pub mod event; + +/// The `http://jabber.org/protocol/pubsub` protocol. pub mod pubsub; pub use self::event::PubSubEvent; diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 5ca62e9a4fb945e262a5d0ffda814fbaf2050be9..d90ca9826ba9831ee20155a05e16780aaee05b35 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#![deny(missing_docs)] + use try_from::TryFrom; use minidom::Element; @@ -20,71 +22,108 @@ use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; // TODO: a better solution would be to split this into a query and a result elements, like for // XEP-0030. generate_element_with_children!( + /// A list of affiliations you have on a service, or on a node. Affiliations, "affiliations", PUBSUB, attributes: [ + /// The optional node name this request pertains to. node: Option = "node" => optional, ], children: [ + /// The actual list of affiliation elements. affiliations: Vec = ("affiliation", PUBSUB) => Affiliation ] ); generate_attribute!( + /// A list of possible affiliations to a node. AffiliationAttribute, "affiliation", { + /// You are a member of this node, you can subscribe and retrieve items. Member => "member", + + /// You don’t have a specific affiliation with this node, you can only subscribe to it. None => "none", + + /// You are banned from this node. Outcast => "outcast", + + /// You are an owner of this node, and can do anything with it. Owner => "owner", + + /// You are a publisher on this node, you can publish and retract items to it. Publisher => "publisher", + + /// You can publish and retract items on this node, but not subscribe or retrive items. PublishOnly => "publish-only", } ); generate_element_with_only_attributes!( + /// An affiliation element. Affiliation, "affiliation", PUBSUB, [ + /// The node this affiliation pertains to. node: NodeName = "node" => required, + + /// The affiliation you currently have on this node. affiliation: AffiliationAttribute = "affiliation" => required, ] ); generate_element_with_children!( + /// Request to configure a new node. Configure, "configure", PUBSUB, child: ( + /// The form to configure it. form: Option = ("x", DATA_FORMS) => DataForm ) ); generate_element_with_only_attributes!( + /// Request to create a new node. Create, "create", PUBSUB, [ + /// The node name to create, if `None` the service will generate one. node: Option = "node" => optional, ] ); generate_element_with_only_attributes!( + /// Request for a default node configuration. Default, "default", PUBSUB, [ + /// The node targetted by this request, otherwise the entire service. node: Option = "node" => optional, + // TODO: do we really want to support collection nodes? // type: String = "type" => optional, ] ); generate_element_with_children!( + /// A request for a list of items. Items, "items", PUBSUB, attributes: [ // TODO: should be an xs:positiveInteger, that is, an unbounded int ≥ 1. + /// Maximum number of items returned. max_items: Option = "max_items" => optional, + + /// The node queried by this request. node: NodeName = "node" => required, + + /// The subscription identifier related to this request. subid: Option = "subid" => optional, ], children: [ + /// The actual list of items returned. items: Vec = ("item", PUBSUB) => Item ] ); +/// An item from a PubSub node. #[derive(Debug, Clone)] pub struct Item { - payload: Option, - id: Option, + /// The payload of this item, in an arbitrary namespace. + pub payload: Option, + + /// The 'id' attribute of this item. + pub id: Option, } impl TryFrom for Item { @@ -116,49 +155,71 @@ impl From for Element { } generate_element_with_children!( + /// The options associated to a subscription request. Options, "options", PUBSUB, attributes: [ + /// The JID affected by this request. jid: Jid = "jid" => required, + + /// The node affected by this request. node: Option = "node" => optional, + + /// The subscription identifier affected by this request. subid: Option = "subid" => optional, ], child: ( + /// The form describing the subscription. form: Option = ("x", DATA_FORMS) => DataForm ) ); generate_element_with_children!( + /// Request to publish items to a node. Publish, "publish", PUBSUB, attributes: [ + /// The target node for this operation. node: NodeName = "node" => required, ], children: [ + /// The items you want to publish. items: Vec = ("item", PUBSUB) => Item ] ); generate_element_with_children!( + /// The options associated to a publish request. PublishOptions, "publish-options", PUBSUB, child: ( + /// The form describing these options. form: Option = ("x", DATA_FORMS) => DataForm ) ); -generate_attribute!(Notify, "notify", bool); +generate_attribute!( + /// Whether a retract request should notify subscribers or not. + Notify, "notify", bool +); generate_element_with_children!( + /// A request to retract some items from a node. Retract, "retract", PUBSUB, attributes: [ + /// The node affected by this request. node: NodeName = "node" => required, + + /// Whether a retract request should notify subscribers or not. notify: Notify = "notify" => default, ], children: [ + /// The items affected by this request. items: Vec = ("item", PUBSUB) => Item ] ); +/// Indicate that the subscription can be configured. #[derive(Debug, Clone)] pub struct SubscribeOptions { + /// If `true`, the configuration is actually required. required: bool, } @@ -199,59 +260,107 @@ impl From for Element { } generate_element_with_only_attributes!( + /// A request to subscribe a JID to a node. Subscribe, "subscribe", PUBSUB, [ + /// The JID being subscribed. jid: Jid = "jid" => required, + + /// The node to subscribe to. node: Option = "node" => optional, ] ); generate_element_with_children!( + /// A request for current subscriptions. Subscriptions, "subscriptions", PUBSUB, attributes: [ + /// The node to query. node: Option = "node" => optional, ], children: [ + /// The list of subscription elements returned. subscription: Vec = ("subscription", PUBSUB) => SubscriptionElem ] ); generate_element_with_children!( + /// A subscription element, describing the state of a subscription. SubscriptionElem, "subscription", PUBSUB, attributes: [ + /// The JID affected by this subscription. jid: Jid = "jid" => required, + + /// The node affected by this subscription. node: Option = "node" => optional, + + /// The subscription identifier for this subscription. subid: Option = "subid" => optional, + + /// The state of the subscription. subscription: Option = "subscription" => optional, ], child: ( + /// The options related to this subscription. subscribe_options: Option = ("subscribe-options", PUBSUB) => SubscribeOptions ) ); generate_element_with_only_attributes!( + /// An unsubscribe request. Unsubscribe, "unsubscribe", PUBSUB, [ + /// The JID affected by this request. jid: Jid = "jid" => required, + + /// The node affected by this request. node: Option = "node" => optional, + + /// The subscription identifier for this subscription. subid: Option = "subid" => optional, ] ); +/// Main payload used to communicate with a PubSub service. +/// +/// `` #[derive(Debug, Clone)] pub enum PubSub { + /// Request to create a new node, with optional suggested name and suggested configuration. Create { + /// The create request. create: Create, + + /// The configure request for the new node. configure: Option }, + + /// Request to publish items to a node, with optional options. Publish { + /// The publish request. publish: Publish, + + /// The options related to this publish request. publish_options: Option }, + + /// A list of affiliations you have on a service, or on a node. Affiliations(Affiliations), + + /// Request for a default node configuration. Default(Default), + + /// A request for a list of items. Items(Items), + + /// A request to retract some items from a node. Retract(Retract), + + /// A request about a subscription. Subscription(SubscriptionElem), + + /// A request for current subscriptions. Subscriptions(Subscriptions), + + /// An unsubscribe request. Unsubscribe(Unsubscribe), } From f52c28fb35057bc9e8c4eb7570a4dcccda9c1b54 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 15 May 2018 01:47:12 +0200 Subject: [PATCH 0619/1020] jingle, jingle_s5b: Add missing checks for unknown attributes and children. --- src/jingle.rs | 4 ++++ src/jingle_s5b.rs | 1 + 2 files changed, 5 insertions(+) diff --git a/src/jingle.rs b/src/jingle.rs index 8bca605474106efd464f3c61d14b016561cb8d77..8d7d309ece38655ac1c90fd2a3dfd5a9a2a183c1 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -120,6 +120,7 @@ impl TryFrom for Content { fn try_from(elem: Element) -> Result { check_self!(elem, "content", JINGLE); + check_no_unknown_attributes!(elem, "content", ["creator", "disposition", "name", "senders"]); let mut content = Content { creator: get_attr!(elem, "creator", required), @@ -146,6 +147,8 @@ impl TryFrom for Content { return Err(Error::ParseError("Content must not have more than one security.")); } content.security = Some(child.clone()); + } else { + return Err(Error::ParseError("Unknown child in content element.")); } } Ok(content) @@ -341,6 +344,7 @@ impl TryFrom for Jingle { fn try_from(root: Element) -> Result { check_self!(root, "jingle", JINGLE, "Jingle"); + check_no_unknown_attributes!(root, "Jingle", ["action", "initiator", "responder", "sid"]); let mut jingle = Jingle { action: get_attr!(root, "action", required), diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 79949d73005f67d6dc38cbf9fe15d932755f7da9..fe9eaa104aef19039584dc1c6dbf0b6cc535d0ad 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -111,6 +111,7 @@ impl TryFrom for Transport { fn try_from(elem: Element) -> Result { check_self!(elem, "transport", JINGLE_S5B); + check_no_unknown_attributes!(elem, "transport", ["sid", "dstaddr", "mode"]); let sid = get_attr!(elem, "sid", required); let dstaddr = get_attr!(elem, "dstaddr", optional); let mode = get_attr!(elem, "mode", default); From 965f6a1f8396a76a3645432b3d3e0138ad672415 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 15 May 2018 01:55:16 +0200 Subject: [PATCH 0620/1020] ecaps2: Use a macro to generate ECaps2. --- src/ecaps2.rs | 61 +++++++++++++++------------------------------------ src/macros.rs | 3 +++ 2 files changed, 21 insertions(+), 43 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 8fcd0c9e539eb5bb2a7bcb74400dac9a4eb0f515..b96585bcd2d7bee710cab5b634d3473cc9142e4d 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,14 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; use hashes::{Hash, Algo}; -use minidom::Element; -use error::Error; use ns; use base64; @@ -20,35 +16,12 @@ use sha3::{Sha3_256, Sha3_512}; use blake2::Blake2b; use digest::{Digest, VariableOutput}; -#[derive(Debug, Clone)] -pub struct ECaps2 { - hashes: Vec, -} - -impl TryFrom for ECaps2 { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "c", ECAPS2, "ecaps2"); - check_no_attributes!(elem, "ecaps2"); - let mut hashes = vec!(); - for child in elem.children() { - hashes.push(Hash::try_from(child.clone())?); - } - Ok(ECaps2 { - hashes: hashes, - }) - } -} - -impl From for Element { - fn from(ecaps2: ECaps2) -> Element { - Element::builder("c") - .ns(ns::ECAPS2) - .append(ecaps2.hashes) - .build() - } -} +generate_element_with_children!( + ECaps2, "c", ECAPS2, + children: [ + hashes: Vec = ("hash", HASHES) => Hash + ] +); fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); @@ -165,7 +138,9 @@ pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery { #[cfg(test)] mod tests { use super::*; - use ecaps2; + use try_from::TryFrom; + use minidom::Element; + use error::Error; #[test] fn test_parse() { @@ -186,14 +161,14 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "This is not a hash element."); + assert_eq!(message, "Unknown child in c element."); } #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = ecaps2::compute_disco(&disco); + let ecaps2 = compute_disco(&disco); assert_eq!(ecaps2.len(), 54); } @@ -256,13 +231,13 @@ mod tests { 98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111, 100, 31, 30, 28, 28]; let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = ecaps2::compute_disco(&disco); + let ecaps2 = compute_disco(&disco); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); + let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); assert_eq!(sha_256.hash, base64::decode("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=").unwrap()); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); + let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); assert_eq!(sha3_256.hash, base64::decode("79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q=").unwrap()); } @@ -428,19 +403,19 @@ mod tests { 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = ecaps2::compute_disco(&disco); + let ecaps2 = compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); + let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); assert_eq!(sha_256.hash, base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap()); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); + let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); assert_eq!(sha3_256.hash, base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap()); } #[test] fn test_blake2b_512() { - let hash = ecaps2::hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); + let hash = hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); let known_hash: Vec = vec!( 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, diff --git a/src/macros.rs b/src/macros.rs index 20dff441d3c4cb90dbc2904eac674029c2d19a55..d4b6427c82a54549ab02fb670d31a438e1e1c042 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -425,6 +425,9 @@ macro_rules! generate_element_with_text { } macro_rules! generate_element_with_children { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); ); From d9f2af6c97cf790bd3ac689388c55989bf52dc57 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 15 May 2018 02:06:38 +0200 Subject: [PATCH 0621/1020] mam: Use a macro to generate Result_. --- src/macros.rs | 3 +++ src/mam.rs | 52 ++++++++++----------------------------------------- 2 files changed, 13 insertions(+), 42 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index d4b6427c82a54549ab02fb670d31a438e1e1c042..df4b03e0a8817b3dc53b7df88177ca855b210d48 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -543,6 +543,9 @@ macro_rules! generate_element_with_children { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); + ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( $(#[$meta])* #[derive(Debug, Clone)] diff --git a/src/mam.rs b/src/mam.rs index d9f8892902a386b140bbcd4d2b0cebe5dae1ddd9..22052cf3d3f2ee0e807fd37ba9dd6221929c8811 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -25,12 +25,16 @@ pub struct Query { pub set: Option, } -#[derive(Debug, Clone)] -pub struct Result_ { - pub queryid: String, - pub id: String, - pub forwarded: Forwarded, -} +generate_element_with_children!( + Result_, "result", MAM, + attributes: [ + id: String = "id" => required, + queryid: String = "queryid" => required, + ], + child: ( + forwarded: Forwarded = ("forwarded", FORWARD) => Forwarded + ) +); #[derive(Debug, Clone)] pub struct Fin { @@ -74,31 +78,6 @@ impl TryFrom for Query { } } -impl TryFrom for Result_ { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "result", MAM); - check_no_unknown_attributes!(elem, "result", ["queryid", "id"]); - let mut forwarded = None; - for child in elem.children() { - if child.is("forwarded", ns::FORWARD) { - forwarded = Some(Forwarded::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in result element.")); - } - } - let forwarded = forwarded.ok_or(Error::ParseError("Mandatory forwarded element missing in result."))?; - let queryid = get_attr!(elem, "queryid", required); - let id = get_attr!(elem, "id", required); - Ok(Result_ { - queryid, - id, - forwarded, - }) - } -} - impl TryFrom for Fin { type Err = Error; @@ -168,17 +147,6 @@ impl From for Element { } } -impl From for Element { - fn from(result: Result_) -> Element { - Element::builder("result") - .ns(ns::MAM) - .attr("queryid", result.queryid) - .attr("id", result.id) - .append(result.forwarded) - .build() - } -} - impl From for Element { fn from(fin: Fin) -> Element { Element::builder("fin") From d29021b85c9c79185ef12619bb5b886adae407ee Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 May 2018 14:48:29 +0200 Subject: [PATCH 0622/1020] iq: Replace clunky payload enums with proper traits. --- src/iq.rs | 170 ++++-------------------------------------------------- 1 file changed, 10 insertions(+), 160 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index 8726a9d5756b1af4f21a57b88c7729dbf825a660..73395c6f28d9bd444cc93b95379286dab131ff30 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -17,163 +17,15 @@ use error::Error; use ns; use stanza_error::StanzaError; -use roster::Roster; -use disco::{DiscoInfoResult, DiscoInfoQuery}; -use ibb::{Open as IbbOpen, Data as IbbData, Close as IbbClose}; -use jingle::Jingle; -use ping::Ping; -use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs}; -/// Lists every known payload of an ``. -#[derive(Debug, Clone)] -pub enum IqGetPayload { - Roster(Roster), - DiscoInfo(DiscoInfoQuery), - Ping(Ping), - MamQuery(MamQuery), - MamPrefs(MamPrefs), +/// Should be implemented on every known payload of an ``. +pub trait IqGetPayload: TryFrom + Into {} - Unknown(Element), -} +/// Should be implemented on every known payload of an ``. +pub trait IqSetPayload: TryFrom + Into {} -/// Lists every known payload of an ``. -#[derive(Debug, Clone)] -pub enum IqSetPayload { - Roster(Roster), - IbbOpen(IbbOpen), - IbbData(IbbData), - IbbClose(IbbClose), - Jingle(Jingle), - MamQuery(MamQuery), - MamPrefs(MamPrefs), - - Unknown(Element), -} - -/// Lists every known payload of an ``. -#[derive(Debug, Clone)] -pub enum IqResultPayload { - Roster(Roster), - DiscoInfo(DiscoInfoResult), - MamQuery(MamQuery), - MamFin(MamFin), - MamPrefs(MamPrefs), - - Unknown(Element), -} - -impl TryFrom for IqGetPayload { - type Err = Error; - - fn try_from(elem: Element) -> Result { - Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - // RFC-6121 - ("query", ns::ROSTER) => IqGetPayload::Roster(Roster::try_from(elem)?), - - // XEP-0030 - ("query", ns::DISCO_INFO) => IqGetPayload::DiscoInfo(DiscoInfoQuery::try_from(elem)?), - - // XEP-0199 - ("ping", ns::PING) => IqGetPayload::Ping(Ping::try_from(elem)?), - - // XEP-0313 - ("query", ns::MAM) => IqGetPayload::MamQuery(MamQuery::try_from(elem)?), - ("prefs", ns::MAM) => IqGetPayload::MamPrefs(MamPrefs::try_from(elem)?), - - _ => IqGetPayload::Unknown(elem), - }) - } -} - -impl From for Element { - fn from(payload: IqGetPayload) -> Element { - match payload { - IqGetPayload::Roster(roster) => roster.into(), - IqGetPayload::DiscoInfo(disco) => disco.into(), - IqGetPayload::Ping(ping) => ping.into(), - IqGetPayload::MamQuery(query) => query.into(), - IqGetPayload::MamPrefs(prefs) => prefs.into(), - - IqGetPayload::Unknown(elem) => elem, - } - } -} - -impl TryFrom for IqSetPayload { - type Err = Error; - - fn try_from(elem: Element) -> Result { - Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - // RFC-6121 - ("query", ns::ROSTER) => IqSetPayload::Roster(Roster::try_from(elem)?), - - // XEP-0047 - ("open", ns::IBB) => IqSetPayload::IbbOpen(IbbOpen::try_from(elem)?), - ("data", ns::IBB) => IqSetPayload::IbbData(IbbData::try_from(elem)?), - ("close", ns::IBB) => IqSetPayload::IbbClose(IbbClose::try_from(elem)?), - - // XEP-0166 - ("jingle", ns::JINGLE) => IqSetPayload::Jingle(Jingle::try_from(elem)?), - - // XEP-0313 - ("query", ns::MAM) => IqSetPayload::MamQuery(MamQuery::try_from(elem)?), - ("prefs", ns::MAM) => IqSetPayload::MamPrefs(MamPrefs::try_from(elem)?), - - _ => IqSetPayload::Unknown(elem), - }) - } -} - -impl From for Element { - fn from(payload: IqSetPayload) -> Element { - match payload { - IqSetPayload::Roster(roster) => roster.into(), - IqSetPayload::IbbOpen(open) => open.into(), - IqSetPayload::IbbData(data) => data.into(), - IqSetPayload::IbbClose(close) => close.into(), - IqSetPayload::Jingle(jingle) => jingle.into(), - IqSetPayload::MamQuery(query) => query.into(), - IqSetPayload::MamPrefs(prefs) => prefs.into(), - - IqSetPayload::Unknown(elem) => elem, - } - } -} - -impl TryFrom for IqResultPayload { - type Err = Error; - - fn try_from(elem: Element) -> Result { - Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - // RFC-6121 - ("query", ns::ROSTER) => IqResultPayload::Roster(Roster::try_from(elem)?), - - // XEP-0030 - ("query", ns::DISCO_INFO) => IqResultPayload::DiscoInfo(DiscoInfoResult::try_from(elem)?), - - // XEP-0313 - ("query", ns::MAM) => IqResultPayload::MamQuery(MamQuery::try_from(elem)?), - ("fin", ns::MAM) => IqResultPayload::MamFin(MamFin::try_from(elem)?), - ("prefs", ns::MAM) => IqResultPayload::MamPrefs(MamPrefs::try_from(elem)?), - - _ => IqResultPayload::Unknown(elem), - }) - } -} - -impl From for Element { - fn from(payload: IqResultPayload) -> Element { - match payload { - IqResultPayload::Roster(roster) => roster.into(), - IqResultPayload::DiscoInfo(disco) => disco.into(), - IqResultPayload::MamQuery(query) => query.into(), - IqResultPayload::MamFin(fin) => fin.into(), - IqResultPayload::MamPrefs(prefs) => prefs.into(), - - IqResultPayload::Unknown(elem) => elem, - } - } -} +/// Should be implemented on every known payload of an ``. +pub trait IqResultPayload: TryFrom + Into {} #[derive(Debug, Clone)] pub enum IqType { @@ -296,6 +148,7 @@ mod tests { use super::*; use stanza_error::{ErrorType, DefinedCondition}; use compare_elements::NamespaceAwareCompare; + use disco::DiscoInfoQuery; #[test] fn test_require_type() { @@ -459,13 +312,10 @@ mod tests { #[cfg(feature = "component")] let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); - let payload = match iq.payload { - IqType::Get(payload) => IqGetPayload::try_from(payload).unwrap(), + let disco_info = match iq.payload { + IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(), _ => panic!(), }; - assert!(match payload { - IqGetPayload::DiscoInfo(DiscoInfoQuery { .. }) => true, - _ => false, - }); + assert!(disco_info.node.is_none()); } } From d5f88d26369ea633286f228c922d7f219c9ffedd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 May 2018 14:49:00 +0200 Subject: [PATCH 0623/1020] iq: Add helper constructors. --- src/iq.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/iq.rs b/src/iq.rs index 73395c6f28d9bd444cc93b95379286dab131ff30..e695dc3e9a50e0a48a363180923d1e4232d6c260 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -55,6 +55,59 @@ pub struct Iq { pub payload: IqType, } +impl Iq { + pub fn from_get(payload: impl IqGetPayload) -> Iq { + Iq { + from: None, + to: None, + id: None, + payload: IqType::Get(payload.into()), + } + } + + pub fn from_set(payload: impl IqSetPayload) -> Iq { + Iq { + from: None, + to: None, + id: None, + payload: IqType::Set(payload.into()), + } + } + + pub fn from_result(payload: Option) -> Iq { + Iq { + from: None, + to: None, + id: None, + payload: IqType::Result(payload.map(|payload| payload.into())), + } + } + + pub fn from_error(payload: StanzaError) -> Iq { + Iq { + from: None, + to: None, + id: None, + payload: IqType::Error(payload), + } + } + + pub fn with_to(mut self, to: Jid) -> Iq { + self.to = Some(to); + self + } + + pub fn with_from(mut self, from: Jid) -> Iq { + self.from = Some(from); + self + } + + pub fn with_id(mut self, id: String) -> Iq { + self.id = Some(id); + self + } +} + impl TryFrom for Iq { type Err = Error; From 412eafb363d056a198d85b3bfeaf16ba8645f7cd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 May 2018 15:08:17 +0200 Subject: [PATCH 0624/1020] iq: Implement the Iq*Payload traits on every possible payload. --- src/bind.rs | 4 ++++ src/blocking.rs | 7 +++++++ src/disco.rs | 9 +++++++++ src/ibb.rs | 7 +++++++ src/ibr.rs | 5 +++++ src/jingle.rs | 3 +++ src/mam.rs | 11 +++++++++++ src/ping.rs | 4 ++++ src/pubsub/pubsub.rs | 5 +++++ src/roster.rs | 5 +++++ src/version.rs | 4 ++++ 11 files changed, 64 insertions(+) diff --git a/src/bind.rs b/src/bind.rs index 4597636146584e53bc80c551e512eb20ced877f2..97a75aa3e750514a60fc8a43b415fbacad36b3f6 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -12,6 +12,7 @@ use minidom::Element; use error::Error; use jid::Jid; use ns; +use iq::{IqSetPayload, IqResultPayload}; #[derive(Debug, Clone, PartialEq)] pub enum Bind { @@ -29,6 +30,9 @@ impl Bind { } } +impl IqSetPayload for Bind {} +impl IqResultPayload for Bind {} + impl TryFrom for Bind { type Err = Error; diff --git a/src/blocking.rs b/src/blocking.rs index b24c9e49d82dea86edc29cdc7cd330f584bf2a5b..11287ba7b41b524e5abfdcd1930c85d2aa365028 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -12,9 +12,12 @@ use minidom::Element; use error::Error; use ns; +use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; generate_empty_element!(BlocklistRequest, "blocklist", BLOCKING); +impl IqGetPayload for BlocklistRequest {} + macro_rules! generate_blocking_element { ($elem:ident, $name:tt) => ( #[derive(Debug, Clone)] @@ -59,6 +62,10 @@ generate_blocking_element!(BlocklistResult, "blocklist"); generate_blocking_element!(Block, "block"); generate_blocking_element!(Unblock, "unblock"); +impl IqResultPayload for BlocklistResult {} +impl IqSetPayload for Block {} +impl IqSetPayload for Unblock {} + generate_empty_element!(Blocked, "blocked", BLOCKING_ERRORS); #[cfg(test)] diff --git a/src/disco.rs b/src/disco.rs index 136a50ad27a995f2773e84cddb7ead3c28e7b9d3..ea49476cf6b0dd210714d53919ea16f35ea28a9d 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -14,6 +14,7 @@ use jid::Jid; use error::Error; use ns; +use iq::{IqGetPayload, IqResultPayload}; use data_forms::{DataForm, DataFormType}; generate_element_with_only_attributes!( @@ -26,6 +27,8 @@ DiscoInfoQuery, "query", DISCO_INFO, [ node: Option = "node" => optional, ]); +impl IqGetPayload for DiscoItemsQuery {} + generate_element_with_only_attributes!( /// Structure representing a `` element. #[derive(PartialEq)] @@ -108,6 +111,8 @@ pub struct DiscoInfoResult { pub extensions: Vec, } +impl IqResultPayload for DiscoInfoResult {} + impl TryFrom for DiscoInfoResult { type Err = Error; @@ -190,6 +195,8 @@ DiscoItemsQuery, "query", DISCO_ITEMS, [ node: Option = "node" => optional, ]); +impl IqGetPayload for DiscoInfoResult {} + generate_element_with_only_attributes!( /// Structure representing an `` element. Item, "item", DISCO_ITEMS, [ @@ -218,6 +225,8 @@ generate_element_with_children!( ] ); +impl IqResultPayload for DiscoItemsResult {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ibb.rs b/src/ibb.rs index 9c2cdccc5f3ea96394acd860baa825bd45748a84..e33ca3bf7c33321c10d35a06165c86fa4251d4c5 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use helpers::Base64; +use iq::IqSetPayload; generate_attribute!(Stanza, "stanza", { Iq => "iq", @@ -17,6 +18,8 @@ generate_element_with_only_attributes!(Open, "open", IBB, [ stanza: Stanza = "stanza" => default, ]); +impl IqSetPayload for Open {} + generate_element_with_text!(Data, "data", IBB, [ seq: u16 = "seq" => required, @@ -25,10 +28,14 @@ generate_element_with_text!(Data, "data", IBB, data: Base64> ); +impl IqSetPayload for Data {} + generate_element_with_only_attributes!(Close, "close", IBB, [ sid: String = "sid" => required, ]); +impl IqSetPayload for Close {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ibr.rs b/src/ibr.rs index 99cbcd57ba831381180551ab682691ee3f272d06..72c02b2b9c2d388418b6f0b122044a229869105f 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -11,6 +11,7 @@ use minidom::Element; use error::Error; +use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use data_forms::DataForm; use ns; @@ -25,6 +26,10 @@ pub struct Query { //pub oob: Option, } +impl IqGetPayload for Query; +impl IqSetPayload for Query; +impl IqResultPayload for Query; + impl TryFrom for Query { type Err = Error; diff --git a/src/jingle.rs b/src/jingle.rs index 8d7d309ece38655ac1c90fd2a3dfd5a9a2a183c1..b64b0e7cccc0d73597b7f44d98ddb4f94e729cef 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -12,6 +12,7 @@ use jid::Jid; use error::Error; use ns; +use iq::IqSetPayload; generate_attribute!(Action, "action", { ContentAccept => "content-accept", @@ -305,6 +306,8 @@ pub struct Jingle { pub other: Vec, } +impl IqSetPayload for Jingle {} + impl Jingle { pub fn new(action: Action, sid: SessionId) -> Jingle { Jingle { diff --git a/src/mam.rs b/src/mam.rs index 22052cf3d3f2ee0e807fd37ba9dd6221929c8811..664947ee49a2a1f72ab447d12bfedddd194d7c8b 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -11,6 +11,7 @@ use jid::Jid; use error::Error; +use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use data_forms::DataForm; use rsm::Set; use forwarding::Forwarded; @@ -25,6 +26,10 @@ pub struct Query { pub set: Option, } +impl IqGetPayload for Query {} +impl IqSetPayload for Query {} +impl IqResultPayload for Query {} + generate_element_with_children!( Result_, "result", MAM, attributes: [ @@ -42,6 +47,8 @@ pub struct Fin { pub set: Set, } +impl IqResultPayload for Fin {} + generate_attribute!(DefaultPrefs, "default", { Always => "always", Never => "never", @@ -55,6 +62,10 @@ pub struct Prefs { pub never: Vec, } +impl IqGetPayload for Prefs {} +impl IqSetPayload for Prefs {} +impl IqResultPayload for Prefs {} + impl TryFrom for Query { type Err = Error; diff --git a/src/ping.rs b/src/ping.rs index 8159ffe9384d2cfe5fb2d1af51b324c6dd935b48..c2f0a4409ca90e0b619bdf699a8acb6045373240 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,8 +5,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use iq::IqGetPayload; + generate_empty_element!(Ping, "ping", PING); +impl IqGetPayload for Ping {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index d90ca9826ba9831ee20155a05e16780aaee05b35..569a954a7fe7754d2de345407f29e9921d98a90e 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -15,6 +15,7 @@ use error::Error; use ns; +use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use data_forms::DataForm; use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; @@ -364,6 +365,10 @@ pub enum PubSub { Unsubscribe(Unsubscribe), } +impl IqGetPayload for PubSub {} +impl IqSetPayload for PubSub {} +impl IqResultPayload for PubSub {} + impl TryFrom for PubSub { type Err = Error; diff --git a/src/roster.rs b/src/roster.rs index 2b62f8088c37c989465214484affb378392ac3b6..e851c485874890d7b242dacaa338a70ca61807b7 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; +use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; generate_elem_id!(Group, "group", ROSTER); @@ -54,6 +55,10 @@ generate_element_with_children!( ] ); +impl IqGetPayload for Roster {} +impl IqSetPayload for Roster {} +impl IqResultPayload for Roster {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/version.rs b/src/version.rs index e01a40d308b8711690935153838e36763062d395..49b23ad93c3b2192d1fa04e8a00baed1d3028b82 100644 --- a/src/version.rs +++ b/src/version.rs @@ -8,6 +8,7 @@ use try_from::TryFrom; use minidom::Element; use error::Error; use ns; +use iq::{IqGetPayload, IqResultPayload}; #[derive(Debug, Clone)] pub struct Version { @@ -16,6 +17,9 @@ pub struct Version { pub os: Option, } +impl IqGetPayload for Version {} +impl IqResultPayload for Version {} + impl TryFrom for Version { type Err = Error; From 0bec19c224edf5a9452d6b4e51fb9c403804cfe9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 May 2018 15:16:15 +0200 Subject: [PATCH 0625/1020] ibr: Fix syntax error. --- src/ibr.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ibr.rs b/src/ibr.rs index 72c02b2b9c2d388418b6f0b122044a229869105f..6964755c822c8ac876c634276db6eff59130e8b6 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -26,9 +26,9 @@ pub struct Query { //pub oob: Option, } -impl IqGetPayload for Query; -impl IqSetPayload for Query; -impl IqResultPayload for Query; +impl IqGetPayload for Query {} +impl IqSetPayload for Query {} +impl IqResultPayload for Query {} impl TryFrom for Query { type Err = Error; From 9bd6fe002d30f6bce89ac10da810619db7ccf3ce Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 17 May 2018 19:24:51 +0200 Subject: [PATCH 0626/1020] disco: Implement IqGetPayload on the correct structs. --- src/disco.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index ea49476cf6b0dd210714d53919ea16f35ea28a9d..4c70edd2f87422cb348b1ee6771eaf8bf77bae45 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -27,7 +27,7 @@ DiscoInfoQuery, "query", DISCO_INFO, [ node: Option = "node" => optional, ]); -impl IqGetPayload for DiscoItemsQuery {} +impl IqGetPayload for DiscoInfoQuery {} generate_element_with_only_attributes!( /// Structure representing a `` element. @@ -195,7 +195,7 @@ DiscoItemsQuery, "query", DISCO_ITEMS, [ node: Option = "node" => optional, ]); -impl IqGetPayload for DiscoInfoResult {} +impl IqGetPayload for DiscoItemsQuery {} generate_element_with_only_attributes!( /// Structure representing an `` element. From c828f938398ac42822e86201d91c7bf563cb18c8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 18 May 2018 19:04:02 +0200 Subject: [PATCH 0627/1020] Add a Stream Management implementation. --- src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/sm.rs | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/sm.rs diff --git a/src/lib.rs b/src/lib.rs index a0b36c34e4d46b974328e375649777593981f02c..220a087f02ea725b1ee9a9f276a8cc4a34be5320 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,6 +119,9 @@ pub mod receipts; /// XEP-0191: Blocking Command pub mod blocking; +/// XEP-0198: Stream Management +pub mod sm; + /// XEP-0199: XMPP Ping pub mod ping; diff --git a/src/ns.rs b/src/ns.rs index 1eb6e3015f45eb47c31cdc08cb6a26487090b28a..e5f79fb5e374a180cb29e184edbd0f79faebab44 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -85,6 +85,9 @@ pub const BLOCKING: &str = "urn:xmpp:blocking"; /// XEP-0191: Blocking Command pub const BLOCKING_ERRORS: &str = "urn:xmpp:blocking:errors"; +/// XEP-0198: Stream Management +pub const SM: &str = "urn:xmpp:sm:3"; + /// XEP-0199: XMPP Ping pub const PING: &str = "urn:xmpp:ping"; diff --git a/src/sm.rs b/src/sm.rs new file mode 100644 index 0000000000000000000000000000000000000000..994c8da7fde17c76276b10ac48f6cd696df1f8c9 --- /dev/null +++ b/src/sm.rs @@ -0,0 +1,113 @@ +// Copyright (c) 2018 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use stanza_error::DefinedCondition; + +generate_element_with_only_attributes!( + A, "a", SM, [ + h: u32 = "h" => required, + ] +); + +impl A { + pub fn new(h: u32) -> A { + A { h } + } +} + +generate_attribute!(ResumeAttr, "resume", bool); + +generate_element_with_only_attributes!( + Enable, "enable", SM, [ + // TODO: should be the infinite integer set ≥ 1. + max: Option = "max" => optional, + resume: ResumeAttr = "resume" => default, + ] +); + +impl Enable { + pub fn new() -> Self { + Enable { + max: None, + resume: ResumeAttr::False, + } + } + + pub fn with_max(mut self, max: u32) -> Self { + self.max = Some(max); + self + } + + pub fn with_resume(mut self) -> Self { + self.resume = ResumeAttr::True; + self + } +} + +generate_element_with_only_attributes!( + Enabled, "enabled", SM, [ + id: Option = "id" => optional, + location: Option = "location" => optional, + // TODO: should be the infinite integer set ≥ 1. + max: Option = "max" => optional, + resume: ResumeAttr = "resume" => default, + ] +); + +generate_element_with_children!( + Failed, "failed", SM, + attributes: [ + h: Option = "h" => optional, + ], + child: ( + // XXX: implement the * handling. + error: Option = ("*", XMPP_STANZAS) => DefinedCondition + ) +); + +generate_empty_element!( + R, "r", SM +); + +generate_element_with_only_attributes!( + Resume, "resume", SM, [ + h: u32 = "h" => required, + previd: String = "previd" => required, + ] +); + +generate_element_with_only_attributes!( + Resumed, "resumed", SM, [ + h: u32 = "h" => required, + previd: String = "previd" => required, + ] +); + +// TODO: add support for optional and required. +generate_empty_element!( + /// Represents availability of Stream Management in ``. + StreamManagement, "sm", SM +); + +#[cfg(test)] +mod tests { + use super::*; + use try_from::TryFrom; + use minidom::Element; + + #[test] + fn a() { + let elem: Element = " Date: Mon, 28 May 2018 16:23:23 +0200 Subject: [PATCH 0628/1020] version: Add a serialisation test. --- src/version.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/version.rs b/src/version.rs index 49b23ad93c3b2192d1fa04e8a00baed1d3028b82..cb8773369a6f4ef41d69cca186b36859e5ddff90 100644 --- a/src/version.rs +++ b/src/version.rs @@ -82,6 +82,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -91,4 +92,17 @@ mod tests { assert_eq!(version.version, String::from("0.3.0")); assert_eq!(version.os, None); } + + #[test] + fn serialisation() { + let version = Version { + name: String::from("xmpp-rs"), + version: String::from("0.3.0"), + os: None, + }; + let elem1 = Element::from(version); + let elem2: Element = "xmpp-rs0.3.0".parse().unwrap(); + println!("{:?}", elem1); + assert!(elem1.compare_to(&elem2)); + } } From cbef3f6e8d281218e7da4d6e52ca3a2e103e9fa7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:24:17 +0200 Subject: [PATCH 0629/1020] muc: Add a serialisation test. --- src/muc/muc.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 4a6fda401064f946dcba19802e8bd63007f25d24..5132adbf6c1901b758680f9ae9ab343b921dd6c5 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -52,6 +52,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_muc_simple() { @@ -98,7 +99,11 @@ mod tests { coucou " .parse().unwrap(); + let elem1 = elem.clone(); let muc = Muc::try_from(elem).unwrap(); assert_eq!(muc.password, Some("coucou".to_owned())); + + let elem2 = Element::from(muc); + assert!(elem1.compare_to(&elem2)); } } From a625b88fce4e1eaeb9609a6f69393554ea1692f7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:29:51 +0200 Subject: [PATCH 0630/1020] macros: Merge all element children into a common syntax. --- src/jingle.rs | 79 +++-------------- src/macros.rs | 207 +++++++++++++++++++------------------------ src/mam.rs | 112 ++++++----------------- src/muc/muc.rs | 51 ++--------- src/muc/user.rs | 142 ++++++++--------------------- src/pubsub/pubsub.rs | 16 ++-- src/sm.rs | 4 +- src/version.rs | 79 +++-------------- 8 files changed, 200 insertions(+), 490 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index b64b0e7cccc0d73597b7f44d98ddb4f94e729cef..951a2fa1d72a570f9baa8f08c4029b1adb1ebe3f 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -66,16 +66,20 @@ generate_attribute!(Disposition, "disposition", { generate_id!(ContentId); -#[derive(Debug, Clone)] -pub struct Content { - pub creator: Creator, - pub disposition: Disposition, - pub name: ContentId, - pub senders: Senders, - pub description: Option, - pub transport: Option, - pub security: Option, -} +generate_element_with_children!( + Content, "content", JINGLE, + attributes: [ + creator: Creator = "creator" => required, + disposition: Disposition = "disposition" => default, + name: ContentId = "name" => required, + senders: Senders = "senders" => default + ], + children: [ + description: Option = ("description", JINGLE) => Element, + transport: Option = ("transport", JINGLE) => Element, + security: Option = ("security", JINGLE) => Element + ] +); impl Content { pub fn new(creator: Creator, name: ContentId) -> Content { @@ -116,61 +120,6 @@ impl Content { } } -impl TryFrom for Content { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "content", JINGLE); - check_no_unknown_attributes!(elem, "content", ["creator", "disposition", "name", "senders"]); - - let mut content = Content { - creator: get_attr!(elem, "creator", required), - disposition: get_attr!(elem, "disposition", default), - name: get_attr!(elem, "name", required), - senders: get_attr!(elem, "senders", default), - description: None, - transport: None, - security: None, - }; - for child in elem.children() { - if child.name() == "description" { - if content.description.is_some() { - return Err(Error::ParseError("Content must not have more than one description.")); - } - content.description = Some(child.clone()); - } else if child.name() == "transport" { - if content.transport.is_some() { - return Err(Error::ParseError("Content must not have more than one transport.")); - } - content.transport = Some(child.clone()); - } else if child.name() == "security" { - if content.security.is_some() { - return Err(Error::ParseError("Content must not have more than one security.")); - } - content.security = Some(child.clone()); - } else { - return Err(Error::ParseError("Unknown child in content element.")); - } - } - Ok(content) - } -} - -impl From for Element { - fn from(content: Content) -> Element { - Element::builder("content") - .ns(ns::JINGLE) - .attr("creator", content.creator) - .attr("disposition", content.disposition) - .attr("name", content.name) - .attr("senders", content.senders) - .append(content.description) - .append(content.transport) - .append(content.security) - .build() - } -} - #[derive(Debug, Clone, PartialEq)] pub enum Reason { AlternativeSession, //(String), diff --git a/src/macros.rs b/src/macros.rs index df4b03e0a8817b3dc53b7df88177ca855b210d48..9adae5197760ab4c196add8d387f35e26ae60085 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -424,129 +424,96 @@ macro_rules! generate_element_with_text { ); } -macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); +macro_rules! start_decl { + (Vec, $type:ty) => ( + Vec<$type> ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + (Option, $type:ty) => ( + Option<$type> ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem { - $( - $(#[$attr_meta])* - pub $attr: $attr_type, - )* - $( - $(#[$child_meta])* - pub $child_ident: Vec<$child_type>, - )* - } - - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { - check_self!(elem, $name, $ns); - check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - let mut parsed_children = vec!(); - for child in elem.children() { - $( - if child.is($child_name, ::ns::$child_ns) { - let parsed_child = $child_constructor::try_from(child.clone())?; - parsed_children.push(parsed_child); - continue; - } - )* - return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); - } - Ok($elem { - $( - $attr: get_attr!(elem, $attr_name, $attr_action), - )* - $( - $child_ident: parsed_children, - )* - }) - } - } + (Required, $type:ty) => ( + $type + ); +} - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) - .ns(::ns::$ns) - $( - .attr($attr_name, elem.$attr) - )* - $( - .append(elem.$child_ident) - )* - .build() - } - } +macro_rules! start_parse_elem { + ($temp:ident: Vec) => ( + let mut $temp = Vec::new(); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor)); + ($temp:ident: Option) => ( + let mut $temp = None; ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor)); + ($temp:ident: Required) => ( + let mut $temp = None; ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem { - $( - $(#[$attr_meta])* - pub $attr: $attr_type, - )* - $(#[$child_meta])* - pub $child_ident: Option<$child_type>, - } +} - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; +macro_rules! do_parse { + ($elem:ident, Element) => ( + $elem.clone() + ); + ($elem:ident, String) => ( + $elem.text() + ); + ($elem:ident, $constructor:ident) => ( + $constructor::try_from($elem.clone())? + ); +} - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { - check_self!(elem, $name, $ns); - check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - let mut parsed_child = None; - for child in elem.children() { - if child.is($child_name, ::ns::$child_ns) { - parsed_child = Some($child_constructor::try_from(child.clone())?); - continue; - } - return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); - } - Ok($elem { - $( - $attr: get_attr!(elem, $attr_name, $attr_action), - )* - $child_ident: parsed_child, - }) - } +macro_rules! do_parse_elem { + ($temp:ident: Vec = $constructor:ident => $elem:ident) => ( + $temp.push(do_parse!($elem, $constructor)); + ); + ($temp:ident: Option = $constructor:ident => $elem:ident) => ( + if $temp.is_some() { + return Err(::error::Error::ParseError(concat!("coucou", " must not have more than one ", "coucou", "."))); } + $temp = Some(do_parse!($elem, $constructor)); + ); + ($temp:ident: Required = $constructor:ident => $elem:ident) => ( + $temp = Some(do_parse!($elem, $constructor)); + ); +} - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) - .ns(::ns::$ns) - $( - .attr($attr_name, elem.$attr) - )* - .append(elem.$child_ident) - .build() - } - } +macro_rules! finish_parse_elem { + ($temp:ident: Vec = $name:tt) => ( + $temp + ); + ($temp:ident: Option = $name:tt) => ( + $temp + ); + ($temp:ident: Required = $name:tt) => ( + $temp.ok_or(::error::Error::ParseError(concat!("Missing child coucou in ", $name, " element.")))? + ); +} + +macro_rules! generate_serialiser { + ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => ( + ::minidom::Element::builder($name) + .ns(::ns::$ns) + .append($parent.$elem) + .build() ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); + ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => ( + $parent.$elem.map(|elem| + ::minidom::Element::builder($name) + .ns(::ns::$ns) + .append(elem) + .build()) ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor)); + ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => ( + $parent.$elem ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => ( +} + +macro_rules! generate_element_with_children { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -554,8 +521,10 @@ macro_rules! generate_element_with_children { $(#[$attr_meta])* pub $attr: $attr_type, )* + $( $(#[$child_meta])* - pub $child_ident: $child_type, + pub $child_ident: start_decl!($coucou, $child_type), + )* } impl ::try_from::TryFrom<::minidom::Element> for $elem { @@ -564,19 +533,25 @@ macro_rules! generate_element_with_children { fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - let mut parsed_child = None; + $( + start_parse_elem!($child_ident: $coucou); + )* for child in elem.children() { + $( if child.is($child_name, ::ns::$child_ns) { - parsed_child = Some($child_constructor::try_from(child.clone())?); + do_parse_elem!($child_ident: $coucou = $child_constructor => child); continue; } + )* return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( $attr: get_attr!(elem, $attr_name, $attr_action), )* - $child_ident: parsed_child.ok_or(::error::Error::ParseError(concat!("Missing child ", $child_name, " in ", $name, " element.")))?, + $( + $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name), + )* }) } } @@ -588,7 +563,9 @@ macro_rules! generate_element_with_children { $( .attr($attr_name, elem.$attr) )* - .append(elem.$child_ident) + $( + .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns))) + )* .build() } } diff --git a/src/mam.rs b/src/mam.rs index 664947ee49a2a1f72ab447d12bfedddd194d7c8b..b835fc505e688c56924979f2ff7f8656d63420b3 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -18,13 +18,17 @@ use forwarding::Forwarded; use ns; -#[derive(Debug, Clone)] -pub struct Query { - pub queryid: Option, - pub node: Option, - pub form: Option, - pub set: Option, -} +generate_element_with_children!( + Query, "query", MAM, + attributes: [ + queryid: Option = "queryid" => optional, + node: Option = "node" => optional + ], + children: [ + form: Option = ("x", DATA_FORMS) => DataForm, + set: Option = ("set", RSM) => Set + ] +); impl IqGetPayload for Query {} impl IqSetPayload for Query {} @@ -36,16 +40,24 @@ generate_element_with_children!( id: String = "id" => required, queryid: String = "queryid" => required, ], - child: ( - forwarded: Forwarded = ("forwarded", FORWARD) => Forwarded - ) + children: [ + forwarded: Required = ("forwarded", FORWARD) => Forwarded + ] ); -#[derive(Debug, Clone)] -pub struct Fin { - pub complete: bool, - pub set: Set, -} +generate_attribute!( + Complete, "complete", bool +); + +generate_element_with_children!( + Fin, "fin", MAM, + attributes: [ + complete: Complete = "complete" => default + ], + children: [ + set: Required = ("set", RSM) => Set + ] +); impl IqResultPayload for Fin {} @@ -66,54 +78,6 @@ impl IqGetPayload for Prefs {} impl IqSetPayload for Prefs {} impl IqResultPayload for Prefs {} -impl TryFrom for Query { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", MAM); - check_no_unknown_attributes!(elem, "query", ["queryid", "node"]); - let mut form = None; - let mut set = None; - for child in elem.children() { - if child.is("x", ns::DATA_FORMS) { - form = Some(DataForm::try_from(child.clone())?); - } else if child.is("set", ns::RSM) { - set = Some(Set::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in query element.")); - } - } - let queryid = get_attr!(elem, "queryid", optional); - let node = get_attr!(elem, "node", optional); - Ok(Query { queryid, node, form, set }) - } -} - -impl TryFrom for Fin { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "fin", MAM); - check_no_unknown_attributes!(elem, "fin", ["complete"]); - let mut set = None; - for child in elem.children() { - if child.is("set", ns::RSM) { - set = Some(Set::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in fin element.")); - } - } - let set = set.ok_or(Error::ParseError("Mandatory set element missing in fin."))?; - let complete = match elem.attr("complete") { - Some(complete) if complete == "true" => true, - Some(complete) if complete == "false" => false, - None => false, - Some(_) => return Err(Error::ParseError("Invalid value for 'complete' attribute.")), - }; - Ok(Fin { complete, set }) - } -} - impl TryFrom for Prefs { type Err = Error; @@ -146,28 +110,6 @@ impl TryFrom for Prefs { } } -impl From for Element { - fn from(query: Query) -> Element { - Element::builder("query") - .ns(ns::MAM) - .attr("queryid", query.queryid) - .attr("node", query.node) - //.append(query.form) - .append(query.set) - .build() - } -} - -impl From for Element { - fn from(fin: Fin) -> Element { - Element::builder("fin") - .ns(ns::MAM) - .attr("complete", if fin.complete { Some("true") } else { None }) - .append(fin.set) - .build() - } -} - fn serialise_jid_list(name: &str, jids: Vec) -> Option { if jids.is_empty() { None diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 5132adbf6c1901b758680f9ae9ab343b921dd6c5..80905a20679928d1cf29f6a171b8fd88ee919a2c 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,53 +5,18 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; - -use error::Error; - -use ns; - -#[derive(Debug, Clone)] -pub struct Muc { - pub password: Option, -} - -impl TryFrom for Muc { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "x", MUC); - check_no_attributes!(elem, "x"); - - let mut password = None; - for child in elem.children() { - if child.is("password", ns::MUC) { - password = Some(child.text()); - } else { - return Err(Error::ParseError("Unknown child in x element.")); - } - } - - Ok(Muc { - password: password, - }) - } -} - -impl From for Element { - fn from(muc: Muc) -> Element { - Element::builder("x") - .ns(ns::MUC) - .append(muc.password) - .build() - } -} +generate_element_with_children!( + Muc, "x", MUC, children: [ + password: Option = ("password", MUC) => String + ] +); #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; + use minidom::Element; + use error::Error; use compare_elements::NamespaceAwareCompare; #[test] diff --git a/src/muc/user.rs b/src/muc/user.rs index f53799cdfc7370e9c5147a157a25b9c1d825ba73..69060cef781823e0a1457d09cca79ec291a32b57 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::{TryFrom, TryInto}; +use try_from::TryFrom; use minidom::Element; @@ -80,6 +80,7 @@ Status, "status", MUC_USER, "code", { /// Optional element used in elements inside presence stanzas of type /// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking /// purposes. -- CHANGELOG 0.17 (2002-10-23) +/// /// Possesses a 'jid' and a 'nick' attribute, so that an action can be attributed either to a real /// JID or to a roomnick. -- CHANGELOG 1.25 (2012-02-08) #[derive(Debug, Clone, PartialEq)] @@ -140,108 +141,25 @@ generate_attribute!(Role, "role", { None => "none", }, Default = None); -#[derive(Debug, Clone)] -pub struct Item { - pub affiliation: Affiliation, - pub jid: Option, - pub nick: Option, - pub role: Role, - pub actor: Option, - pub continue_: Option, - pub reason: Option, -} - -impl TryFrom for Item { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "item", MUC_USER); - check_no_unknown_attributes!(elem, "item", ["affiliation", "jid", "nick", "role"]); - let mut actor: Option = None; - let mut continue_: Option = None; - let mut reason: Option = None; - for child in elem.children() { - if child.is("actor", ns::MUC_USER) { - actor = Some(child.clone().try_into()?); - } else if child.is("continue", ns::MUC_USER) { - continue_ = Some(child.clone().try_into()?); - } else if child.is("reason", ns::MUC_USER) { - reason = Some(child.clone().try_into()?); - } else { - return Err(Error::ParseError("Unknown child in item element.")); - } - } - - let affiliation: Affiliation = get_attr!(elem, "affiliation", required); - let jid: Option = get_attr!(elem, "jid", optional); - let nick: Option = get_attr!(elem, "nick", optional); - let role: Role = get_attr!(elem, "role", required); - - Ok(Item{ - affiliation: affiliation, - jid: jid, - nick: nick, - role: role, - actor: actor, - continue_: continue_, - reason: reason, - }) - } -} - -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::MUC_USER) - .attr("affiliation", item.affiliation) - .attr("jid", item.jid) - .attr("nick", item.nick) - .attr("role", item.role) - .append(item.actor) - .append(item.continue_) - .append(item.reason) - .build() - } -} - -#[derive(Debug, Clone)] -pub struct MucUser { - pub status: Vec, - pub items: Vec, -} - -impl TryFrom for MucUser { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "x", MUC_USER); - check_no_attributes!(elem, "x"); - let mut status = vec!(); - let mut items = vec!(); - for child in elem.children() { - if child.is("status", ns::MUC_USER) { - status.push(Status::try_from(child.clone())?); - } else if child.is("item", ns::MUC_USER) { - items.push(Item::try_from(child.clone())?); - } else { - return Err(Error::ParseError("Unknown child in x element.")); - } - } - Ok(MucUser { - status, - items, - }) - } -} - -impl From for Element { - fn from(muc_user: MucUser) -> Element { - Element::builder("x") - .ns(ns::MUC_USER) - .append(muc_user.status) - .build() - } -} +generate_element_with_children!( + Item, "item", MUC_USER, attributes: [ + affiliation: Affiliation = "affiliation" => required, + jid: Option = "jid" => optional, + nick: Option = "nick" => optional, + role: Role = "role" => required + ], children: [ + actor: Option = ("actor", MUC_USER) => Actor, + continue_: Option = ("continue", MUC_USER) => Continue, + reason: Option = ("reason", MUC_USER) => Reason + ] +); + +generate_element_with_children!( + MucUser, "x", MUC_USER, children: [ + status: Vec = ("status", MUC_USER) => Status, + items: Vec = ("item", MUC_USER) => Item + ] +); #[cfg(test)] mod tests { @@ -257,6 +175,24 @@ mod tests { MucUser::try_from(elem).unwrap(); } + #[test] + fn statuses_and_items() { + let elem: Element = " + + + + + + ".parse().unwrap(); + let muc_user = MucUser::try_from(elem).unwrap(); + assert_eq!(muc_user.status.len(), 2); + assert_eq!(muc_user.status[0], Status::AffiliationChange); + assert_eq!(muc_user.status[1], Status::ConfigShowsUnavailableMembers); + assert_eq!(muc_user.items.len(), 1); + assert_eq!(muc_user.items[0].affiliation, Affiliation::Member); + assert_eq!(muc_user.items[0].role, Role::Moderator); + } + #[test] fn test_invalid_child() { let elem: Element = " diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 569a954a7fe7754d2de345407f29e9921d98a90e..840232b0f2c15c804cd343e77ac0b0a4af3b754e 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -72,10 +72,10 @@ generate_element_with_only_attributes!( generate_element_with_children!( /// Request to configure a new node. Configure, "configure", PUBSUB, - child: ( + children: [ /// The form to configure it. form: Option = ("x", DATA_FORMS) => DataForm - ) + ] ); generate_element_with_only_attributes!( @@ -168,10 +168,10 @@ generate_element_with_children!( /// The subscription identifier affected by this request. subid: Option = "subid" => optional, ], - child: ( + children: [ /// The form describing the subscription. form: Option = ("x", DATA_FORMS) => DataForm - ) + ] ); generate_element_with_children!( @@ -190,10 +190,10 @@ generate_element_with_children!( generate_element_with_children!( /// The options associated to a publish request. PublishOptions, "publish-options", PUBSUB, - child: ( + children: [ /// The form describing these options. form: Option = ("x", DATA_FORMS) => DataForm - ) + ] ); generate_attribute!( @@ -300,10 +300,10 @@ generate_element_with_children!( /// The state of the subscription. subscription: Option = "subscription" => optional, ], - child: ( + children: [ /// The options related to this subscription. subscribe_options: Option = ("subscribe-options", PUBSUB) => SubscribeOptions - ) + ] ); generate_element_with_only_attributes!( diff --git a/src/sm.rs b/src/sm.rs index 994c8da7fde17c76276b10ac48f6cd696df1f8c9..4030bf71c3eabb919e4b17aca796b6e2a97632e7 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -62,10 +62,10 @@ generate_element_with_children!( attributes: [ h: Option = "h" => optional, ], - child: ( + children: [ // XXX: implement the * handling. error: Option = ("*", XMPP_STANZAS) => DefinedCondition - ) + ] ); generate_empty_element!( diff --git a/src/version.rs b/src/version.rs index cb8773369a6f4ef41d69cca186b36859e5ddff90..94cfcbb28c95f08c42420291983e220545e4e800 100644 --- a/src/version.rs +++ b/src/version.rs @@ -4,84 +4,25 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use minidom::Element; -use error::Error; -use ns; use iq::{IqGetPayload, IqResultPayload}; -#[derive(Debug, Clone)] -pub struct Version { - pub name: String, - pub version: String, - pub os: Option, -} +generate_element_with_children!( + Version, "query", VERSION, + children: [ + name: Required = ("name", VERSION) => String, + version: Required = ("version", VERSION) => String, + os: Option = ("os", VERSION) => String + ] +); impl IqGetPayload for Version {} impl IqResultPayload for Version {} -impl TryFrom for Version { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "query", VERSION, "version"); - check_no_attributes!(elem, "version"); - let mut name = None; - let mut version = None; - let mut os = None; - for child in elem.children() { - if child.is("name", ns::VERSION) { - if name.is_some() { - return Err(Error::ParseError("More than one name in version element.")); - } - name = Some(child.text()); - } else if child.is("version", ns::VERSION) { - if version.is_some() { - return Err(Error::ParseError("More than one version in version element.")); - } - version = Some(child.text()); - } else if child.is("os", ns::VERSION) { - if os.is_some() { - return Err(Error::ParseError("More than one os in version element.")); - } - os = Some(child.text()); - } else { - return Err(Error::ParseError("Unknown child in version element.")); - } - } - let name = name.unwrap(); - let version = version.unwrap(); - Ok(Version { - name, - version, - os, - }) - } -} - -impl From for Element { - fn from(version: Version) -> Element { - Element::builder("query") - .ns(ns::VERSION) - .append(Element::builder("name") - .ns(ns::VERSION) - .append(version.name) - .build()) - .append(Element::builder("version") - .ns(ns::VERSION) - .append(version.version) - .build()) - .append(Element::builder("os") - .ns(ns::VERSION) - .append(version.os) - .build()) - .build() - } -} - #[cfg(test)] mod tests { use super::*; + use try_from::TryFrom; + use minidom::Element; use compare_elements::NamespaceAwareCompare; #[test] From ef227c461769d4023182d2ea0b2e8064c76bcbbe Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:37:22 +0200 Subject: [PATCH 0631/1020] macros: Simplify generate_element_with_only_attributes!(). --- src/macros.rs | 53 ++++++++++----------------------------------------- 1 file changed, 10 insertions(+), 43 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 9adae5197760ab4c196add8d387f35e26ae60085..3c07f180f2979aeb69fdadaa1f3085fe2d5371aa 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -287,43 +287,10 @@ macro_rules! generate_empty_element { macro_rules! generate_element_with_only_attributes { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]); + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( - $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $elem { - $( - $(#[$attr_meta])* - pub $attr: $attr_type, - )* - } - - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { - check_self!(elem, $name, $ns); - check_no_children!(elem, $name); - check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); - Ok($elem { - $( - $attr: get_attr!(elem, $attr_name, $attr_action), - )* - }) - } - } - - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) - .ns(::ns::$ns) - $( - .attr($attr_name, elem.$attr) - )* - .build() - } - } + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); ); } @@ -507,13 +474,13 @@ macro_rules! generate_serialiser { } macro_rules! generate_element_with_children { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -536,10 +503,10 @@ macro_rules! generate_element_with_children { $( start_parse_elem!($child_ident: $coucou); )* - for child in elem.children() { + for _child in elem.children() { $( - if child.is($child_name, ::ns::$child_ns) { - do_parse_elem!($child_ident: $coucou = $child_constructor => child); + if _child.is($child_name, ::ns::$child_ns) { + do_parse_elem!($child_ident: $coucou = $child_constructor => _child); continue; } )* From 9c598fbdf984e9d46cd393721d1d8530655fc1f8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:42:35 +0200 Subject: [PATCH 0632/1020] macros: Rename generate_element_with_only_attributes!() into generate_element!(). --- src/disco.rs | 20 ++++++++++++-------- src/eme.rs | 5 +++-- src/ibb.rs | 6 ++++-- src/idle.rs | 3 ++- src/jingle_ft.rs | 3 ++- src/jingle_ibb.rs | 3 ++- src/jingle_s5b.rs | 3 ++- src/macros.rs | 18 +++++++++--------- src/message_correct.rs | 3 ++- src/muc/user.rs | 3 ++- src/pubsub/pubsub.rs | 25 +++++++++++++++---------- src/receipts.rs | 3 ++- src/sm.rs | 25 +++++++++++++++---------- src/stanza_id.rs | 6 ++++-- src/stream.rs | 3 ++- src/websocket.rs | 3 ++- 16 files changed, 80 insertions(+), 52 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 4c70edd2f87422cb348b1ee6771eaf8bf77bae45..679b06e5e197b6556a1728f36c67a8b5b3d9a302 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -17,22 +17,24 @@ use ns; use iq::{IqGetPayload, IqResultPayload}; use data_forms::{DataForm, DataFormType}; -generate_element_with_only_attributes!( +generate_element!( /// Structure representing a `` element. /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -DiscoInfoQuery, "query", DISCO_INFO, [ +DiscoInfoQuery, "query", DISCO_INFO, +attributes: [ /// Node on which we are doing the discovery. node: Option = "node" => optional, ]); impl IqGetPayload for DiscoInfoQuery {} -generate_element_with_only_attributes!( +generate_element!( /// Structure representing a `` element. #[derive(PartialEq)] -Feature, "feature", DISCO_INFO, [ +Feature, "feature", DISCO_INFO, +attributes: [ /// Namespace of the feature we want to represent. var: String = "var" => required, ]); @@ -185,21 +187,23 @@ impl From for Element { } } -generate_element_with_only_attributes!( +generate_element!( /// Structure representing a `` element. /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -DiscoItemsQuery, "query", DISCO_ITEMS, [ +DiscoItemsQuery, "query", DISCO_ITEMS, +attributes: [ /// Node on which we are doing the discovery. node: Option = "node" => optional, ]); impl IqGetPayload for DiscoItemsQuery {} -generate_element_with_only_attributes!( +generate_element!( /// Structure representing an `` element. -Item, "item", DISCO_ITEMS, [ +Item, "item", DISCO_ITEMS, +attributes: [ /// JID of the entity pointed by this item. jid: Jid = "jid" => required, /// Node of the entity pointed by this item. diff --git a/src/eme.rs b/src/eme.rs index ff3737b06d9b57c80c64fda8bf7c297987ddc8fb..a276ba25e13a6fd683d5b29d66127d613e790301 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,9 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -generate_element_with_only_attributes!( +generate_element!( /// Structure representing an `` element. -ExplicitMessageEncryption, "encryption", EME, [ +ExplicitMessageEncryption, "encryption", EME, +attributes: [ /// Namespace of the encryption scheme used. namespace: String = "namespace" => required, diff --git a/src/ibb.rs b/src/ibb.rs index e33ca3bf7c33321c10d35a06165c86fa4251d4c5..2edb8ee5020cd549ab2159aa4e13ede90e9fde74 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -12,7 +12,8 @@ generate_attribute!(Stanza, "stanza", { Message => "message", }, Default = Iq); -generate_element_with_only_attributes!(Open, "open", IBB, [ +generate_element!(Open, "open", IBB, +attributes: [ block_size: u16 = "block-size" => required, sid: String = "sid" => required, stanza: Stanza = "stanza" => default, @@ -30,7 +31,8 @@ generate_element_with_text!(Data, "data", IBB, impl IqSetPayload for Data {} -generate_element_with_only_attributes!(Close, "close", IBB, [ +generate_element!(Close, "close", IBB, +attributes: [ sid: String = "sid" => required, ]); diff --git a/src/idle.rs b/src/idle.rs index 1312efb96fd60200f5c16f4e587f468c8179bf1c..22e844601178aec040acafc97a1fefed006724a3 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -6,7 +6,8 @@ use date::DateTime; -generate_element_with_only_attributes!(Idle, "idle", IDLE, [ +generate_element!(Idle, "idle", IDLE, +attributes: [ since: DateTime = "since" => required, ]); diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index eb7e9169e0aeb71bfec2eb301b8da270d0f43fe9..b9e1d3ef7dfad82f79bc5db8ec8ea2d1c5862bbe 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -287,7 +287,8 @@ impl From for Element { } } -generate_element_with_only_attributes!(Received, "received", JINGLE_FT, [ +generate_element!(Received, "received", JINGLE_FT, +attributes: [ name: ContentId = "name" => required, creator: Creator = "creator" => required, ]); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 2e68e685b0b84e6ce4102621fa05e393198f95c0..156ba4d314879c32c53bb9a0dd51362293bf400f 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -8,7 +8,8 @@ use ibb::Stanza; generate_id!(StreamId); -generate_element_with_only_attributes!(Transport, "transport", JINGLE_IBB, [ +generate_element!(Transport, "transport", JINGLE_IBB, +attributes: [ block_size: u16 = "block-size" => required, sid: StreamId = "sid" => required, stanza: Stanza = "stanza" => default, diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index fe9eaa104aef19039584dc1c6dbf0b6cc535d0ad..94f3c0c741ffeec2fe6445c8bccb3c963e469648 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -30,7 +30,8 @@ generate_id!(CandidateId); generate_id!(StreamId); -generate_element_with_only_attributes!(Candidate, "candidate", JINGLE_S5B, [ +generate_element!(Candidate, "candidate", JINGLE_S5B, +attributes: [ cid: CandidateId = "cid" => required, host: IpAddr = "host" => required, jid: Jid = "jid" => required, diff --git a/src/macros.rs b/src/macros.rs index 3c07f180f2979aeb69fdadaa1f3085fe2d5371aa..9b9501042e2db2d57ec9fae876f9e9394b4fb311 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -285,15 +285,6 @@ macro_rules! generate_empty_element { ); } -macro_rules! generate_element_with_only_attributes { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); - ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); - ); -} - macro_rules! generate_id { ($elem:ident) => ( #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -473,6 +464,15 @@ macro_rules! generate_serialiser { ); } +macro_rules! generate_element { + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( + generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + ); +} + macro_rules! generate_element_with_children { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); diff --git a/src/message_correct.rs b/src/message_correct.rs index 3dbb86e49b5838bbfbf732bf376a8f2b0d772122..f301979764db485e21343fc0fb2ab3f0fc6bce02 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,7 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -generate_element_with_only_attributes!(Replace, "replace", MESSAGE_CORRECT, [ +generate_element!(Replace, "replace", MESSAGE_CORRECT, +attributes: [ id: String = "id" => required, ]); diff --git a/src/muc/user.rs b/src/muc/user.rs index 69060cef781823e0a1457d09cca79ec291a32b57..15cea9e419811c8df1c5b771d63b7f8717cff89b 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -120,7 +120,8 @@ impl From for Element { } } -generate_element_with_only_attributes!(Continue, "continue", MUC_USER, [ +generate_element!(Continue, "continue", MUC_USER, +attributes: [ thread: Option = "thread" => optional, ]); diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 840232b0f2c15c804cd343e77ac0b0a4af3b754e..807ce9083de35501f3263d7f041dd60151df645e 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -58,9 +58,10 @@ generate_attribute!( } ); -generate_element_with_only_attributes!( +generate_element!( /// An affiliation element. - Affiliation, "affiliation", PUBSUB, [ + Affiliation, "affiliation", PUBSUB, + attributes: [ /// The node this affiliation pertains to. node: NodeName = "node" => required, @@ -78,17 +79,19 @@ generate_element_with_children!( ] ); -generate_element_with_only_attributes!( +generate_element!( /// Request to create a new node. - Create, "create", PUBSUB, [ + Create, "create", PUBSUB, + attributes: [ /// The node name to create, if `None` the service will generate one. node: Option = "node" => optional, ] ); -generate_element_with_only_attributes!( +generate_element!( /// Request for a default node configuration. - Default, "default", PUBSUB, [ + Default, "default", PUBSUB, + attributes: [ /// The node targetted by this request, otherwise the entire service. node: Option = "node" => optional, @@ -260,9 +263,10 @@ impl From for Element { } } -generate_element_with_only_attributes!( +generate_element!( /// A request to subscribe a JID to a node. - Subscribe, "subscribe", PUBSUB, [ + Subscribe, "subscribe", PUBSUB, + attributes: [ /// The JID being subscribed. jid: Jid = "jid" => required, @@ -306,9 +310,10 @@ generate_element_with_children!( ] ); -generate_element_with_only_attributes!( +generate_element!( /// An unsubscribe request. - Unsubscribe, "unsubscribe", PUBSUB, [ + Unsubscribe, "unsubscribe", PUBSUB, + attributes: [ /// The JID affected by this request. jid: Jid = "jid" => required, diff --git a/src/receipts.rs b/src/receipts.rs index dfaf54734a12f012208e5e2b95795ddb5b73410e..a557d008336c49e6fe0b9c2d835bf6680dac2cdd 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -6,7 +6,8 @@ generate_empty_element!(Request, "request", RECEIPTS); -generate_element_with_only_attributes!(Received, "received", RECEIPTS, [ +generate_element!(Received, "received", RECEIPTS, +attributes: [ id: Option = "id" => optional, ]); diff --git a/src/sm.rs b/src/sm.rs index 4030bf71c3eabb919e4b17aca796b6e2a97632e7..3558c8e72c75e987c954dbd10a170b597bcc3b8a 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -6,8 +6,9 @@ use stanza_error::DefinedCondition; -generate_element_with_only_attributes!( - A, "a", SM, [ +generate_element!( + A, "a", SM, + attributes: [ h: u32 = "h" => required, ] ); @@ -20,8 +21,9 @@ impl A { generate_attribute!(ResumeAttr, "resume", bool); -generate_element_with_only_attributes!( - Enable, "enable", SM, [ +generate_element!( + Enable, "enable", SM, + attributes: [ // TODO: should be the infinite integer set ≥ 1. max: Option = "max" => optional, resume: ResumeAttr = "resume" => default, @@ -47,8 +49,9 @@ impl Enable { } } -generate_element_with_only_attributes!( - Enabled, "enabled", SM, [ +generate_element!( + Enabled, "enabled", SM, + attributes: [ id: Option = "id" => optional, location: Option = "location" => optional, // TODO: should be the infinite integer set ≥ 1. @@ -72,15 +75,17 @@ generate_empty_element!( R, "r", SM ); -generate_element_with_only_attributes!( - Resume, "resume", SM, [ +generate_element!( + Resume, "resume", SM, + attributes: [ h: u32 = "h" => required, previd: String = "previd" => required, ] ); -generate_element_with_only_attributes!( - Resumed, "resumed", SM, [ +generate_element!( + Resumed, "resumed", SM, + attributes: [ h: u32 = "h" => required, previd: String = "previd" => required, ] diff --git a/src/stanza_id.rs b/src/stanza_id.rs index b321b46c8c19c4523131d65b37f4222a2113952f..16a8d9fc7e3e6f86b85f5dc1b162b1f2b15e4566 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -6,12 +6,14 @@ use jid::Jid; -generate_element_with_only_attributes!(StanzaId, "stanza-id", SID, [ +generate_element!(StanzaId, "stanza-id", SID, +attributes: [ id: String = "id" => required, by: Jid = "by" => required, ]); -generate_element_with_only_attributes!(OriginId, "origin-id", SID, [ +generate_element!(OriginId, "origin-id", SID, +attributes: [ id: String = "id" => required, ]); diff --git a/src/stream.rs b/src/stream.rs index 93a71c41d9a96249e93a19967bd16168f37f330b..aa48ad9f29ebcd97db28cd3098b436c6dcebfa6a 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -6,7 +6,8 @@ use jid::Jid; -generate_element_with_only_attributes!(Stream, "stream", STREAM, [ +generate_element!(Stream, "stream", STREAM, +attributes: [ from: Option = "from" => optional, to: Option = "to" => optional, id: Option = "id" => optional, diff --git a/src/websocket.rs b/src/websocket.rs index b5aa7903457af5d89ff46813471bd8ff1aa0a20a..78d0cada5a8ed3dfba02aefc568f65466f839f74 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -6,7 +6,8 @@ use jid::Jid; -generate_element_with_only_attributes!(Open, "open", WEBSOCKET, [ +generate_element!(Open, "open", WEBSOCKET, +attributes: [ from: Option = "from" => optional, to: Option = "to" => optional, id: Option = "id" => optional, From 3fb99988d54ba2d23d07a678183d74382be3753e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 16:45:13 +0200 Subject: [PATCH 0633/1020] macros: Merge generate_element_with_children!() into generate_element!(). --- src/disco.rs | 2 +- src/ecaps2.rs | 2 +- src/jingle.rs | 2 +- src/jingle_ft.rs | 2 +- src/macros.rs | 11 ++++------- src/mam.rs | 6 +++--- src/media_element.rs | 2 +- src/muc/muc.rs | 2 +- src/muc/user.rs | 4 ++-- src/pubsub/pubsub.rs | 18 +++++++++--------- src/roster.rs | 4 ++-- src/sm.rs | 2 +- src/version.rs | 2 +- 13 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 679b06e5e197b6556a1728f36c67a8b5b3d9a302..b6b653c59f865dfd58d4863877dea230f196c0a6 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -212,7 +212,7 @@ attributes: [ name: Option = "name" => optional, ]); -generate_element_with_children!( +generate_element!( /// Structure representing a `` element. /// diff --git a/src/ecaps2.rs b/src/ecaps2.rs index b96585bcd2d7bee710cab5b634d3473cc9142e4d..4caa7d35e785ad42fae8e885e77c120583265c48 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -16,7 +16,7 @@ use sha3::{Sha3_256, Sha3_512}; use blake2::Blake2b; use digest::{Digest, VariableOutput}; -generate_element_with_children!( +generate_element!( ECaps2, "c", ECAPS2, children: [ hashes: Vec = ("hash", HASHES) => Hash diff --git a/src/jingle.rs b/src/jingle.rs index 951a2fa1d72a570f9baa8f08c4029b1adb1ebe3f..1600fed1824a20dd7324e5a4eddf0437681c9a0c 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -66,7 +66,7 @@ generate_attribute!(Disposition, "disposition", { generate_id!(ContentId); -generate_element_with_children!( +generate_element!( Content, "content", JINGLE, attributes: [ creator: Creator = "creator" => required, diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index b9e1d3ef7dfad82f79bc5db8ec8ea2d1c5862bbe..4cb0fa52365d350bcc729cef354947dab1a8c573 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -18,7 +18,7 @@ use minidom::Element; use error::Error; use ns; -generate_element_with_children!( +generate_element!( #[derive(PartialEq, Default)] Range, "range", JINGLE_FT, attributes: [ diff --git a/src/macros.rs b/src/macros.rs index 9b9501042e2db2d57ec9fae876f9e9394b4fb311..48a2702d3574df765c2fc8b75e5a6d7f41f3f841 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -466,19 +466,16 @@ macro_rules! generate_serialiser { macro_rules! generate_element { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); ); -} - -macro_rules! generate_element_with_children { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( - generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( $(#[$meta])* diff --git a/src/mam.rs b/src/mam.rs index b835fc505e688c56924979f2ff7f8656d63420b3..b1965667a8790f07391cd85f83285d56e6aafd9e 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -18,7 +18,7 @@ use forwarding::Forwarded; use ns; -generate_element_with_children!( +generate_element!( Query, "query", MAM, attributes: [ queryid: Option = "queryid" => optional, @@ -34,7 +34,7 @@ impl IqGetPayload for Query {} impl IqSetPayload for Query {} impl IqResultPayload for Query {} -generate_element_with_children!( +generate_element!( Result_, "result", MAM, attributes: [ id: String = "id" => required, @@ -49,7 +49,7 @@ generate_attribute!( Complete, "complete", bool ); -generate_element_with_children!( +generate_element!( Fin, "fin", MAM, attributes: [ complete: Complete = "complete" => default diff --git a/src/media_element.rs b/src/media_element.rs index 43f0d7acd101e18f254e8c020a43396fe73911df..9b9133c552abf633371ea7d0514754e9fd98d898 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -13,7 +13,7 @@ generate_element_with_text!(URI, "uri", MEDIA_ELEMENT, uri: TrimmedPlainText ); -generate_element_with_children!(MediaElement, "media", MEDIA_ELEMENT, +generate_element!(MediaElement, "media", MEDIA_ELEMENT, attributes: [ width: Option = "width" => optional, height: Option = "height" => optional diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 80905a20679928d1cf29f6a171b8fd88ee919a2c..355b089fffcb004eb587dad7b64f13f6a9c69aa9 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -generate_element_with_children!( +generate_element!( Muc, "x", MUC, children: [ password: Option = ("password", MUC) => String ] diff --git a/src/muc/user.rs b/src/muc/user.rs index 15cea9e419811c8df1c5b771d63b7f8717cff89b..8baa62710039bfed01987ad0974173032cd0dd49 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -142,7 +142,7 @@ generate_attribute!(Role, "role", { None => "none", }, Default = None); -generate_element_with_children!( +generate_element!( Item, "item", MUC_USER, attributes: [ affiliation: Affiliation = "affiliation" => required, jid: Option = "jid" => optional, @@ -155,7 +155,7 @@ generate_element_with_children!( ] ); -generate_element_with_children!( +generate_element!( MucUser, "x", MUC_USER, children: [ status: Vec = ("status", MUC_USER) => Status, items: Vec = ("item", MUC_USER) => Item diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 807ce9083de35501f3263d7f041dd60151df645e..8ecb89b6d8f116cad794e17a33c1a303c7f8dff4 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -22,7 +22,7 @@ use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; // TODO: a better solution would be to split this into a query and a result elements, like for // XEP-0030. -generate_element_with_children!( +generate_element!( /// A list of affiliations you have on a service, or on a node. Affiliations, "affiliations", PUBSUB, attributes: [ @@ -70,7 +70,7 @@ generate_element!( ] ); -generate_element_with_children!( +generate_element!( /// Request to configure a new node. Configure, "configure", PUBSUB, children: [ @@ -100,7 +100,7 @@ generate_element!( ] ); -generate_element_with_children!( +generate_element!( /// A request for a list of items. Items, "items", PUBSUB, attributes: [ @@ -158,7 +158,7 @@ impl From for Element { } } -generate_element_with_children!( +generate_element!( /// The options associated to a subscription request. Options, "options", PUBSUB, attributes: [ @@ -177,7 +177,7 @@ generate_element_with_children!( ] ); -generate_element_with_children!( +generate_element!( /// Request to publish items to a node. Publish, "publish", PUBSUB, attributes: [ @@ -190,7 +190,7 @@ generate_element_with_children!( ] ); -generate_element_with_children!( +generate_element!( /// The options associated to a publish request. PublishOptions, "publish-options", PUBSUB, children: [ @@ -204,7 +204,7 @@ generate_attribute!( Notify, "notify", bool ); -generate_element_with_children!( +generate_element!( /// A request to retract some items from a node. Retract, "retract", PUBSUB, attributes: [ @@ -275,7 +275,7 @@ generate_element!( ] ); -generate_element_with_children!( +generate_element!( /// A request for current subscriptions. Subscriptions, "subscriptions", PUBSUB, attributes: [ @@ -288,7 +288,7 @@ generate_element_with_children!( ] ); -generate_element_with_children!( +generate_element!( /// A subscription element, describing the state of a subscription. SubscriptionElem, "subscription", PUBSUB, attributes: [ diff --git a/src/roster.rs b/src/roster.rs index e851c485874890d7b242dacaa338a70ca61807b7..bbd0c39967411c99f0c3f6eb21b9f3bc3cc30589 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -17,7 +17,7 @@ generate_attribute!(Subscription, "subscription", { Remove => "remove", }, Default = None); -generate_element_with_children!( +generate_element!( /// Contact from the user’s contact list. #[derive(PartialEq)] Item, "item", ROSTER, @@ -38,7 +38,7 @@ generate_element_with_children!( ] ); -generate_element_with_children!( +generate_element!( /// The contact list of the user. Roster, "query", ROSTER, attributes: [ diff --git a/src/sm.rs b/src/sm.rs index 3558c8e72c75e987c954dbd10a170b597bcc3b8a..20e9fc6183cfaabefc131751e70d27f2c9d10896 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -60,7 +60,7 @@ generate_element!( ] ); -generate_element_with_children!( +generate_element!( Failed, "failed", SM, attributes: [ h: Option = "h" => optional, diff --git a/src/version.rs b/src/version.rs index 94cfcbb28c95f08c42420291983e220545e4e800..1265c1629bc90ed3ad8073290c4bc9fc4d9aa581 100644 --- a/src/version.rs +++ b/src/version.rs @@ -6,7 +6,7 @@ use iq::{IqGetPayload, IqResultPayload}; -generate_element_with_children!( +generate_element!( Version, "query", VERSION, children: [ name: Required = ("name", VERSION) => String, From 45fdb3d5e2a34926195cf59a9159089d6f282c34 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 17:04:40 +0200 Subject: [PATCH 0634/1020] macros: Improve error messages. --- src/macros.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 48a2702d3574df765c2fc8b75e5a6d7f41f3f841..971fe7606da2ca49c47ace48207adcc6cb06e727 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -419,29 +419,32 @@ macro_rules! do_parse { } macro_rules! do_parse_elem { - ($temp:ident: Vec = $constructor:ident => $elem:ident) => ( + ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( $temp.push(do_parse!($elem, $constructor)); ); - ($temp:ident: Option = $constructor:ident => $elem:ident) => ( + ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( if $temp.is_some() { - return Err(::error::Error::ParseError(concat!("coucou", " must not have more than one ", "coucou", "."))); + return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); } $temp = Some(do_parse!($elem, $constructor)); ); - ($temp:ident: Required = $constructor:ident => $elem:ident) => ( + ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( + if $temp.is_some() { + return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); + } $temp = Some(do_parse!($elem, $constructor)); ); } macro_rules! finish_parse_elem { - ($temp:ident: Vec = $name:tt) => ( + ($temp:ident: Vec = $name:tt, $parent_name:tt) => ( $temp ); - ($temp:ident: Option = $name:tt) => ( + ($temp:ident: Option = $name:tt, $parent_name:tt) => ( $temp ); - ($temp:ident: Required = $name:tt) => ( - $temp.ok_or(::error::Error::ParseError(concat!("Missing child coucou in ", $name, " element.")))? + ($temp:ident: Required = $name:tt, $parent_name:tt) => ( + $temp.ok_or(::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))? ); } @@ -503,7 +506,7 @@ macro_rules! generate_element { for _child in elem.children() { $( if _child.is($child_name, ::ns::$child_ns) { - do_parse_elem!($child_ident: $coucou = $child_constructor => _child); + do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name); continue; } )* @@ -514,7 +517,7 @@ macro_rules! generate_element { $attr: get_attr!(elem, $attr_name, $attr_action), )* $( - $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name), + $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name), )* }) } From bb60af153163214befc0c4a127a33fdb27df365c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 28 May 2018 17:05:04 +0200 Subject: [PATCH 0635/1020] data_forms: Use a macro for ` element. pub fn new(h: u32) -> A { A { h } } } -generate_attribute!(ResumeAttr, "resume", bool); +generate_attribute!( + /// Whether to allow resumption of a previous stream. + ResumeAttr, "resume", bool +); generate_element!( + /// Client request for enabling stream management. Enable, "enable", SM, attributes: [ + /// The preferred resumption time in seconds by the client. // TODO: should be the infinite integer set ≥ 1. max: Option = "max" => optional, + + /// Whether the client wants to be allowed to resume the stream. resume: ResumeAttr = "resume" => default, ] ); impl Enable { + /// Generates a new `` element. pub fn new() -> Self { Enable { max: None, @@ -40,56 +49,86 @@ impl Enable { } } + /// Sets the preferred resumption time in seconds. pub fn with_max(mut self, max: u32) -> Self { self.max = Some(max); self } + /// Asks for resumption to be possible. pub fn with_resume(mut self) -> Self { self.resume = ResumeAttr::True; self } } +generate_id!( + /// A random identifier used for stream resumption. + StreamId +); + generate_element!( + /// Server response once stream management is enabled. Enabled, "enabled", SM, attributes: [ - id: Option = "id" => optional, + /// A random identifier used for stream resumption. + id: Option = "id" => optional, + + /// The preferred IP, domain, IP:port or domain:port location for + /// resumption. location: Option = "location" => optional, + + /// The preferred resumption time in seconds by the server. // TODO: should be the infinite integer set ≥ 1. max: Option = "max" => optional, + + /// Whether stream resumption is allowed. resume: ResumeAttr = "resume" => default, ] ); generate_element!( + /// A stream management error happened. Failed, "failed", SM, attributes: [ + /// The last handled stanza. h: Option = "h" => optional, ], children: [ + /// The error returned. // XXX: implement the * handling. error: Option = ("*", XMPP_STANZAS) => DefinedCondition ] ); generate_empty_element!( + /// Requests the currently received stanzas by the other party. R, "r", SM ); generate_element!( + /// Requests a stream resumption. Resume, "resume", SM, attributes: [ + /// The last handled stanza. h: u32 = "h" => required, - previd: String = "previd" => required, + + /// The previous id given by the server on + /// [enabled](struct.Enabled.html). + previd: StreamId = "previd" => required, ] ); generate_element!( + /// The response by the server for a successfully resumed stream. Resumed, "resumed", SM, attributes: [ + /// The last handled stanza. h: u32 = "h" => required, - previd: String = "previd" => required, + + /// The previous id given by the server on + /// [enabled](struct.Enabled.html). + previd: StreamId = "previd" => required, ] ); @@ -117,4 +156,30 @@ mod tests { let elem: Element = "".parse().unwrap(); StreamManagement::try_from(elem).unwrap(); } + + #[test] + fn resume() { + let elem: Element = "".parse().unwrap(); + let enable = Enable::try_from(elem).unwrap(); + assert_eq!(enable.max, None); + assert_eq!(enable.resume, ResumeAttr::True); + + let elem: Element = "".parse().unwrap(); + let enabled = Enabled::try_from(elem).unwrap(); + let previd = enabled.id.unwrap(); + assert_eq!(enabled.resume, ResumeAttr::True); + assert_eq!(previd, StreamId(String::from("coucou"))); + assert_eq!(enabled.max, Some(600)); + assert_eq!(enabled.location, None); + + let elem: Element = "".parse().unwrap(); + let resume = Resume::try_from(elem).unwrap(); + assert_eq!(resume.h, 5); + assert_eq!(resume.previd, previd); + + let elem: Element = "".parse().unwrap(); + let resumed = Resumed::try_from(elem).unwrap(); + assert_eq!(resumed.h, 5); + assert_eq!(resumed.previd, previd); + } } From eeeae25cb10b94528abf55daf8b90c0f499e9151 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 8 Aug 2018 20:52:27 +0200 Subject: [PATCH 0722/1020] jingle_ft: Document this module. --- src/jingle_ft.rs | 63 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 75656e5043ca6c5f8528c674935fcfc22f4ad83b..e206ab3c55c8f29b2507dee502cebdcd86dfbb68 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::str::FromStr; @@ -21,18 +19,25 @@ use error::Error; use ns; generate_element!( + /// Represents a range in a file. #[derive(PartialEq, Default)] Range, "range", JINGLE_FT, attributes: [ + /// The offset in bytes from the beginning of the file. offset: u64 = "offset" => default, + + /// The length in bytes of the range, or None to be the entire + /// remaining of the file. length: Option = "length" => optional ], children: [ + /// List of hashes for this range. hashes: Vec = ("hash", HASHES) => Hash ] ); impl Range { + /// Creates a new range. pub fn new() -> Range { Default::default() } @@ -40,20 +45,38 @@ impl Range { type Lang = String; -generate_id!(Desc); +generate_id!( + /// Wrapper for a file description. + Desc +); +/// Represents a file to be transferred. #[derive(Debug, Clone)] pub struct File { + /// The date of last modification of this file. pub date: Option, + + /// The MIME type of this file. pub media_type: Option, + + /// The name of this file. pub name: Option, + + /// The description of this file, possibly localised. pub descs: BTreeMap, + + /// The size of this file, in bytes. pub size: Option, + + /// Used to request only a part of this file. pub range: Option, + + /// A list of hashes matching this entire file. pub hashes: Vec, } impl File { + /// Creates a new file descriptor. pub fn new() -> File { File { date: None, @@ -66,41 +89,50 @@ impl File { } } + /// Sets the date of last modification on this file. pub fn with_date(mut self, date: DateTime) -> File { self.date = Some(date); self } + /// Sets the date of last modification on this file from an ISO-8601 + /// string. pub fn with_date_str(mut self, date: &str) -> Result { self.date = Some(DateTime::from_str(date)?); Ok(self) } + /// Sets the MIME type of this file. pub fn with_media_type(mut self, media_type: String) -> File { self.media_type = Some(media_type); self } + /// Sets the name of this file. pub fn with_name(mut self, name: String) -> File { self.name = Some(name); self } + /// Sets a description for this file. pub fn add_desc(mut self, lang: &str, desc: Desc) -> File { self.descs.insert(Lang::from(lang), desc); self } + /// Sets the file size of this file, in bytes. pub fn with_size(mut self, size: u64) -> File { self.size = Some(size); self } + /// Request only a range of this file. pub fn with_range(mut self, range: Range) -> File { self.range = Some(range); self } + /// Add a hash on this file. pub fn add_hash(mut self, hash: Hash) -> File { self.hashes.push(hash); self @@ -211,8 +243,11 @@ impl From for Element { root.build() } } + +/// A wrapper element for a file. #[derive(Debug, Clone)] pub struct Description { + /// The actual file descriptor. pub file: File, } @@ -247,10 +282,16 @@ impl From for Element { } } +/// A checksum for checking that the file has been transferred correctly. #[derive(Debug, Clone)] pub struct Checksum { + /// The identifier of the file transfer content. pub name: ContentId, + + /// The creator of this file transfer. pub creator: Creator, + + /// The file being checksummed. pub file: File, } @@ -289,11 +330,17 @@ impl From for Element { } } -generate_element!(Received, "received", JINGLE_FT, -attributes: [ - name: ContentId = "name" => required, - creator: Creator = "creator" => required, -]); +generate_element!( + /// A notice that the file transfer has been completed. + Received, "received", JINGLE_FT, + attributes: [ + /// The content identifier of this Jingle session. + name: ContentId = "name" => required, + + /// The creator of this file transfer. + creator: Creator = "creator" => required, + ] +); #[cfg(test)] mod tests { From 22b424f43a1e86497f715a9c07d0bebcd4da34d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 8 Aug 2018 21:07:22 +0200 Subject: [PATCH 0723/1020] jingle_s5b: Document this module. --- src/jingle_s5b.rs | 111 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 23 deletions(-) diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 92627d3b726b4b0d59782c9b1a93b4b7d52da4d0..f6f05be3416394f0e50d0fc4a8d23c0f48c466b9 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::net::IpAddr; @@ -16,33 +14,72 @@ use error::Error; use ns; -generate_attribute!(Type, "type", { - Assisted => "assisted", - Direct => "direct", - Proxy => "proxy", - Tunnel => "tunnel", -}, Default = Direct); +generate_attribute!( + /// The type of the connection being proposed by this candidate. + Type, "type", { + /// Direct connection using NAT assisting technologies like NAT-PMP or + /// UPnP-IGD. + Assisted => "assisted", + + /// Direct connection using the given interface. + Direct => "direct", + + /// SOCKS5 relay. + Proxy => "proxy", + + /// Tunnel protocol such as Teredo. + Tunnel => "tunnel", + }, Default = Direct +); + +generate_attribute!( + /// Which mode to use for the connection. + Mode, "mode", { + /// Use TCP, which is the default. + Tcp => "tcp", + + /// Use UDP. + Udp => "udp", + }, Default = Tcp +); + +generate_id!( + /// An identifier for a candidate. + CandidateId +); + +generate_id!( + /// An identifier for a stream. + StreamId +); + +generate_element!( + /// A candidate for a connection. + Candidate, "candidate", JINGLE_S5B, + attributes: [ + /// The identifier for this candidate. + cid: CandidateId = "cid" => required, -generate_attribute!(Mode, "mode", { - Tcp => "tcp", - Udp => "udp", -}, Default = Tcp); + /// The host to connect to. + host: IpAddr = "host" => required, -generate_id!(CandidateId); + /// The JID to request at the given end. + jid: Jid = "jid" => required, -generate_id!(StreamId); + /// The port to connect to. + port: Option = "port" => optional, -generate_element!(Candidate, "candidate", JINGLE_S5B, -attributes: [ - cid: CandidateId = "cid" => required, - host: IpAddr = "host" => required, - jid: Jid = "jid" => required, - port: Option = "port" => optional, - priority: u32 = "priority" => required, - type_: Type = "type" => default, -]); + /// The priority of this candidate, computed using this formula: + /// priority = (2^16)*(type preference) + (local preference) + priority: u32 = "priority" => required, + + /// The type of the connection being proposed by this candidate. + type_: Type = "type" => default, + ] +); impl Candidate { + /// Creates a new candidate with the given parameters. pub fn new(cid: CandidateId, host: IpAddr, jid: Jid, priority: u32) -> Candidate { Candidate { cid, @@ -54,36 +91,61 @@ impl Candidate { } } + /// Sets the port of this candidate. pub fn with_port(mut self, port: u16) -> Candidate { self.port = Some(port); self } + /// Sets the type of this candidate. pub fn with_type(mut self, type_: Type) -> Candidate { self.type_ = type_; self } } +/// The payload of a transport. #[derive(Debug, Clone)] pub enum TransportPayload { + /// The responder informs the initiator that the bytestream pointed by this + /// candidate has been activated. Activated(CandidateId), + + /// A list of suggested candidates. Candidates(Vec), + + /// Both parties failed to use a candidate, they should fallback to another + /// transport. CandidateError, + + /// The candidate pointed here should be used by both parties. CandidateUsed(CandidateId), + + /// This entity can’t connect to the SOCKS5 proxy. ProxyError, + + /// XXX: Invalid, should not be found in the wild. None, } +/// Describes a Jingle transport using a direct or proxied connection. #[derive(Debug, Clone)] pub struct Transport { + /// The stream identifier for this transport. pub sid: StreamId, + + /// The destination address. pub dstaddr: Option, + + /// The mode to be used for the transfer. pub mode: Mode, + + /// The payload of this transport. pub payload: TransportPayload, } impl Transport { + /// Creates a new transport element. pub fn new(sid: StreamId) -> Transport { Transport { sid, @@ -93,16 +155,19 @@ impl Transport { } } + /// Sets the destination address of this transport. pub fn with_dstaddr(mut self, dstaddr: String) -> Transport { self.dstaddr = Some(dstaddr); self } + /// Sets the mode of this transport. pub fn with_mode(mut self, mode: Mode) -> Transport { self.mode = mode; self } + /// Sets the payload of this transport. pub fn with_payload(mut self, payload: TransportPayload) -> Transport { self.payload = payload; self From d3039127ddb3628c510757bb4af9962be7ef7243 Mon Sep 17 00:00:00 2001 From: O01eg Date: Sat, 1 Sep 2018 22:59:02 +0300 Subject: [PATCH 0724/1020] Move from tokio-core to tokio. --- Cargo.toml | 10 +- examples/echo_bot.rs | 10 +- examples/echo_component.rs | 12 +-- src/client/mod.rs | 11 +- src/component/mod.rs | 11 +- src/happy_eyeballs.rs | 200 ++++++++++++++++++++++--------------- src/lib.rs | 5 +- src/starttls.rs | 14 +-- 8 files changed, 158 insertions(+), 115 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02a706d46ee65b2090cf5b1418f4ad65f0c8f653..9c6c8d1b29bf3159cdaf206fdfe463251976d07f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,18 +12,18 @@ keywords = ["xmpp", "tokio"] [dependencies] futures = "0.1" -tokio-core = "0.1" +tokio = "0.1" tokio-io = "0.1" tokio-codec = "0.1" bytes = "0.4.9" xml5ever = "0.12" minidom = "0.9" -# TODO: update to 0.2.0 -native-tls = "0.1" -tokio-tls = "0.1" +native-tls = "0.2" +tokio-tls = "0.2" sasl = "0.4" jid = { version = "0.5", features = ["minidom"] } -domain = "0.2" +trust-dns-resolver = "0.9.1" +trust-dns-proto = "0.4.0" xmpp-parsers = "0.11" idna = "0.1" try_from = "0.2" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 462f7fe870f1af721d32837b26db62f8e093a922..138222609515f49ee5cc68c24b98c2c94f40f409 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,5 +1,5 @@ extern crate futures; -extern crate tokio_core; +extern crate tokio; extern crate tokio_xmpp; extern crate jid; extern crate minidom; @@ -9,8 +9,8 @@ extern crate try_from; use std::env::args; use std::process::exit; use try_from::TryFrom; -use tokio_core::reactor::Core; use futures::{Stream, Sink, future}; +use tokio::runtime::current_thread::Runtime; use tokio_xmpp::Client; use minidom::Element; use xmpp_parsers::presence::{Presence, Type as PresenceType, Show as PresenceShow}; @@ -27,9 +27,9 @@ fn main() { let password = &args[2]; // tokio_core context - let mut core = Core::new().unwrap(); + let mut rt = Runtime::new().unwrap(); // Client instance - let client = Client::new(jid, password, core.handle()).unwrap(); + let client = Client::new(jid, password).unwrap(); // Make the two interfaces for sending and receiving independent // of each other so we can move one into a closure. @@ -64,7 +64,7 @@ fn main() { }); // Start polling `done` - match core.run(done) { + match rt.block_on(done) { Ok(_) => (), Err(e) => { println!("Fatal: {}", e); diff --git a/examples/echo_component.rs b/examples/echo_component.rs index 745e0771a6e50514100f0a25db28a5878abf3062..1239354aa0fceb19cebfb8e039bd298da548d7fe 100644 --- a/examples/echo_component.rs +++ b/examples/echo_component.rs @@ -1,5 +1,5 @@ extern crate futures; -extern crate tokio_core; +extern crate tokio; extern crate tokio_xmpp; extern crate jid; extern crate minidom; @@ -10,7 +10,7 @@ use std::env::args; use std::process::exit; use std::str::FromStr; use try_from::TryFrom; -use tokio_core::reactor::Core; +use tokio::runtime::current_thread::Runtime; use futures::{Stream, Sink, future}; use tokio_xmpp::Component; use minidom::Element; @@ -30,10 +30,10 @@ fn main() { let port: u16 = args.get(4).unwrap().parse().unwrap_or(5347u16); // tokio_core context - let mut core = Core::new().unwrap(); + let mut rt = Runtime::new().unwrap(); // Component instance - println!("{} {} {} {} {:?}", jid, password, server, port, core.handle()); - let component = Component::new(jid, password, server, port, core.handle()).unwrap(); + println!("{} {} {} {}", jid, password, server, port); + let component = Component::new(jid, password, server, port).unwrap(); // Make the two interfaces for sending and receiving independent // of each other so we can move one into a closure. @@ -70,7 +70,7 @@ fn main() { }); // Start polling `done` - match core.run(done) { + match rt.block_on(done) { Ok(_) => (), Err(e) => { println!("Fatal: {}", e); diff --git a/src/client/mod.rs b/src/client/mod.rs index 44dc8d0c34dd488fb066c38f8bfba965b2488008..d4151163ec6f040a3a684ef4248ce8abe99712ea 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,8 +1,7 @@ use std::mem::replace; use std::str::FromStr; use std::error::Error; -use tokio_core::reactor::Handle; -use tokio_core::net::TcpStream; +use tokio::net::TcpStream; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_tls::TlsStream; use futures::{future, Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; @@ -44,17 +43,17 @@ impl Client { /// /// Start polling the returned instance so that it will connect /// and yield events. - pub fn new(jid: &str, password: &str, handle: Handle) -> Result { + pub fn new(jid: &str, password: &str) -> Result { let jid = Jid::from_str(jid)?; let password = password.to_owned(); - let connect = Self::make_connect(jid.clone(), password.clone(), handle); + let connect = Self::make_connect(jid.clone(), password.clone()); Ok(Client { jid, state: ClientState::Connecting(connect), }) } - fn make_connect(jid: Jid, password: String, handle: Handle) -> Box> { + fn make_connect(jid: Jid, password: String) -> Box> { let username = jid.node.as_ref().unwrap().to_owned(); let jid1 = jid.clone(); let jid2 = jid.clone(); @@ -66,7 +65,7 @@ impl Client { return Box::new(future::err(format!("{:?}", e))), }; Box::new( - Connecter::from_lookup(handle, &domain, "_xmpp-client._tcp", 5222) + Connecter::from_lookup(&domain, "_xmpp-client._tcp", 5222) .expect("Connector::from_lookup") .and_then(move |tcp_stream| xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_CLIENT.to_owned()) diff --git a/src/component/mod.rs b/src/component/mod.rs index bf9a005f24ce2bf3873edcab11304d83e030cecc..1cf8a39bd096f7c271723199af25eaf447136257 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -4,8 +4,7 @@ use std::mem::replace; use std::str::FromStr; use std::error::Error; -use tokio_core::reactor::Handle; -use tokio_core::net::TcpStream; +use tokio::net::TcpStream; use tokio_io::{AsyncRead, AsyncWrite}; use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; use minidom::Element; @@ -41,21 +40,21 @@ impl Component { /// /// Start polling the returned instance so that it will connect /// and yield events. - pub fn new(jid: &str, password: &str, server: &str, port: u16, handle: Handle) -> Result { + pub fn new(jid: &str, password: &str, server: &str, port: u16) -> Result { let jid = Jid::from_str(jid)?; let password = password.to_owned(); - let connect = Self::make_connect(jid.clone(), password, server, port, handle); + let connect = Self::make_connect(jid.clone(), password, server, port); Ok(Component { jid, state: ComponentState::Connecting(connect), }) } - fn make_connect(jid: Jid, password: String, server: &str, port: u16, handle: Handle) -> Box> { + fn make_connect(jid: Jid, password: String, server: &str, port: u16) -> Box> { let jid1 = jid.clone(); let password = password; Box::new( - Connecter::from_lookup(handle, server, "_xmpp-component._tcp", port) + Connecter::from_lookup(server, "_xmpp-component._tcp", port) .expect("Connector::from_lookup") .and_then(move |tcp_stream| { xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_COMPONENT_ACCEPT.to_owned()) diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 7b95f8232402aa51a486aa83d312215af521160b..9edd2ca5b37b3dc56409a4c3eb079ef43e805d7b 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -1,37 +1,46 @@ -use std::str::FromStr; -use std::collections::HashMap; -use std::net::SocketAddr; -use futures::{Future, Poll, Async, Stream}; -use tokio_core::reactor::Handle; -use tokio_core::net::{TcpStream, TcpStreamNew}; -use domain::resolv::Resolver; -use domain::resolv::lookup::srv::{lookup_srv, LookupSrv, LookupSrvStream}; -use domain::bits::DNameBuf; +use std::mem; +use std::net::{SocketAddr, IpAddr}; +use std::collections::{BTreeMap, btree_map}; +use std::collections::VecDeque; +use futures::{Future, Poll, Async}; +use tokio::net::{ConnectFuture, TcpStream}; +use trust_dns_resolver::{IntoName, Name, ResolverFuture, error::ResolveError}; +use trust_dns_resolver::lookup::SrvLookupFuture; +use trust_dns_resolver::lookup_ip::LookupIpFuture; +use trust_dns_proto::rr::rdata::srv::SRV; pub struct Connecter { - handle: Handle, - resolver: Resolver, - lookup: Option, - srvs: Option, - connects: HashMap, + fallback_port: u16, + name: Name, + domain: Name, + resolver_future: Box + Send>, + resolver_opt: Option, + srv_lookup_opt: Option, + srvs_opt: Option>, + ip_lookup_opt: Option<(u16, LookupIpFuture)>, + ips_opt: Option<(u16, VecDeque)>, + connect_opt: Option, } impl Connecter { - pub fn from_lookup(handle: Handle, domain: &str, srv: &str, fallback_port: u16) -> Result { - let domain = DNameBuf::from_str(domain) - .map_err(|e| format!("{}", e))?; - let srv = DNameBuf::from_str(srv) - .map_err(|e| format!("{}", e))?; + pub fn from_lookup(domain: &str, srv: &str, fallback_port: u16) -> Result { + let resolver_future = ResolverFuture::from_system_conf() + .map_err(|e| format!("Configure resolver: {:?}", e))?; - let resolver = Resolver::new(&handle); - let lookup = lookup_srv(resolver.clone(), srv, domain, fallback_port); + let name = format!("{}.{}.", srv, domain).into_name() + .map_err(|e| format!("Parse service name: {:?}", e))?; Ok(Connecter { - handle, - resolver, - lookup: Some(lookup), - srvs: None, - connects: HashMap::new(), + fallback_port, + name, + domain: domain.into_name().map_err(|e| format!("Parse domain name: {:?}", e))?, + resolver_future, + resolver_opt: None, + srv_lookup_opt: None, + srvs_opt: None, + ip_lookup_opt: None, + ips_opt: None, + connect_opt: None, }) } } @@ -41,69 +50,104 @@ impl Future for Connecter { type Error = String; fn poll(&mut self) -> Poll { - match self.lookup.as_mut().map(|lookup| lookup.poll()) { - None | Some(Ok(Async::NotReady)) => (), - Some(Ok(Async::Ready(found_srvs))) => { - self.lookup = None; - match found_srvs { - Some(srvs) => - self.srvs = Some(srvs.to_stream(self.resolver.clone())), - None => - return Err("No SRV records".to_owned()), + if self.resolver_opt.is_none() { + //println!("Poll resolver future"); + match self.resolver_future.poll() { + Ok(Async::Ready(resolver)) => { + self.resolver_opt = Some(resolver); } - }, - Some(Err(e)) => - return Err(format!("{}", e)), + Ok(Async::NotReady) => return Ok(Async::NotReady), + Err(e) => return Err(format!("Cann't get resolver: {:?}", e)), + } } - match self.srvs.as_mut().map(|srv| srv.poll()) { - None | Some(Ok(Async::NotReady)) => (), - Some(Ok(Async::Ready(None))) => - self.srvs = None, - Some(Ok(Async::Ready(Some(srv_item)))) => { - let handle = &self.handle; - for addr in srv_item.to_socket_addrs() { - self.connects.entry(addr) - .or_insert_with(|| { - // println!("Connect to {}", addr); - TcpStream::connect(&addr, handle) - }); + if let Some(ref resolver) = self.resolver_opt { + if self.srvs_opt.is_none() { + if self.srv_lookup_opt.is_none() { + //println!("Lookup srv: {:?}", self.name); + self.srv_lookup_opt = Some(resolver.lookup_srv(&self.name)); } - }, - Some(Err(e)) => - return Err(format!("{}", e)), - } - let mut connected_stream = None; - self.connects.retain(|_, connect| { - if connected_stream.is_some() { - return false; + if let Some(ref mut srv_lookup) = self.srv_lookup_opt { + match srv_lookup.poll() { + Ok(Async::Ready(t)) => { + let mut srvs = BTreeMap::new(); + for srv in t.iter() { + srvs.insert(srv.priority(), srv.clone()); + } + srvs.insert(65535, SRV::new(65535, 0, self.fallback_port, self.domain.clone())); + self.srvs_opt = Some(srvs.into_iter()); + } + Ok(Async::NotReady) => return Ok(Async::NotReady), + Err(_) => { + //println!("Ignore SVR error: {:?}", e); + let mut srvs = BTreeMap::new(); + srvs.insert(65535, SRV::new(65535, 0, self.fallback_port, self.domain.clone())); + self.srvs_opt = Some(srvs.into_iter()); + }, + } + } + } + + if self.connect_opt.is_none() { + if self.ips_opt.is_none() { + if self.ip_lookup_opt.is_none() { + if let Some(ref mut srvs) = self.srvs_opt { + if let Some((_, srv)) = srvs.next() { + //println!("Lookup ip: {:?}", srv); + self.ip_lookup_opt = Some((srv.port(), resolver.lookup_ip(srv.target()))); + } else { + return Err("Cann't connect".to_string()); + } + } + } + + if let Some((port, mut ip_lookup)) = mem::replace(&mut self.ip_lookup_opt, None) { + match ip_lookup.poll() { + Ok(Async::Ready(t)) => { + let mut ip_deque = VecDeque::new(); + ip_deque.extend(t.iter()); + //println!("IPs: {:?}", ip_deque); + self.ips_opt = Some((port, ip_deque)); + self.ip_lookup_opt = None; + }, + Ok(Async::NotReady) => { + self.ip_lookup_opt = Some((port, ip_lookup)); + return Ok(Async::NotReady) + }, + Err(_) => { + //println!("Ignore lookup error: {:?}", e); + self.ip_lookup_opt = None; + } + } + } + } + + if let Some((port, mut ip_deque)) = mem::replace(&mut self.ips_opt, None) { + if let Some(ip) = ip_deque.pop_front() { + //println!("Connect to {:?}:{}", ip, port); + self.connect_opt = Some(TcpStream::connect(&SocketAddr::new(ip, port))); + self.ips_opt = Some((port, ip_deque)); + } + } } - match connect.poll() { - Ok(Async::NotReady) => true, - Ok(Async::Ready(tcp_stream)) => { - // Success! - connected_stream = Some(tcp_stream); - false - }, - Err(_e) => { - // println!("{}", _e); - false - }, + if let Some(mut connect_future) = mem::replace(&mut self.connect_opt, None) { + match connect_future.poll() { + Ok(Async::Ready(t)) => return Ok(Async::Ready(t)), + Ok(Async::NotReady) => { + self.connect_opt = Some(connect_future); + return Ok(Async::NotReady) + } + Err(_) => { + //println!("Ignore connect error: {:?}", e); + }, + } } - }); - if let Some(tcp_stream) = connected_stream { - return Ok(Async::Ready(tcp_stream)); - } - if self.lookup.is_none() && - self.srvs.is_none() && - self.connects.is_empty() - { - return Err("All connection attempts failed".to_owned()); } Ok(Async::NotReady) } } + diff --git a/src/lib.rs b/src/lib.rs index ebba1f1bd9baf5f1f77197ff0aff7bd684a99c72..13f374dbd9acebeef45b9261a56eb15e2c0d08e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ //! XMPP implemeentation with asynchronous I/O using Tokio. extern crate futures; -extern crate tokio_core; +extern crate tokio; extern crate tokio_io; extern crate tokio_codec; extern crate bytes; @@ -14,7 +14,8 @@ extern crate native_tls; extern crate tokio_tls; extern crate sasl; extern crate jid; -extern crate domain; +extern crate trust_dns_resolver; +extern crate trust_dns_proto; extern crate idna; extern crate xmpp_parsers; extern crate try_from; diff --git a/src/starttls.rs b/src/starttls.rs index 6ac23acfb4d2025461005633c6a15b0969d08a24..1d56a9cdb3bcb5cced18636a1be4cdf27d45f62d 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -3,8 +3,8 @@ use futures::{Future, Sink, Poll, Async}; use futures::stream::Stream; use futures::sink; use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_tls::{TlsStream, TlsConnectorExt, ConnectAsync}; -use native_tls::TlsConnector; +use tokio_tls::{TlsStream, TlsConnector, Connect}; +use native_tls::TlsConnector as NativeTlsConnector; use minidom::Element; use jid::Jid; @@ -25,7 +25,7 @@ enum StartTlsClientState { Invalid, SendStartTls(sink::Send>), AwaitProceed(XMPPStream), - StartingTls(ConnectAsync), + StartingTls(Connect), } impl StartTlsClient { @@ -53,7 +53,7 @@ impl Future for StartTlsClient { fn poll(&mut self) -> Poll { let old_state = replace(&mut self.state, StartTlsClientState::Invalid); let mut retry = false; - + let (new_state, result) = match old_state { StartTlsClientState::SendStartTls(mut send) => match send.poll() { @@ -73,9 +73,9 @@ impl Future for StartTlsClient { if stanza.name() == "proceed" => { let stream = xmpp_stream.stream.into_inner(); - let connect = TlsConnector::builder().unwrap() - .build().unwrap() - .connect_async(&self.jid.domain, stream); + let connect = TlsConnector::from(NativeTlsConnector::builder() + .build().unwrap()) + .connect(&self.jid.domain, stream); let new_state = StartTlsClientState::StartingTls(connect); retry = true; (new_state, Ok(Async::NotReady)) From e9d30f16c3b6f8cceb355834b22daef6cd329ebb Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 6 Sep 2018 17:46:06 +0200 Subject: [PATCH 0725/1020] unstringify Error type --- Cargo.toml | 1 + src/client/auth.rs | 33 +++++++++------ src/client/bind.rs | 10 ++--- src/client/mod.rs | 85 +++++++++++++++++++------------------- src/component/auth.rs | 13 +++--- src/component/mod.rs | 43 +++++++++----------- src/error.rs | 94 +++++++++++++++++++++++++++++++++++++++++++ src/happy_eyeballs.rs | 21 +++++----- src/lib.rs | 6 ++- src/starttls.rs | 9 +++-- src/stream_start.rs | 74 ++++++++++++++++++++++++++++++---- src/xmpp_codec.rs | 57 ++------------------------ 12 files changed, 279 insertions(+), 167 deletions(-) create mode 100644 src/error.rs diff --git a/Cargo.toml b/Cargo.toml index 02a706d46ee65b2090cf5b1418f4ad65f0c8f653..f348e860a9ad7434385abc70a24dbcb4adc0fa34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,4 @@ xmpp-parsers = "0.11" idna = "0.1" try_from = "0.2" quick-xml = "0.12" +derive-error = "0.0.4" diff --git a/src/client/auth.rs b/src/client/auth.rs index 0dc66d1654455c29bf20e5ab0857d020fecdd324..97a6a12d30ba918adab6f3642cc540e0551e9092 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -13,9 +13,11 @@ use try_from::TryFrom; use xmpp_codec::Packet; use xmpp_stream::XMPPStream; use stream_start::StreamStart; +use {Error, AuthError, ProtocolError}; const NS_XMPP_SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; + pub struct ClientAuth { state: ClientAuthState, mechanism: Box, @@ -29,7 +31,7 @@ enum ClientAuthState { } impl ClientAuth { - pub fn new(stream: XMPPStream, creds: Credentials) -> Result { + pub fn new(stream: XMPPStream, creds: Credentials) -> Result { let mechs: Vec> = vec![ Box::new(Scram::::from_credentials(creds.clone()).unwrap()), Box::new(Scram::::from_credentials(creds.clone()).unwrap()), @@ -40,7 +42,7 @@ impl ClientAuth { let mech_names: Vec = match stream.stream_features.get_child("mechanisms", NS_XMPP_SASL) { None => - return Err("No auth mechanisms".to_owned()), + return Err(AuthError::NoMechanism.into()), Some(mechs) => mechs.children() .filter(|child| child.is("mechanism", NS_XMPP_SASL)) @@ -53,13 +55,18 @@ impl ClientAuth { let name = mech.name().to_owned(); if mech_names.iter().any(|name1| *name1 == name) { // println!("SASL mechanism selected: {:?}", name); - let initial = mech.initial()?; + let initial = match mech.initial() { + Ok(initial) => initial, + Err(e) => return Err(AuthError::Sasl(e).into()), + }; let mut this = ClientAuth { state: ClientAuthState::Invalid, mechanism: mech, }; - let mechanism = XMPPMechanism::from_str(&name) - .map_err(|e| format!("{:?}", e))?; + let mechanism = match XMPPMechanism::from_str(&name) { + Ok(mechanism) => mechanism, + Err(e) => return Err(ProtocolError::Parsers(e).into()), + }; this.send( stream, Auth { @@ -71,7 +78,7 @@ impl ClientAuth { } } - Err("No supported SASL mechanism available".to_owned()) + Err(AuthError::NoMechanism.into()) } fn send>(&mut self, stream: XMPPStream, nonza: N) { @@ -83,7 +90,7 @@ impl ClientAuth { impl Future for ClientAuth { type Item = XMPPStream; - type Error = String; + type Error = Error; fn poll(&mut self) -> Poll { let state = replace(&mut self.state, ClientAuthState::Invalid); @@ -100,13 +107,14 @@ impl Future for ClientAuth { Ok(Async::NotReady) }, Err(e) => - Err(format!("{}", e)), + Err(e.into()), }, ClientAuthState::WaitRecv(mut stream) => match stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { if let Ok(challenge) = Challenge::try_from(stanza.clone()) { - let response = self.mechanism.response(&challenge.data)?; + let response = self.mechanism.response(&challenge.data) + .map_err(AuthError::Sasl)?; self.send(stream, Response { data: response }); self.poll() } else if let Ok(_) = Success::try_from(stanza.clone()) { @@ -114,8 +122,7 @@ impl Future for ClientAuth { self.state = ClientAuthState::Start(start); self.poll() } else if let Ok(failure) = Failure::try_from(stanza) { - let e = format!("{:?}", failure.defined_condition); - Err(e) + Err(AuthError::Fail(failure.defined_condition).into()) } else { Ok(Async::NotReady) } @@ -129,7 +136,7 @@ impl Future for ClientAuth { Ok(Async::NotReady) }, Err(e) => - Err(format!("{}", e)), + Err(ProtocolError::Parser(e).into()) }, ClientAuthState::Start(mut start) => match start.poll() { @@ -140,7 +147,7 @@ impl Future for ClientAuth { Ok(Async::NotReady) }, Err(e) => - Err(format!("{}", e)), + Err(e.into()) }, ClientAuthState::Invalid => unreachable!(), diff --git a/src/client/bind.rs b/src/client/bind.rs index 1ef268cc6a4f48f8ef6e01b02bba7d7ec297370b..974860befe8a616ad1f55e1bdb69aa5a312e531a 100644 --- a/src/client/bind.rs +++ b/src/client/bind.rs @@ -1,5 +1,4 @@ use std::mem::replace; -use std::error::Error; use futures::{Future, Poll, Async, sink, Stream}; use tokio_io::{AsyncRead, AsyncWrite}; use xmpp_parsers::iq::{Iq, IqType}; @@ -8,6 +7,7 @@ use try_from::TryFrom; use xmpp_codec::Packet; use xmpp_stream::XMPPStream; +use {Error, ProtocolError}; const NS_XMPP_BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind"; const BIND_REQ_ID: &str = "resource-bind"; @@ -42,7 +42,7 @@ impl ClientBind { impl Future for ClientBind { type Item = XMPPStream; - type Error = String; + type Error = Error; fn poll(&mut self) -> Poll { let state = replace(self, ClientBind::Invalid); @@ -61,7 +61,7 @@ impl Future for ClientBind { Ok(Async::NotReady) }, Err(e) => - Err(e.description().to_owned()), + Err(e.into()) } }, ClientBind::WaitRecv(mut stream) => { @@ -80,7 +80,7 @@ impl Future for ClientBind { Ok(Async::Ready(stream)) }, _ => - Err("resource bind response".to_owned()), + Err(ProtocolError::InvalidBindResponse.into()), } } else { Ok(Async::NotReady) @@ -96,7 +96,7 @@ impl Future for ClientBind { Ok(Async::NotReady) }, Err(e) => - Err(e.description().to_owned()), + Err(e.into()), } }, ClientBind::Invalid => diff --git a/src/client/mod.rs b/src/client/mod.rs index 44dc8d0c34dd488fb066c38f8bfba965b2488008..b50e5392f60df95fd15bd98f26e44075e1cd77a6 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,11 +1,11 @@ use std::mem::replace; use std::str::FromStr; -use std::error::Error; +use std::error::Error as StdError; use tokio_core::reactor::Handle; use tokio_core::net::TcpStream; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_tls::TlsStream; -use futures::{future, Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; +use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink, done}; use minidom::Element; use jid::{Jid, JidParseError}; use sasl::common::{Credentials, ChannelBinding}; @@ -16,6 +16,7 @@ use super::xmpp_stream; use super::starttls::{NS_XMPP_TLS, StartTlsClient}; use super::happy_eyeballs::Connecter; use super::event::Event; +use super::{Error, ProtocolError}; mod auth; use self::auth::ClientAuth; @@ -35,7 +36,7 @@ const NS_JABBER_CLIENT: &str = "jabber:client"; enum ClientState { Invalid, Disconnected, - Connecting(Box>), + Connecting(Box>), Connected(XMPPStream), } @@ -50,47 +51,47 @@ impl Client { let connect = Self::make_connect(jid.clone(), password.clone(), handle); Ok(Client { jid, - state: ClientState::Connecting(connect), + state: ClientState::Connecting(Box::new(connect)), }) } - fn make_connect(jid: Jid, password: String, handle: Handle) -> Box> { + fn make_connect(jid: Jid, password: String, handle: Handle) -> impl Future { let username = jid.node.as_ref().unwrap().to_owned(); let jid1 = jid.clone(); let jid2 = jid.clone(); let password = password; - let domain = match idna::domain_to_ascii(&jid.domain) { - Ok(domain) => - domain, - Err(e) => - return Box::new(future::err(format!("{:?}", e))), - }; - Box::new( - Connecter::from_lookup(handle, &domain, "_xmpp-client._tcp", 5222) - .expect("Connector::from_lookup") - .and_then(move |tcp_stream| - xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_CLIENT.to_owned()) - .map_err(|e| format!("{}", e)) - ).and_then(|xmpp_stream| { - if Self::can_starttls(&xmpp_stream) { - Ok(Self::starttls(xmpp_stream)) - } else { - Err("No STARTTLS".to_owned()) - } - }).and_then(|starttls| - starttls - ).and_then(|tls_stream| - XMPPStream::start(tls_stream, jid2, NS_JABBER_CLIENT.to_owned()) - .map_err(|e| format!("{}", e)) - ).and_then(move |xmpp_stream| { - Self::auth(xmpp_stream, username, password).expect("auth") - }).and_then(|xmpp_stream| { - Self::bind(xmpp_stream) - }).and_then(|xmpp_stream| { - // println!("Bound to {}", xmpp_stream.jid); - Ok(xmpp_stream) - }) - ) + done(idna::domain_to_ascii(&jid.domain)) + .map_err(|_| Error::Idna) + .and_then(|domain| + done(Connecter::from_lookup(handle, &domain, "_xmpp-client._tcp", 5222)) + .map_err(Error::Domain) + ) + .and_then(|connecter| + connecter + .map_err(Error::Connection) + ).and_then(move |tcp_stream| + xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_CLIENT.to_owned()) + ).and_then(|xmpp_stream| { + if Self::can_starttls(&xmpp_stream) { + Ok(Self::starttls(xmpp_stream)) + } else { + Err(Error::Protocol(ProtocolError::NoTls)) + } + }).and_then(|starttls| + // TODO: flatten? + starttls + ).and_then(|tls_stream| + XMPPStream::start(tls_stream, jid2, NS_JABBER_CLIENT.to_owned()) + ).and_then(move |xmpp_stream| + done(Self::auth(xmpp_stream, username, password)) + // TODO: flatten? + ).and_then(|auth| auth) + .and_then(|xmpp_stream| { + Self::bind(xmpp_stream) + }).and_then(|xmpp_stream| { + // println!("Bound to {}", xmpp_stream.jid); + Ok(xmpp_stream) + }) } fn can_starttls(stream: &xmpp_stream::XMPPStream) -> bool { @@ -103,7 +104,7 @@ impl Client { StartTlsClient::from_stream(stream) } - fn auth(stream: xmpp_stream::XMPPStream, username: String, password: String) -> Result, String> { + fn auth(stream: xmpp_stream::XMPPStream, username: String, password: String) -> Result, Error> { let creds = Credentials::default() .with_username(username) .with_password(password) @@ -118,14 +119,14 @@ impl Client { impl Stream for Client { type Item = Event; - type Error = String; + type Error = Error; fn poll(&mut self) -> Poll, Self::Error> { let state = replace(&mut self.state, ClientState::Invalid); match state { ClientState::Invalid => - Err("invalid client state".to_owned()), + Err(Error::InvalidState), ClientState::Disconnected => Ok(Async::Ready(None)), ClientState::Connecting(mut connect) => { @@ -148,7 +149,7 @@ impl Stream for Client { Ok(Async::NotReady) => (), Ok(Async::Ready(())) => (), Err(e) => - return Err(e.description().to_owned()), + return Err(Error::Io(e)), }; // Poll stream @@ -168,7 +169,7 @@ impl Stream for Client { Ok(Async::NotReady) }, Err(e) => - Err(e.description().to_owned()), + Err(e.into()), } }, } diff --git a/src/component/auth.rs b/src/component/auth.rs index 7801c92f2dd4538a9c551b62b21c5f0e2cba1e52..3b66511450527beff1c4901b68b6027d50414a1e 100644 --- a/src/component/auth.rs +++ b/src/component/auth.rs @@ -5,6 +5,7 @@ use xmpp_parsers::component::Handshake; use xmpp_codec::Packet; use xmpp_stream::XMPPStream; +use {Error, AuthError}; const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; @@ -19,7 +20,8 @@ enum ComponentAuthState { } impl ComponentAuth { - pub fn new(stream: XMPPStream, password: String) -> Result { + // TODO: doesn't have to be a Result<> actually + pub fn new(stream: XMPPStream, password: String) -> Result { // FIXME: huge hack, shouldn’t be an element! let sid = stream.stream_features.name().to_owned(); let mut this = ComponentAuth { @@ -42,7 +44,7 @@ impl ComponentAuth { impl Future for ComponentAuth { type Item = XMPPStream; - type Error = String; + type Error = Error; fn poll(&mut self) -> Poll { let state = replace(&mut self.state, ComponentAuthState::Invalid); @@ -59,7 +61,7 @@ impl Future for ComponentAuth { Ok(Async::NotReady) }, Err(e) => - Err(format!("{}", e)), + Err(e.into()), }, ComponentAuthState::WaitRecv(mut stream) => match stream.poll() { @@ -72,8 +74,7 @@ impl Future for ComponentAuth { Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) if stanza.is("error", "http://etherx.jabber.org/streams") => { - let e = "Authentication failure"; - Err(e.to_owned()) + Err(AuthError::ComponentFail.into()) }, Ok(Async::Ready(event)) => { println!("ComponentAuth ignore {:?}", event); @@ -84,7 +85,7 @@ impl Future for ComponentAuth { Ok(Async::NotReady) }, Err(e) => - Err(format!("{}", e)), + Err(e.into()), }, ComponentAuthState::Invalid => unreachable!(), diff --git a/src/component/mod.rs b/src/component/mod.rs index bf9a005f24ce2bf3873edcab11304d83e030cecc..08ea6343871c21df540d43ddd49e314d1f53f991 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -3,11 +3,11 @@ //! allowed to use any user and resource identifiers in their stanzas. use std::mem::replace; use std::str::FromStr; -use std::error::Error; +use std::error::Error as StdError; use tokio_core::reactor::Handle; use tokio_core::net::TcpStream; use tokio_io::{AsyncRead, AsyncWrite}; -use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; +use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink, done}; use minidom::Element; use jid::{Jid, JidParseError}; @@ -15,6 +15,7 @@ use super::xmpp_codec::Packet; use super::xmpp_stream; use super::happy_eyeballs::Connecter; use super::event::Event; +use super::Error; mod auth; use self::auth::ComponentAuth; @@ -32,7 +33,7 @@ const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; enum ComponentState { Invalid, Disconnected, - Connecting(Box>), + Connecting(Box>), Connected(XMPPStream), } @@ -47,43 +48,39 @@ impl Component { let connect = Self::make_connect(jid.clone(), password, server, port, handle); Ok(Component { jid, - state: ComponentState::Connecting(connect), + state: ComponentState::Connecting(Box::new(connect)), }) } - fn make_connect(jid: Jid, password: String, server: &str, port: u16, handle: Handle) -> Box> { + fn make_connect(jid: Jid, password: String, server: &str, port: u16, handle: Handle) -> impl Future { let jid1 = jid.clone(); let password = password; - Box::new( - Connecter::from_lookup(handle, server, "_xmpp-component._tcp", port) - .expect("Connector::from_lookup") - .and_then(move |tcp_stream| { - xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_COMPONENT_ACCEPT.to_owned()) - .map_err(|e| format!("{}", e)) - }).and_then(move |xmpp_stream| { - Self::auth(xmpp_stream, password).expect("auth") - }).and_then(|xmpp_stream| { - // println!("Bound to {}", xmpp_stream.jid); - Ok(xmpp_stream) - }) - ) + done(Connecter::from_lookup(handle, server, "_xmpp-component._tcp", port)) + .map_err(Error::Domain) + .and_then(|connecter| connecter + .map_err(Error::Connection) + ).and_then(move |tcp_stream| { + xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_COMPONENT_ACCEPT.to_owned()) + }).and_then(move |xmpp_stream| { + Self::auth(xmpp_stream, password).expect("auth") + }) } - fn auth(stream: xmpp_stream::XMPPStream, password: String) -> Result, String> { + fn auth(stream: xmpp_stream::XMPPStream, password: String) -> Result, Error> { ComponentAuth::new(stream, password) } } impl Stream for Component { type Item = Event; - type Error = String; + type Error = Error; fn poll(&mut self) -> Poll, Self::Error> { let state = replace(&mut self.state, ComponentState::Invalid); match state { ComponentState::Invalid => - Err("invalid client state".to_owned()), + Err(Error::InvalidState), ComponentState::Disconnected => Ok(Async::Ready(None)), ComponentState::Connecting(mut connect) => { @@ -106,7 +103,7 @@ impl Stream for Component { Ok(Async::NotReady) => (), Ok(Async::Ready(())) => (), Err(e) => - return Err(e.description().to_owned()), + return Err(e.into()), }; // Poll stream @@ -129,7 +126,7 @@ impl Stream for Component { Ok(Async::NotReady) }, Err(e) => - Err(e.description().to_owned()), + Err(e.into()), } }, } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..7384f92ee71b2ad8382b57418d1d48c296dcf104 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,94 @@ +use std::io::Error as IoError; +use std::error::Error as StdError; +use std::str::Utf8Error; +use std::borrow::Cow; +use std::fmt; +use domain::resolv::error::Error as DNSError; +use domain::bits::name::FromStrError; +use native_tls::Error as TlsError; +use xmpp_parsers::error::Error as ParsersError; +use xmpp_parsers::sasl::DefinedCondition as SaslDefinedCondition; + +#[derive(Debug, Error)] +pub enum Error { + Io(IoError), + Connection(ConnecterError), + /// DNS label conversion error, no details available from module + /// `idna` + Idna, + Domain(FromStrError), + Protocol(ProtocolError), + Auth(AuthError), + Tls(TlsError), + /// Shoud never happen + InvalidState, +} + +/// Causes for stream parsing errors +#[derive(Debug, Error)] +pub enum ParserError { + /// Encoding error + Utf8(Utf8Error), + /// XML parse error + Parse(ParseError), + /// Illegal `` + ShortTag, + /// Required by `impl Decoder` + IO(IoError), +} + +impl From for Error { + fn from(e: ParserError) -> Self { + ProtocolError::Parser(e).into() + } +} + +/// XML parse error wrapper type +#[derive(Debug)] +pub struct ParseError(pub Cow<'static, str>); + +impl StdError for ParseError { + fn description(&self) -> &str { + self.0.as_ref() + } + fn cause(&self) -> Option<&StdError> { + None + } +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +#[derive(Debug, Error)] +pub enum ProtocolError { + Parser(ParserError), + #[error(non_std)] + Parsers(ParsersError), + NoTls, + InvalidBindResponse, + NoStreamNamespace, + NoStreamId, + InvalidToken, +} + +#[derive(Debug, Error)] +pub enum AuthError { + /// No SASL mechanism available + NoMechanism, + #[error(no_from, non_std, msg_embedded)] + Sasl(String), + #[error(non_std)] + Fail(SaslDefinedCondition), + #[error(no_from)] + ComponentFail, +} + +#[derive(Debug, Error)] +pub enum ConnecterError { + NoSrv, + AllFailed, + DNS(DNSError), +} diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 7b95f8232402aa51a486aa83d312215af521160b..498079323f09ba10a159e9e77d59e934b7e2dc73 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -6,7 +6,8 @@ use tokio_core::reactor::Handle; use tokio_core::net::{TcpStream, TcpStreamNew}; use domain::resolv::Resolver; use domain::resolv::lookup::srv::{lookup_srv, LookupSrv, LookupSrvStream}; -use domain::bits::DNameBuf; +use domain::bits::name::{DNameBuf, FromStrError}; +use ConnecterError; pub struct Connecter { handle: Handle, @@ -17,11 +18,9 @@ pub struct Connecter { } impl Connecter { - pub fn from_lookup(handle: Handle, domain: &str, srv: &str, fallback_port: u16) -> Result { - let domain = DNameBuf::from_str(domain) - .map_err(|e| format!("{}", e))?; - let srv = DNameBuf::from_str(srv) - .map_err(|e| format!("{}", e))?; + pub fn from_lookup(handle: Handle, domain: &str, srv: &str, fallback_port: u16) -> Result { + let domain = DNameBuf::from_str(domain)?; + let srv = DNameBuf::from_str(srv)?; let resolver = Resolver::new(&handle); let lookup = lookup_srv(resolver.clone(), srv, domain, fallback_port); @@ -38,7 +37,7 @@ impl Connecter { impl Future for Connecter { type Item = TcpStream; - type Error = String; + type Error = ConnecterError; fn poll(&mut self) -> Poll { match self.lookup.as_mut().map(|lookup| lookup.poll()) { @@ -49,11 +48,11 @@ impl Future for Connecter { Some(srvs) => self.srvs = Some(srvs.to_stream(self.resolver.clone())), None => - return Err("No SRV records".to_owned()), + return Err(ConnecterError::NoSrv), } }, Some(Err(e)) => - return Err(format!("{}", e)), + return Err(e.into()), } match self.srvs.as_mut().map(|srv| srv.poll()) { @@ -71,7 +70,7 @@ impl Future for Connecter { } }, Some(Err(e)) => - return Err(format!("{}", e)), + return Err(e.into()), } let mut connected_stream = None; @@ -101,7 +100,7 @@ impl Future for Connecter { self.srvs.is_none() && self.connects.is_empty() { - return Err("All connection attempts failed".to_owned()); + return Err(ConnecterError::AllFailed); } Ok(Async::NotReady) diff --git a/src/lib.rs b/src/lib.rs index ebba1f1bd9baf5f1f77197ff0aff7bd684a99c72..19a0da68c2c35b0d89e648d70d85428a6b4e6e60 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![deny(unsafe_code, unused, missing_docs)] +// #![deny(unsafe_code, unused, missing_docs)] //! XMPP implemeentation with asynchronous I/O using Tokio. @@ -18,6 +18,8 @@ extern crate domain; extern crate idna; extern crate xmpp_parsers; extern crate try_from; +#[macro_use] +extern crate derive_error; pub mod xmpp_codec; pub mod xmpp_stream; @@ -31,3 +33,5 @@ mod client; pub use client::Client; mod component; pub use component::Component; +mod error; +pub use error::{Error, ProtocolError, AuthError, ConnecterError, ParseError, ParserError}; diff --git a/src/starttls.rs b/src/starttls.rs index 6ac23acfb4d2025461005633c6a15b0969d08a24..444a9cb8b4b864f63f854ba0513afb1d1711adfd 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -10,6 +10,7 @@ use jid::Jid; use xmpp_codec::Packet; use xmpp_stream::XMPPStream; +use Error; /// XMPP TLS XML namespace pub const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; @@ -48,7 +49,7 @@ impl StartTlsClient { impl Future for StartTlsClient { type Item = TlsStream; - type Error = String; + type Error = Error; fn poll(&mut self) -> Poll { let old_state = replace(&mut self.state, StartTlsClientState::Invalid); @@ -65,7 +66,7 @@ impl Future for StartTlsClient { Ok(Async::NotReady) => (StartTlsClientState::SendStartTls(send), Ok(Async::NotReady)), Err(e) => - (StartTlsClientState::SendStartTls(send), Err(format!("{}", e))), + (StartTlsClientState::SendStartTls(send), Err(e.into())), }, StartTlsClientState::AwaitProceed(mut xmpp_stream) => match xmpp_stream.poll() { @@ -87,7 +88,7 @@ impl Future for StartTlsClient { Ok(_) => (StartTlsClientState::AwaitProceed(xmpp_stream), Ok(Async::NotReady)), Err(e) => - (StartTlsClientState::AwaitProceed(xmpp_stream), Err(format!("{}", e))), + (StartTlsClientState::AwaitProceed(xmpp_stream), Err(Error::Protocol(e.into()))), }, StartTlsClientState::StartingTls(mut connect) => match connect.poll() { @@ -96,7 +97,7 @@ impl Future for StartTlsClient { Ok(Async::NotReady) => (StartTlsClientState::StartingTls(connect), Ok(Async::NotReady)), Err(e) => - (StartTlsClientState::Invalid, Err(format!("{}", e))), + (StartTlsClientState::Invalid, Err(e.into())), }, StartTlsClientState::Invalid => unreachable!(), diff --git a/src/stream_start.rs b/src/stream_start.rs index aa1284e2f76634190d7440daccc86acb3512619a..8871840250b1f418dc58e8aaf9dd96d6dab35e3d 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -1,13 +1,15 @@ use std::mem::replace; -use std::borrow::Cow; +// use std::error::Error as StdError; +// use std::{fmt, io}; use futures::{Future, Async, Poll, Stream, sink, Sink}; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_codec::Framed; use jid::Jid; use minidom::Element; -use xmpp_codec::{XMPPCodec, Packet, ParserError}; +use xmpp_codec::{XMPPCodec, Packet}; use xmpp_stream::XMPPStream; +use {Error, ProtocolError}; const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; @@ -43,7 +45,7 @@ impl StreamStart { impl Future for StreamStart { type Item = XMPPStream; - type Error = ParserError; + type Error = Error; fn poll(&mut self) -> Poll { let old_state = replace(&mut self.state, StreamStartState::Invalid); @@ -67,7 +69,7 @@ impl Future for StreamStart { let stream_ns = match stream_attrs.get("xmlns") { Some(ns) => ns.clone(), None => - return Err(ParserError::Parse(Cow::from("Missing stream namespace"))), + return Err(ProtocolError::NoStreamNamespace.into()), }; if self.ns == "jabber:client" { retry = true; @@ -77,7 +79,7 @@ impl Future for StreamStart { let id = match stream_attrs.get("id") { Some(id) => id.clone(), None => - return Err(ParserError::Parse(Cow::from("No stream id"))), + return Err(ProtocolError::NoStreamId.into()), }; // FIXME: huge hack, shouldn’t be an element! let stream = XMPPStream::new(self.jid.clone(), stream, self.ns.clone(), Element::builder(id).build()); @@ -85,11 +87,11 @@ impl Future for StreamStart { } }, Ok(Async::Ready(_)) => - return Err(ParserError::Parse(Cow::from("Invalid XML event received"))), + return Err(ProtocolError::InvalidToken.into()), Ok(Async::NotReady) => (StreamStartState::RecvStart(stream), Ok(Async::NotReady)), Err(e) => - return Err(e), + return Err(ProtocolError::from(e).into()), }, StreamStartState::RecvFeatures(mut stream, stream_ns) => match stream.poll() { @@ -103,7 +105,7 @@ impl Future for StreamStart { Ok(Async::Ready(_)) | Ok(Async::NotReady) => (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)), Err(e) => - return Err(e), + return Err(ProtocolError::from(e).into()), }, StreamStartState::Invalid => unreachable!(), @@ -117,3 +119,59 @@ impl Future for StreamStart { } } } + +// #[derive(Debug)] +// pub enum StreamStartError { +// MissingStreamNs, +// MissingStreamId, +// Unexpected, +// Parser(ParserError), +// IO(io::Error), +// } + +// impl From for StreamStartError { +// fn from(e: io::Error) -> Self { +// StreamStartError::IO(e) +// } +// } + +// impl From for StreamStartError { +// fn from(e: ParserError) -> Self { +// match e { +// ParserError::IO(e) => StreamStartError::IO(e), +// _ => StreamStartError::Parser(e) +// } +// } +// } + +// impl StdError for StreamStartError { +// fn description(&self) -> &str { +// match *self { +// StreamStartError::MissingStreamNs => "Missing stream namespace", +// StreamStartError::MissingStreamId => "Missing stream id", +// StreamStartError::Unexpected => "Unexpected", +// StreamStartError::Parser(ref pe) => pe.description(), +// StreamStartError::IO(ref ie) => ie.description(), +// } +// } + +// fn cause(&self) -> Option<&StdError> { +// match *self { +// StreamStartError::Parser(ref pe) => pe.cause(), +// StreamStartError::IO(ref ie) => ie.cause(), +// _ => None, +// } +// } +// } + +// impl fmt::Display for StreamStartError { +// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +// match *self { +// StreamStartError::MissingStreamNs => write!(f, "Missing stream namespace"), +// StreamStartError::MissingStreamId => write!(f, "Missing stream id"), +// StreamStartError::Unexpected => write!(f, "Received unexpected data"), +// StreamStartError::Parser(ref pe) => write!(f, "{}", pe), +// StreamStartError::IO(ref ie) => write!(f, "{}", ie), +// } +// } +// } diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 09ea1ea4c123895ab253d3b8591fd13bef7e1a90..37e5a78c8dccb8d9d43ac6e2dc8bbc4d49205603 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -6,19 +6,17 @@ use std::iter::FromIterator; use std::cell::RefCell; use std::rc::Rc; use std::fmt::Write; -use std::str::{from_utf8, Utf8Error}; +use std::str::from_utf8; use std::io; use std::collections::HashMap; use std::collections::vec_deque::VecDeque; -use std::error::Error as StdError; -use std::fmt; -use std::borrow::Cow; use tokio_codec::{Encoder, Decoder}; use minidom::Element; use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind}; use xml5ever::interface::Attribute; use bytes::{BytesMut, BufMut}; use quick_xml::Writer as EventWriter; +use {ParserError, ParseError}; /// Anything that can be sent or received on an XMPP/XML stream #[derive(Debug)] @@ -33,55 +31,6 @@ pub enum Packet { StreamEnd, } -/// Causes for stream parsing errors -#[derive(Debug)] -pub enum ParserError { - /// Encoding error - Utf8(Utf8Error), - /// XML parse error - Parse(Cow<'static, str>), - /// Illegal `` - ShortTag, - /// Required by `impl Decoder` - IO(io::Error), -} - -impl From for ParserError { - fn from(e: io::Error) -> Self { - ParserError::IO(e) - } -} - -impl StdError for ParserError { - fn description(&self) -> &str { - match *self { - ParserError::Utf8(ref ue) => ue.description(), - ParserError::Parse(ref pe) => pe, - ParserError::ShortTag => "short tag", - ParserError::IO(ref ie) => ie.description(), - } - } - - fn cause(&self) -> Option<&StdError> { - match *self { - ParserError::Utf8(ref ue) => ue.cause(), - ParserError::IO(ref ie) => ie.cause(), - _ => None, - } - } -} - -impl fmt::Display for ParserError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ParserError::Utf8(ref ue) => write!(f, "{}", ue), - ParserError::Parse(ref pe) => write!(f, "{}", pe), - ParserError::ShortTag => write!(f, "Short tag"), - ParserError::IO(ref ie) => write!(f, "{}", ie), - } - } -} - type QueueItem = Result; /// Parser state @@ -220,7 +169,7 @@ impl TokenSink for ParserSink { self.push_queue(Packet::StreamEnd), Token::ParseError(s) => { // println!("ParseError: {:?}", s); - self.push_queue_error(ParserError::Parse(s)); + self.push_queue_error(ParserError::Parse(ParseError(s))); }, _ => (), } From f4007511ea32fba0dfca2074506459782f4a51e4 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 6 Sep 2018 23:57:42 +0200 Subject: [PATCH 0726/1020] improve style --- src/client/auth.rs | 37 +++++++++------------ src/client/bind.rs | 6 ++-- src/client/mod.rs | 6 ++-- src/component/auth.rs | 6 ++-- src/component/mod.rs | 6 ++-- src/happy_eyeballs.rs | 8 ++--- src/stream_start.rs | 76 ++++--------------------------------------- 7 files changed, 37 insertions(+), 108 deletions(-) diff --git a/src/client/auth.rs b/src/client/auth.rs index 97a6a12d30ba918adab6f3642cc540e0551e9092..55dfe2d2ac6a91c4137af9db756edcafd6ac904e 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -40,33 +40,26 @@ impl ClientAuth { ]; let mech_names: Vec = - match stream.stream_features.get_child("mechanisms", NS_XMPP_SASL) { - None => - return Err(AuthError::NoMechanism.into()), - Some(mechs) => - mechs.children() - .filter(|child| child.is("mechanism", NS_XMPP_SASL)) - .map(|mech_el| mech_el.text()) - .collect(), - }; + stream.stream_features.get_child("mechanisms", NS_XMPP_SASL) + .ok_or(AuthError::NoMechanism)? + .children() + .filter(|child| child.is("mechanism", NS_XMPP_SASL)) + .map(|mech_el| mech_el.text()) + .collect(); // println!("SASL mechanisms offered: {:?}", mech_names); for mut mech in mechs { let name = mech.name().to_owned(); if mech_names.iter().any(|name1| *name1 == name) { // println!("SASL mechanism selected: {:?}", name); - let initial = match mech.initial() { - Ok(initial) => initial, - Err(e) => return Err(AuthError::Sasl(e).into()), - }; + let initial = mech.initial() + .map_err(AuthError::Sasl)?; let mut this = ClientAuth { state: ClientAuthState::Invalid, mechanism: mech, }; - let mechanism = match XMPPMechanism::from_str(&name) { - Ok(mechanism) => mechanism, - Err(e) => return Err(ProtocolError::Parsers(e).into()), - }; + let mechanism = XMPPMechanism::from_str(&name) + .map_err(ProtocolError::Parsers)?; this.send( stream, Auth { @@ -78,7 +71,7 @@ impl ClientAuth { } } - Err(AuthError::NoMechanism.into()) + Err(AuthError::NoMechanism)? } fn send>(&mut self, stream: XMPPStream, nonza: N) { @@ -107,7 +100,7 @@ impl Future for ClientAuth { Ok(Async::NotReady) }, Err(e) => - Err(e.into()), + Err(e)?, }, ClientAuthState::WaitRecv(mut stream) => match stream.poll() { @@ -122,7 +115,7 @@ impl Future for ClientAuth { self.state = ClientAuthState::Start(start); self.poll() } else if let Ok(failure) = Failure::try_from(stanza) { - Err(AuthError::Fail(failure.defined_condition).into()) + Err(AuthError::Fail(failure.defined_condition))? } else { Ok(Async::NotReady) } @@ -136,7 +129,7 @@ impl Future for ClientAuth { Ok(Async::NotReady) }, Err(e) => - Err(ProtocolError::Parser(e).into()) + Err(ProtocolError::Parser(e))? }, ClientAuthState::Start(mut start) => match start.poll() { @@ -147,7 +140,7 @@ impl Future for ClientAuth { Ok(Async::NotReady) }, Err(e) => - Err(e.into()) + Err(e) }, ClientAuthState::Invalid => unreachable!(), diff --git a/src/client/bind.rs b/src/client/bind.rs index 974860befe8a616ad1f55e1bdb69aa5a312e531a..b0268e6eb777d6b66a6e8ee905a0a71975da682f 100644 --- a/src/client/bind.rs +++ b/src/client/bind.rs @@ -61,7 +61,7 @@ impl Future for ClientBind { Ok(Async::NotReady) }, Err(e) => - Err(e.into()) + Err(e)? } }, ClientBind::WaitRecv(mut stream) => { @@ -80,7 +80,7 @@ impl Future for ClientBind { Ok(Async::Ready(stream)) }, _ => - Err(ProtocolError::InvalidBindResponse.into()), + Err(ProtocolError::InvalidBindResponse)?, } } else { Ok(Async::NotReady) @@ -96,7 +96,7 @@ impl Future for ClientBind { Ok(Async::NotReady) }, Err(e) => - Err(e.into()), + Err(e)?, } }, ClientBind::Invalid => diff --git a/src/client/mod.rs b/src/client/mod.rs index e3a36528e30179c951e9270b2082db29a3f4b113..6c3226c291795afcfc8ad337eba15ff296285833 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -147,7 +147,7 @@ impl Stream for Client { Ok(Async::NotReady) => (), Ok(Async::Ready(())) => (), Err(e) => - return Err(e.into()), + return Err(e)?, }; // Poll stream @@ -167,7 +167,7 @@ impl Stream for Client { Ok(Async::NotReady) }, Err(e) => - Err(e.into()), + Err(e)?, } }, } @@ -190,7 +190,7 @@ impl Sink for Client { Ok(AsyncSink::Ready) }, Err(e) => - Err(e.into()), + Err(e)?, }, _ => Ok(AsyncSink::NotReady(item)), diff --git a/src/component/auth.rs b/src/component/auth.rs index 3b66511450527beff1c4901b68b6027d50414a1e..b108ef3ffffe120ca7c038873677b070055cebc0 100644 --- a/src/component/auth.rs +++ b/src/component/auth.rs @@ -31,7 +31,7 @@ impl ComponentAuth { stream, Handshake::from_password_and_stream_id(&password, &sid) ); - return Ok(this); + Ok(this) } fn send(&mut self, stream: XMPPStream, handshake: Handshake) { @@ -61,7 +61,7 @@ impl Future for ComponentAuth { Ok(Async::NotReady) }, Err(e) => - Err(e.into()), + Err(e)? }, ComponentAuthState::WaitRecv(mut stream) => match stream.poll() { @@ -85,7 +85,7 @@ impl Future for ComponentAuth { Ok(Async::NotReady) }, Err(e) => - Err(e.into()), + Err(e)? }, ComponentAuthState::Invalid => unreachable!(), diff --git a/src/component/mod.rs b/src/component/mod.rs index 150ffd9a4d46344a31f8a1eac93b8bab5cde61a3..088d20d5675f98a5d62e4a41813c9147362fe8d8 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -100,7 +100,7 @@ impl Stream for Component { Ok(Async::NotReady) => (), Ok(Async::Ready(())) => (), Err(e) => - return Err(e.into()), + return Err(e)?, }; // Poll stream @@ -123,7 +123,7 @@ impl Stream for Component { Ok(Async::NotReady) }, Err(e) => - Err(e.into()), + Err(e)?, } }, } @@ -146,7 +146,7 @@ impl Sink for Component { Ok(AsyncSink::Ready) }, Err(e) => - Err(e.into()), + Err(e)?, }, _ => Ok(AsyncSink::NotReady(item)), diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 0a5fd235873afd608556039106b55c4c3b04bf49..e3bebe9983fe5d789ca5da1b4ff4e83ff9f86cd0 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -50,13 +50,11 @@ impl Future for Connecter { fn poll(&mut self) -> Poll { if self.resolver_opt.is_none() { //println!("Poll resolver future"); - match self.resolver_future.poll() { - Ok(Async::Ready(resolver)) => + match self.resolver_future.poll()? { + Async::Ready(resolver) => self.resolver_opt = Some(resolver), - Ok(Async::NotReady) => + Async::NotReady => return Ok(Async::NotReady), - Err(e) => - return Err(e.into()), } } diff --git a/src/stream_start.rs b/src/stream_start.rs index 8871840250b1f418dc58e8aaf9dd96d6dab35e3d..ce9db27a8128eb42ac1296405c125379b5343cee 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -1,6 +1,4 @@ use std::mem::replace; -// use std::error::Error as StdError; -// use std::{fmt, io}; use futures::{Future, Async, Poll, Stream, sink, Sink}; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_codec::Framed; @@ -66,22 +64,18 @@ impl Future for StreamStart { StreamStartState::RecvStart(mut stream) => match stream.poll() { Ok(Async::Ready(Some(Packet::StreamStart(stream_attrs)))) => { - let stream_ns = match stream_attrs.get("xmlns") { - Some(ns) => ns.clone(), - None => - return Err(ProtocolError::NoStreamNamespace.into()), - }; + let stream_ns = stream_attrs.get("xmlns") + .ok_or(ProtocolError::NoStreamNamespace)? + .clone(); if self.ns == "jabber:client" { retry = true; // TODO: skip RecvFeatures for version < 1.0 (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)) } else { - let id = match stream_attrs.get("id") { - Some(id) => id.clone(), - None => - return Err(ProtocolError::NoStreamId.into()), - }; - // FIXME: huge hack, shouldn’t be an element! + let id = stream_attrs.get("id") + .ok_or(ProtocolError::NoStreamId)? + .clone(); + // FIXME: huge hack, shouldn’t be an element! let stream = XMPPStream::new(self.jid.clone(), stream, self.ns.clone(), Element::builder(id).build()); (StreamStartState::Invalid, Ok(Async::Ready(stream))) } @@ -119,59 +113,3 @@ impl Future for StreamStart { } } } - -// #[derive(Debug)] -// pub enum StreamStartError { -// MissingStreamNs, -// MissingStreamId, -// Unexpected, -// Parser(ParserError), -// IO(io::Error), -// } - -// impl From for StreamStartError { -// fn from(e: io::Error) -> Self { -// StreamStartError::IO(e) -// } -// } - -// impl From for StreamStartError { -// fn from(e: ParserError) -> Self { -// match e { -// ParserError::IO(e) => StreamStartError::IO(e), -// _ => StreamStartError::Parser(e) -// } -// } -// } - -// impl StdError for StreamStartError { -// fn description(&self) -> &str { -// match *self { -// StreamStartError::MissingStreamNs => "Missing stream namespace", -// StreamStartError::MissingStreamId => "Missing stream id", -// StreamStartError::Unexpected => "Unexpected", -// StreamStartError::Parser(ref pe) => pe.description(), -// StreamStartError::IO(ref ie) => ie.description(), -// } -// } - -// fn cause(&self) -> Option<&StdError> { -// match *self { -// StreamStartError::Parser(ref pe) => pe.cause(), -// StreamStartError::IO(ref ie) => ie.cause(), -// _ => None, -// } -// } -// } - -// impl fmt::Display for StreamStartError { -// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -// match *self { -// StreamStartError::MissingStreamNs => write!(f, "Missing stream namespace"), -// StreamStartError::MissingStreamId => write!(f, "Missing stream id"), -// StreamStartError::Unexpected => write!(f, "Received unexpected data"), -// StreamStartError::Parser(ref pe) => write!(f, "{}", pe), -// StreamStartError::IO(ref ie) => write!(f, "{}", ie), -// } -// } -// } From 81191041c48c56c27ebcdae8c6b61aa48cd3419a Mon Sep 17 00:00:00 2001 From: Astro Date: Fri, 7 Sep 2018 00:12:00 +0200 Subject: [PATCH 0727/1020] improve style: flatten future --- src/client/mod.rs | 8 +- src/component/mod.rs | 4 +- src/happy_eyeballs.rs | 233 ++++++++++++++++++++++++------------------ 3 files changed, 138 insertions(+), 107 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 6c3226c291795afcfc8ad337eba15ff296285833..cf4553a052f29aaeb67922f6eb39dd41434ece3f 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -61,7 +61,7 @@ impl Client { done(idna::domain_to_ascii(&jid.domain)) .map_err(|_| Error::Idna) .and_then(|domain| - done(Connecter::from_lookup(&domain, "_xmpp-client._tcp", 5222)) + done(Connecter::from_lookup(&domain, Some("_xmpp-client._tcp"), 5222)) .map_err(Error::Connection) ) .and_then(|connecter| @@ -75,10 +75,8 @@ impl Client { } else { Err(Error::Protocol(ProtocolError::NoTls)) } - }).and_then(|starttls| - // TODO: flatten? - starttls - ).and_then(|tls_stream| + }).flatten() + .and_then(|tls_stream| XMPPStream::start(tls_stream, jid2, NS_JABBER_CLIENT.to_owned()) ).and_then(move |xmpp_stream| done(Self::auth(xmpp_stream, username, password)) diff --git a/src/component/mod.rs b/src/component/mod.rs index 088d20d5675f98a5d62e4a41813c9147362fe8d8..e34ecd4da5a0b02184058080822b6de51a45ae33 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -53,8 +53,8 @@ impl Component { fn make_connect(jid: Jid, password: String, server: &str, port: u16) -> impl Future { let jid1 = jid.clone(); let password = password; - done(Connecter::from_lookup(server, "_xmpp-component._tcp", port)) - .and_then(|connecter| connecter) + done(Connecter::from_lookup(server, None, port)) + .flatten() .map_err(Error::Connection) .and_then(move |tcp_stream| { xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_COMPONENT_ACCEPT.to_owned()) diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index e3bebe9983fe5d789ca5da1b4ff4e83ff9f86cd0..6b22cca04cf589c0eb319c0556376c2a71addbf2 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -1,44 +1,61 @@ use std::mem; -use std::net::{SocketAddr, IpAddr}; -use std::collections::{BTreeMap, btree_map}; +use std::net::SocketAddr; +use std::collections::BTreeMap; use std::collections::VecDeque; +use std::cell::RefCell; use futures::{Future, Poll, Async}; use tokio::net::{ConnectFuture, TcpStream}; use trust_dns_resolver::{IntoName, Name, ResolverFuture, error::ResolveError}; use trust_dns_resolver::lookup::SrvLookupFuture; use trust_dns_resolver::lookup_ip::LookupIpFuture; -use trust_dns_proto::rr::rdata::srv::SRV; use ConnecterError; +enum State { + AwaitResolver(Box + Send>), + ResolveSrv(ResolverFuture, SrvLookupFuture), + ResolveTarget(ResolverFuture, LookupIpFuture, u16), + Connecting(Option, Vec>), + Invalid, +} + pub struct Connecter { fallback_port: u16, - name: Name, + srv_domain: Option, domain: Name, - resolver_future: Box + Send>, - resolver_opt: Option, - srv_lookup_opt: Option, - srvs_opt: Option>, - ip_lookup_opt: Option<(u16, LookupIpFuture)>, - ips_opt: Option<(u16, VecDeque)>, - connect_opt: Option, + state: State, + targets: VecDeque<(Name, u16)>, } impl Connecter { - pub fn from_lookup(domain: &str, srv: &str, fallback_port: u16) -> Result { + pub fn from_lookup(domain: &str, srv: Option<&str>, fallback_port: u16) -> Result { + if let Ok(ip) = domain.parse() { + // use specified IP address, not domain name, skip the whole dns part + let connect = + RefCell::new(TcpStream::connect(&SocketAddr::new(ip, fallback_port))); + return Ok(Connecter { + fallback_port, + srv_domain: None, + domain: "nohost".into_name()?, + state: State::Connecting(None, vec![connect]), + targets: VecDeque::new(), + }); + } + let resolver_future = ResolverFuture::from_system_conf()?; - let name = format!("{}.{}.", srv, domain).into_name()?; + let state = State::AwaitResolver(resolver_future); + let srv_domain = match srv { + Some(srv) => + Some(format!("{}.{}.", srv, domain).into_name()?), + None => + None, + }; Ok(Connecter { fallback_port, - name, + srv_domain, domain: domain.into_name()?, - resolver_future, - resolver_opt: None, - srv_lookup_opt: None, - srvs_opt: None, - ip_lookup_opt: None, - ips_opt: None, - connect_opt: None, + state, + targets: VecDeque::new(), }) } } @@ -48,102 +65,118 @@ impl Future for Connecter { type Error = ConnecterError; fn poll(&mut self) -> Poll { - if self.resolver_opt.is_none() { - //println!("Poll resolver future"); - match self.resolver_future.poll()? { - Async::Ready(resolver) => - self.resolver_opt = Some(resolver), - Async::NotReady => - return Ok(Async::NotReady), - } - } - - if let Some(ref resolver) = self.resolver_opt { - if self.srvs_opt.is_none() { - if self.srv_lookup_opt.is_none() { - //println!("Lookup srv: {:?}", self.name); - self.srv_lookup_opt = Some(resolver.lookup_srv(&self.name)); - } - - if let Some(ref mut srv_lookup) = self.srv_lookup_opt { - match srv_lookup.poll() { - Ok(Async::Ready(t)) => { - let mut srvs = BTreeMap::new(); - for srv in t.iter() { - srvs.insert(srv.priority(), srv.clone()); + let state = mem::replace(&mut self.state, State::Invalid); + match state { + State::AwaitResolver(mut resolver_future) => { + match resolver_future.poll()? { + Async::NotReady => { + self.state = State::AwaitResolver(resolver_future); + Ok(Async::NotReady) + } + Async::Ready(resolver) => { + match &self.srv_domain { + &Some(ref srv_domain) => { + let srv_lookup = resolver.lookup_srv(srv_domain); + self.state = State::ResolveSrv(resolver, srv_lookup); + } + None => { + self.targets = + [(self.domain.clone(), self.fallback_port)].into_iter() + .cloned() + .collect(); + self.state = State::Connecting(Some(resolver), vec![]); } - srvs.insert(65535, SRV::new(65535, 0, self.fallback_port, self.domain.clone())); - self.srvs_opt = Some(srvs.into_iter()); } - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(_) => { - //println!("Ignore SVR error: {:?}", e); - let mut srvs = BTreeMap::new(); - srvs.insert(65535, SRV::new(65535, 0, self.fallback_port, self.domain.clone())); - self.srvs_opt = Some(srvs.into_iter()); - }, + self.poll() } } } - - if self.connect_opt.is_none() { - if self.ips_opt.is_none() { - if self.ip_lookup_opt.is_none() { - if let Some(ref mut srvs) = self.srvs_opt { - if let Some((_, srv)) = srvs.next() { - //println!("Lookup ip: {:?}", srv); - self.ip_lookup_opt = Some((srv.port(), resolver.lookup_ip(srv.target()))); - } else { - return Err(ConnecterError::NoSrv); - } - } + State::ResolveSrv(resolver, mut srv_lookup) => { + match srv_lookup.poll() { + Ok(Async::NotReady) => { + self.state = State::ResolveSrv(resolver, srv_lookup); + Ok(Async::NotReady) } - - if let Some((port, mut ip_lookup)) = mem::replace(&mut self.ip_lookup_opt, None) { - match ip_lookup.poll() { - Ok(Async::Ready(t)) => { - let mut ip_deque = VecDeque::new(); - ip_deque.extend(t.iter()); - //println!("IPs: {:?}", ip_deque); - self.ips_opt = Some((port, ip_deque)); - self.ip_lookup_opt = None; - }, - Ok(Async::NotReady) => { - self.ip_lookup_opt = Some((port, ip_lookup)); - return Ok(Async::NotReady) - }, - Err(_) => { - //println!("Ignore lookup error: {:?}", e); - self.ip_lookup_opt = None; - } - } + Ok(Async::Ready(srv_result)) => { + let mut srv_map: BTreeMap<_, _> = + srv_result.iter() + .map(|srv| (srv.priority(), (srv.target().clone(), srv.port()))) + .collect(); + let targets = + srv_map.into_iter() + .map(|(_, tp)| tp) + .collect(); + self.targets = targets; + self.state = State::Connecting(Some(resolver), vec![]); + self.poll() + } + Err(_) => { + // ignore, fallback + self.targets = + [(self.domain.clone(), self.fallback_port)].into_iter() + .cloned() + .collect(); + self.state = State::Connecting(Some(resolver), vec![]); + self.poll() } } - - if let Some((port, mut ip_deque)) = mem::replace(&mut self.ips_opt, None) { - if let Some(ip) = ip_deque.pop_front() { - //println!("Connect to {:?}:{}", ip, port); - self.connect_opt = Some(TcpStream::connect(&SocketAddr::new(ip, port))); - self.ips_opt = Some((port, ip_deque)); + } + State::Connecting(resolver, mut connects) => { + if resolver.is_some() && + connects.len() == 0 && + self.targets.len() > 0 { + let resolver = resolver.unwrap(); + let (host, port) = self.targets.pop_front().unwrap(); + let ip_lookup = resolver.lookup_ip(host); + self.state = State::ResolveTarget(resolver, ip_lookup, port); + self.poll() + } else if connects.len() > 0 { + let mut success = None; + connects.retain(|connect| { + match connect.borrow_mut().poll() { + Ok(Async::NotReady) => true, + Ok(Async::Ready(connection)) => { + success = Some(connection); + false + } + Err(_) => false, + } + }); + match success { + Some(connection) => + Ok(Async::Ready(connection)), + None => { + self.state = State::Connecting(resolver, connects); + Ok(Async::NotReady) + }, } + } else { + Err(ConnecterError::AllFailed) } } - - if let Some(mut connect_future) = mem::replace(&mut self.connect_opt, None) { - match connect_future.poll() { - Ok(Async::Ready(t)) => return Ok(Async::Ready(t)), + State::ResolveTarget(resolver, mut ip_lookup, port) => { + match ip_lookup.poll() { Ok(Async::NotReady) => { - self.connect_opt = Some(connect_future); - return Ok(Async::NotReady) + self.state = State::ResolveTarget(resolver, ip_lookup, port); + Ok(Async::NotReady) + } + Ok(Async::Ready(ip_result)) => { + let connects = + ip_result.iter() + .map(|ip| RefCell::new(TcpStream::connect(&SocketAddr::new(ip, port)))) + .collect(); + self.state = State::Connecting(Some(resolver), connects); + self.poll() } Err(_) => { - //println!("Ignore connect error: {:?}", e); - }, + // ignore, next… + self.state = State::Connecting(Some(resolver), vec![]); + self.poll() + } } } + _ => panic!("") } - - Ok(Async::NotReady) } } From ce2ce363b02af8dbef6e23fbc9e833b1b98fdc6a Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 8 Sep 2018 01:35:26 +0200 Subject: [PATCH 0728/1020] happy_eyeballs: propagate actual connection error --- src/client/mod.rs | 6 ++---- src/component/mod.rs | 1 - src/happy_eyeballs.rs | 43 +++++++++++++++++++++++++++++-------------- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index cf4553a052f29aaeb67922f6eb39dd41434ece3f..d2af19c3bc56f1cc3eb6769bbe43da3b7d9e4e4e 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -64,10 +64,8 @@ impl Client { done(Connecter::from_lookup(&domain, Some("_xmpp-client._tcp"), 5222)) .map_err(Error::Connection) ) - .and_then(|connecter| - connecter - .map_err(Error::Connection) - ).and_then(move |tcp_stream| + .flatten() + .and_then(move |tcp_stream| xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_CLIENT.to_owned()) ).and_then(|xmpp_stream| { if Self::can_starttls(&xmpp_stream) { diff --git a/src/component/mod.rs b/src/component/mod.rs index e34ecd4da5a0b02184058080822b6de51a45ae33..ce55e7ecdba1c7548c62c2b1e3c15fe4e4197c9e 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -55,7 +55,6 @@ impl Component { let password = password; done(Connecter::from_lookup(server, None, port)) .flatten() - .map_err(Error::Connection) .and_then(move |tcp_stream| { xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_COMPONENT_ACCEPT.to_owned()) }).and_then(move |xmpp_stream| { diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 6b22cca04cf589c0eb319c0556376c2a71addbf2..4a1185642e66a8bf0c5192d29af92244a66d2883 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -8,7 +8,7 @@ use tokio::net::{ConnectFuture, TcpStream}; use trust_dns_resolver::{IntoName, Name, ResolverFuture, error::ResolveError}; use trust_dns_resolver::lookup::SrvLookupFuture; use trust_dns_resolver::lookup_ip::LookupIpFuture; -use ConnecterError; +use {Error, ConnecterError}; enum State { AwaitResolver(Box + Send>), @@ -24,6 +24,7 @@ pub struct Connecter { domain: Name, state: State, targets: VecDeque<(Name, u16)>, + error: Option, } impl Connecter { @@ -38,6 +39,7 @@ impl Connecter { domain: "nohost".into_name()?, state: State::Connecting(None, vec![connect]), targets: VecDeque::new(), + error: None, }); } @@ -56,19 +58,20 @@ impl Connecter { domain: domain.into_name()?, state, targets: VecDeque::new(), + error: None, }) } } impl Future for Connecter { type Item = TcpStream; - type Error = ConnecterError; + type Error = Error; fn poll(&mut self) -> Poll { let state = mem::replace(&mut self.state, State::Invalid); match state { State::AwaitResolver(mut resolver_future) => { - match resolver_future.poll()? { + match resolver_future.poll().map_err(ConnecterError::Resolve)? { Async::NotReady => { self.state = State::AwaitResolver(resolver_future); Ok(Async::NotReady) @@ -122,14 +125,12 @@ impl Future for Connecter { } } State::Connecting(resolver, mut connects) => { - if resolver.is_some() && - connects.len() == 0 && - self.targets.len() > 0 { - let resolver = resolver.unwrap(); - let (host, port) = self.targets.pop_front().unwrap(); - let ip_lookup = resolver.lookup_ip(host); - self.state = State::ResolveTarget(resolver, ip_lookup, port); - self.poll() + if resolver.is_some() && connects.len() == 0 && self.targets.len() > 0 { + let resolver = resolver.unwrap(); + let (host, port) = self.targets.pop_front().unwrap(); + let ip_lookup = resolver.lookup_ip(host); + self.state = State::ResolveTarget(resolver, ip_lookup, port); + self.poll() } else if connects.len() > 0 { let mut success = None; connects.retain(|connect| { @@ -139,7 +140,12 @@ impl Future for Connecter { success = Some(connection); false } - Err(_) => false, + Err(e) => { + if self.error.is_none() { + self.error = Some(e.into()); + } + false + }, } }); match success { @@ -151,7 +157,13 @@ impl Future for Connecter { }, } } else { - Err(ConnecterError::AllFailed) + // All targets tried + match self.error.take() { + None => + Err(ConnecterError::AllFailed.into()), + Some(e) => + Err(e), + } } } State::ResolveTarget(resolver, mut ip_lookup, port) => { @@ -168,7 +180,10 @@ impl Future for Connecter { self.state = State::Connecting(Some(resolver), connects); self.poll() } - Err(_) => { + Err(e) => { + if self.error.is_none() { + self.error = Some(ConnecterError::Resolve(e).into()); + } // ignore, next… self.state = State::Connecting(Some(resolver), vec![]); self.poll() From d3fab29d388754f21f76a881b1fffe0dc9b5d7d9 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 8 Sep 2018 01:42:23 +0200 Subject: [PATCH 0729/1020] rm stale DomainError, add error docs --- src/error.rs | 46 ++++++++++++++++++++++------------------------ src/lib.rs | 2 +- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/error.rs b/src/error.rs index 541bbbfd4b1cb5733dabb33cfaf6ddb99a337244..43f5af1480b34de631a5a9dcd3679bb87d305611 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,15 +10,21 @@ use trust_dns_proto::error::ProtoError; use xmpp_parsers::error::Error as ParsersError; use xmpp_parsers::sasl::DefinedCondition as SaslDefinedCondition; +/// Top-level error type #[derive(Debug, Error)] pub enum Error { + /// I/O error Io(IoError), + /// Error resolving DNS and establishing a connection Connection(ConnecterError), /// DNS label conversion error, no details available from module /// `idna` Idna, + /// Protocol-level error Protocol(ProtocolError), + /// Authentication error Auth(AuthError), + /// TLS error Tls(TlsError), /// Shoud never happen InvalidState, @@ -62,57 +68,49 @@ impl fmt::Display for ParseError { } } +/// XMPP protocol-level error #[derive(Debug, Error)] pub enum ProtocolError { + /// XML parser error Parser(ParserError), + /// Error with expected stanza schema #[error(non_std)] Parsers(ParsersError), + /// No TLS available NoTls, + /// Invalid response to resource binding InvalidBindResponse, + /// No xmlns attribute in NoStreamNamespace, + /// No id attribute in NoStreamId, + /// Encountered an unexpected XML token InvalidToken, } +/// Authentication error #[derive(Debug, Error)] pub enum AuthError { - /// No SASL mechanism available + /// No matching SASL mechanism available NoMechanism, + /// Local SASL implementation error #[error(no_from, non_std, msg_embedded)] Sasl(String), + /// Failure from server #[error(non_std)] Fail(SaslDefinedCondition), + /// Component authentication failure #[error(no_from)] ComponentFail, } +/// Error establishing connection #[derive(Debug, Error)] pub enum ConnecterError { - NoSrv, + /// All attempts failed, no error available AllFailed, - /// DNS name error - Domain(DomainError), - /// DNS resolution error + /// DNS protocol error Dns(ProtoError), /// DNS resolution error Resolve(ResolveError), } - -/// DNS name error wrapper type -#[derive(Debug)] -pub struct DomainError(pub String); - -impl StdError for DomainError { - fn description(&self) -> &str { - &self.0 - } - fn cause(&self) -> Option<&StdError> { - None - } -} - -impl fmt::Display for DomainError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} diff --git a/src/lib.rs b/src/lib.rs index b23aefc6d400c450b824ea0bf49961346f3ce145..bed95b0722ccc7eecfe9b6bc4397d94343f136e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -// #![deny(unsafe_code, unused, missing_docs)] +#![deny(unsafe_code, unused, missing_docs)] //! XMPP implemeentation with asynchronous I/O using Tokio. From 662c61b801c5ec689e9b2e06146faf58813bb0c3 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 8 Sep 2018 02:12:49 +0200 Subject: [PATCH 0730/1020] happy_eyeballs: enable parallel A/AAAA resolution --- src/client/mod.rs | 1 - src/happy_eyeballs.rs | 25 +++++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index d2af19c3bc56f1cc3eb6769bbe43da3b7d9e4e4e..72634a2ee02c93077f967a7b00b30e908d8c11c4 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -62,7 +62,6 @@ impl Client { .map_err(|_| Error::Idna) .and_then(|domain| done(Connecter::from_lookup(&domain, Some("_xmpp-client._tcp"), 5222)) - .map_err(Error::Connection) ) .flatten() .and_then(move |tcp_stream| diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 4a1185642e66a8bf0c5192d29af92244a66d2883..6d201fdee5d85ad861f92dc57d5758d873f528ab 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -1,4 +1,5 @@ use std::mem; +use std::io::Error as IoError; use std::net::SocketAddr; use std::collections::BTreeMap; use std::collections::VecDeque; @@ -8,6 +9,8 @@ use tokio::net::{ConnectFuture, TcpStream}; use trust_dns_resolver::{IntoName, Name, ResolverFuture, error::ResolveError}; use trust_dns_resolver::lookup::SrvLookupFuture; use trust_dns_resolver::lookup_ip::LookupIpFuture; +use trust_dns_resolver::system_conf; +use trust_dns_resolver::config::LookupIpStrategy; use {Error, ConnecterError}; enum State { @@ -27,8 +30,14 @@ pub struct Connecter { error: Option, } +fn resolver_future() -> Result + Send>, IoError> { + let (conf, mut opts) = system_conf::read_system_conf()?; + opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6; + Ok(ResolverFuture::new(conf, opts)) +} + impl Connecter { - pub fn from_lookup(domain: &str, srv: Option<&str>, fallback_port: u16) -> Result { + pub fn from_lookup(domain: &str, srv: Option<&str>, fallback_port: u16) -> Result { if let Ok(ip) = domain.parse() { // use specified IP address, not domain name, skip the whole dns part let connect = @@ -36,18 +45,21 @@ impl Connecter { return Ok(Connecter { fallback_port, srv_domain: None, - domain: "nohost".into_name()?, + domain: "nohost".into_name() + .map_err(ConnecterError::Dns)?, state: State::Connecting(None, vec![connect]), targets: VecDeque::new(), error: None, }); } - let resolver_future = ResolverFuture::from_system_conf()?; - let state = State::AwaitResolver(resolver_future); + let state = State::AwaitResolver(resolver_future()?); let srv_domain = match srv { Some(srv) => - Some(format!("{}.{}.", srv, domain).into_name()?), + Some(format!("{}.{}.", srv, domain) + .into_name() + .map_err(ConnecterError::Dns)? + ), None => None, }; @@ -55,7 +67,8 @@ impl Connecter { Ok(Connecter { fallback_port, srv_domain, - domain: domain.into_name()?, + domain: domain.into_name() + .map_err(ConnecterError::Dns)?, state, targets: VecDeque::new(), error: None, From 3954435d57aa13886b431b6c508423162ed75760 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 8 Sep 2018 02:13:18 +0200 Subject: [PATCH 0731/1020] tokio-xmpp 0.2.0 --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 87c6bc83a191da9a08e7d1216c82c48de1ec7147..09a0343714303b99bcd4d454dd3f300666d8243d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tokio-xmpp" -version = "0.1.2" -authors = ["Astro ", "Emmanuel Gil Peyrot ", "pep "] +version = "0.2.0" +authors = ["Astro ", "Emmanuel Gil Peyrot ", "pep ", "O01eg "] description = "Asynchronous XMPP for Rust with tokio" license = "MPL-2.0" homepage = "https://github.com/astro/tokio-xmpp" @@ -15,7 +15,7 @@ futures = "0.1" tokio = "0.1" tokio-io = "0.1" tokio-codec = "0.1" -bytes = "0.4.9" +bytes = "0.4" xml5ever = "0.12" minidom = "0.9" native-tls = "0.2" From 3c829655810e1f2bb67dc55e1b3181b7ac4b2372 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 8 Sep 2018 02:16:45 +0200 Subject: [PATCH 0732/1020] README: TODO update --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 52e43abd48e3b0c2092fc39398ce4740a092d399..eccc836f75a474b8eba96906767dfb7cdc5af3e4 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,4 @@ - [ ] minidom ns - [ ] replace debug output with log crate - [ ] customize tls verify? -- [ ] Error type -- [ ] unexpected event errors -- [x] doc -- [ ] tests +- [ ] more tests From 388941b483bac1f56604d49805ab8321e1370cba Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Sep 2018 21:26:21 +0200 Subject: [PATCH 0733/1020] stanza_error: Document this module. --- src/stanza_error.rs | 221 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 187 insertions(+), 34 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 85396cac5465a867c508bed3c2addf3842c51cbe..6b2cff9baa8e2fed158dede225ef643e0ddb03cc 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::collections::BTreeMap; @@ -15,47 +13,202 @@ use error::Error; use jid::Jid; use ns; -generate_attribute!(ErrorType, "type", { - Auth => "auth", - Cancel => "cancel", - Continue => "continue", - Modify => "modify", - Wait => "wait", -}); - -generate_element_enum!(DefinedCondition, "condition", XMPP_STANZAS, { - BadRequest => "bad-request", - Conflict => "conflict", - FeatureNotImplemented => "feature-not-implemented", - Forbidden => "forbidden", - Gone => "gone", - InternalServerError => "internal-server-error", - ItemNotFound => "item-not-found", - JidMalformed => "jid-malformed", - NotAcceptable => "not-acceptable", - NotAllowed => "not-allowed", - NotAuthorized => "not-authorized", - PolicyViolation => "policy-violation", - RecipientUnavailable => "recipient-unavailable", - Redirect => "redirect", - RegistrationRequired => "registration-required", - RemoteServerNotFound => "remote-server-not-found", - RemoteServerTimeout => "remote-server-timeout", - ResourceConstraint => "resource-constraint", - ServiceUnavailable => "service-unavailable", - SubscriptionRequired => "subscription-required", - UndefinedCondition => "undefined-condition", - UnexpectedRequest => "unexpected-request", -}); +generate_attribute!( + /// The type of the error. + ErrorType, "type", { + /// Retry after providing credentials. + Auth => "auth", + + /// Do not retry (the error cannot be remedied). + Cancel => "cancel", + + /// Proceed (the condition was only a warning). + Continue => "continue", + + /// Retry after changing the data sent. + Modify => "modify", + + /// Retry after waiting (the error is temporary). + Wait => "wait", + } +); + +generate_element_enum!( + /// List of valid error conditions. + DefinedCondition, "condition", XMPP_STANZAS, { + /// The sender has sent a stanza containing XML that does not conform + /// to the appropriate schema or that cannot be processed (e.g., an IQ + /// stanza that includes an unrecognized value of the 'type' attribute, + /// or an element that is qualified by a recognized namespace but that + /// violates the defined syntax for the element); the associated error + /// type SHOULD be "modify". + BadRequest => "bad-request", + + /// Access cannot be granted because an existing resource exists with + /// the same name or address; the associated error type SHOULD be + /// "cancel". + Conflict => "conflict", + + /// The feature represented in the XML stanza is not implemented by the + /// intended recipient or an intermediate server and therefore the + /// stanza cannot be processed (e.g., the entity understands the + /// namespace but does not recognize the element name); the associated + /// error type SHOULD be "cancel" or "modify". + FeatureNotImplemented => "feature-not-implemented", + + /// The requesting entity does not possess the necessary permissions to + /// perform an action that only certain authorized roles or individuals + /// are allowed to complete (i.e., it typically relates to + /// authorization rather than authentication); the associated error + /// type SHOULD be "auth". + Forbidden => "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 (which MUST be a Uniform Resource + /// Identifier [URI] or Internationalized Resource Identifier [IRI] at + /// which the entity can be contacted, typically an XMPP IRI as + /// specified in [XMPP‑URI]). + Gone => "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". + InternalServerError => "internal-server-error", + + /// The addressed JID or item requested cannot be found; the associated + /// error type SHOULD be "cancel". + ItemNotFound => "item-not-found", + + /// The sending entity has provided (e.g., during resource binding) or + /// communicated (e.g., in the 'to' address of a stanza) an XMPP + /// address or aspect thereof that violates the rules defined in + /// [XMPP‑ADDR]; the associated error type SHOULD be "modify". + JidMalformed => "jid-malformed", + + /// The recipient or server understands the request but cannot process + /// it because the request does not meet criteria defined by the + /// recipient or server (e.g., a request to subscribe to information + /// that does not simultaneously include configuration parameters + /// needed by the recipient); the associated error type SHOULD be + /// "modify". + NotAcceptable => "not-acceptable", + + /// The recipient or server does not allow any entity to perform the + /// action (e.g., sending to entities at a blacklisted domain); the + /// associated error type SHOULD be "cancel". + NotAllowed => "not-allowed", + + /// The sender needs to provide credentials before being allowed to + /// perform the action, or has provided improper credentials (the name + /// "not-authorized", which was borrowed from the "401 Unauthorized" + /// error of [HTTP], might lead the reader to think that this condition + /// relates to authorization, but instead it is typically used in + /// relation to authentication); the associated error type SHOULD be + /// "auth". + NotAuthorized => "not-authorized", + + /// The entity has violated some local service policy (e.g., a message + /// contains words that are prohibited by the service) and the server + /// MAY choose to specify the policy in the element or in an + /// application-specific condition element; the associated error type + /// SHOULD be "modify" or "wait" depending on the policy being + /// violated. + PolicyViolation => "policy-violation", + + /// The intended recipient is temporarily unavailable, undergoing + /// maintenance, etc.; the associated error type SHOULD be "wait". + RecipientUnavailable => "recipient-unavailable", + + /// The recipient or server is redirecting requests for this + /// information to another entity, typically in a temporary fashion (as + /// opposed to the error condition, which is used for permanent + /// addressing failures); the associated error type SHOULD be "modify" + /// and the error stanza SHOULD contain the alternate address in the + /// XML character data of the element (which MUST be a URI + /// or IRI with which the sender can communicate, typically an XMPP IRI + /// as specified in [XMPP‑URI]). + Redirect => "redirect", + + /// The requesting entity is not authorized to access the requested + /// service because prior registration is necessary (examples of prior + /// registration include members-only rooms in XMPP multi-user chat + /// [XEP‑0045] and gateways to non-XMPP instant messaging services, + /// which traditionally required registration in order to use the + /// gateway [XEP‑0100]); the associated error type SHOULD be "auth". + RegistrationRequired => "registration-required", + + /// A remote server or service specified as part or all of the JID of + /// the intended recipient does not exist or cannot be resolved (e.g., + /// there is no _xmpp-server._tcp DNS SRV record, the A or AAAA + /// fallback resolution fails, or A/AAAA lookups succeed but there is + /// no response on the IANA-registered port 5269); the associated error + /// type SHOULD be "cancel". + RemoteServerNotFound => "remote-server-not-found", + + /// A remote server or service specified as part or all of the JID of + /// the intended recipient (or needed to fulfill a request) was + /// resolved but communications could not be established within a + /// reasonable amount of time (e.g., an XML stream cannot be + /// established at the resolved IP address and port, or an XML stream + /// can be established but stream negotiation fails because of problems + /// with TLS, SASL, Server Dialback, etc.); the associated error type + /// SHOULD be "wait" (unless the error is of a more permanent nature, + /// e.g., the remote server is found but it cannot be authenticated or + /// it violates security policies). + RemoteServerTimeout => "remote-server-timeout", + + /// The server or recipient is busy or lacks the system resources + /// necessary to service the request; the associated error type SHOULD + /// be "wait". + ResourceConstraint => "resource-constraint", + + /// The server or recipient does not currently provide the requested + /// service; the associated error type SHOULD be "cancel". + ServiceUnavailable => "service-unavailable", + + /// The requesting entity is not authorized to access the requested + /// service because a prior subscription is necessary (examples of + /// prior subscription include authorization to receive presence + /// information as defined in [XMPP‑IM] and opt-in data feeds for XMPP + /// publish-subscribe as defined in [XEP‑0060]); the associated error + /// type SHOULD be "auth". + SubscriptionRequired => "subscription-required", + + /// The error condition is not one of those defined by the other + /// conditions in this list; any error type can be associated with this + /// condition, and it SHOULD NOT be used except in conjunction with an + /// application-specific condition. + UndefinedCondition => "undefined-condition", + + /// The recipient or server understood the request but was not + /// expecting it at this time (e.g., the request was out of order); the + /// associated error type SHOULD be "wait" or "modify". + UnexpectedRequest => "unexpected-request", + } +); pub type Lang = String; +/// The representation of a stanza error. #[derive(Debug, Clone)] pub struct StanzaError { + /// The type of this error. pub type_: ErrorType, + + /// The JID of the entity who set this error. pub by: Option, + + /// One of the defined conditions for this error to happen. pub defined_condition: DefinedCondition, + + /// Human-readable description of this error. pub texts: BTreeMap, + + /// A protocol-specific extension for this error. pub other: Option, } From f3366b94bbc43f109b8d02b6871eae75cdc55add Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 20:28:29 +0200 Subject: [PATCH 0734/1020] stanza_error: Fix compilation error due to an extra pub. --- src/stanza_error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 6b2cff9baa8e2fed158dede225ef643e0ddb03cc..edec4d5bd6fdb0853ffe9cb19a9372f4774764be 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -191,7 +191,7 @@ generate_element_enum!( } ); -pub type Lang = String; +type Lang = String; /// The representation of a stanza error. #[derive(Debug, Clone)] From 709666bb911b93e73d095e7122921f891f1eacff Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 20:28:50 +0200 Subject: [PATCH 0735/1020] jingle: Document most of this module. --- src/jingle.rs | 255 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 203 insertions(+), 52 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 264aa1820bef2b935c7df7e7c82b4f599718b2b6..dab53712a34aa59ecee96a0f37668704da766710 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -16,64 +16,170 @@ use error::Error; use ns; use iq::IqSetPayload; -generate_attribute!(Action, "action", { - ContentAccept => "content-accept", - ContentAdd => "content-add", - ContentModify => "content-modify", - ContentReject => "content-reject", - ContentRemove => "content-remove", - DescriptionInfo => "description-info", - SecurityInfo => "security-info", - SessionAccept => "session-accept", - SessionInfo => "session-info", - SessionInitiate => "session-initiate", - SessionTerminate => "session-terminate", - TransportAccept => "transport-accept", - TransportInfo => "transport-info", - TransportReject => "transport-reject", - TransportReplace => "transport-replace", -}); - -generate_attribute!(Creator, "creator", { - Initiator => "initiator", - Responder => "responder", -}); - -generate_attribute!(Senders, "senders", { - Both => "both", - Initiator => "initiator", - None => "none", - Responder => "responder", -}, Default = Both); - -// From https://www.iana.org/assignments/cont-disp/cont-disp.xhtml -generate_attribute!(Disposition, "disposition", { - Inline => "inline", - Attachment => "attachment", - FormData => "form-data", - Signal => "signal", - Alert => "alert", - Icon => "icon", - Render => "render", - RecipientListHistory => "recipient-list-history", - Session => "session", - Aib => "aib", - EarlySession => "early-session", - RecipientList => "recipient-list", - Notification => "notification", - ByReference => "by-reference", - InfoPackage => "info-package", - RecordingSession => "recording-session", -}, Default = Session); - -generate_id!(ContentId); +generate_attribute!( + /// The action attribute. + Action, "action", { + /// Accept a content-add action received from another party. + ContentAccept => "content-accept", + + /// Add one or more new content definitions to the session. + ContentAdd => "content-add", + + /// Change the directionality of media sending. + ContentModify => "content-modify", + + /// Reject a content-add action received from another party. + ContentReject => "content-reject", + + /// Remove one or more content definitions from the session. + ContentRemove => "content-remove", + + /// Exchange information about parameters for an application type. + DescriptionInfo => "description-info", + + /// Exchange information about security preconditions. + SecurityInfo => "security-info", + + /// Definitively accept a session negotiation. + SessionAccept => "session-accept", + + /// Send session-level information, such as a ping or a ringing message. + SessionInfo => "session-info", + + /// Request negotiation of a new Jingle session. + SessionInitiate => "session-initiate", + + /// End an existing session. + SessionTerminate => "session-terminate", + + /// Accept a transport-replace action received from another party. + TransportAccept => "transport-accept", + + /// Exchange transport candidates. + TransportInfo => "transport-info", + + /// Reject a transport-replace action received from another party. + TransportReject => "transport-reject", + + /// Redefine a transport method or replace it with a different method. + TransportReplace => "transport-replace", + } +); + +generate_attribute!( + /// Which party originally generated the content type. + Creator, "creator", { + /// This content was created by the initiator of this session. + Initiator => "initiator", + + /// This content was created by the responder of this session. + Responder => "responder", + } +); + +generate_attribute!( + /// Which parties in the session will be generating content. + Senders, "senders", { + /// Both parties can send for this content. + Both => "both", + + /// Only the initiator can send for this content. + Initiator => "initiator", + + /// No one can send for this content. + None => "none", + + /// Only the responder can send for this content. + Responder => "responder", + }, Default = Both +); + +generate_attribute!( + /// How the content definition is to be interpreted by the recipient. The + /// meaning of this attribute matches the "Content-Disposition" header as + /// defined in RFC 2183 and applied to SIP by RFC 3261. + /// + /// Possible values are defined here: + /// https://www.iana.org/assignments/cont-disp/cont-disp.xhtml + Disposition, "disposition", { + /// Displayed automatically. + Inline => "inline", + + /// User controlled display. + Attachment => "attachment", + + /// Process as form response. + FormData => "form-data", + + /// Tunneled content to be processed silently. + Signal => "signal", + + /// The body is a custom ring tone to alert the user. + Alert => "alert", + + /// The body is displayed as an icon to the user. + Icon => "icon", + + /// The body should be displayed to the user. + Render => "render", + + /// The body contains a list of URIs that indicates the recipients of + /// the request. + RecipientListHistory => "recipient-list-history", + + /// The body describes a communications session, for example, an + /// RFC2327 SDP body. + Session => "session", + + /// Authenticated Identity Body. + Aib => "aib", + + /// The body describes an early communications session, for example, + /// and [RFC2327] SDP body. + EarlySession => "early-session", + + /// The body includes a list of URIs to which URI-list services are to + /// be applied. + RecipientList => "recipient-list", + + /// The payload of the message carrying this Content-Disposition header + /// field value is an Instant Message Disposition Notification as + /// requested in the corresponding Instant Message. + Notification => "notification", + + /// The body needs to be handled according to a reference to the body + /// that is located in the same SIP message as the body. + ByReference => "by-reference", + + /// The body contains information associated with an Info Package. + InfoPackage => "info-package", + + /// The body describes either metadata about the RS or the reason for + /// the metadata snapshot request as determined by the MIME value + /// indicated in the Content-Type. + RecordingSession => "recording-session", + }, Default = Session +); + +generate_id!( + /// An unique identifier in a session, referencing a + /// [struct.Content.html](Content element). + ContentId +); generate_element!( Content, "content", JINGLE, attributes: [ + /// Who created this content. creator: Creator = "creator" => required, + + /// How the content definition is to be interpreted by the recipient. disposition: Disposition = "disposition" => default, + + /// A per-session unique identifier for this content. name: ContentId = "name" => required, + + /// Who can send data for this content. senders: Senders = "senders" => default ], children: [ @@ -124,22 +230,64 @@ impl Content { #[derive(Debug, Clone, PartialEq)] pub enum Reason { + /// The party prefers to use an existing session with the peer rather than + /// initiate a new session; the Jingle session ID of the alternative + /// session SHOULD be provided as the XML character data of the + /// child. AlternativeSession, //(String), + + /// The party is busy and cannot accept a session. Busy, + + /// The initiator wishes to formally cancel the session initiation request. Cancel, + + /// The action is related to connectivity problems. ConnectivityError, + + /// The party wishes to formally decline the session. Decline, + + /// The session length has exceeded a pre-defined time limit (e.g., a + /// meeting hosted at a conference service). Expired, + + /// The party has been unable to initialize processing related to the + /// application type. FailedApplication, + + /// The party has been unable to establish connectivity for the transport + /// method. FailedTransport, + + /// The action is related to a non-specific application error. GeneralError, + + /// The entity is going offline or is no longer available. Gone, + + /// The party supports the offered application type but does not support + /// the offered or negotiated parameters. IncompatibleParameters, + + /// The action is related to media processing problems. MediaError, + + /// The action is related to a violation of local security policies. SecurityError, + + /// The action is generated during the normal course of state management + /// and does not reflect any error. Success, + + /// A request has not been answered so the sender is timing out the + /// request. Timeout, + + /// The party supports none of the offered application types. UnsupportedApplications, + + /// The party supports none of the offered transport methods. UnsupportedTransports, } @@ -244,7 +392,10 @@ impl From for Element { } } -generate_id!(SessionId); +generate_id!( + /// Unique identifier for a session between two JIDs. + SessionId +); #[derive(Debug, Clone)] pub struct Jingle { From e41de29d9d9bb6a77cc740164bbcab41e42f9e0c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 20:51:48 +0200 Subject: [PATCH 0736/1020] presence: Make PresencePayload a trait, and implement it on every payload. --- src/caps.rs | 3 +++ src/delay.rs | 3 +++ src/ecaps2.rs | 3 +++ src/idle.rs | 3 +++ src/muc/muc.rs | 3 +++ src/presence.rs | 63 ++------------------------------------------- src/stanza_error.rs | 3 +++ 7 files changed, 20 insertions(+), 61 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index a63ab7f736e9759d3e45603b91fafa2b827b545b..77901ba165ad9aa2b0e828aa71fb8593e410d33b 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -6,6 +6,7 @@ use try_from::TryFrom; +use presence::PresencePayload; use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; use hashes::{Hash, Algo}; @@ -39,6 +40,8 @@ pub struct Caps { pub hash: Hash, } +impl PresencePayload for Caps {} + impl TryFrom for Caps { type Err = Error; diff --git a/src/delay.rs b/src/delay.rs index 21450da0382225a7d2b950434087ec4ba45e6ced..cd0a85f2b9616bbe1e60511eab28283023995f8b 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use presence::PresencePayload; use date::DateTime; use jid::Jid; @@ -26,6 +27,8 @@ generate_element!( ) ); +impl PresencePayload for Delay {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 4e40484d581f4148f082d5e946afe3938d698099..82ab1c4adfac58d1b293c0313f23e9430835b0f8 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use presence::PresencePayload; use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use data_forms::DataForm; use hashes::{Hash, Algo}; @@ -27,6 +28,8 @@ generate_element!( ] ); +impl PresencePayload for ECaps2 {} + fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); diff --git a/src/idle.rs b/src/idle.rs index f96dadb5e5047e5d6ee97972a1c5d794838e438e..2704654735b03784db8855fa558a4f7aa9e548e6 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use presence::PresencePayload; use date::DateTime; generate_element!( @@ -15,6 +16,8 @@ generate_element!( ] ); +impl PresencePayload for Idle {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 4d81b85c2a57776b741f7524769df4942b8e1f34..c363107a4f3f818a2293fa3a565c0700f9f0776d 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,6 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use presence::PresencePayload; use date::DateTime; generate_element!( @@ -36,6 +37,8 @@ generate_element!( ] ); +impl PresencePayload for Muc {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/presence.rs b/src/presence.rs index 639bf04632c5efc58c340c4036d054f300bfa4e3..e81358fb8853fa83d3e379b93ade6a3467e27678 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -19,12 +19,8 @@ use error::Error; use ns; -use stanza_error::StanzaError; -use muc::Muc; -use caps::Caps; -use delay::Delay; -use idle::Idle; -use ecaps2::ECaps2; +/// Should be implemented on every known payload of a ``. +pub trait PresencePayload: TryFrom + Into {} #[derive(Debug, Clone, PartialEq)] pub enum Show { @@ -79,61 +75,6 @@ pub type Status = String; pub type Priority = i8; -/// Lists every known payload of a ``. -#[derive(Debug, Clone)] -pub enum PresencePayload { - StanzaError(StanzaError), - Muc(Muc), - Caps(Caps), - Delay(Delay), - Idle(Idle), - ECaps2(ECaps2), - - Unknown(Element), -} - -impl TryFrom for PresencePayload { - type Err = Error; - - fn try_from(elem: Element) -> Result { - Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - ("error", ns::DEFAULT_NS) => PresencePayload::StanzaError(StanzaError::try_from(elem)?), - - // XEP-0045 - ("x", ns::MUC) => PresencePayload::Muc(Muc::try_from(elem)?), - - // XEP-0115 - ("c", ns::CAPS) => PresencePayload::Caps(Caps::try_from(elem)?), - - // XEP-0203 - ("delay", ns::DELAY) => PresencePayload::Delay(Delay::try_from(elem)?), - - // XEP-0319 - ("idle", ns::IDLE) => PresencePayload::Idle(Idle::try_from(elem)?), - - // XEP-0390 - ("c", ns::ECAPS2) => PresencePayload::ECaps2(ECaps2::try_from(elem)?), - - _ => PresencePayload::Unknown(elem), - }) - } -} - -impl From for Element { - fn from(payload: PresencePayload) -> Element { - match payload { - PresencePayload::StanzaError(stanza_error) => stanza_error.into(), - PresencePayload::Muc(muc) => muc.into(), - PresencePayload::Caps(caps) => caps.into(), - PresencePayload::Delay(delay) => delay.into(), - PresencePayload::Idle(idle) => idle.into(), - PresencePayload::ECaps2(ecaps2) => ecaps2.into(), - - PresencePayload::Unknown(elem) => elem, - } - } -} - #[derive(Debug, Clone, PartialEq)] pub enum Type { /// This value is not an acceptable 'type' attribute, it is only used diff --git a/src/stanza_error.rs b/src/stanza_error.rs index edec4d5bd6fdb0853ffe9cb19a9372f4774764be..be2766144081942a90d7ffce153e2e457423b34e 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,6 +9,7 @@ use std::collections::BTreeMap; use minidom::Element; +use presence::PresencePayload; use error::Error; use jid::Jid; use ns; @@ -212,6 +213,8 @@ pub struct StanzaError { pub other: Option, } +impl PresencePayload for StanzaError {} + impl TryFrom for StanzaError { type Err = Error; From 07cccad356365a521dd57be065674814a516cf4d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 20:58:27 +0200 Subject: [PATCH 0737/1020] message: Make MessagePayload a trait, and implement it on every payload. --- src/attention.rs | 4 ++ src/chatstates.rs | 4 ++ src/delay.rs | 2 + src/eme.rs | 4 ++ src/mam.rs | 3 ++ src/message.rs | 91 +----------------------------------------- src/message_correct.rs | 4 ++ src/receipts.rs | 6 +++ src/stanza_error.rs | 2 + src/stanza_id.rs | 5 +++ 10 files changed, 36 insertions(+), 89 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index 7b4226fb974e1b57e63c3278b01eabd5d0741ce5..398b2bfd2f3424189dca01d8875bfe0ea8025e7e 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,11 +4,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; + generate_empty_element!( /// Requests the attention of the recipient. Attention, "attention", ATTENTION ); +impl MessagePayload for Attention {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/chatstates.rs b/src/chatstates.rs index 339ed2b534a094d5aa7e8bbdc527ea577c7e6008..c9fabd5264285d188ce3752cab97777bdd1d4454 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; + generate_element_enum!( /// Enum representing chatstate elements part of the /// `http://jabber.org/protocol/chatstates` namespace. @@ -25,6 +27,8 @@ generate_element_enum!( } ); +impl MessagePayload for ChatState {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/delay.rs b/src/delay.rs index cd0a85f2b9616bbe1e60511eab28283023995f8b..581e72e26184428f4320bb80429fa4f55f481d43 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; use presence::PresencePayload; use date::DateTime; @@ -27,6 +28,7 @@ generate_element!( ) ); +impl MessagePayload for Delay {} impl PresencePayload for Delay {} #[cfg(test)] diff --git a/src/eme.rs b/src/eme.rs index a276ba25e13a6fd683d5b29d66127d613e790301..f596dc8f8b4c17751121a364bc09d5ccc1cc84e7 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; + generate_element!( /// Structure representing an `` element. ExplicitMessageEncryption, "encryption", EME, @@ -16,6 +18,8 @@ attributes: [ name: Option = "name" => optional, ]); +impl MessagePayload for ExplicitMessageEncryption {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/mam.rs b/src/mam.rs index ad4eb92e7ad28334ab429da45073b007fc9deb00..11850a29177e557c2a00d5c0c6ca11bdf237cb8b 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -11,6 +11,7 @@ use jid::Jid; use error::Error; +use message::MessagePayload; use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use data_forms::DataForm; use rsm::{SetQuery, SetResult}; @@ -65,6 +66,8 @@ generate_element!( ] ); +impl MessagePayload for Result_ {} + generate_attribute!( /// True when the end of a MAM query has been reached. Complete, "complete", bool diff --git a/src/message.rs b/src/message.rs index 7600d1049ad81567b76c027dfa35f970a645c42c..dab55a28a12c4ee5ec7cec0a12d9116ad8f9da37 100644 --- a/src/message.rs +++ b/src/message.rs @@ -17,95 +17,8 @@ use error::Error; use ns; -use stanza_error::StanzaError; -use chatstates::ChatState; -use receipts::{Request as ReceiptRequest, Received as ReceiptReceived}; -use delay::Delay; -use attention::Attention; -use message_correct::Replace; -use eme::ExplicitMessageEncryption; -use stanza_id::{StanzaId, OriginId}; -use mam::Result_ as MamResult; - -/// Lists every known payload of a ``. -#[derive(Debug, Clone)] -pub enum MessagePayload { - StanzaError(StanzaError), - ChatState(ChatState), - ReceiptRequest(ReceiptRequest), - ReceiptReceived(ReceiptReceived), - Delay(Delay), - Attention(Attention), - MessageCorrect(Replace), - ExplicitMessageEncryption(ExplicitMessageEncryption), - StanzaId(StanzaId), - OriginId(OriginId), - MamResult(MamResult), - - Unknown(Element), -} - -impl TryFrom for MessagePayload { - type Err = Error; - - fn try_from(elem: Element) -> Result { - Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) { - ("error", ns::DEFAULT_NS) => MessagePayload::StanzaError(StanzaError::try_from(elem)?), - - // XEP-0085 - ("active", ns::CHATSTATES) - | ("inactive", ns::CHATSTATES) - | ("composing", ns::CHATSTATES) - | ("paused", ns::CHATSTATES) - | ("gone", ns::CHATSTATES) => MessagePayload::ChatState(ChatState::try_from(elem)?), - - // XEP-0184 - ("request", ns::RECEIPTS) => MessagePayload::ReceiptRequest(ReceiptRequest::try_from(elem)?), - ("received", ns::RECEIPTS) => MessagePayload::ReceiptReceived(ReceiptReceived::try_from(elem)?), - - // XEP-0203 - ("delay", ns::DELAY) => MessagePayload::Delay(Delay::try_from(elem)?), - - // XEP-0224 - ("attention", ns::ATTENTION) => MessagePayload::Attention(Attention::try_from(elem)?), - - // XEP-0308 - ("replace", ns::MESSAGE_CORRECT) => MessagePayload::MessageCorrect(Replace::try_from(elem)?), - - // XEP-0313 - ("result", ns::MAM) => MessagePayload::MamResult(MamResult::try_from(elem)?), - - // XEP-0359 - ("stanza-id", ns::SID) => MessagePayload::StanzaId(StanzaId::try_from(elem)?), - ("origin-id", ns::SID) => MessagePayload::OriginId(OriginId::try_from(elem)?), - - // XEP-0380 - ("encryption", ns::EME) => MessagePayload::ExplicitMessageEncryption(ExplicitMessageEncryption::try_from(elem)?), - - _ => MessagePayload::Unknown(elem), - }) - } -} - -impl From for Element { - fn from(payload: MessagePayload) -> Element { - match payload { - MessagePayload::StanzaError(stanza_error) => stanza_error.into(), - MessagePayload::Attention(attention) => attention.into(), - MessagePayload::ChatState(chatstate) => chatstate.into(), - MessagePayload::ReceiptRequest(request) => request.into(), - MessagePayload::ReceiptReceived(received) => received.into(), - MessagePayload::Delay(delay) => delay.into(), - MessagePayload::MessageCorrect(replace) => replace.into(), - MessagePayload::ExplicitMessageEncryption(eme) => eme.into(), - MessagePayload::StanzaId(stanza_id) => stanza_id.into(), - MessagePayload::OriginId(origin_id) => origin_id.into(), - MessagePayload::MamResult(result) => result.into(), - - MessagePayload::Unknown(elem) => elem, - } - } -} +/// Should be implemented on every known payload of a ``. +pub trait MessagePayload: TryFrom + Into {} generate_attribute!( /// The type of a message. diff --git a/src/message_correct.rs b/src/message_correct.rs index 6ff06bc3f1242b138cb71268624005015d3f39d7..04834518d8cb01f37ed43a8bcc014d0e82408888 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; + generate_element!( /// Defines that the message containing this payload should replace a /// previous message, identified by the id. @@ -14,6 +16,8 @@ generate_element!( ] ); +impl MessagePayload for Replace {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/receipts.rs b/src/receipts.rs index 10c0e51c64b23b2b4753fdc1ca2133d4b6bf01fe..898a5dd65cc351801f9c828d90f8764c2b1f291f 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,12 +4,16 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; + generate_empty_element!( /// Requests that this message is acked by the final recipient once /// received. Request, "request", RECEIPTS ); +impl MessagePayload for Request {} + generate_element!( /// Notes that a previous message has correctly been received, it is /// referenced by its 'id' attribute. @@ -20,6 +24,8 @@ generate_element!( ] ); +impl MessagePayload for Received {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/stanza_error.rs b/src/stanza_error.rs index be2766144081942a90d7ffce153e2e457423b34e..37e71be0209127d278183f57cd60cfb8d45cdac0 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,6 +9,7 @@ use std::collections::BTreeMap; use minidom::Element; +use message::MessagePayload; use presence::PresencePayload; use error::Error; use jid::Jid; @@ -213,6 +214,7 @@ pub struct StanzaError { pub other: Option, } +impl MessagePayload for StanzaError {} impl PresencePayload for StanzaError {} impl TryFrom for StanzaError { diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 07815107c6c9738920e238a0c888cea8a9fe557c..07e947a3e403e1fba0cc2130e4e1755d1f9fc860 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use message::MessagePayload; use jid::Jid; generate_element!( @@ -19,6 +20,8 @@ generate_element!( ] ); +impl MessagePayload for StanzaId {} + generate_element!( /// A hack for MUC before version 1.31 to track a message which may have /// its 'id' attribute changed. @@ -29,6 +32,8 @@ generate_element!( ] ); +impl MessagePayload for OriginId {} + #[cfg(test)] mod tests { use super::*; From fbe22e6db17e65b5e20a9d7a0efcb51c6c4c7145 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 20:58:53 +0200 Subject: [PATCH 0738/1020] eme: Fix indentation. --- src/eme.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/eme.rs b/src/eme.rs index f596dc8f8b4c17751121a364bc09d5ccc1cc84e7..51eb2a6d3483135fe0977a26e597a695a3e1e12f 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -7,16 +7,17 @@ use message::MessagePayload; generate_element!( -/// Structure representing an `` element. -ExplicitMessageEncryption, "encryption", EME, -attributes: [ - /// Namespace of the encryption scheme used. - namespace: String = "namespace" => required, + /// Structure representing an `` element. + ExplicitMessageEncryption, "encryption", EME, + attributes: [ + /// Namespace of the encryption scheme used. + namespace: String = "namespace" => required, - /// User-friendly name for the encryption scheme, should be `None` for OTR, - /// legacy OpenPGP and OX. - name: Option = "name" => optional, -]); + /// User-friendly name for the encryption scheme, should be `None` for OTR, + /// legacy OpenPGP and OX. + name: Option = "name" => optional, + ] +); impl MessagePayload for ExplicitMessageEncryption {} From c508275f40d7cc80fb80d7e3b8a8a963d9c760d1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 21:08:46 +0200 Subject: [PATCH 0739/1020] presence: Finish to document this module. --- src/presence.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index e81358fb8853fa83d3e379b93ade6a3467e27678..8bde17801226156970b6065ebdb4f546632412d3 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,8 +5,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::str::FromStr; use std::collections::BTreeMap; @@ -22,12 +20,23 @@ use ns; /// Should be implemented on every known payload of a ``. pub trait PresencePayload: TryFrom + Into {} +/// Specifies the availability of an entity or resource. #[derive(Debug, Clone, PartialEq)] pub enum Show { + /// Not an actual show value, but an indication there is no show set. None, + + /// The entity or resource is temporarily away. Away, + + /// The entity or resource is actively interested in chatting. Chat, + + /// The entity or resource is busy (dnd = "Do Not Disturb"). Dnd, + + /// The entity or resource is away for an extended period (xa = "eXtended + /// Away"). Xa, } @@ -70,22 +79,41 @@ impl IntoElements for Show { } } -pub type Lang = String; -pub type Status = String; +type Lang = String; +type Status = String; -pub type Priority = i8; +type Priority = i8; +/// #[derive(Debug, Clone, PartialEq)] pub enum Type { /// This value is not an acceptable 'type' attribute, it is only used /// internally to signal the absence of 'type'. None, + + /// An error has occurred regarding processing of a previously sent + /// presence stanza; if the presence stanza is of type "error", it MUST + /// include an child element (refer to [XMPP‑CORE]). Error, + + /// A request for an entity's current presence; SHOULD be generated only by + /// a server on behalf of a user. Probe, + + /// The sender wishes to subscribe to the recipient's presence. Subscribe, + + /// The sender has allowed the recipient to receive their presence. Subscribed, + + /// The sender is no longer available for communication. Unavailable, + + /// The sender is unsubscribing from the receiver's presence. Unsubscribe, + + /// The subscription request has been denied or a previously granted + /// subscription has been canceled. Unsubscribed, } @@ -132,17 +160,34 @@ impl IntoAttributeValue for Type { /// The main structure representing the `` stanza. #[derive(Debug, Clone)] pub struct Presence { + /// The sender of this presence. pub from: Option, + + /// The recipient of this presence. pub to: Option, + + /// The identifier, unique on this stream, of this stanza. pub id: Option, + + /// The type of this presence stanza. pub type_: Type, + + /// The availability of the sender of this presence. pub show: Show, + + /// A localised list of statuses defined in this presence. pub statuses: BTreeMap, + + /// The sender’s resource priority, if negative it won’t receive messages + /// that haven’t been directed to it. pub priority: Priority, + + /// A list of payloads contained in this presence. pub payloads: Vec, } impl Presence { + /// Create a new presence of this type. pub fn new(type_: Type) -> Presence { Presence { from: None, @@ -156,31 +201,40 @@ impl Presence { } } + /// Set the emitter of this presence, this should only be useful for + /// servers and components, as clients can only send presences from their + /// own resource (which is implicit). pub fn with_from(mut self, from: Option) -> Presence { self.from = from; self } + /// Set the recipient of this presence, this is only useful for directed + /// presences. pub fn with_to(mut self, to: Option) -> Presence { self.to = to; self } + /// Set the identifier for this presence. pub fn with_id(mut self, id: Option) -> Presence { self.id = id; self } + /// Set the availability information of this presence. pub fn with_show(mut self, show: Show) -> Presence { self.show = show; self } + /// Set the priority of this presence. pub fn with_priority(mut self, priority: i8) -> Presence { self.priority = priority; self } + /// Set the payloads of this presence. pub fn with_payloads(mut self, payloads: Vec) -> Presence { self.payloads = payloads; self From 5582a48b4e49f74ab3b5e6024cae9c7da31e635f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 21:09:05 +0200 Subject: [PATCH 0740/1020] message: Everything is already documented, remove the allow(missing_docs). --- src/message.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/message.rs b/src/message.rs index dab55a28a12c4ee5ec7cec0a12d9116ad8f9da37..7506a3fa513b612b3afdddbf8b010dd48078fa02 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::collections::BTreeMap; From 0da5639be521a26a346bc6f768912164bda9d1ff Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 21:15:50 +0200 Subject: [PATCH 0741/1020] jingle: Document this module. --- src/jingle.rs | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index dab53712a34aa59ecee96a0f37668704da766710..3102b1052abc3c3287f4713e4310cb9f83f3ba34 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,8 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#![allow(missing_docs)] - use try_from::TryFrom; use std::str::FromStr; @@ -168,6 +166,8 @@ generate_id!( ); generate_element!( + /// Describes a session’s content, there can be multiple content in one + /// session. Content, "content", JINGLE, attributes: [ /// Who created this content. @@ -183,13 +183,19 @@ generate_element!( senders: Senders = "senders" => default ], children: [ + /// What to send. description: Option = ("description", JINGLE) => Element, + + /// How to send it. transport: Option = ("transport", JINGLE) => Element, + + /// With which security. security: Option = ("security", JINGLE) => Element ] ); impl Content { + /// Create a new content. pub fn new(creator: Creator, name: ContentId) -> Content { Content { creator, @@ -202,32 +208,38 @@ impl Content { } } + /// Set how the content is to be interpreted by the recipient. pub fn with_disposition(mut self, disposition: Disposition) -> Content { self.disposition = disposition; self } + /// Specify who can send data for this content. pub fn with_senders(mut self, senders: Senders) -> Content { self.senders = senders; self } + /// Set the description of this content. pub fn with_description(mut self, description: Element) -> Content { self.description = Some(description); self } + /// Set the transport of this content. pub fn with_transport(mut self, transport: Element) -> Content { self.transport = Some(transport); self } + /// Set the security of this content. pub fn with_security(mut self, security: Element) -> Content { self.security = Some(security); self } } +/// Lists the possible reasons to be included in a Jingle iq. #[derive(Debug, Clone, PartialEq)] pub enum Reason { /// The party prefers to use an existing session with the peer rather than @@ -343,9 +355,13 @@ impl From for Element { } } +/// Informs the recipient of something. #[derive(Debug, Clone)] pub struct ReasonElement { + /// The list of possible reasons to be included in a Jingle iq. pub reason: Reason, + + /// A human-readable description of this reason. pub text: Option, } @@ -397,20 +413,35 @@ generate_id!( SessionId ); +/// The main Jingle container, to be included in an iq stanza. #[derive(Debug, Clone)] pub struct Jingle { + /// The action to execute on both ends. pub action: Action, + + /// Who the initiator is. pub initiator: Option, + + /// Who the responder is. pub responder: Option, + + /// Unique session identifier between two entities. pub sid: SessionId, + + /// A list of contents to be negociated in this session. pub contents: Vec, + + /// An optional reason. pub reason: Option, + + /// Payloads to be included. pub other: Vec, } impl IqSetPayload for Jingle {} impl Jingle { + /// Create a new Jingle element. pub fn new(action: Action, sid: SessionId) -> Jingle { Jingle { action: action, @@ -423,21 +454,25 @@ impl Jingle { } } + /// Set the initiator’s JID. pub fn with_initiator(mut self, initiator: Jid) -> Jingle { self.initiator = Some(initiator); self } + /// Set the responder’s JID. pub fn with_responder(mut self, responder: Jid) -> Jingle { self.responder = Some(responder); self } + /// Add a content to this Jingle container. pub fn add_content(mut self, content: Content) -> Jingle { self.contents.push(content); self } + /// Set the reason in this Jingle container. pub fn set_reason(mut self, content: Content) -> Jingle { self.contents.push(content); self From 2878b0c5465b1155d2b92c1f71e8bf1a32e34e2c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 21:20:02 +0200 Subject: [PATCH 0742/1020] ChangeLog: Add imminent version 0.11.1. --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1827f093c51ab54210dd88dcccd83cd07a17cff1..ffe9b95f53389a7bd5aaaa98599a07f9c57dde39 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version 0.11.1: +2018-09-20 Emmanuel Gil Peyrot + * Improvements: + - Document all of the modules. + Version 0.11.0: 2018-08-03 Emmanuel Gil Peyrot * Breaking changes: From 4567b66d26ad0a116e9445ebe6fa38ed44d5a1de Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Sep 2018 21:20:18 +0200 Subject: [PATCH 0743/1020] Cargo.toml: Release version 0.11.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2c2a34f947583b9d640f073e4d395d6b9a5c3bf6..2e5bba5f6352d271ff149f7f224374f0c9092662 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.11.0" +version = "0.11.1" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 4ab3890cd30b5bd5279a7e22b5546dc437afcb54 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 1 Oct 2018 12:34:33 +0200 Subject: [PATCH 0745/1020] Use tokio::net::tcp::ConnectFuture directly. This removes this warning: ``` warning: use of deprecated item 'tokio::net::ConnectFuture': use `tokio::net::tcp::ConnectFuture` instead ``` --- src/happy_eyeballs.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 6d201fdee5d85ad861f92dc57d5758d873f528ab..6e7f02757fc0bd4711af1b9687ebc45e02217a07 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -5,7 +5,8 @@ use std::collections::BTreeMap; use std::collections::VecDeque; use std::cell::RefCell; use futures::{Future, Poll, Async}; -use tokio::net::{ConnectFuture, TcpStream}; +use tokio::net::TcpStream; +use tokio::net::tcp::ConnectFuture; use trust_dns_resolver::{IntoName, Name, ResolverFuture, error::ResolveError}; use trust_dns_resolver::lookup::SrvLookupFuture; use trust_dns_resolver::lookup_ip::LookupIpFuture; From 9cb4f003418ae56d0db0c148a6309167dfe5c7d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 12 Oct 2018 17:23:34 +0200 Subject: [PATCH 0746/1020] =?UTF-8?q?caps,=20ecaps2:=20Update=20to=20RustC?= =?UTF-8?q?rypto=C2=A00.8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 10 +++++----- src/caps.rs | 16 ++++++---------- src/ecaps2.rs | 16 ++++++---------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2e5bba5f6352d271ff149f7f224374f0c9092662..a438baec39d7e80575db8297beac0a521c737301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,11 @@ license = "MPL-2.0" minidom = "0.9.1" jid = { version = "0.5.2", features = ["minidom"] } base64 = "0.9.2" -digest = "0.7.5" -sha-1 = "0.7.0" -sha2 = "0.7.1" -sha3 = "0.7.3" -blake2 = "0.7.1" +digest = "0.8" +sha-1 = "0.8" +sha2 = "0.8" +sha3 = "0.8" +blake2 = "0.8" chrono = "0.4.5" try_from = "0.2.2" diff --git a/src/caps.rs b/src/caps.rs index 77901ba165ad9aa2b0e828aa71fb8593e410d33b..25c851fbaba2d0906840d6711fc5a229f7ab2245 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -19,8 +19,8 @@ use base64; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use blake2::Blake2b; -use digest::{Digest, VariableOutput}; +use blake2::VarBlake2b; +use digest::{Digest, VariableOutput, Input}; /// Represents a capability hash for a given client. #[derive(Debug, Clone)] @@ -182,18 +182,14 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { get_hash_vec(hash.as_slice()) }, Algo::Blake2b_256 => { - let mut hasher = Blake2b::default(); + let mut hasher = VarBlake2b::new(32).unwrap(); hasher.input(data); - let mut buf: [u8; 32] = [0; 32]; - let hash = hasher.variable_result(&mut buf).unwrap(); - get_hash_vec(hash) + hasher.vec_result() }, Algo::Blake2b_512 => { - let mut hasher = Blake2b::default(); + let mut hasher = VarBlake2b::new(64).unwrap(); hasher.input(data); - let mut buf: [u8; 64] = [0; 64]; - let hash = hasher.variable_result(&mut buf).unwrap(); - get_hash_vec(hash) + hasher.vec_result() }, Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 82ab1c4adfac58d1b293c0313f23e9430835b0f8..6e71040d51c86afc85f50b3d511f4ddfc2241118 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -14,8 +14,8 @@ use base64; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use blake2::Blake2b; -use digest::{Digest, VariableOutput}; +use blake2::VarBlake2b; +use digest::{Digest, VariableOutput, Input}; generate_element!( /// Represents a set of capability hashes, all of them must correspond to @@ -121,18 +121,14 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { get_hash_vec(hash.as_slice()) }, Algo::Blake2b_256 => { - let mut hasher = Blake2b::default(); + let mut hasher = VarBlake2b::new(32).unwrap(); hasher.input(data); - let mut buf: [u8; 32] = [0; 32]; - let hash = hasher.variable_result(&mut buf).unwrap(); - get_hash_vec(hash) + hasher.vec_result() }, Algo::Blake2b_512 => { - let mut hasher = Blake2b::default(); + let mut hasher = VarBlake2b::new(64).unwrap(); hasher.input(data); - let mut buf: [u8; 64] = [0; 64]; - let hash = hasher.variable_result(&mut buf).unwrap(); - get_hash_vec(hash) + hasher.vec_result() }, Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), From 72c63920a8b0d20f956696add58d602855bed7bd Mon Sep 17 00:00:00 2001 From: Bastien Orivel Date: Sun, 21 Oct 2018 20:24:32 +0200 Subject: [PATCH 0747/1020] Update quick-xml and bump version --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f49a66c3ea95c404741de098a78057f69b80e0d..fa66cfe377938713b8b89f75ae8a726ce78a861d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.9.1" +version = "0.10.0" authors = [ "lumi ", "Emmanuel Gil Peyrot ", @@ -20,6 +20,6 @@ license = "MIT" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -quick-xml = "0.12.1" +quick-xml = "0.13" failure = "0.1.1" failure_derive = "0.1.1" From 40aedcf184e5c4f70255f2f2cebbb9e9c9b9dfee Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 26 Oct 2018 14:26:16 +0200 Subject: [PATCH 0748/1020] Test the size of every struct defined here. --- src/attention.rs | 5 +++++ src/bind.rs | 5 +++++ src/blocking.rs | 8 ++++++++ src/bookmarks.rs | 7 +++++++ src/caps.rs | 5 +++++ src/chatstates.rs | 5 +++++ src/component.rs | 5 +++++ src/data_forms.rs | 9 +++++++++ src/date.rs | 5 +++++ src/delay.rs | 5 +++++ src/disco.rs | 12 ++++++++++++ src/ecaps2.rs | 5 +++++ src/eme.rs | 5 +++++ src/forwarding.rs | 5 +++++ src/hashes.rs | 6 ++++++ src/ibb.rs | 8 ++++++++ src/ibr.rs | 5 +++++ src/idle.rs | 5 +++++ src/iq.rs | 6 ++++++ src/jingle.rs | 14 ++++++++++++++ src/jingle_ft.rs | 9 +++++++++ src/jingle_ibb.rs | 5 +++++ src/jingle_message.rs | 5 +++++ src/jingle_s5b.rs | 11 +++++++++++ src/macros.rs | 6 ++++++ src/mam.rs | 11 +++++++++++ src/media_element.rs | 6 ++++++ src/message.rs | 9 +++++++++ src/message_correct.rs | 5 +++++ src/mood.rs | 6 ++++++ src/nick.rs | 5 +++++ src/ping.rs | 5 +++++ src/presence.rs | 7 +++++++ src/receipts.rs | 6 ++++++ src/roster.rs | 8 ++++++++ src/rsm.rs | 6 ++++++ src/sasl.rs | 12 ++++++++++++ src/sm.rs | 14 ++++++++++++++ src/stanza_error.rs | 7 +++++++ src/stanza_id.rs | 6 ++++++ src/stream.rs | 5 +++++ src/version.rs | 6 ++++++ src/websocket.rs | 5 +++++ 43 files changed, 295 insertions(+) diff --git a/src/attention.rs b/src/attention.rs index 398b2bfd2f3424189dca01d8875bfe0ea8025e7e..4bfffd7eb7fe725943adfbcb1c3289cc330c5b65 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -20,6 +20,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(Attention, 0); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/bind.rs b/src/bind.rs index 1922093b303c8dadcc14337bdac2e903a635a120..6bfa8a310edc4a765fb8cab864dab159eb339fb0 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -98,6 +98,11 @@ impl From for Element { mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(Bind, 80); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/blocking.rs b/src/blocking.rs index 6b6894c9ad5c14e68c1321d7ad4114b8395c94e2..87a2fbd2bcdc002d897bc33a9d969c84ebb6e62c 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -98,6 +98,14 @@ generate_empty_element!( mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(BlocklistRequest, 0); + assert_size!(BlocklistResult, 24); + assert_size!(Block, 24); + assert_size!(Unblock, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 11b4a42131af17549f18115bf90e1d8beac4da30..8ae0639205358f30feb399b0a9e93750f2e9dacf 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -74,6 +74,13 @@ mod tests { use minidom::Element; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(Conference, 152); + assert_size!(Url, 48); + assert_size!(Storage, 48); + } + #[test] fn empty() { let elem: Element = "".parse().unwrap(); diff --git a/src/caps.rs b/src/caps.rs index 25c851fbaba2d0906840d6711fc5a229f7ab2245..7fcaea801731e2ae4f238d3b14cae73c4d67a34d 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -211,6 +211,11 @@ mod tests { use caps; use base64; + #[test] + fn test_size() { + assert_size!(Caps, 104); + } + #[test] fn test_parse() { let elem: Element = "".parse().unwrap(); diff --git a/src/chatstates.rs b/src/chatstates.rs index c9fabd5264285d188ce3752cab97777bdd1d4454..477bdded08d5b9083ac95e3995acb2bde604ea85 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -37,6 +37,11 @@ mod tests { use error::Error; use ns; + #[test] + fn test_size() { + assert_size!(ChatState, 1); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/component.rs b/src/component.rs index 27229f25aaeab1bc8befc999a83a11f5c5561b32..503e103423c05bfff07fcbd9d51289661450c131 100644 --- a/src/component.rs +++ b/src/component.rs @@ -47,6 +47,11 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(Handshake, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/data_forms.rs b/src/data_forms.rs index a43977974eb84b0efbccfbac91893fe19ef142cb..3fccd72622e8983631b495192cd1e93d0dbf11db 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -268,6 +268,15 @@ impl From for Element { mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(Option_, 48); + assert_size!(FieldType, 1); + assert_size!(Field, 128); + assert_size!(DataFormType, 1); + assert_size!(DataForm, 104); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/date.rs b/src/date.rs index a344eb58d97dc4ad297de5fd471811c8594fd2f9..5f2038de7a4576fec17c8a5f1a618bfa3d6343fa 100644 --- a/src/date.rs +++ b/src/date.rs @@ -43,6 +43,11 @@ mod tests { use chrono::{Datelike, Timelike}; use std::error::Error as StdError; + #[test] + fn test_size() { + assert_size!(DateTime, 16); + } + #[test] fn test_simple() { let date: DateTime = "2002-09-10T23:08:25Z".parse().unwrap(); diff --git a/src/delay.rs b/src/delay.rs index 581e72e26184428f4320bb80429fa4f55f481d43..c4f7518f6f85da2fcef3fd698fb770d8f00a5e5d 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -39,6 +39,11 @@ mod tests { use error::Error; use std::str::FromStr; + #[test] + fn test_size() { + assert_size!(Delay, 112); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/disco.rs b/src/disco.rs index 59166eb82bd2d3db50663a287edccbdb5074b9d3..7b435530a6c72bec563000c459fd94c153106413 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -235,6 +235,18 @@ mod tests { use compare_elements::NamespaceAwareCompare; use std::str::FromStr; + #[test] + fn test_size() { + assert_size!(Identity, 96); + assert_size!(Feature, 24); + assert_size!(DiscoInfoQuery, 24); + assert_size!(DiscoInfoResult, 96); + + assert_size!(Item, 120); + assert_size!(DiscoItemsQuery, 24); + assert_size!(DiscoItemsResult, 48); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 6e71040d51c86afc85f50b3d511f4ddfc2241118..d4811b5cc917d1ba59081a25d4dfd218dda80f57 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -152,6 +152,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(ECaps2, 24); + } + #[test] fn test_parse() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); diff --git a/src/eme.rs b/src/eme.rs index 51eb2a6d3483135fe0977a26e597a695a3e1e12f..271b842e38ae0e24b41727660cb1b689844296fe 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -28,6 +28,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(ExplicitMessageEncryption, 48); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/forwarding.rs b/src/forwarding.rs index b65b450367bf195ed29ce2135fc7f62088792812..fbfacb11ec2caaabc3a7afe5b9579598cedab932 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -30,6 +30,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(Forwarded, 392); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/hashes.rs b/src/hashes.rs index bf7fafbd797d6b2e5b991cecdc335c2ccd430d5c..7063dbc66ff754e8716b974329564581cd29c5e3 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -133,6 +133,12 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(Algo, 32); + assert_size!(Hash, 56); + } + #[test] fn test_simple() { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); diff --git a/src/ibb.rs b/src/ibb.rs index bede2f0b8ba148a57524621f775ca04370e5665b..e595fe4ab6a7709bd0b4d48ba8a82bc22e511e2c 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -76,6 +76,14 @@ mod tests { use error::Error; use std::error::Error as StdError; + #[test] + fn test_size() { + assert_size!(Stanza, 1); + assert_size!(Open, 32); + assert_size!(Data, 56); + assert_size!(Close, 24); + } + #[test] fn test_simple() { let sid = StreamId(String::from("coucou")); diff --git a/src/ibr.rs b/src/ibr.rs index 72324deeec3d7dbe86d7cdce05595a6a3ba29325..71256b642a0de42366eb438d15827e25df6be909 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -96,6 +96,11 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(Query, 152); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/idle.rs b/src/idle.rs index 2704654735b03784db8855fa558a4f7aa9e548e6..45352a229a58509a5ed7e6a04d90bf458a9a04f0 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -27,6 +27,11 @@ mod tests { use std::str::FromStr; use std::error::Error as StdError; + #[test] + fn test_size() { + assert_size!(Idle, 16); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/iq.rs b/src/iq.rs index aeeb836cc112ef5904d0196a911fc362038046b3..b5f27bf06a545720576a160abaa9f71e349f6c4f 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -226,6 +226,12 @@ mod tests { use compare_elements::NamespaceAwareCompare; use disco::DiscoInfoQuery; + #[test] + fn test_size() { + assert_size!(IqType, 216); + assert_size!(Iq, 384); + } + #[test] fn test_require_type() { #[cfg(not(feature = "component"))] diff --git a/src/jingle.rs b/src/jingle.rs index 3102b1052abc3c3287f4713e4310cb9f83f3ba34..38577f8415e86fc9fe1591a1a97eb91119e9fc10 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -533,6 +533,20 @@ impl From for Element { mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(Action, 1); + assert_size!(Creator, 1); + assert_size!(Senders, 1); + assert_size!(Disposition, 1); + assert_size!(ContentId, 24); + assert_size!(Content, 344); + assert_size!(Reason, 1); + assert_size!(ReasonElement, 32); + assert_size!(SessionId, 24); + assert_size!(Jingle, 256); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index e206ab3c55c8f29b2507dee502cebdcd86dfbb68..0a3c022a1ab6518e459d17e9be26f73ab9bc754e 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -348,6 +348,15 @@ mod tests { use hashes::Algo; use base64; + #[test] + fn test_size() { + assert_size!(Range, 48); + assert_size!(File, 184); + assert_size!(Description, 184); + assert_size!(Checksum, 216); + assert_size!(Received, 32); + } + #[test] fn test_description() { let elem: Element = r#" diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 7438133c56681055a5d52236bb01fe7564324afd..c56c73d490a3907512b668e29a52f2af1477ff7b 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -29,6 +29,11 @@ mod tests { use error::Error; use std::error::Error as StdError; + #[test] + fn test_size() { + assert_size!(Transport, 32); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/jingle_message.rs b/src/jingle_message.rs index d8e30382f61871c1bc00b33e5aa0f5ec32560888..150e7a03134367da32a384d78827147921a1e52c 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -121,6 +121,11 @@ impl From for Element { mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(JingleMI, 136); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index f6f05be3416394f0e50d0fc4a8d23c0f48c466b9..a5fdff0deb3b63dad66b32a5b6727344819c7a1b 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -277,6 +277,17 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(Type, 1); + assert_size!(Mode, 1); + assert_size!(CandidateId, 24); + assert_size!(StreamId, 24); + assert_size!(Candidate, 128); + assert_size!(TransportPayload, 32); + assert_size!(Transport, 88); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/macros.rs b/src/macros.rs index 5793fef04ee05c21a427cef9dc101fa621546e01..3deade1d361315e1d3692e5e13a87947798b565d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -510,3 +510,9 @@ macro_rules! generate_element { } ); } + +macro_rules! assert_size ( + ($t:ty, $sz:expr) => ( + assert_eq!(::std::mem::size_of::<$t>(), $sz); + ); +); diff --git a/src/mam.rs b/src/mam.rs index 11850a29177e557c2a00d5c0c6ca11bdf237cb8b..44564120a727b058a6f58740ecda9e3dbb612ae0 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -191,6 +191,17 @@ mod tests { use super::*; use std::str::FromStr; + #[test] + fn test_size() { + assert_size!(QueryId, 24); + assert_size!(Query, 232); + assert_size!(Result_, 440); + assert_size!(Complete, 1); + assert_size!(Fin, 88); + assert_size!(DefaultPrefs, 1); + assert_size!(Prefs, 56); + } + #[test] fn test_query() { let elem: Element = "".parse().unwrap(); diff --git a/src/media_element.rs b/src/media_element.rs index 7ad264b2251d6c184c380893275081a062a27c1b..722780f2f03ceb12fb0b8dcaa96e9eee362cdb86 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -51,6 +51,12 @@ mod tests { use data_forms::DataForm; use std::error::Error as StdError; + #[test] + fn test_size() { + assert_size!(URI, 48); + assert_size!(MediaElement, 56); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/message.rs b/src/message.rs index 7506a3fa513b612b3afdddbf8b010dd48078fa02..2e9d9c659d9509cfc5da8e71400e82bdf1a12905 100644 --- a/src/message.rs +++ b/src/message.rs @@ -234,6 +234,15 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(MessageType, 1); + assert_size!(Body, 24); + assert_size!(Subject, 24); + assert_size!(Thread, 24); + assert_size!(Message, 272); + } + #[test] fn test_simple() { #[cfg(not(feature = "component"))] diff --git a/src/message_correct.rs b/src/message_correct.rs index 04834518d8cb01f37ed43a8bcc014d0e82408888..e8233ee632d6b5295e1c180d1e14aff328958e54 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -25,6 +25,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(Replace, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/mood.rs b/src/mood.rs index 8612a42c18caa9511169b73c89ed5d831eaacc9b..6bf8bd2383cf1fce59068131b62872132cd3a205 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -272,6 +272,12 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(MoodEnum, 1); + assert_size!(Text, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/nick.rs b/src/nick.rs index 9844e9318c8670319e3f032e9717c10d9ffbfade..2d2f90c4c7d212d491c244e4b5aa97a8c752d0e4 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -16,6 +16,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(Nick, 24); + } + #[test] fn test_simple() { let elem: Element = "Link Mauve".parse().unwrap(); diff --git a/src/ping.rs b/src/ping.rs index 0af81c0da3f2b06cd4a0e68be9c9a7b63820c4c7..557f535a7038ab4417cefed415e5ad22b9c7e7dd 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -22,6 +22,11 @@ mod tests { use minidom::Element; use error::Error; + #[test] + fn test_size() { + assert_size!(Ping, 0); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/presence.rs b/src/presence.rs index 8bde17801226156970b6065ebdb4f546632412d3..02a9a9c7c890c95e6dad4173f70a3fb378c42901 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -323,6 +323,13 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(Show, 1); + assert_size!(Type, 1); + assert_size!(Presence, 224); + } + #[test] fn test_simple() { #[cfg(not(feature = "component"))] diff --git a/src/receipts.rs b/src/receipts.rs index 898a5dd65cc351801f9c828d90f8764c2b1f291f..ecf0a21cabe54828889a5dcfba0e002f8765486a 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -33,6 +33,12 @@ mod tests { use minidom::Element; use ns; + #[test] + fn test_size() { + assert_size!(Request, 0); + assert_size!(Received, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/roster.rs b/src/roster.rs index 0bb743932007edd30a7881c3123d323c388f040d..eeebddf80cf192dc77db449c23567241c7affca4 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -85,6 +85,14 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(Group, 24); + assert_size!(Subscription, 1); + assert_size!(Item, 128); + assert_size!(Roster, 48); + } + #[test] fn test_get() { let elem: Element = "".parse().unwrap(); diff --git a/src/rsm.rs b/src/rsm.rs index d16751a1f6890e910d74edd25072e87bb18d6369..2f176932430ff022cc9136282b1b4f5fc2852987 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -158,6 +158,12 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(SetQuery, 80); + assert_size!(SetResult, 80); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/sasl.rs b/src/sasl.rs index f27167b1fdaaff082b22af98f3ecad9a8318c17d..5272fd0d315b786196877e542ee3d9a7de809551 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -215,6 +215,18 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(Mechanism, 1); + assert_size!(Auth, 32); + assert_size!(Challenge, 24); + assert_size!(Response, 24); + assert_size!(Abort, 0); + assert_size!(Success, 24); + assert_size!(DefinedCondition, 1); + assert_size!(Failure, 32); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/sm.rs b/src/sm.rs index e93aa24f189115dea05edee7ff5a573df6075416..dfc3dff204493cb77f5b2339aa441396098dd06b 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -144,6 +144,20 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(A, 4); + assert_size!(ResumeAttr, 1); + assert_size!(Enable, 12); + assert_size!(StreamId, 24); + assert_size!(Enabled, 64); + assert_size!(Failed, 12); + assert_size!(R, 0); + assert_size!(Resume, 32); + assert_size!(Resumed, 32); + assert_size!(StreamManagement, 0); + } + #[test] fn a() { let elem: Element = " for Element { mod tests { use super::*; + #[test] + fn test_size() { + assert_size!(ErrorType, 1); + assert_size!(DefinedCondition, 1); + assert_size!(StanzaError, 208); + } + #[test] fn test_simple() { #[cfg(not(feature = "component"))] diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 07e947a3e403e1fba0cc2130e4e1755d1f9fc860..9dc3698a6c64896a3cb88074ca98b88ed5228ea8 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -42,6 +42,12 @@ mod tests { use error::Error; use std::str::FromStr; + #[test] + fn test_size() { + assert_size!(StanzaId, 96); + assert_size!(OriginId, 24); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/stream.rs b/src/stream.rs index 693796dcb2022f23a4c7328339c1aeb3a87229d9..b81ba7710208e552c94e66af77125e7bb286063c 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -76,6 +76,11 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(Stream, 216); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); diff --git a/src/version.rs b/src/version.rs index 4354df50d282cf7a14a365f201f6a6972e5647b1..7ed89743bf4bf35a4e6346728ebfa74658474e1b 100644 --- a/src/version.rs +++ b/src/version.rs @@ -43,6 +43,12 @@ mod tests { use minidom::Element; use compare_elements::NamespaceAwareCompare; + #[test] + fn test_size() { + assert_size!(VersionQuery, 0); + assert_size!(VersionResult, 72); + } + #[test] fn simple() { let elem: Element = "xmpp-rs0.3.0".parse().unwrap(); diff --git a/src/websocket.rs b/src/websocket.rs index 102690cde582f37a5d8a06e329369fa36343c91b..2f87437b1c1a3074db3919b454d0a64cd8f5137f 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -75,6 +75,11 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[test] + fn test_size() { + assert_size!(Open, 216); + } + #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); From 7a2dcbee017a1757e5f581df7fa1363dea9dc223 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 Oct 2018 13:10:48 +0100 Subject: [PATCH 0749/1020] Test struct sizes on 32-bit too. --- src/bind.rs | 7 +++++++ src/blocking.rs | 10 ++++++++++ src/bookmarks.rs | 9 +++++++++ src/caps.rs | 7 +++++++ src/component.rs | 7 +++++++ src/data_forms.rs | 11 +++++++++++ src/delay.rs | 7 +++++++ src/disco.rs | 14 ++++++++++++++ src/ecaps2.rs | 7 +++++++ src/eme.rs | 7 +++++++ src/forwarding.rs | 7 +++++++ src/hashes.rs | 8 ++++++++ src/ibb.rs | 10 ++++++++++ src/ibr.rs | 7 +++++++ src/iq.rs | 8 ++++++++ src/jingle.rs | 16 ++++++++++++++++ src/jingle_ft.rs | 11 +++++++++++ src/jingle_ibb.rs | 7 +++++++ src/jingle_message.rs | 7 +++++++ src/jingle_s5b.rs | 13 +++++++++++++ src/mam.rs | 13 +++++++++++++ src/media_element.rs | 8 ++++++++ src/message.rs | 11 +++++++++++ src/message_correct.rs | 7 +++++++ src/mood.rs | 8 ++++++++ src/nick.rs | 7 +++++++ src/presence.rs | 9 +++++++++ src/receipts.rs | 8 ++++++++ src/roster.rs | 10 ++++++++++ src/rsm.rs | 8 ++++++++ src/sasl.rs | 14 ++++++++++++++ src/sm.rs | 16 ++++++++++++++++ src/stanza_error.rs | 9 +++++++++ src/stanza_id.rs | 8 ++++++++ src/stream.rs | 7 +++++++ src/version.rs | 8 ++++++++ src/websocket.rs | 7 +++++++ 37 files changed, 338 insertions(+) diff --git a/src/bind.rs b/src/bind.rs index 6bfa8a310edc4a765fb8cab864dab159eb339fb0..b6ec55d44f291ec3c03b4b681cd2401e22365171 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -98,6 +98,13 @@ impl From for Element { mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Bind, 40); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Bind, 80); diff --git a/src/blocking.rs b/src/blocking.rs index 87a2fbd2bcdc002d897bc33a9d969c84ebb6e62c..cd3f8fd83f7e38c806034c07d69e6fd9cd73a5e3 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -98,6 +98,16 @@ generate_empty_element!( mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(BlocklistRequest, 0); + assert_size!(BlocklistResult, 12); + assert_size!(Block, 12); + assert_size!(Unblock, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(BlocklistRequest, 0); diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 8ae0639205358f30feb399b0a9e93750f2e9dacf..7f68930dd7cb72ffd2450acf08526f10a9e676de 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -74,6 +74,15 @@ mod tests { use minidom::Element; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Conference, 76); + assert_size!(Url, 24); + assert_size!(Storage, 24); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Conference, 152); diff --git a/src/caps.rs b/src/caps.rs index 7fcaea801731e2ae4f238d3b14cae73c4d67a34d..22ac1930e678763bb8f905ccd6062fed04362c6a 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -211,6 +211,13 @@ mod tests { use caps; use base64; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Caps, 52); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Caps, 104); diff --git a/src/component.rs b/src/component.rs index 503e103423c05bfff07fcbd9d51289661450c131..0c9319cdb655d1bebc951b23fe34cf516bf02591 100644 --- a/src/component.rs +++ b/src/component.rs @@ -47,6 +47,13 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Handshake, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Handshake, 24); diff --git a/src/data_forms.rs b/src/data_forms.rs index 3fccd72622e8983631b495192cd1e93d0dbf11db..edf2608ce43ed8801b50feabd88883b01db6926b 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -268,6 +268,17 @@ impl From for Element { mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Option_, 24); + assert_size!(FieldType, 1); + assert_size!(Field, 64); + assert_size!(DataFormType, 1); + assert_size!(DataForm, 52); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Option_, 48); diff --git a/src/delay.rs b/src/delay.rs index c4f7518f6f85da2fcef3fd698fb770d8f00a5e5d..46fec0fbdfd252d76dfc294a243bdd47c85a7d58 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -39,6 +39,13 @@ mod tests { use error::Error; use std::str::FromStr; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Delay, 64); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Delay, 112); diff --git a/src/disco.rs b/src/disco.rs index 7b435530a6c72bec563000c459fd94c153106413..7be0efc77bf6ed1f578886b349150e2172198361 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -235,6 +235,20 @@ mod tests { use compare_elements::NamespaceAwareCompare; use std::str::FromStr; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Identity, 48); + assert_size!(Feature, 12); + assert_size!(DiscoInfoQuery, 12); + assert_size!(DiscoInfoResult, 48); + + assert_size!(Item, 60); + assert_size!(DiscoItemsQuery, 12); + assert_size!(DiscoItemsResult, 24); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Identity, 96); diff --git a/src/ecaps2.rs b/src/ecaps2.rs index d4811b5cc917d1ba59081a25d4dfd218dda80f57..ea5e03df463b725498c035c1dcb74e9a994d5897 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -152,6 +152,13 @@ mod tests { use minidom::Element; use error::Error; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(ECaps2, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(ECaps2, 24); diff --git a/src/eme.rs b/src/eme.rs index 271b842e38ae0e24b41727660cb1b689844296fe..417bbe5214b0343cce932ba4086ce6dc2cf6bfa4 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -28,6 +28,13 @@ mod tests { use minidom::Element; use error::Error; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(ExplicitMessageEncryption, 24); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(ExplicitMessageEncryption, 48); diff --git a/src/forwarding.rs b/src/forwarding.rs index fbfacb11ec2caaabc3a7afe5b9579598cedab932..11efb610caa330668b512c2780b985f90c2e083d 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -30,6 +30,13 @@ mod tests { use minidom::Element; use error::Error; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Forwarded, 204); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Forwarded, 392); diff --git a/src/hashes.rs b/src/hashes.rs index 7063dbc66ff754e8716b974329564581cd29c5e3..5fbf5b90933e827506c11d5d225c276f1194fe13 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -133,6 +133,14 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Algo, 16); + assert_size!(Hash, 28); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Algo, 32); diff --git a/src/ibb.rs b/src/ibb.rs index e595fe4ab6a7709bd0b4d48ba8a82bc22e511e2c..87b335ec1e9b3de8381793175b9e6149279db292 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -76,6 +76,16 @@ mod tests { use error::Error; use std::error::Error as StdError; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Stanza, 1); + assert_size!(Open, 16); + assert_size!(Data, 28); + assert_size!(Close, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Stanza, 1); diff --git a/src/ibr.rs b/src/ibr.rs index 71256b642a0de42366eb438d15827e25df6be909..4d76a9d826db9d6e9cccdd2533080a67936e552c 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -96,6 +96,13 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Query, 88); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Query, 152); diff --git a/src/iq.rs b/src/iq.rs index b5f27bf06a545720576a160abaa9f71e349f6c4f..ff1e7b1fe47929cf7634df4b1946f75150e76dbe 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -226,6 +226,14 @@ mod tests { use compare_elements::NamespaceAwareCompare; use disco::DiscoInfoQuery; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(IqType, 108); + assert_size!(Iq, 192); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(IqType, 216); diff --git a/src/jingle.rs b/src/jingle.rs index 38577f8415e86fc9fe1591a1a97eb91119e9fc10..7e4ea82b5ee6331d14ba853ce2d807c96ca776f5 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -533,6 +533,22 @@ impl From for Element { mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Action, 1); + assert_size!(Creator, 1); + assert_size!(Senders, 1); + assert_size!(Disposition, 1); + assert_size!(ContentId, 12); + assert_size!(Content, 172); + assert_size!(Reason, 1); + assert_size!(ReasonElement, 16); + assert_size!(SessionId, 12); + assert_size!(Jingle, 128); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Action, 1); diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 0a3c022a1ab6518e459d17e9be26f73ab9bc754e..604e6ee5c716981f4d5a0b5f75438c2fb192a10e 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -348,6 +348,17 @@ mod tests { use hashes::Algo; use base64; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Range, 40); + assert_size!(File, 128); + assert_size!(Description, 128); + assert_size!(Checksum, 144); + assert_size!(Received, 16); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Range, 48); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index c56c73d490a3907512b668e29a52f2af1477ff7b..e02a43474cb691efe25d9cfc2ad8239844302262 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -29,6 +29,13 @@ mod tests { use error::Error; use std::error::Error as StdError; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Transport, 16); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Transport, 32); diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 150e7a03134367da32a384d78827147921a1e52c..1bef32698422508e9b25511095f8cd8656fd4612 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -121,6 +121,13 @@ impl From for Element { mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(JingleMI, 68); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(JingleMI, 136); diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index a5fdff0deb3b63dad66b32a5b6727344819c7a1b..904a4d77f9db5743ed1d80c6aa1bbb21ad9d6b89 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -277,6 +277,19 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Type, 1); + assert_size!(Mode, 1); + assert_size!(CandidateId, 12); + assert_size!(StreamId, 12); + assert_size!(Candidate, 80); + assert_size!(TransportPayload, 16); + assert_size!(Transport, 44); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Type, 1); diff --git a/src/mam.rs b/src/mam.rs index 44564120a727b058a6f58740ecda9e3dbb612ae0..819bc46261af2237b84d7e9cee700f8ab62bc1af 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -191,6 +191,19 @@ mod tests { use super::*; use std::str::FromStr; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(QueryId, 12); + assert_size!(Query, 116); + assert_size!(Result_, 228); + assert_size!(Complete, 1); + assert_size!(Fin, 44); + assert_size!(DefaultPrefs, 1); + assert_size!(Prefs, 28); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(QueryId, 24); diff --git a/src/media_element.rs b/src/media_element.rs index 722780f2f03ceb12fb0b8dcaa96e9eee362cdb86..dc0c2af7a7e86a580ddba48bd60b43cb929766e7 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -51,6 +51,14 @@ mod tests { use data_forms::DataForm; use std::error::Error as StdError; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(URI, 24); + assert_size!(MediaElement, 28); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(URI, 48); diff --git a/src/message.rs b/src/message.rs index 2e9d9c659d9509cfc5da8e71400e82bdf1a12905..9c392b790991c6d3f35f0fa96487f553c781d3b4 100644 --- a/src/message.rs +++ b/src/message.rs @@ -234,6 +234,17 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(MessageType, 1); + assert_size!(Body, 12); + assert_size!(Subject, 12); + assert_size!(Thread, 12); + assert_size!(Message, 136); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(MessageType, 1); diff --git a/src/message_correct.rs b/src/message_correct.rs index e8233ee632d6b5295e1c180d1e14aff328958e54..06b433f9b047702aae15111a31614b0d562d1fc2 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -25,6 +25,13 @@ mod tests { use minidom::Element; use error::Error; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Replace, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Replace, 24); diff --git a/src/mood.rs b/src/mood.rs index 6bf8bd2383cf1fce59068131b62872132cd3a205..59204322be515e7f3ea7ff4bbba16f280e45e5d3 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -272,6 +272,14 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(MoodEnum, 1); + assert_size!(Text, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(MoodEnum, 1); diff --git a/src/nick.rs b/src/nick.rs index 2d2f90c4c7d212d491c244e4b5aa97a8c752d0e4..7a3666571039a7005541dc04b739d53d4f91fefc 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -16,6 +16,13 @@ mod tests { use minidom::Element; use error::Error; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Nick, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Nick, 24); diff --git a/src/presence.rs b/src/presence.rs index 02a9a9c7c890c95e6dad4173f70a3fb378c42901..db6ac7a60c171d848f5d87c22403b092426cf365 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -323,6 +323,15 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Show, 1); + assert_size!(Type, 1); + assert_size!(Presence, 112); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Show, 1); diff --git a/src/receipts.rs b/src/receipts.rs index ecf0a21cabe54828889a5dcfba0e002f8765486a..49759c773fe7590890490a709c0d4ffda4f55886 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -33,6 +33,14 @@ mod tests { use minidom::Element; use ns; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Request, 0); + assert_size!(Received, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Request, 0); diff --git a/src/roster.rs b/src/roster.rs index eeebddf80cf192dc77db449c23567241c7affca4..c1f8641eda53b54c626752d5a083f98b4cb63c9c 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -85,6 +85,16 @@ mod tests { use std::str::FromStr; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Group, 12); + assert_size!(Subscription, 1); + assert_size!(Item, 64); + assert_size!(Roster, 24); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Group, 24); diff --git a/src/rsm.rs b/src/rsm.rs index 2f176932430ff022cc9136282b1b4f5fc2852987..fb321fa55cb76a1b1a02460d0c98df335367167c 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -158,6 +158,14 @@ mod tests { use super::*; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(SetQuery, 40); + assert_size!(SetResult, 40); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(SetQuery, 80); diff --git a/src/sasl.rs b/src/sasl.rs index 5272fd0d315b786196877e542ee3d9a7de809551..1ec44af087b4b457aa56ec7296d2534aa3eeb9f2 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -215,6 +215,20 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Mechanism, 1); + assert_size!(Auth, 16); + assert_size!(Challenge, 12); + assert_size!(Response, 12); + assert_size!(Abort, 0); + assert_size!(Success, 12); + assert_size!(DefinedCondition, 1); + assert_size!(Failure, 16); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Mechanism, 1); diff --git a/src/sm.rs b/src/sm.rs index dfc3dff204493cb77f5b2339aa441396098dd06b..404b54eb64e2b076a917c4501d2b39285906b284 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -144,6 +144,22 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(A, 4); + assert_size!(ResumeAttr, 1); + assert_size!(Enable, 12); + assert_size!(StreamId, 12); + assert_size!(Enabled, 36); + assert_size!(Failed, 12); + assert_size!(R, 0); + assert_size!(Resume, 16); + assert_size!(Resumed, 16); + assert_size!(StreamManagement, 0); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(A, 4); diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 480b1d8092c40d8c3b6442a4c573efaa3318e662..750823d382b7ea5ce723222c22a0ca8e5b18f93d 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -289,6 +289,15 @@ impl From for Element { mod tests { use super::*; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(ErrorType, 1); + assert_size!(DefinedCondition, 1); + assert_size!(StanzaError, 104); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(ErrorType, 1); diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 9dc3698a6c64896a3cb88074ca98b88ed5228ea8..e45b528c0c16c665908c3dab11a1b89179b4294b 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -42,6 +42,14 @@ mod tests { use error::Error; use std::str::FromStr; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(StanzaId, 48); + assert_size!(OriginId, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(StanzaId, 96); diff --git a/src/stream.rs b/src/stream.rs index b81ba7710208e552c94e66af77125e7bb286063c..ad8ec4f1cf8371565b31f9ac72a4dfe7c572104d 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -76,6 +76,13 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Stream, 108); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Stream, 216); diff --git a/src/version.rs b/src/version.rs index 7ed89743bf4bf35a4e6346728ebfa74658474e1b..cbd958395d24e2b949c5c995c33dc90472d8d8f9 100644 --- a/src/version.rs +++ b/src/version.rs @@ -43,6 +43,14 @@ mod tests { use minidom::Element; use compare_elements::NamespaceAwareCompare; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(VersionQuery, 0); + assert_size!(VersionResult, 36); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(VersionQuery, 0); diff --git a/src/websocket.rs b/src/websocket.rs index 2f87437b1c1a3074db3919b454d0a64cd8f5137f..f2d28ebfe2892ff84787a67375b53de73134822c 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -75,6 +75,13 @@ mod tests { use try_from::TryFrom; use minidom::Element; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Open, 108); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Open, 216); From e5c1be68d066f7bff37ad9a69de19e62224d0540 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Nov 2018 17:25:24 +0100 Subject: [PATCH 0750/1020] muc: Add constructors. --- src/muc/muc.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/muc/muc.rs b/src/muc/muc.rs index c363107a4f3f818a2293fa3a565c0700f9f0776d..def14baea0f470a5c801d8de164fab189a2598fa 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -10,6 +10,7 @@ use date::DateTime; generate_element!( /// Represents the query for messages before our join. + #[derive(PartialEq)] History, "history", MUC, attributes: [ /// How many characters of history to send, in XML characters. @@ -26,8 +27,40 @@ generate_element!( ] ); +impl History { + pub fn new() -> Self { + History { + maxchars: None, + maxstanzas: None, + seconds: None, + since: None, + } + } + + pub fn with_maxchars(mut self, maxchars: u32) -> Self { + self.maxchars = Some(maxchars); + self + } + + pub fn with_maxstanzas(mut self, maxstanzas: u32) -> Self { + self.maxstanzas = Some(maxstanzas); + self + } + + pub fn with_seconds(mut self, seconds: u32) -> Self { + self.seconds = Some(seconds); + self + } + + pub fn with_since(mut self, since: DateTime) -> Self { + self.since = Some(since); + self + } +} + generate_element!( /// Represents a room join request. + #[derive(PartialEq)] Muc, "x", MUC, children: [ /// Password to use when the room is protected by a password. password: Option = ("password", MUC) => String, @@ -39,6 +72,25 @@ generate_element!( impl PresencePayload for Muc {} +impl Muc { + pub fn new() -> Self { + Muc { + password: None, + history: None, + } + } + + pub fn with_password(mut self, password: String) -> Self { + self.password = Some(password); + self + } + + pub fn with_history(mut self, history: History) -> Self { + self.history = Some(history); + self + } +} + #[cfg(test)] mod tests { use super::*; @@ -110,6 +162,9 @@ mod tests { " .parse().unwrap(); let muc = Muc::try_from(elem).unwrap(); + let muc2 = Muc::new().with_history(History::new().with_maxstanzas(0)); + assert_eq!(muc, muc2); + let history = muc.history.unwrap(); assert_eq!(history.maxstanzas, Some(0)); assert_eq!(history.maxchars, None); From e2c0068af9cb582762d0f13cd3e9db72328516e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 2 Nov 2018 16:27:51 +0100 Subject: [PATCH 0751/1020] disco: Remove errors for out-of-order elements, allowed in XEP-0030 2.5rc3. --- src/disco.rs | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 7be0efc77bf6ed1f578886b349150e2172198361..e76b21f21c52b3e3a59c002c8d712c6b9566a2d2 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -126,26 +126,15 @@ impl TryFrom for DiscoInfoResult { features: vec!(), extensions: vec!(), }; - let mut parsing_identities_done = false; - let mut parsing_features_done = false; for child in elem.children() { if child.is("identity", ns::DISCO_INFO) { - if parsing_identities_done { - return Err(Error::ParseError("Identity found after features or data forms in disco#info.")); - } let identity = Identity::try_from(child.clone())?; result.identities.push(identity); } else if child.is("feature", ns::DISCO_INFO) { - parsing_identities_done = true; - if parsing_features_done { - return Err(Error::ParseError("Feature found after data forms in disco#info.")); - } let feature = Feature::try_from(child.clone())?; result.features.push(feature); } else if child.is("x", ns::DATA_FORMS) { - parsing_identities_done = true; - parsing_features_done = true; let data_form = DataForm::try_from(child.clone())?; if data_form.type_ != DataFormType::Result_ { return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")); @@ -271,6 +260,24 @@ mod tests { assert!(query.extensions.is_empty()); } + #[test] + fn test_identity_after_feature() { + let elem: Element = "".parse().unwrap(); + let query = DiscoInfoResult::try_from(elem).unwrap(); + assert_eq!(query.identities.len(), 1); + assert_eq!(query.features.len(), 1); + assert!(query.extensions.is_empty()); + } + + #[test] + fn test_feature_after_dataform() { + let elem: Element = "coucou".parse().unwrap(); + let query = DiscoInfoResult::try_from(elem).unwrap(); + assert_eq!(query.identities.len(), 1); + assert_eq!(query.features.len(), 1); + assert_eq!(query.extensions.len(), 1); + } + #[test] fn test_extension() { let elem: Element = "example".parse().unwrap(); @@ -368,22 +375,6 @@ mod tests { _ => panic!(), }; assert_eq!(message, "disco#info feature not present in disco#info."); - - let elem: Element = "".parse().unwrap(); - let error = DiscoInfoResult::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Identity found after features or data forms in disco#info."); - - let elem: Element = "coucou".parse().unwrap(); - let error = DiscoInfoResult::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Feature found after data forms in disco#info."); } #[test] From c420c87bf5d7d15c80fc599e7c3a0a038204fadb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 2 Nov 2018 16:28:40 +0100 Subject: [PATCH 0752/1020] muc: Document more constructors. --- src/muc/muc.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/muc/muc.rs b/src/muc/muc.rs index def14baea0f470a5c801d8de164fab189a2598fa..5e5d65e86fdb220c5b16e0668ba77bccd6ea08e3 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -28,6 +28,7 @@ generate_element!( ); impl History { + /// Create a new empty history element. pub fn new() -> Self { History { maxchars: None, @@ -37,21 +38,25 @@ impl History { } } + /// Set how many characters of history to send. pub fn with_maxchars(mut self, maxchars: u32) -> Self { self.maxchars = Some(maxchars); self } + /// Set how many messages to send. pub fn with_maxstanzas(mut self, maxstanzas: u32) -> Self { self.maxstanzas = Some(maxstanzas); self } + /// Only send messages received in these last seconds. pub fn with_seconds(mut self, seconds: u32) -> Self { self.seconds = Some(seconds); self } + /// Only send messages received since this date. pub fn with_since(mut self, since: DateTime) -> Self { self.since = Some(since); self @@ -73,6 +78,7 @@ generate_element!( impl PresencePayload for Muc {} impl Muc { + /// Create a new MUC join element. pub fn new() -> Self { Muc { password: None, @@ -80,11 +86,13 @@ impl Muc { } } + /// Join a room with this password. pub fn with_password(mut self, password: String) -> Self { self.password = Some(password); self } + /// Join a room with only that much history. pub fn with_history(mut self, history: History) -> Self { self.history = Some(history); self From 3f4586cabad93314659553c6e11931396cf91ad3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 2 Nov 2018 16:29:11 +0100 Subject: [PATCH 0753/1020] presence: Add status and payload insertion helpers. --- src/presence.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/presence.rs b/src/presence.rs index db6ac7a60c171d848f5d87c22403b092426cf365..2cb115f5270043d5026251ac84be06be76d21f72 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -239,6 +239,16 @@ impl Presence { self.payloads = payloads; self } + + /// Set the availability information of this presence. + pub fn set_status(&mut self, lang: Lang, status: Status) { + self.statuses.insert(lang, status); + } + + /// Add a payload to this presence. + pub fn add_payload(&mut self, payload: P) { + self.payloads.push(payload.into()); + } } impl TryFrom for Presence { From 6b2dd8fe5e27a168b96e89432a5c1eb7fc2f90c2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:27:30 +0100 Subject: [PATCH 0754/1020] Run `cargo fix --edition` to move to Edition 2018. --- Cargo.toml | 1 + src/attention.rs | 4 +- src/bind.rs | 6 +-- src/blocking.rs | 6 +-- src/bookmarks.rs | 2 +- src/caps.rs | 14 +++---- src/chatstates.rs | 6 +-- src/component.rs | 2 +- src/data_forms.rs | 6 +-- src/date.rs | 2 +- src/delay.rs | 10 ++--- src/disco.rs | 10 ++--- src/ecaps2.rs | 12 +++--- src/eme.rs | 4 +- src/forwarding.rs | 6 +-- src/hashes.rs | 4 +- src/helpers.rs | 2 +- src/ibb.rs | 6 +-- src/ibr.rs | 10 ++--- src/idle.rs | 6 +-- src/iq.rs | 12 +++--- src/jingle.rs | 6 +-- src/jingle_ft.rs | 12 +++--- src/jingle_ibb.rs | 4 +- src/jingle_message.rs | 6 +-- src/jingle_s5b.rs | 6 +-- src/macros.rs | 90 +++++++++++++++++++++--------------------- src/mam.rs | 16 ++++---- src/media_element.rs | 6 +-- src/message.rs | 6 +-- src/message_correct.rs | 4 +- src/muc/muc.rs | 8 ++-- src/muc/user.rs | 6 +-- src/nick.rs | 2 +- src/ping.rs | 4 +- src/presence.rs | 6 +-- src/pubsub/event.rs | 12 +++--- src/pubsub/pubsub.rs | 12 +++--- src/receipts.rs | 4 +- src/roster.rs | 6 +-- src/rsm.rs | 6 +-- src/sasl.rs | 6 +-- src/sm.rs | 2 +- src/stanza_error.rs | 8 ++-- src/stanza_id.rs | 4 +- src/version.rs | 4 +- 46 files changed, 189 insertions(+), 188 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a438baec39d7e80575db8297beac0a521c737301..8fd7253bf291da6fcd186a53058766ae6d1c6611 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://hg.linkmauve.fr/xmpp-parsers" keywords = ["xmpp", "xml"] categories = ["parsing", "network-programming"] license = "MPL-2.0" +edition = "2018" [dependencies] minidom = "0.9.1" diff --git a/src/attention.rs b/src/attention.rs index 4bfffd7eb7fe725943adfbcb1c3289cc330c5b65..e71b26446f9a2335c5a548b04aad51de44edd5a0 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; generate_empty_element!( /// Requests the attention of the recipient. @@ -18,7 +18,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[test] fn test_size() { diff --git a/src/bind.rs b/src/bind.rs index b6ec55d44f291ec3c03b4b681cd2401e22365171..81187ae3e76123b369d065448c5fe2e0045c1c98 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -9,10 +9,10 @@ use try_from::TryFrom; use minidom::Element; -use error::Error; +use crate::error::Error; use jid::Jid; -use ns; -use iq::{IqSetPayload, IqResultPayload}; +use crate::ns; +use crate::iq::{IqSetPayload, IqResultPayload}; /// The request for resource binding, which is the process by which a client /// can obtain a full JID and start exchanging on the XMPP network. diff --git a/src/blocking.rs b/src/blocking.rs index cd3f8fd83f7e38c806034c07d69e6fd9cd73a5e3..fd89c4846e38b2562ebf23ee0e5458aac46cb361 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -9,10 +9,10 @@ use try_from::TryFrom; use jid::Jid; use minidom::Element; -use error::Error; +use crate::error::Error; -use ns; -use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use crate::ns; +use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; generate_empty_element!( /// The element requesting the blocklist, the result iq will contain a diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 7f68930dd7cb72ffd2450acf08526f10a9e676de..8f88845fe81d2c476906aa1dfeff01287a1190e5 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -72,7 +72,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/caps.rs b/src/caps.rs index 22ac1930e678763bb8f905ccd6062fed04362c6a..ce44e54c4749c2db1d470c9440d6c08300b44380 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -6,14 +6,14 @@ use try_from::TryFrom; -use presence::PresencePayload; -use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; -use data_forms::DataForm; -use hashes::{Hash, Algo}; +use crate::presence::PresencePayload; +use crate::disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; +use crate::data_forms::DataForm; +use crate::hashes::{Hash, Algo}; use minidom::Element; -use error::Error; -use ns; +use crate::error::Error; +use crate::ns; use base64; use sha1::Sha1; @@ -208,7 +208,7 @@ pub fn query_caps(caps: Caps) -> DiscoInfoQuery { #[cfg(test)] mod tests { use super::*; - use caps; + use crate::caps; use base64; #[cfg(target_pointer_width = "32")] diff --git a/src/chatstates.rs b/src/chatstates.rs index 477bdded08d5b9083ac95e3995acb2bde604ea85..2d4d3830cacc60c3df5a6b105447327e1426194b 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; generate_element_enum!( /// Enum representing chatstate elements part of the @@ -34,8 +34,8 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; - use ns; + use crate::error::Error; + use crate::ns; #[test] fn test_size() { diff --git a/src/component.rs b/src/component.rs index 0c9319cdb655d1bebc951b23fe34cf516bf02591..1fbb7ff35710b108c53d39282bbbe8f2652cbd38 100644 --- a/src/component.rs +++ b/src/component.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use helpers::PlainText; +use crate::helpers::PlainText; use sha1::Sha1; use digest::Digest; diff --git a/src/data_forms.rs b/src/data_forms.rs index edf2608ce43ed8801b50feabd88883b01db6926b..622820d329d446b38db7b57032a652bdd75d69f4 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -8,10 +8,10 @@ use try_from::TryFrom; use minidom::Element; -use error::Error; -use ns; +use crate::error::Error; +use crate::ns; -use media_element::MediaElement; +use crate::media_element::MediaElement; generate_element!( /// Represents one of the possible values for a list- field. diff --git a/src/date.rs b/src/date.rs index 5f2038de7a4576fec17c8a5f1a618bfa3d6343fa..34bef2425ea81879cfb350107b95717189ad0860 100644 --- a/src/date.rs +++ b/src/date.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use minidom::{IntoAttributeValue, IntoElements, ElementEmitter}; use chrono::{DateTime as ChronoDateTime, FixedOffset}; -use error::Error; +use crate::error::Error; /// Implements the DateTime profile of XEP-0082, which represents a /// non-recurring moment in time, with an accuracy of seconds or fraction of diff --git a/src/delay.rs b/src/delay.rs index 46fec0fbdfd252d76dfc294a243bdd47c85a7d58..e67b290691dd2f0ab2e08f58de02d1c00d9366d5 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -4,13 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; -use presence::PresencePayload; -use date::DateTime; +use crate::message::MessagePayload; +use crate::presence::PresencePayload; +use crate::date::DateTime; use jid::Jid; -use helpers::PlainText; +use crate::helpers::PlainText; generate_element!( /// Notes when and by whom a message got stored for later delivery. @@ -36,7 +36,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/disco.rs b/src/disco.rs index e76b21f21c52b3e3a59c002c8d712c6b9566a2d2..7c765fb1c5fce85c13eedba597d84c77394eadbd 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -9,11 +9,11 @@ use try_from::TryFrom; use minidom::Element; use jid::Jid; -use error::Error; -use ns; +use crate::error::Error; +use crate::ns; -use iq::{IqGetPayload, IqResultPayload}; -use data_forms::{DataForm, DataFormType}; +use crate::iq::{IqGetPayload, IqResultPayload}; +use crate::data_forms::{DataForm, DataFormType}; generate_element!( /// Structure representing a `` element. @@ -221,7 +221,7 @@ impl IqResultPayload for DiscoItemsResult {} #[cfg(test)] mod tests { use super::*; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/ecaps2.rs b/src/ecaps2.rs index ea5e03df463b725498c035c1dcb74e9a994d5897..ecadc1531d2517e73fafff164d952e8a775fba1f 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -4,12 +4,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use presence::PresencePayload; -use disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; -use data_forms::DataForm; -use hashes::{Hash, Algo}; +use crate::presence::PresencePayload; +use crate::disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; +use crate::data_forms::DataForm; +use crate::hashes::{Hash, Algo}; -use ns; +use crate::ns; use base64; use sha2::{Sha256, Sha512}; @@ -150,7 +150,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/eme.rs b/src/eme.rs index 417bbe5214b0343cce932ba4086ce6dc2cf6bfa4..5c13f5107ae0b955c49ef3d14fbf203cd3105ce3 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; generate_element!( /// Structure representing an `` element. @@ -26,7 +26,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/forwarding.rs b/src/forwarding.rs index 11efb610caa330668b512c2780b985f90c2e083d..9f725132d40e832d9b8aff96024e951f950ba556 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use delay::Delay; -use message::Message; +use crate::delay::Delay; +use crate::message::Message; generate_element!( /// Contains a forwarded stanza, either standalone or part of another @@ -28,7 +28,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/hashes.rs b/src/hashes.rs index 5fbf5b90933e827506c11d5d225c276f1194fe13..93a17291f127b3c78b2b725556dfff1ab261bbc0 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -8,9 +8,9 @@ use std::str::FromStr; use minidom::IntoAttributeValue; -use error::Error; +use crate::error::Error; -use helpers::Base64; +use crate::helpers::Base64; use base64; /// List of the algorithms we support, or Unknown. diff --git a/src/helpers.rs b/src/helpers.rs index 4db2d2c1c729512d6772a9bd2a075b0024ed28b5..9d4f3ecb405771063854168e88414c4977aabf7f 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use base64; -use error::Error; +use crate::error::Error; /// Codec for plain text content. pub struct PlainText; diff --git a/src/ibb.rs b/src/ibb.rs index 87b335ec1e9b3de8381793175b9e6149279db292..19af588174dce9c0d1c488a519cd625cb48dc072 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use helpers::Base64; -use iq::IqSetPayload; +use crate::helpers::Base64; +use crate::iq::IqSetPayload; generate_id!( /// An identifier matching a stream. @@ -73,7 +73,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::error::Error as StdError; #[cfg(target_pointer_width = "32")] diff --git a/src/ibr.rs b/src/ibr.rs index 4d76a9d826db9d6e9cccdd2533080a67936e552c..4afe719e38ee4fa2c1eaa4700a29ecbd8f3920fc 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -9,12 +9,12 @@ use try_from::TryFrom; use minidom::Element; -use error::Error; +use crate::error::Error; -use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; -use data_forms::DataForm; +use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use crate::data_forms::DataForm; -use ns; +use crate::ns; /// Query for registering against a service. #[derive(Debug, Clone)] @@ -94,7 +94,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/idle.rs b/src/idle.rs index 45352a229a58509a5ed7e6a04d90bf458a9a04f0..53ededdad8797cb9a29ba0c65c5700c19bafb82e 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use presence::PresencePayload; -use date::DateTime; +use crate::presence::PresencePayload; +use crate::date::DateTime; generate_element!( /// Represents the last time the user interacted with their system. @@ -23,7 +23,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::str::FromStr; use std::error::Error as StdError; diff --git a/src/iq.rs b/src/iq.rs index ff1e7b1fe47929cf7634df4b1946f75150e76dbe..1657fcb650d3c89e83f6644f37622c954b819350 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -12,11 +12,11 @@ use minidom::IntoAttributeValue; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; -use stanza_error::StanzaError; +use crate::stanza_error::StanzaError; /// Should be implemented on every known payload of an ``. pub trait IqGetPayload: TryFrom + Into {} @@ -222,9 +222,9 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use stanza_error::{ErrorType, DefinedCondition}; - use compare_elements::NamespaceAwareCompare; - use disco::DiscoInfoQuery; + use crate::stanza_error::{ErrorType, DefinedCondition}; + use crate::compare_elements::NamespaceAwareCompare; + use crate::disco::DiscoInfoQuery; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/jingle.rs b/src/jingle.rs index 7e4ea82b5ee6331d14ba853ce2d807c96ca776f5..720eae864dda1c5f708ce17c6e3288e4aa27df55 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -10,9 +10,9 @@ use std::str::FromStr; use minidom::Element; use jid::Jid; -use error::Error; -use ns; -use iq::IqSetPayload; +use crate::error::Error; +use crate::ns; +use crate::iq::IqSetPayload; generate_attribute!( /// The action attribute. diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 604e6ee5c716981f4d5a0b5f75438c2fb192a10e..15bb4f3285ce6981e66b3215de0235311b6ffcb5 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -9,14 +9,14 @@ use std::str::FromStr; use std::collections::BTreeMap; -use hashes::Hash; -use jingle::{Creator, ContentId}; -use date::DateTime; +use crate::hashes::Hash; +use crate::jingle::{Creator, ContentId}; +use crate::date::DateTime; use minidom::Element; -use error::Error; -use ns; +use crate::error::Error; +use crate::ns; generate_element!( /// Represents a range in a file. @@ -345,7 +345,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use hashes::Algo; + use crate::hashes::Algo; use base64; #[cfg(target_pointer_width = "32")] diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index e02a43474cb691efe25d9cfc2ad8239844302262..129f7ad40d0efc3078bfbcd27a5dc90fd7eb5c41 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use ibb::{Stanza, StreamId}; +use crate::ibb::{Stanza, StreamId}; generate_element!( /// Describes an [In-Band Bytestream](https://xmpp.org/extensions/xep-0047.html) @@ -26,7 +26,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::error::Error as StdError; #[cfg(target_pointer_width = "32")] diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 1bef32698422508e9b25511095f8cd8656fd4612..2f90d2a5f9da75427b3cb393a087658a9f26a547 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -8,11 +8,11 @@ use try_from::TryFrom; use minidom::Element; -use error::Error; +use crate::error::Error; -use jingle::SessionId; +use crate::jingle::SessionId; -use ns; +use crate::ns; /// Defines a protocol for broadcasting Jingle requests to all of the clients /// of a user. diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 904a4d77f9db5743ed1d80c6aa1bbb21ad9d6b89..4d660d281530e345f0fd0410cf54f61bf28f336d 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -10,9 +10,9 @@ use std::net::IpAddr; use minidom::Element; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; generate_attribute!( /// The type of the connection being proposed by this candidate. @@ -275,7 +275,7 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/macros.rs b/src/macros.rs index 3deade1d361315e1d3692e5e13a87947798b565d..da96b67a61b227820390b8df7dbfec6fef79b689 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -24,7 +24,7 @@ macro_rules! get_attr { ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( match $elem.attr($attr) { Some($value) => $func, - None => return Err(::error::Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), + None => return Err(crate::error::Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), } ); ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( @@ -52,11 +52,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = ::error::Error; - fn from_str(s: &str) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -78,11 +78,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = ::error::Error; - fn from_str(s: &str) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -111,12 +111,12 @@ macro_rules! generate_attribute { False, } impl ::std::str::FromStr for $elem { - type Err = ::error::Error; - fn from_str(s: &str) -> Result { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result { Ok(match s { "true" | "1" => $elem::True, "false" | "0" => $elem::False, - _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -150,14 +150,14 @@ macro_rules! generate_element_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); Ok(match elem.name() { $($enum_name => $elem::$enum,)+ - _ => return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))), + _ => return Err(crate::error::Error::ParseError(concat!("This is not a ", $name, " element."))), }) } } @@ -165,7 +165,7 @@ macro_rules! generate_element_enum { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder(match elem { $($elem::$enum => $enum_name,)+ - }).ns(::ns::$ns) + }).ns(crate::ns::$ns) .build() } } @@ -186,21 +186,21 @@ macro_rules! generate_attribute_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); Ok(match get_attr!(elem, $attr, required) { $($enum_name => $elem::$enum,)+ - _ => return Err(::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), + _ => return Err(crate::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), }) } } impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) .attr($attr, match elem { $($elem::$enum => $enum_name,)+ }) @@ -215,16 +215,16 @@ macro_rules! check_self { check_self!($elem, $name, $ns, $name); ); ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => ( - if !$elem.is($name, ::ns::$ns) { - return Err(::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); + if !$elem.is($name, crate::ns::$ns) { + return Err(crate::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); } ); } macro_rules! check_ns_only { ($elem:ident, $name:tt, $ns:ident) => ( - if !$elem.has_ns(::ns::$ns) { - return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))); + if !$elem.has_ns(crate::ns::$ns) { + return Err(crate::error::Error::ParseError(concat!("This is not a ", $name, " element."))); } ); } @@ -232,7 +232,7 @@ macro_rules! check_ns_only { macro_rules! check_no_children { ($elem:ident, $name:tt) => ( for _ in $elem.children() { - return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } ); } @@ -240,7 +240,7 @@ macro_rules! check_no_children { macro_rules! check_no_attributes { ($elem:ident, $name:tt) => ( for _ in $elem.attrs() { - return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } ); } @@ -253,7 +253,7 @@ macro_rules! check_no_unknown_attributes { continue; } )* - return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } ); } @@ -265,9 +265,9 @@ macro_rules! generate_empty_element { pub struct $elem; impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; + type Err = crate::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -278,7 +278,7 @@ macro_rules! generate_empty_element { impl From<$elem> for ::minidom::Element { fn from(_: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) .build() } } @@ -291,8 +291,8 @@ macro_rules! generate_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = ::error::Error; - fn from_str(s: &str) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } @@ -311,15 +311,15 @@ macro_rules! generate_elem_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = ::error::Error; - fn from_str(s: &str) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + type Err = crate::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -330,7 +330,7 @@ macro_rules! generate_elem_id { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) .append(elem.0) .build() } @@ -380,13 +380,13 @@ macro_rules! do_parse_elem { ); ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( if $temp.is_some() { - return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); + return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); } $temp = Some(do_parse!($elem, $constructor)); ); ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( if $temp.is_some() { - return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); + return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); } $temp = Some(do_parse!($elem, $constructor)); ); @@ -400,21 +400,21 @@ macro_rules! finish_parse_elem { $temp ); ($temp:ident: Required = $name:tt, $parent_name:tt) => ( - $temp.ok_or(::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))? + $temp.ok_or(crate::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))? ); } macro_rules! generate_serialiser { ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => ( ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) .append($parent.$elem) .build() ); ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => ( $parent.$elem.map(|elem| ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) .append(elem) .build()) ); @@ -461,9 +461,9 @@ macro_rules! generate_element { } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = ::error::Error; + type Err = crate::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); $( @@ -471,12 +471,12 @@ macro_rules! generate_element { )* for _child in elem.children() { $( - if _child.is($child_name, ::ns::$child_ns) { + if _child.is($child_name, crate::ns::$child_ns) { do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name); continue; } )* - return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( @@ -495,7 +495,7 @@ macro_rules! generate_element { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(::ns::$ns) + .ns(crate::ns::$ns) $( .attr($attr_name, elem.$attr) )* diff --git a/src/mam.rs b/src/mam.rs index 819bc46261af2237b84d7e9cee700f8ab62bc1af..ec88ead6fb654e6c0022cbe59975d38959ed1c80 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -9,16 +9,16 @@ use try_from::TryFrom; use minidom::Element; use jid::Jid; -use error::Error; +use crate::error::Error; -use message::MessagePayload; -use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; -use data_forms::DataForm; -use rsm::{SetQuery, SetResult}; -use forwarding::Forwarded; -use pubsub::NodeName; +use crate::message::MessagePayload; +use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use crate::data_forms::DataForm; +use crate::rsm::{SetQuery, SetResult}; +use crate::forwarding::Forwarded; +use crate::pubsub::NodeName; -use ns; +use crate::ns; generate_id!( /// An identifier matching a result message to the query requesting it. diff --git a/src/media_element.rs b/src/media_element.rs index dc0c2af7a7e86a580ddba48bd60b43cb929766e7..2bc512d8753ee61814a59d65f32197768b6f3188 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use helpers::TrimmedPlainText; +use crate::helpers::TrimmedPlainText; generate_element!( /// Represents an URI used in a media element. @@ -47,8 +47,8 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; - use data_forms::DataForm; + use crate::error::Error; + use crate::data_forms::DataForm; use std::error::Error as StdError; #[cfg(target_pointer_width = "32")] diff --git a/src/message.rs b/src/message.rs index 9c392b790991c6d3f35f0fa96487f553c781d3b4..caae40ab7c81107ea153bd8243b062df9fc1c3ae 100644 --- a/src/message.rs +++ b/src/message.rs @@ -11,9 +11,9 @@ use minidom::Element; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; /// Should be implemented on every known payload of a ``. pub trait MessagePayload: TryFrom + Into {} @@ -232,7 +232,7 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/message_correct.rs b/src/message_correct.rs index 06b433f9b047702aae15111a31614b0d562d1fc2..24b56919132099984fa576f9bd4939c08ff066c7 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; generate_element!( /// Defines that the message containing this payload should replace a @@ -23,7 +23,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 5e5d65e86fdb220c5b16e0668ba77bccd6ea08e3..a49c1bb85656d8fc11f222e8b515cd18d4a8ede2 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,8 +5,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use presence::PresencePayload; -use date::DateTime; +use crate::presence::PresencePayload; +use crate::date::DateTime; generate_element!( /// Represents the query for messages before our join. @@ -104,9 +104,9 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::str::FromStr; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[test] fn test_muc_simple() { diff --git a/src/muc/user.rs b/src/muc/user.rs index bf0fb3080625282a5d0a0aab474d9c0b1772e246..7d114abd63c82b2d33ccf82c4ed9e8a40c3fab6c 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -11,9 +11,9 @@ use minidom::Element; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; generate_attribute_enum!( /// Lists all of the possible status codes used in MUC presences. @@ -238,7 +238,7 @@ generate_element!( mod tests { use super::*; use std::error::Error as StdError; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { diff --git a/src/nick.rs b/src/nick.rs index 7a3666571039a7005541dc04b739d53d4f91fefc..360348bd8761074ce8ff9fa1bad2b51dfaf88982 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -14,7 +14,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/ping.rs b/src/ping.rs index 557f535a7038ab4417cefed415e5ad22b9c7e7dd..ddb636d3f3fa91d6b7c7883202acd83e14f8bc24 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use iq::IqGetPayload; +use crate::iq::IqGetPayload; generate_empty_element!( /// Represents a ping to the recipient, which must be answered with an @@ -20,7 +20,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; #[test] fn test_size() { diff --git a/src/presence.rs b/src/presence.rs index 2cb115f5270043d5026251ac84be06be76d21f72..aece0cff7c25c83db0b216c5f5a2e779d20d422e 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -13,9 +13,9 @@ use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; /// Should be implemented on every known payload of a ``. pub trait PresencePayload: TryFrom + Into {} @@ -331,7 +331,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 61b4396ea25e61ebe5bb3c26ebb55c6e72dac82a..c1ccace3983e20bbf0da4fcc4a4898ef90c42c43 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -8,15 +8,15 @@ use try_from::TryFrom; use minidom::Element; use jid::Jid; -use date::DateTime; +use crate::date::DateTime; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; -use data_forms::DataForm; +use crate::data_forms::DataForm; -use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; +use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; /// One PubSub item from a node. #[derive(Debug, Clone)] @@ -288,7 +288,7 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[test] fn missing_items() { diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 52f510079dbb6de251df7958e94412d40dd0da0f..cf3d980396cf1efc200985de342927e84b2c7c42 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -9,14 +9,14 @@ use try_from::TryFrom; use minidom::Element; use jid::Jid; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; -use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; -use data_forms::DataForm; +use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use crate::data_forms::DataForm; -use pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; +use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; // TODO: a better solution would be to split this into a query and a result elements, like for // XEP-0030. @@ -497,7 +497,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[test] fn create() { diff --git a/src/receipts.rs b/src/receipts.rs index 49759c773fe7590890490a709c0d4ffda4f55886..61877f576d2ebbb2d11aef84f53de0b39c20dec9 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; generate_empty_element!( /// Requests that this message is acked by the final recipient once @@ -31,7 +31,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use ns; + use crate::ns; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/roster.rs b/src/roster.rs index c1f8641eda53b54c626752d5a083f98b4cb63c9c..86d023098767cb2a530bb2f16cc84d4a8276eaca 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use jid::Jid; -use iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; generate_elem_id!( /// Represents a group a contact is part of. @@ -81,9 +81,9 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::str::FromStr; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/rsm.rs b/src/rsm.rs index fb321fa55cb76a1b1a02460d0c98df335367167c..6bb02ba736f23d2be3ae3986907d50fb411b51de 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -8,9 +8,9 @@ use try_from::TryFrom; use minidom::Element; -use error::Error; +use crate::error::Error; -use ns; +use crate::ns; /// Requests paging through a potentially big set of items (represented by an /// UID). @@ -156,7 +156,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/sasl.rs b/src/sasl.rs index 1ec44af087b4b457aa56ec7296d2534aa3eeb9f2..30fa653a6baca3df08534c76befbd3ad905a8de7 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -8,10 +8,10 @@ use std::collections::BTreeMap; use try_from::TryFrom; use minidom::Element; -use error::Error; -use ns; +use crate::error::Error; +use crate::ns; -use helpers::Base64; +use crate::helpers::Base64; generate_attribute!( /// The list of available SASL mechanisms. diff --git a/src/sm.rs b/src/sm.rs index 404b54eb64e2b076a917c4501d2b39285906b284..b0d08d3e62cca65162dab61c306206b3a8715c73 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use stanza_error::DefinedCondition; +use crate::stanza_error::DefinedCondition; generate_element!( /// Acknowledgement of the currently received stanzas. diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 750823d382b7ea5ce723222c22a0ca8e5b18f93d..ec31ecb1d5d23d7562b580af934eef458c6bd11c 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,11 +9,11 @@ use std::collections::BTreeMap; use minidom::Element; -use message::MessagePayload; -use presence::PresencePayload; -use error::Error; +use crate::message::MessagePayload; +use crate::presence::PresencePayload; +use crate::error::Error; use jid::Jid; -use ns; +use crate::ns; generate_attribute!( /// The type of the error. diff --git a/src/stanza_id.rs b/src/stanza_id.rs index e45b528c0c16c665908c3dab11a1b89179b4294b..87595d4324bc9a26ba8b13f6a76ff3f3ebfc0087 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use message::MessagePayload; +use crate::message::MessagePayload; use jid::Jid; generate_element!( @@ -39,7 +39,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use error::Error; + use crate::error::Error; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/version.rs b/src/version.rs index cbd958395d24e2b949c5c995c33dc90472d8d8f9..4e969f4a2004dfa6457b543de1ffcfc593b4acea 100644 --- a/src/version.rs +++ b/src/version.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use iq::{IqGetPayload, IqResultPayload}; +use crate::iq::{IqGetPayload, IqResultPayload}; generate_empty_element!( /// Represents a query for the software version a remote entity is using. @@ -41,7 +41,7 @@ mod tests { use super::*; use try_from::TryFrom; use minidom::Element; - use compare_elements::NamespaceAwareCompare; + use crate::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] From d517b8e32e4fe4552a7811aaa1ca21bfa236eb03 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:29:30 +0100 Subject: [PATCH 0755/1020] Bump base64 and try_from crates. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8fd7253bf291da6fcd186a53058766ae6d1c6611..2d3122cb9084fd17e1434003c591f8e2036d1454 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,14 +16,14 @@ edition = "2018" [dependencies] minidom = "0.9.1" jid = { version = "0.5.2", features = ["minidom"] } -base64 = "0.9.2" +base64 = "0.10" digest = "0.8" sha-1 = "0.8" sha2 = "0.8" sha3 = "0.8" blake2 = "0.8" chrono = "0.4.5" -try_from = "0.2.2" +try_from = "0.3.2" [features] # Build xmpp-parsers to make components instead of clients. From efd7bd5f2f407d803ea6900e3565b9684af23a75 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:32:05 +0100 Subject: [PATCH 0756/1020] Run `cargo fmt`. --- src/attention.rs | 16 +- src/bind.rs | 43 +++-- src/blocking.rs | 44 ++++-- src/bookmarks.rs | 13 +- src/caps.rs | 81 +++++----- src/chatstates.rs | 20 ++- src/compare_elements.rs | 23 +-- src/component.rs | 21 ++- src/data_forms.rs | 100 +++++++----- src/date.rs | 11 +- src/delay.rs | 32 ++-- src/disco.rs | 127 ++++++++++----- src/ecaps2.rs | 336 ++++++++++++++++++++-------------------- src/eme.rs | 25 ++- src/error.rs | 11 +- src/forwarding.rs | 13 +- src/hashes.rs | 27 ++-- src/helpers.rs | 6 +- src/ibb.rs | 35 +++-- src/ibr.rs | 93 +++++++---- src/idle.rs | 48 ++++-- src/iq.rs | 109 ++++++++----- src/jingle.rs | 74 +++++---- src/jingle_ft.rs | 196 +++++++++++++++-------- src/jingle_ibb.rs | 32 +++- src/jingle_message.rs | 68 ++++---- src/jingle_s5b.rs | 115 +++++++------- src/lib.rs | 18 +-- src/macros.rs | 161 ++++++++++++------- src/mam.rs | 112 +++++++++----- src/media_element.rs | 82 +++++++--- src/message.rs | 140 ++++++++++------- src/message_correct.rs | 28 +++- src/mood.rs | 14 +- src/muc/muc.rs | 38 +++-- src/muc/user.rs | 183 +++++++++++++++------- src/nick.rs | 24 ++- src/ping.rs | 12 +- src/presence.rs | 226 ++++++++++++++++++--------- src/pubsub/event.rs | 226 ++++++++++++++++----------- src/pubsub/pubsub.rs | 217 +++++++++++++++++--------- src/receipts.rs | 12 +- src/roster.rs | 84 +++++++--- src/rsm.rs | 94 ++++++++--- src/sasl.rs | 65 +++++--- src/sm.rs | 30 +++- src/stanza_error.rs | 60 ++++--- src/stanza_id.rs | 29 +++- src/stream.rs | 2 +- src/version.rs | 18 ++- src/websocket.rs | 6 +- 51 files changed, 2276 insertions(+), 1324 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index e71b26446f9a2335c5a548b04aad51de44edd5a0..be33bb3b6f07d002163bfa2ee46d4f3a7bc6b6c3 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -8,7 +8,9 @@ use crate::message::MessagePayload; generate_empty_element!( /// Requests the attention of the recipient. - Attention, "attention", ATTENTION + Attention, + "attention", + ATTENTION ); impl MessagePayload for Attention {} @@ -16,9 +18,9 @@ impl MessagePayload for Attention {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[test] fn test_size() { @@ -33,7 +35,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Attention::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -44,7 +48,9 @@ mod tests { #[test] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Attention::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/bind.rs b/src/bind.rs index 81187ae3e76123b369d065448c5fe2e0045c1c98..ddda0392b67b2105977923cf0f47e62a4dbf8e75 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -4,15 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; -use try_from::TryFrom; - -use minidom::Element; - use crate::error::Error; -use jid::Jid; +use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; -use crate::iq::{IqSetPayload, IqResultPayload}; +use jid::Jid; +use minidom::Element; +use std::str::FromStr; +use try_from::TryFrom; /// The request for resource binding, which is the process by which a client /// can obtain a full JID and start exchanging on the XMPP network. @@ -74,23 +72,16 @@ impl TryFrom for Bind { impl From for Element { fn from(bind: Bind) -> Element { Element::builder("bind") - .ns(ns::BIND) - .append(match bind { - Bind::None => vec!(), - Bind::Resource(resource) => vec!( - Element::builder("resource") - .ns(ns::BIND) - .append(resource) - .build() - ), - Bind::Jid(jid) => vec!( - Element::builder("jid") - .ns(ns::BIND) - .append(jid) - .build() - ), - }) - .build() + .ns(ns::BIND) + .append(match bind { + Bind::None => vec![], + Bind::Resource(resource) => vec![Element::builder("resource") + .ns(ns::BIND) + .append(resource) + .build()], + Bind::Jid(jid) => vec![Element::builder("jid").ns(ns::BIND).append(jid).build()], + }) + .build() } } @@ -112,7 +103,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let bind = Bind::try_from(elem).unwrap(); assert_eq!(bind, Bind::None); } diff --git a/src/blocking.rs b/src/blocking.rs index fd89c4846e38b2562ebf23ee0e5458aac46cb361..d34fee3d97a1935c5d825e7bfc836408e74885a2 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -4,20 +4,19 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use jid::Jid; -use minidom::Element; - use crate::error::Error; - +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; +use jid::Jid; +use minidom::Element; +use try_from::TryFrom; generate_empty_element!( /// The element requesting the blocklist, the result iq will contain a /// [BlocklistResult]. - BlocklistRequest, "blocklist", BLOCKING + BlocklistRequest, + "blocklist", + BLOCKING ); impl IqGetPayload for BlocklistRequest {} @@ -67,7 +66,8 @@ macro_rules! generate_blocking_element { generate_blocking_element!( /// The element containing the current blocklist, as a reply from /// [BlocklistRequest]. - BlocklistResult, "blocklist" + BlocklistResult, + "blocklist" ); impl IqResultPayload for BlocklistResult {} @@ -75,7 +75,8 @@ impl IqResultPayload for BlocklistResult {} // TODO: Prevent zero elements from being allowed. generate_blocking_element!( /// A query to block one or more JIDs. - Block, "block" + Block, + "block" ); impl IqSetPayload for Block {} @@ -84,14 +85,17 @@ generate_blocking_element!( /// A query to unblock one or more JIDs, or all of them. /// /// Warning: not putting any JID there means clearing out the blocklist. - Unblock, "unblock" + Unblock, + "unblock" ); impl IqSetPayload for Unblock {} generate_empty_element!( /// The application-specific error condition when a message is blocked. - Blocked, "blocked", BLOCKING_ERRORS + Blocked, + "blocked", + BLOCKING_ERRORS ); #[cfg(test)] @@ -138,7 +142,7 @@ mod tests { #[test] fn test_items() { let elem: Element = "".parse().unwrap(); - let two_items = vec!( + let two_items = vec![ Jid { node: Some(String::from("coucou")), domain: String::from("coucou"), @@ -149,7 +153,7 @@ mod tests { domain: String::from("domain"), resource: None, }, - ); + ]; let request_elem = elem.clone(); let error = BlocklistRequest::try_from(request_elem).unwrap_err(); @@ -174,7 +178,9 @@ mod tests { #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let request_elem = elem.clone(); let error = BlocklistRequest::try_from(request_elem).unwrap_err(); let message = match error { @@ -191,7 +197,9 @@ mod tests { }; assert_eq!(message, "Unknown attribute in blocklist element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Block::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -199,7 +207,9 @@ mod tests { }; assert_eq!(message, "Unknown attribute in block element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Unblock::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 8f88845fe81d2c476906aa1dfeff01287a1190e5..6048b2f074023e7df09ef2103777d42fcdd6e0c9 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -8,7 +8,9 @@ use jid::Jid; generate_attribute!( /// Whether a conference bookmark should be joined automatically. - Autojoin, "autojoin", bool + Autojoin, + "autojoin", + bool ); generate_element!( @@ -70,9 +72,9 @@ impl Storage { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::compare_elements::NamespaceAwareCompare; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -111,7 +113,10 @@ mod tests { assert_eq!(storage.urls[0].url, "https://example.org/"); assert_eq!(storage.conferences.len(), 1); assert_eq!(storage.conferences[0].autojoin, Autojoin::True); - assert_eq!(storage.conferences[0].jid, Jid::bare("test-muc", "muc.localhost")); + assert_eq!( + storage.conferences[0].jid, + Jid::bare("test-muc", "muc.localhost") + ); assert_eq!(storage.conferences[0].name, "Test MUC"); assert_eq!(storage.conferences[0].clone().nick.unwrap(), "Coucou"); assert_eq!(storage.conferences[0].clone().password.unwrap(), "secret"); diff --git a/src/caps.rs b/src/caps.rs index ce44e54c4749c2db1d470c9440d6c08300b44380..72b04263f14bfd58a51276de57be39026c63d21e 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -4,23 +4,20 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use crate::presence::PresencePayload; -use crate::disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery}; use crate::data_forms::DataForm; -use crate::hashes::{Hash, Algo}; - -use minidom::Element; +use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}; use crate::error::Error; +use crate::hashes::{Algo, Hash}; use crate::ns; +use crate::presence::PresencePayload; use base64; - +use blake2::VarBlake2b; +use digest::{Digest, Input, VariableOutput}; +use minidom::Element; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use blake2::VarBlake2b; -use digest::{Digest, VariableOutput, Input}; +use try_from::TryFrom; /// Represents a capability hash for a given client. #[derive(Debug, Clone)] @@ -65,12 +62,12 @@ impl TryFrom for Caps { impl From for Element { fn from(caps: Caps) -> Element { Element::builder("c") - .ns(ns::CAPS) - .attr("ext", caps.ext) - .attr("hash", caps.hash.algo) - .attr("node", caps.node) - .attr("ver", base64::encode(&caps.hash.hash)) - .build() + .ns(ns::CAPS) + .attr("ext", caps.ext) + .attr("hash", caps.hash.algo) + .attr("node", caps.node) + .attr("ver", base64::encode(&caps.hash.hash)) + .build() } } @@ -81,8 +78,8 @@ fn compute_item(field: &str) -> Vec { } fn compute_items Vec>(things: &[T], encode: F) -> Vec { - let mut string: Vec = vec!(); - let mut accumulator: Vec> = vec!(); + let mut string: Vec = vec![]; + let mut accumulator: Vec> = vec![]; for thing in things { let bytes = encode(thing); accumulator.push(bytes); @@ -114,7 +111,7 @@ fn compute_identities(identities: &[Identity]) -> Vec { fn compute_extensions(extensions: &[DataForm]) -> Vec { compute_items(extensions, |extension| { - let mut bytes = vec!(); + let mut bytes = vec![]; // TODO: maybe handle the error case? if let Some(ref form_type) = extension.form_type { bytes.extend_from_slice(form_type.as_bytes()); @@ -125,8 +122,9 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec { continue; } bytes.append(&mut compute_item(&field.var)); - bytes.append(&mut compute_items(&field.values, - |value| compute_item(value))); + bytes.append(&mut compute_items(&field.values, |value| { + compute_item(value) + })); } bytes }) @@ -143,7 +141,7 @@ pub fn compute_disco(disco: &DiscoInfoResult) -> Vec { let features_string = compute_features(&disco.features); let extensions_string = compute_extensions(&disco.extensions); - let mut final_string = vec!(); + let mut final_string = vec![]; final_string.extend(identities_string); final_string.extend(features_string); final_string.extend(extensions_string); @@ -164,33 +162,33 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { Algo::Sha_1 => { let hash = Sha1::digest(data); get_hash_vec(hash.as_slice()) - }, + } Algo::Sha_256 => { let hash = Sha256::digest(data); get_hash_vec(hash.as_slice()) - }, + } Algo::Sha_512 => { let hash = Sha512::digest(data); get_hash_vec(hash.as_slice()) - }, + } Algo::Sha3_256 => { let hash = Sha3_256::digest(data); get_hash_vec(hash.as_slice()) - }, + } Algo::Sha3_512 => { let hash = Sha3_512::digest(data); get_hash_vec(hash.as_slice()) - }, + } Algo::Blake2b_256 => { let mut hasher = VarBlake2b::new(32).unwrap(); hasher.input(data); hasher.vec_result() - }, + } Algo::Blake2b_512 => { let mut hasher = VarBlake2b::new(64).unwrap(); hasher.input(data); hasher.vec_result() - }, + } Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, algo: algo, @@ -229,7 +227,10 @@ mod tests { let caps = Caps::try_from(elem).unwrap(); assert_eq!(caps.node, String::from("coucou")); assert_eq!(caps.hash.algo, Algo::Sha_256); - assert_eq!(caps.hash.hash, base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap()); + assert_eq!( + caps.hash.hash, + base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap() + ); } #[test] @@ -262,8 +263,10 @@ mod tests { -"#.parse().unwrap(); - +"# + .parse() + .unwrap(); + let data = b"client/pc//Exodus 0.9.1".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); ChatState::try_from(elem).unwrap(); } #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -61,7 +65,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -72,7 +78,9 @@ mod tests { #[test] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = ChatState::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/compare_elements.rs b/src/compare_elements.rs index 85c81e75e8e5168870f9739a7f014886cbea1935..df2e4157cc2930f7bc6625280e352407bbe532ae 100644 --- a/src/compare_elements.rs +++ b/src/compare_elements.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use minidom::{Node, Element}; +use minidom::{Element, Node}; pub trait NamespaceAwareCompare { /// Namespace-aware comparison for tests @@ -14,10 +14,10 @@ pub trait NamespaceAwareCompare { impl NamespaceAwareCompare for Node { fn compare_to(&self, other: &Self) -> bool { match (self, other) { - (&Node::Element(ref elem1), &Node::Element(ref elem2)) => - Element::compare_to(elem1, elem2), - (&Node::Text(ref text1), &Node::Text(ref text2)) => - text1 == text2, + (&Node::Element(ref elem1), &Node::Element(ref elem2)) => { + Element::compare_to(elem1, elem2) + } + (&Node::Text(ref text1), &Node::Text(ref text2)) => text1 == text2, _ => false, } } @@ -25,20 +25,21 @@ impl NamespaceAwareCompare for Node { impl NamespaceAwareCompare for Element { fn compare_to(&self, other: &Self) -> bool { - if self.name() == other.name() && - self.ns() == other.ns() && - self.attrs().eq(other.attrs()) + if self.name() == other.name() && self.ns() == other.ns() && self.attrs().eq(other.attrs()) { let child_elems = self.children().count(); - let text_is_whitespace = self.texts() + let text_is_whitespace = self + .texts() .all(|text| text.chars().all(char::is_whitespace)); if child_elems > 0 && text_is_whitespace { // Ignore all the whitespace text nodes - self.children().zip(other.children()) + self.children() + .zip(other.children()) .all(|(node1, node2)| node1.compare_to(node2)) } else { // Compare with text nodes - self.nodes().zip(other.nodes()) + self.nodes() + .zip(other.nodes()) .all(|(node1, node2)| node1.compare_to(node2)) } } else { diff --git a/src/component.rs b/src/component.rs index 1fbb7ff35710b108c53d39282bbbe8f2652cbd38..cb42fd7a319e9b2223e93b8ff6fb2b104ced3af4 100644 --- a/src/component.rs +++ b/src/component.rs @@ -5,8 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::helpers::PlainText; -use sha1::Sha1; use digest::Digest; +use sha1::Sha1; generate_element!( /// The main authentication mechanism for components. @@ -25,9 +25,7 @@ generate_element!( impl Handshake { /// Creates a successful reply from a server. pub fn new() -> Handshake { - Handshake { - data: None, - } + Handshake { data: None } } /// Creates an authentication request from the component. @@ -44,8 +42,8 @@ impl Handshake { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -61,11 +59,15 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let handshake = Handshake::try_from(elem).unwrap(); assert_eq!(handshake.data, None); - let elem: Element = "Coucou".parse().unwrap(); + let elem: Element = "Coucou" + .parse() + .unwrap(); let handshake = Handshake::try_from(elem).unwrap(); assert_eq!(handshake.data, Some(String::from("Coucou"))); } @@ -76,6 +78,9 @@ mod tests { assert_eq!(handshake.data, None); let handshake = Handshake::from_password_and_stream_id("123456", "sid"); - assert_eq!(handshake.data, Some(String::from("9accec263ab84a43c6037ccf7cd48cb1d3f6df8e"))); + assert_eq!( + handshake.data, + Some(String::from("9accec263ab84a43c6037ccf7cd48cb1d3f6df8e")) + ); } } diff --git a/src/data_forms.rs b/src/data_forms.rs index 622820d329d446b38db7b57032a652bdd75d69f4..9d7608dfc3cceb0fa504a77375568f9cda421542 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -4,14 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; - use crate::error::Error; -use crate::ns; - use crate::media_element::MediaElement; +use crate::ns; +use minidom::Element; +use try_from::TryFrom; generate_element!( /// Represents one of the possible values for a list- field. @@ -98,8 +95,7 @@ pub struct Field { impl Field { fn is_list(&self) -> bool { - self.type_ == FieldType::ListSingle || - self.type_ == FieldType::ListMulti + self.type_ == FieldType::ListSingle || self.type_ == FieldType::ListMulti } } @@ -114,9 +110,9 @@ impl TryFrom for Field { type_: get_attr!(elem, "type", default), label: get_attr!(elem, "label", optional), required: false, - options: vec!(), - values: vec!(), - media: vec!(), + options: vec![], + values: vec![], + media: vec![], }; for element in elem.children() { if element.is("value", ns::DATA_FORMS) { @@ -140,7 +136,9 @@ impl TryFrom for Field { let media_element = MediaElement::try_from(element.clone())?; field.media.push(media_element); } else { - return Err(Error::ParseError("Field child isn’t a value, option or media element.")); + return Err(Error::ParseError( + "Field child isn’t a value, option or media element.", + )); } } Ok(field) @@ -150,17 +148,30 @@ impl TryFrom for Field { impl From for Element { fn from(field: Field) -> Element { Element::builder("field") - .ns(ns::DATA_FORMS) - .attr("var", field.var) - .attr("type", field.type_) - .attr("label", field.label) - .append(if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None }) - .append(field.options) - .append(field.values.into_iter().map(|value| { - Element::builder("value").ns(ns::DATA_FORMS).append(value).build() - }).collect::>()) - .append(field.media) - .build() + .ns(ns::DATA_FORMS) + .attr("var", field.var) + .attr("type", field.type_) + .attr("label", field.label) + .append(if field.required { + Some(Element::builder("required").ns(ns::DATA_FORMS).build()) + } else { + None + }) + .append(field.options) + .append( + field + .values + .into_iter() + .map(|value| { + Element::builder("value") + .ns(ns::DATA_FORMS) + .append(value) + .build() + }) + .collect::>(), + ) + .append(field.media) + .build() } } @@ -215,7 +226,7 @@ impl TryFrom for DataForm { form_type: None, title: None, instructions: None, - fields: vec!(), + fields: vec![], }; for child in elem.children() { if child.is("title", ns::DATA_FORMS) { @@ -227,7 +238,9 @@ impl TryFrom for DataForm { form.title = Some(child.text()); } else if child.is("instructions", ns::DATA_FORMS) { if form.instructions.is_some() { - return Err(Error::ParseError("More than one instructions in form element.")); + return Err(Error::ParseError( + "More than one instructions in form element.", + )); } check_no_children!(child, "instructions"); check_no_attributes!(child, "instructions"); @@ -255,12 +268,19 @@ impl TryFrom for DataForm { impl From for Element { fn from(form: DataForm) -> Element { Element::builder("x") - .ns(ns::DATA_FORMS) - .attr("type", form.type_) - .append(form.title.map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title))) - .append(form.instructions.map(|text| Element::builder("instructions").ns(ns::DATA_FORMS).append(text))) - .append(form.fields) - .build() + .ns(ns::DATA_FORMS) + .attr("type", form.type_) + .append( + form.title + .map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title)), + ) + .append(form.instructions.map(|text| { + Element::builder("instructions") + .ns(ns::DATA_FORMS) + .append(text) + })) + .append(form.fields) + .build() } } @@ -318,7 +338,9 @@ mod tests { #[test] fn test_wrong_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = DataForm::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -329,12 +351,17 @@ mod tests { #[test] fn option() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let option = Option_::try_from(elem).unwrap(); assert_eq!(&option.label.unwrap(), "Coucou !"); assert_eq!(&option.value, "coucou"); - let elem: Element = " -"#.parse().unwrap(); - let expected = vec![103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100, - 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, - 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 97, - 99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47, - 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, - 111, 116, 111, 99, 111, 108, 47, 97, 99, 116, 105, 118, 105, 116, - 121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116, 116, 112, 58, - 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, - 111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, - 101, 97, 109, 115, 31, 104, 116, 116, 112, 58,47, 47, 106, 97, 98, - 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, - 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115, 31, - 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, - 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111, - 109, 109, 97, 110, 100, 115, 31,104,116, 116, 112, 58, 47, 47, 106, - 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, - 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102, - 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, - 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, - 100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31, 104, 116, - 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, - 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108, - 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, - 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 102, - 101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31, 104, 116, 116, - 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, - 112, 114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, - 99, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, - 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,111, 108, 47, - 103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121, 31, - 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, - 114, 103,47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, - 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, - 46, 111, 114, 103, 47, 112, 114, 111,116, 111, 99, 111, 108, 47, - 105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, - 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,111, - 99, 111, 108, 47, 109, 111, 111, 100, 31, 104, 116, 116, 112, 58, - 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, - 111, 116, 111, 99, 111,108, 47, 109, 111, 111, 100, 43, 110, 111, - 116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, - 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, - 99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116, - 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, - 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, - 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, - 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47, - 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, - 114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47, - 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, - 111, 116, 111, 99, 111, 108, 47, 116, 117, 110, 101, 31, 104, 116, - 116, 112, 58, 47, 47, 119, 119, 119, 46, 102, 97, 99, 101, 98, 111, - 111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109, 101, - 115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47, - 119, 119, 119, 46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101, - 120, 116, 101, 110, 115, 105, 111, 110, 115, 47, 120, 101, 112, 45, - 48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115, 45, 109, 101, - 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31, - 106, 97, 98, 98, 101, 114,58, 105,113, 58, 97, 118, 97, 116, 97, - 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 98, 114, 111, - 119, 115, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, - 100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, - 102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101, - 114, 58, 105, 113, 58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114, - 58, 105, 113, 58, 105, 110, 98, 97, 110, 100, 31, 106, 97, 98, 98, - 101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105, 110, 107, 31, - 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31, - 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106, - 97,98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99, - 121, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111, - 115, 116, 101, 114,31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, - 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, - 58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, - 114, 58, 120, 58, 100, 97, 116, 97, 31, 106, 97, 98, 98, 101, 114, - 58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98, 98, 101, - 114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, - 112, 112, 58, 97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97, - 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121,31, 117, 114, - 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, 117, 114, - 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116, - 115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, - 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 101, - 110, 31, 84, 107, 97, 98, 98, 101, 114,31, 30, 99, 108, 105, 101, - 110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208, - 176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79, - 82, 77, 95, 84, 89, 80, 69, 31, 117, 114, 110, 58, 120, 109, 112, - 112, 58, 100, 97, 116, 97, 102, 111, 114, 109, 115, 58, 115, 111, - 102, 116, 119, 97, 114, 101,105, 110, 102, 111, 31, 30, 111, 115, - 31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118, - 101, 114, 115, 105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102, - 116, 119, 97, 114, 101, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30, - 115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114, 115, 105, - 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, - 48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, - 47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28]; +"# + .parse() + .unwrap(); + let expected = vec![ + 103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100, 31, 104, 116, 116, 112, 58, 47, 47, + 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, + 108, 47, 97, 99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, + 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, + 97, 99, 116, 105, 118, 105, 116, 121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116, + 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, + 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, + 114, 111, 116, 111, 99, 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115, + 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, + 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111, 109, 109, 97, 110, 100, 115, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, + 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102, 111, + 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, + 112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101, + 109, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, + 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108, 31, 104, 116, + 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, + 116, 111, 99, 111, 108, 47, 102, 101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31, + 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, + 114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 31, 104, 116, 116, + 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, + 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121, + 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, + 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, + 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, + 108, 47, 105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, + 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111, + 111, 100, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, + 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111, 111, 100, 43, 110, 111, + 116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, + 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 114, 111, 115, 116, 101, + 114, 120, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, + 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112, + 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, + 99, 111, 108, 47, 115, 105, 47, 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, + 101, 45, 116, 114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47, 47, + 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, + 108, 47, 116, 117, 110, 101, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, + 102, 97, 99, 101, 98, 111, 111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109, + 101, 115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, + 46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101, 120, 116, 101, 110, 115, 105, 111, + 110, 115, 47, 120, 101, 112, 45, 48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115, + 45, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31, 106, 97, + 98, 98, 101, 114, 58, 105, 113, 58, 97, 118, 97, 116, 97, 114, 31, 106, 97, 98, 98, + 101, 114, 58, 105, 113, 58, 98, 114, 111, 119, 115, 101, 31, 106, 97, 98, 98, 101, 114, + 58, 105, 113, 58, 100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, + 102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, + 58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 105, 110, 98, 97, + 110, 100, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105, + 110, 107, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31, 106, + 97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106, 97, 98, 98, 101, 114, + 58, 105, 113, 58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114, 58, + 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, + 113, 58, 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 118, 101, + 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 100, 97, 116, 97, + 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98, + 98, 101, 114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, + 97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, + 105, 102, 121, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, + 117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116, 115, 31, + 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, 101, 31, 28, 99, 108, 105, + 101, 110, 116, 31, 112, 99, 31, 101, 110, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30, + 99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208, + 176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79, 82, 77, 95, 84, 89, + 80, 69, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 100, 97, 116, 97, 102, 111, 114, + 109, 115, 58, 115, 111, 102, 116, 119, 97, 114, 101, 105, 110, 102, 111, 31, 30, 111, + 115, 31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118, 101, 114, 115, + 105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 31, 84, 107, + 97, 98, 98, 101, 114, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114, + 115, 105, 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49, + 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46, 54, + 98, 50, 41, 31, 30, 29, 28, + ]; let disco = DiscoInfoResult::try_from(elem).unwrap(); let ecaps2 = compute_disco(&disco); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); - assert_eq!(sha_256.hash, base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap()); + assert_eq!( + sha_256.hash, + base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap() + ); let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); - assert_eq!(sha3_256.hash, base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap()); + assert_eq!( + sha3_256.hash, + base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap() + ); } #[test] fn test_blake2b_512() { let hash = hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); - let known_hash: Vec = vec!( - 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, - 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, - 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, - 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, - ); + let known_hash: Vec = vec![ + 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, + 0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, + 0xDB, 0xFF, 0xA2, 0xD1, 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, + 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, + 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, + ]; assert_eq!(hash.hash, known_hash); } } diff --git a/src/eme.rs b/src/eme.rs index 5c13f5107ae0b955c49ef3d14fbf203cd3105ce3..4b8ffbe6bcb2a6ee0e990b9a8b73ad9094e05e2a 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -24,9 +24,9 @@ impl MessagePayload for ExplicitMessageEncryption {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -42,7 +42,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let encryption = ExplicitMessageEncryption::try_from(elem).unwrap(); assert_eq!(encryption.namespace, "urn:xmpp:otr:0"); assert_eq!(encryption.name, None); @@ -55,7 +57,9 @@ mod tests { #[test] fn test_unknown() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = ExplicitMessageEncryption::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -66,7 +70,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = ExplicitMessageEncryption::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -77,8 +83,13 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); - let eme = ExplicitMessageEncryption { namespace: String::from("coucou"), name: None }; + let elem: Element = "" + .parse() + .unwrap(); + let eme = ExplicitMessageEncryption { + namespace: String::from("coucou"), + name: None, + }; let elem2 = eme.into(); assert_eq!(elem, elem2); } diff --git a/src/error.rs b/src/error.rs index 36bd03471a344f68c719295992974f573c8aa1de..f2378a19b4d99314abc43445a86335a960d10085 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,15 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use base64; +use chrono; +use jid; use std::convert::From; -use std::num; -use std::string; use std::fmt; use std::net; - -use base64; -use jid; -use chrono; +use std::num; +use std::string; /// Contains one of the potential errors triggered while parsing an /// [Element](../struct.Element.html) into a specialised struct. diff --git a/src/forwarding.rs b/src/forwarding.rs index 9f725132d40e832d9b8aff96024e951f950ba556..e36ebf8eed09979f79e984e5a2d0ffe937e01f6b 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -26,9 +26,9 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -50,7 +50,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Forwarded::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -62,7 +64,10 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let forwarded = Forwarded { delay: None, stanza: None }; + let forwarded = Forwarded { + delay: None, + stanza: None, + }; let elem2 = forwarded.into(); assert_eq!(elem, elem2); } diff --git a/src/hashes.rs b/src/hashes.rs index 93a17291f127b3c78b2b725556dfff1ab261bbc0..a7773ee1cda05bc2b509270586cb361cbd59cd38 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,14 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::str::FromStr; - -use minidom::IntoAttributeValue; - use crate::error::Error; - use crate::helpers::Base64; use base64; +use minidom::IntoAttributeValue; +use std::str::FromStr; /// List of the algorithms we support, or Unknown. #[allow(non_camel_case_types)] @@ -114,10 +111,7 @@ generate_element!( impl Hash { /// Creates a [Hash] element with the given algo and data. pub fn new(algo: Algo, hash: Vec) -> Hash { - Hash { - algo, - hash, - } + Hash { algo, hash } } /// Like [new](#method.new) but takes base64-encoded data before decoding @@ -130,8 +124,8 @@ impl Hash { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -152,12 +146,17 @@ mod tests { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); let hash = Hash::try_from(elem).unwrap(); assert_eq!(hash.algo, Algo::Sha_256); - assert_eq!(hash.hash, base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap()); + assert_eq!( + hash.hash, + base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap() + ); } #[test] fn test_unknown() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Hash::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -168,7 +167,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Hash::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/helpers.rs b/src/helpers.rs index 9d4f3ecb405771063854168e88414c4977aabf7f..e4d2324475a6eb3d13bb27eab7db787c4638d49e 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use base64; use crate::error::Error; +use base64; /// Codec for plain text content. pub struct PlainText; @@ -19,9 +19,7 @@ impl PlainText { } pub fn encode(string: &Option) -> Option { - string.as_ref().map(|text| { - text.to_owned() - }) + string.as_ref().map(|text| text.to_owned()) } } diff --git a/src/ibb.rs b/src/ibb.rs index 19af588174dce9c0d1c488a519cd625cb48dc072..f778ddf1aa3aebdb55c3e778ec4946dcc3419947 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -8,8 +8,9 @@ use crate::helpers::Base64; use crate::iq::IqSetPayload; generate_id!( -/// An identifier matching a stream. -StreamId); + /// An identifier matching a stream. + StreamId +); generate_attribute!( /// Which stanza type to use to exchange data. @@ -71,10 +72,10 @@ impl IqSetPayload for Close {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; use std::error::Error as StdError; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -98,26 +99,36 @@ mod tests { fn test_simple() { let sid = StreamId(String::from("coucou")); - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let open = Open::try_from(elem).unwrap(); assert_eq!(open.block_size, 3); assert_eq!(open.sid, sid); assert_eq!(open.stanza, Stanza::Iq); - let elem: Element = "AAAA".parse().unwrap(); + let elem: Element = + "AAAA" + .parse() + .unwrap(); let data = Data::try_from(elem).unwrap(); assert_eq!(data.seq, 0); assert_eq!(data.sid, sid); assert_eq!(data.data, vec!(0, 0, 0)); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let close = Close::try_from(elem).unwrap(); assert_eq!(close.sid, sid); } #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -125,7 +136,9 @@ mod tests { }; assert_eq!(message, "Required attribute 'block-size' missing."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, @@ -133,7 +146,9 @@ mod tests { }; assert_eq!(message.description(), "invalid digit found in string"); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Open::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(error) => error, diff --git a/src/ibr.rs b/src/ibr.rs index 4afe719e38ee4fa2c1eaa4700a29ecbd8f3920fc..39aa229d68dbfabcc353a2fdd7f9c64d1b587758 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -4,17 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::collections::HashMap; -use try_from::TryFrom; - -use minidom::Element; - -use crate::error::Error; - -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use crate::data_forms::DataForm; - +use crate::error::Error; +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; +use minidom::Element; +use std::collections::HashMap; +use try_from::TryFrom; /// Query for registering against a service. #[derive(Debug, Clone)] @@ -31,7 +27,6 @@ pub struct Query { /// A data form the user must fill before being allowed to register. pub form: Option, - // Not yet implemented. //pub oob: Option, } @@ -55,9 +50,26 @@ impl TryFrom for Query { let namespace = child.ns().unwrap(); if namespace == ns::REGISTER { let name = child.name(); - let fields = vec!["address", "city", "date", "email", "first", "instructions", - "key", "last", "misc", "name", "nick", "password", "phone", - "state", "text", "url", "username", "zip"]; + let fields = vec![ + "address", + "city", + "date", + "email", + "first", + "instructions", + "key", + "last", + "misc", + "name", + "nick", + "password", + "phone", + "state", + "text", + "url", + "username", + "zip", + ]; if fields.binary_search(&name).is_ok() { query.fields.insert(name.to_owned(), child.text()); } else if name == "registered" { @@ -80,14 +92,26 @@ impl TryFrom for Query { impl From for Element { fn from(query: Query) -> Element { Element::builder("query") - .ns(ns::REGISTER) - .append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None }) - .append(query.fields.into_iter().map(|(name, value)| { - Element::builder(name).ns(ns::REGISTER).append(value) - }).collect::>()) - .append(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None }) - .append(query.form) - .build() + .ns(ns::REGISTER) + .append(if query.registered { + Some(Element::builder("registered").ns(ns::REGISTER)) + } else { + None + }) + .append( + query + .fields + .into_iter() + .map(|(name, value)| Element::builder(name).ns(ns::REGISTER).append(value)) + .collect::>(), + ) + .append(if query.remove { + Some(Element::builder("remove").ns(ns::REGISTER)) + } else { + None + }) + .append(query.form) + .build() } } @@ -126,7 +150,9 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let query = Query::try_from(elem).unwrap(); assert_eq!(query.registered, false); assert_eq!(query.fields["instructions"], "\n Choose a username and password for use with this service.\n Please also provide your email address.\n "); @@ -172,16 +198,27 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let elem1 = elem.clone(); let query = Query::try_from(elem).unwrap(); assert_eq!(query.registered, false); assert!(!query.fields["instructions"].is_empty()); let form = query.form.clone().unwrap(); assert!(!form.instructions.unwrap().is_empty()); - assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("first"))).is_ok()); - assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok()); - assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err()); + assert!(form + .fields + .binary_search_by(|field| field.var.cmp(&String::from("first"))) + .is_ok()); + assert!(form + .fields + .binary_search_by(|field| field.var.cmp(&String::from("x-gender"))) + .is_ok()); + assert!(form + .fields + .binary_search_by(|field| field.var.cmp(&String::from("coucou"))) + .is_err()); let elem2 = query.into(); assert!(elem1.compare_to(&elem2)); } @@ -208,7 +245,9 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let elem1 = elem.clone(); let query = Query::try_from(elem).unwrap(); assert_eq!(query.registered, false); diff --git a/src/idle.rs b/src/idle.rs index 53ededdad8797cb9a29ba0c65c5700c19bafb82e..df2d005d67b6a6361b3e02f51d961ee534f528a5 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::presence::PresencePayload; use crate::date::DateTime; +use crate::presence::PresencePayload; generate_element!( /// Represents the last time the user interacted with their system. @@ -21,11 +21,11 @@ impl PresencePayload for Idle {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; - use std::str::FromStr; + use minidom::Element; use std::error::Error as StdError; + use std::str::FromStr; + use try_from::TryFrom; #[test] fn test_size() { @@ -34,13 +34,17 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); Idle::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -63,7 +67,9 @@ mod tests { #[test] fn test_invalid_date() { // There is no thirteenth month. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -72,7 +78,9 @@ mod tests { assert_eq!(message.description(), "input is out of range"); // Timezone ≥24:00 aren’t allowed. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -81,7 +89,9 @@ mod tests { assert_eq!(message.description(), "input is out of range"); // Timezone without the : separator aren’t allowed. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -90,7 +100,9 @@ mod tests { assert_eq!(message.description(), "input contains invalid characters"); // No seconds, error message could be improved. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -99,7 +111,9 @@ mod tests { assert_eq!(message.description(), "input contains invalid characters"); // TODO: maybe we’ll want to support this one, as per XEP-0082 §4. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -108,7 +122,9 @@ mod tests { assert_eq!(message.description(), "input contains invalid characters"); // No timezone. - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { Error::ChronoParseError(string) => string, @@ -119,8 +135,12 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); - let idle = Idle { since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap() }; + let elem: Element = "" + .parse() + .unwrap(); + let idle = Idle { + since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap(), + }; let elem2 = idle.into(); assert_eq!(elem, elem2); } diff --git a/src/iq.rs b/src/iq.rs index 1657fcb650d3c89e83f6644f37622c954b819350..d4ee8b4197fc5d696017fdabff473adc0e21798b 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -5,18 +5,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; -use minidom::IntoAttributeValue; - -use jid::Jid; - use crate::error::Error; - use crate::ns; - use crate::stanza_error::StanzaError; +use jid::Jid; +use minidom::Element; +use minidom::IntoAttributeValue; +use try_from::TryFrom; /// Should be implemented on every known payload of an ``. pub trait IqGetPayload: TryFrom + Into {} @@ -45,12 +40,15 @@ pub enum IqType { impl<'a> IntoAttributeValue for &'a IqType { fn into_attribute_value(self) -> Option { - Some(match *self { - IqType::Get(_) => "get", - IqType::Set(_) => "set", - IqType::Result(_) => "result", - IqType::Error(_) => "error", - }.to_owned()) + Some( + match *self { + IqType::Get(_) => "get", + IqType::Set(_) => "set", + IqType::Result(_) => "result", + IqType::Error(_) => "error", + } + .to_owned(), + ) } } @@ -201,16 +199,14 @@ impl TryFrom for Iq { impl From for Element { fn from(iq: Iq) -> Element { let mut stanza = Element::builder("iq") - .ns(ns::DEFAULT_NS) - .attr("from", iq.from) - .attr("to", iq.to) - .attr("id", iq.id) - .attr("type", &iq.payload) - .build(); + .ns(ns::DEFAULT_NS) + .attr("from", iq.from) + .attr("to", iq.to) + .attr("id", iq.id) + .attr("type", &iq.payload) + .build(); let elem = match iq.payload { - IqType::Get(elem) - | IqType::Set(elem) - | IqType::Result(Some(elem)) => elem, + IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem, IqType::Error(error) => error.into(), IqType::Result(None) => return stanza, }; @@ -222,9 +218,9 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::stanza_error::{ErrorType, DefinedCondition}; use crate::compare_elements::NamespaceAwareCompare; use crate::disco::DiscoInfoQuery; + use crate::stanza_error::{DefinedCondition, ErrorType}; #[cfg(target_pointer_width = "32")] #[test] @@ -259,11 +255,15 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); #[cfg(feature = "component")] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let iq = Iq::try_from(elem).unwrap(); let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); @@ -271,7 +271,7 @@ mod tests { assert_eq!(iq.id, None); assert!(match iq.payload { IqType::Get(element) => element.compare_to(&query), - _ => false + _ => false, }); } @@ -280,11 +280,15 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); #[cfg(feature = "component")] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let iq = Iq::try_from(elem).unwrap(); let vcard: Element = "".parse().unwrap(); assert_eq!(iq.from, None); @@ -292,7 +296,7 @@ mod tests { assert_eq!(iq.id, None); assert!(match iq.payload { IqType::Set(element) => element.compare_to(&vcard), - _ => false + _ => false, }); } @@ -301,7 +305,9 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -317,13 +323,19 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); #[cfg(feature = "component")] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let iq = Iq::try_from(elem).unwrap(); - let query: Element = "".parse().unwrap(); + let query: Element = "" + .parse() + .unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); assert_eq!(iq.id, None); @@ -341,14 +353,18 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); #[cfg(feature = "component")] let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); @@ -357,10 +373,13 @@ mod tests { IqType::Error(error) => { assert_eq!(error.type_, ErrorType::Cancel); assert_eq!(error.by, None); - assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable); + assert_eq!( + error.defined_condition, + DefinedCondition::ServiceUnavailable + ); assert_eq!(error.texts.len(), 0); assert_eq!(error.other, None); - }, + } _ => panic!(), } } @@ -368,9 +387,13 @@ mod tests { #[test] fn test_children_invalid() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Iq::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -384,7 +407,9 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let iq2 = Iq { from: None, to: None, diff --git a/src/jingle.rs b/src/jingle.rs index 720eae864dda1c5f708ce17c6e3288e4aa27df55..2a3c09669e2eb1ec0a73901024ee99a2afd12a81 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,15 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use std::str::FromStr; - -use minidom::Element; -use jid::Jid; - use crate::error::Error; -use crate::ns; use crate::iq::IqSetPayload; +use crate::ns; +use jid::Jid; +use minidom::Element; +use std::str::FromStr; +use try_from::TryFrom; generate_attribute!( /// The action attribute. @@ -351,7 +349,8 @@ impl From for Element { Reason::Timeout => "timeout", Reason::UnsupportedApplications => "unsupported-applications", Reason::UnsupportedTransports => "unsupported-transports", - }).build() + }) + .build() } } @@ -379,19 +378,25 @@ impl TryFrom for ReasonElement { match child.name() { "text" => { if text.is_some() { - return Err(Error::ParseError("Reason must not have more than one text.")); + return Err(Error::ParseError( + "Reason must not have more than one text.", + )); } text = Some(child.text()); - }, + } name => { if reason.is_some() { - return Err(Error::ParseError("Reason must not have more than one reason.")); + return Err(Error::ParseError( + "Reason must not have more than one reason.", + )); } reason = Some(name.parse()?); - }, + } } } - let reason = reason.ok_or(Error::ParseError("Reason doesn’t contain a valid reason."))?; + let reason = reason.ok_or(Error::ParseError( + "Reason doesn’t contain a valid reason.", + ))?; Ok(ReasonElement { reason: reason, text: text, @@ -402,9 +407,9 @@ impl TryFrom for ReasonElement { impl From for Element { fn from(reason: ReasonElement) -> Element { Element::builder("reason") - .append(Element::from(reason.reason)) - .append(reason.text) - .build() + .append(Element::from(reason.reason)) + .append(reason.text) + .build() } } @@ -491,9 +496,9 @@ impl TryFrom for Jingle { initiator: get_attr!(root, "initiator", optional), responder: get_attr!(root, "responder", optional), sid: get_attr!(root, "sid", required), - contents: vec!(), + contents: vec![], reason: None, - other: vec!(), + other: vec![], }; for child in root.children().cloned() { @@ -502,7 +507,9 @@ impl TryFrom for Jingle { jingle.contents.push(content); } else if child.is("reason", ns::JINGLE) { if jingle.reason.is_some() { - return Err(Error::ParseError("Jingle must not have more than one reason.")); + return Err(Error::ParseError( + "Jingle must not have more than one reason.", + )); } let reason = ReasonElement::try_from(child)?; jingle.reason = Some(reason); @@ -518,14 +525,14 @@ impl TryFrom for Jingle { impl From for Element { fn from(jingle: Jingle) -> Element { Element::builder("jingle") - .ns(ns::JINGLE) - .attr("action", jingle.action) - .attr("initiator", jingle.initiator) - .attr("responder", jingle.responder) - .attr("sid", jingle.sid) - .append(jingle.contents) - .append(jingle.reason) - .build() + .ns(ns::JINGLE) + .attr("action", jingle.action) + .attr("initiator", jingle.initiator) + .attr("responder", jingle.responder) + .attr("sid", jingle.sid) + .append(jingle.contents) + .append(jingle.reason) + .build() } } @@ -565,7 +572,10 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.action, Action::SessionInitiate); assert_eq!(jingle.sid, SessionId(String::from("coucou"))); @@ -581,7 +591,9 @@ mod tests { }; assert_eq!(message, "Required attribute 'action' missing."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -589,7 +601,9 @@ mod tests { }; assert_eq!(message, "Required attribute 'sid' missing."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 15bb4f3285ce6981e66b3215de0235311b6ffcb5..381eb21fa288b1c37ac00ef139d4f033750b454c 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -4,19 +4,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use std::str::FromStr; - -use std::collections::BTreeMap; - -use crate::hashes::Hash; -use crate::jingle::{Creator, ContentId}; use crate::date::DateTime; - -use minidom::Element; - use crate::error::Error; +use crate::hashes::Hash; +use crate::jingle::{ContentId, Creator}; use crate::ns; +use minidom::Element; +use std::collections::BTreeMap; +use std::str::FromStr; +use try_from::TryFrom; generate_element!( /// Represents a range in a file. @@ -153,7 +149,7 @@ impl TryFrom for File { descs: BTreeMap::new(), size: None, range: None, - hashes: vec!(), + hashes: vec![], }; for child in elem.children() { @@ -164,7 +160,9 @@ impl TryFrom for File { file.date = Some(child.text().parse()?); } else if child.is("media-type", ns::JINGLE_FT) { if file.media_type.is_some() { - return Err(Error::ParseError("File must not have more than one media-type.")); + return Err(Error::ParseError( + "File must not have more than one media-type.", + )); } file.media_type = Some(child.text()); } else if child.is("name", ns::JINGLE_FT) { @@ -176,7 +174,9 @@ impl TryFrom for File { let lang = get_attr!(child, "xml:lang", default); let desc = Desc(child.text()); if file.descs.insert(lang, desc).is_some() { - return Err(Error::ParseError("Desc element present twice for the same xml:lang.")); + return Err(Error::ParseError( + "Desc element present twice for the same xml:lang.", + )); } } else if child.is("size", ns::JINGLE_FT) { if file.size.is_some() { @@ -201,38 +201,47 @@ impl TryFrom for File { impl From for Element { fn from(file: File) -> Element { - let mut root = Element::builder("file") - .ns(ns::JINGLE_FT); + let mut root = Element::builder("file").ns(ns::JINGLE_FT); if let Some(date) = file.date { - root = root.append(Element::builder("date") - .ns(ns::JINGLE_FT) - .append(date) - .build()); + root = root.append( + Element::builder("date") + .ns(ns::JINGLE_FT) + .append(date) + .build(), + ); } if let Some(media_type) = file.media_type { - root = root.append(Element::builder("media-type") - .ns(ns::JINGLE_FT) - .append(media_type) - .build()); + root = root.append( + Element::builder("media-type") + .ns(ns::JINGLE_FT) + .append(media_type) + .build(), + ); } if let Some(name) = file.name { - root = root.append(Element::builder("name") - .ns(ns::JINGLE_FT) - .append(name) - .build()); + root = root.append( + Element::builder("name") + .ns(ns::JINGLE_FT) + .append(name) + .build(), + ); } for (lang, desc) in file.descs.into_iter() { - root = root.append(Element::builder("desc") - .ns(ns::JINGLE_FT) - .attr("xml:lang", lang) - .append(desc.0) - .build()); + root = root.append( + Element::builder("desc") + .ns(ns::JINGLE_FT) + .attr("xml:lang", lang) + .append(desc.0) + .build(), + ); } if let Some(size) = file.size { - root = root.append(Element::builder("size") - .ns(ns::JINGLE_FT) - .append(format!("{}", size)) - .build()); + root = root.append( + Element::builder("size") + .ns(ns::JINGLE_FT) + .append(format!("{}", size)) + .build(), + ); } if let Some(range) = file.range { root = root.append(range); @@ -260,12 +269,16 @@ impl TryFrom for Description { let mut file = None; for child in elem.children() { if file.is_some() { - return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + return Err(Error::ParseError( + "JingleFT description element must have exactly one child.", + )); } file = Some(File::try_from(child.clone())?); } if file.is_none() { - return Err(Error::ParseError("JingleFT description element must have exactly one child.")); + return Err(Error::ParseError( + "JingleFT description element must have exactly one child.", + )); } Ok(Description { file: file.unwrap(), @@ -276,9 +289,9 @@ impl TryFrom for Description { impl From for Element { fn from(description: Description) -> Element { Element::builder("description") - .ns(ns::JINGLE_FT) - .append(description.file) - .build() + .ns(ns::JINGLE_FT) + .append(description.file) + .build() } } @@ -304,12 +317,16 @@ impl TryFrom for Checksum { let mut file = None; for child in elem.children() { if file.is_some() { - return Err(Error::ParseError("JingleFT checksum element must have exactly one child.")); + return Err(Error::ParseError( + "JingleFT checksum element must have exactly one child.", + )); } file = Some(File::try_from(child.clone())?); } if file.is_none() { - return Err(Error::ParseError("JingleFT checksum element must have exactly one child.")); + return Err(Error::ParseError( + "JingleFT checksum element must have exactly one child.", + )); } Ok(Checksum { name: get_attr!(elem, "name", required), @@ -322,11 +339,11 @@ impl TryFrom for Checksum { impl From for Element { fn from(checksum: Checksum) -> Element { Element::builder("checksum") - .ns(ns::JINGLE_FT) - .attr("name", checksum.name) - .attr("creator", checksum.creator) - .append(checksum.file) - .build() + .ns(ns::JINGLE_FT) + .attr("name", checksum.name) + .attr("creator", checksum.creator) + .append(checksum.file) + .build() } } @@ -381,16 +398,24 @@ mod tests { algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48= -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, Some(String::from("text/plain"))); assert_eq!(desc.file.name, Some(String::from("test.txt"))); assert_eq!(desc.file.descs, BTreeMap::new()); - assert_eq!(desc.file.date, DateTime::from_str("2015-07-26T21:46:00+01:00").ok()); + assert_eq!( + desc.file.date, + DateTime::from_str("2015-07-26T21:46:00+01:00").ok() + ); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); - assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()); + assert_eq!( + desc.file.hashes[0].hash, + base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap() + ); } #[test] @@ -402,7 +427,9 @@ mod tests { algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48= -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let desc = Description::try_from(elem).unwrap(); assert_eq!(desc.file.media_type, None); assert_eq!(desc.file.name, None); @@ -411,7 +438,10 @@ mod tests { assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); - assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()); + assert_eq!( + desc.file.hashes[0].hash, + base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap() + ); } #[test] @@ -426,11 +456,19 @@ mod tests { algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48= -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let desc = Description::try_from(elem).unwrap(); - assert_eq!(desc.file.descs.keys().cloned().collect::>(), ["en", "fr"]); + assert_eq!( + desc.file.descs.keys().cloned().collect::>(), + ["en", "fr"] + ); assert_eq!(desc.file.descs["en"], Desc(String::from("Secret file!"))); - assert_eq!(desc.file.descs["fr"], Desc(String::from("Fichier secret !"))); + assert_eq!( + desc.file.descs["fr"], + Desc(String::from("Fichier secret !")) + ); let elem: Element = r#" @@ -442,7 +480,9 @@ mod tests { algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48= -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let error = Description::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -478,7 +518,10 @@ mod tests { }; assert_eq!(message, "Unknown attribute in received element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Received::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -498,16 +541,31 @@ mod tests { #[test] fn test_checksum() { let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); - let hash = vec!(195, 73, 156, 39, 41, 115, 10, 127, 128, 126, 251, 134, 118, 169, 45, 203, 111, 138, 63, 143); + let hash = vec![ + 195, 73, 156, 39, 41, 115, 10, 127, 128, 126, 251, 134, 118, 169, 45, 203, 111, 138, + 63, 143, + ]; let checksum = Checksum::try_from(elem).unwrap(); assert_eq!(checksum.name, ContentId(String::from("coucou"))); assert_eq!(checksum.creator, Creator::Initiator); - assert_eq!(checksum.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() })); + assert_eq!( + checksum.file.hashes, + vec!(Hash { + algo: Algo::Sha_1, + hash: hash.clone() + }) + ); let elem2 = Element::from(checksum); let checksum2 = Checksum::try_from(elem2).unwrap(); assert_eq!(checksum2.name, ContentId(String::from("coucou"))); assert_eq!(checksum2.creator, Creator::Initiator); - assert_eq!(checksum2.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() })); + assert_eq!( + checksum2.file.hashes, + vec!(Hash { + algo: Algo::Sha_1, + hash: hash.clone() + }) + ); let elem: Element = "".parse().unwrap(); let error = Checksum::try_from(elem).unwrap_err(); @@ -544,14 +602,22 @@ mod tests { #[test] fn test_range() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let range = Range::try_from(elem).unwrap(); assert_eq!(range.offset, 0); assert_eq!(range.length, None); assert_eq!(range.hashes, vec!()); let elem: Element = "kHp5RSzW/h7Gm1etSf90Mr5PC/k=".parse().unwrap(); - let hashes = vec!(Hash { algo: Algo::Sha_1, hash: vec!(144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79, 11, 249) }); + let hashes = vec![Hash { + algo: Algo::Sha_1, + hash: vec![ + 144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79, + 11, 249, + ], + }]; let range = Range::try_from(elem).unwrap(); assert_eq!(range.offset, 2048); assert_eq!(range.length, Some(1024)); @@ -562,7 +628,9 @@ mod tests { assert_eq!(range2.length, Some(1024)); assert_eq!(range2.hashes, hashes); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Range::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 129f7ad40d0efc3078bfbcd27a5dc90fd7eb5c41..1addc1ee4f9c5d41a87494cab0f3af5e9df17ced 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -24,10 +24,10 @@ attributes: [ #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; use std::error::Error as StdError; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -43,7 +43,10 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.block_size, 3); assert_eq!(transport.sid, StreamId(String::from("coucou"))); @@ -52,7 +55,9 @@ mod tests { #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -60,15 +65,23 @@ mod tests { }; assert_eq!(message, "Required attribute 'block-size' missing."); - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, _ => panic!(), }; - assert_eq!(message.description(), "number too large to fit in target type"); + assert_eq!( + message.description(), + "number too large to fit in target type" + ); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseIntError(error) => error, @@ -76,7 +89,10 @@ mod tests { }; assert_eq!(message.description(), "invalid digit found in string"); - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 2f90d2a5f9da75427b3cb393a087658a9f26a547..02c1e9924fb3e3449fce3c132ca26e7ee509aa7d 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -4,15 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; - use crate::error::Error; - use crate::jingle::SessionId; - use crate::ns; +use minidom::Element; +use try_from::TryFrom; /// Defines a protocol for broadcasting Jingle requests to all of the clients /// of a user. @@ -72,9 +68,11 @@ impl TryFrom for JingleMI { } JingleMI::Propose { sid: get_sid(elem)?, - description: description.ok_or(Error::ParseError("Propose element doesn’t contain a description."))?, + description: description.ok_or(Error::ParseError( + "Propose element doesn’t contain a description.", + ))?, } - }, + } "retract" => JingleMI::Retract(check_empty_and_get_sid(elem)?), "accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?), "proceed" => JingleMI::Proceed(check_empty_and_get_sid(elem)?), @@ -87,33 +85,24 @@ impl TryFrom for JingleMI { impl From for Element { fn from(jingle_mi: JingleMI) -> Element { match jingle_mi { - JingleMI::Propose { sid, description } => { - Element::builder("propose") - .ns(ns::JINGLE_MESSAGE) - .attr("id", sid) - .append(description) - }, - JingleMI::Retract(sid) => { - Element::builder("retract") - .ns(ns::JINGLE_MESSAGE) - .attr("id", sid) - } - JingleMI::Accept(sid) => { - Element::builder("accept") - .ns(ns::JINGLE_MESSAGE) - .attr("id", sid) - } - JingleMI::Proceed(sid) => { - Element::builder("proceed") - .ns(ns::JINGLE_MESSAGE) - .attr("id", sid) - } - JingleMI::Reject(sid) => { - Element::builder("reject") - .ns(ns::JINGLE_MESSAGE) - .attr("id", sid) - } - }.build() + JingleMI::Propose { sid, description } => Element::builder("propose") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid) + .append(description), + JingleMI::Retract(sid) => Element::builder("retract") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid), + JingleMI::Accept(sid) => Element::builder("accept") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid), + JingleMI::Proceed(sid) => Element::builder("proceed") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid), + JingleMI::Reject(sid) => Element::builder("reject") + .ns(ns::JINGLE_MESSAGE) + .attr("id", sid), + } + .build() } } @@ -135,13 +124,18 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); JingleMI::try_from(elem).unwrap(); } #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = JingleMI::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 4d660d281530e345f0fd0410cf54f61bf28f336d..e85390caa2b89b1b62a3cd2dcbbd226feac00db0 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -4,15 +4,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use std::net::IpAddr; - -use minidom::Element; -use jid::Jid; - use crate::error::Error; - use crate::ns; +use jid::Jid; +use minidom::Element; +use std::net::IpAddr; +use try_from::TryFrom; generate_attribute!( /// The type of the connection being proposed by this candidate. @@ -187,37 +184,50 @@ impl TryFrom for Transport { let mut payload = None; for child in elem.children() { payload = Some(if child.is("candidate", ns::JINGLE_S5B) { - let mut candidates = match payload { - Some(TransportPayload::Candidates(candidates)) => candidates, - Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")), - None => vec!(), - }; + let mut candidates = + match payload { + Some(TransportPayload::Candidates(candidates)) => candidates, + Some(_) => return Err(Error::ParseError( + "Non-candidate child already present in JingleS5B transport element.", + )), + None => vec![], + }; candidates.push(Candidate::try_from(child.clone())?); TransportPayload::Candidates(candidates) } else if child.is("activated", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element.")); + return Err(Error::ParseError( + "Non-activated child already present in JingleS5B transport element.", + )); } let cid = get_attr!(child, "cid", required); TransportPayload::Activated(cid) } else if child.is("candidate-error", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element.")); + return Err(Error::ParseError( + "Non-candidate-error child already present in JingleS5B transport element.", + )); } TransportPayload::CandidateError } else if child.is("candidate-used", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element.")); + return Err(Error::ParseError( + "Non-candidate-used child already present in JingleS5B transport element.", + )); } let cid = get_attr!(child, "cid", required); TransportPayload::CandidateUsed(cid) } else if child.is("proxy-error", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element.")); + return Err(Error::ParseError( + "Non-proxy-error child already present in JingleS5B transport element.", + )); } TransportPayload::ProxyError } else { - return Err(Error::ParseError("Unknown child in JingleS5B transport element.")); + return Err(Error::ParseError( + "Unknown child in JingleS5B transport element.", + )); }); } let payload = payload.unwrap_or(TransportPayload::None); @@ -233,49 +243,40 @@ impl TryFrom for Transport { impl From for Element { fn from(transport: Transport) -> Element { Element::builder("transport") - .ns(ns::JINGLE_S5B) - .attr("sid", transport.sid) - .attr("dstaddr", transport.dstaddr) - .attr("mode", transport.mode) - .append(match transport.payload { - TransportPayload::Candidates(candidates) => { - candidates.into_iter() - .map(Element::from) - .collect::>() - }, - TransportPayload::Activated(cid) => { - vec!(Element::builder("activated") - .ns(ns::JINGLE_S5B) - .attr("cid", cid) - .build()) - }, - TransportPayload::CandidateError => { - vec!(Element::builder("candidate-error") - .ns(ns::JINGLE_S5B) - .build()) - }, - TransportPayload::CandidateUsed(cid) => { - vec!(Element::builder("candidate-used") - .ns(ns::JINGLE_S5B) - .attr("cid", cid) - .build()) - }, - TransportPayload::ProxyError => { - vec!(Element::builder("proxy-error") - .ns(ns::JINGLE_S5B) - .build()) - }, - TransportPayload::None => vec!(), - }) - .build() + .ns(ns::JINGLE_S5B) + .attr("sid", transport.sid) + .attr("dstaddr", transport.dstaddr) + .attr("mode", transport.mode) + .append(match transport.payload { + TransportPayload::Candidates(candidates) => candidates + .into_iter() + .map(Element::from) + .collect::>(), + TransportPayload::Activated(cid) => vec![Element::builder("activated") + .ns(ns::JINGLE_S5B) + .attr("cid", cid) + .build()], + TransportPayload::CandidateError => vec![Element::builder("candidate-error") + .ns(ns::JINGLE_S5B) + .build()], + TransportPayload::CandidateUsed(cid) => vec![Element::builder("candidate-used") + .ns(ns::JINGLE_S5B) + .attr("cid", cid) + .build()], + TransportPayload::ProxyError => { + vec![Element::builder("proxy-error").ns(ns::JINGLE_S5B).build()] + } + TransportPayload::None => vec![], + }) + .build() } } #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; use crate::compare_elements::NamespaceAwareCompare; + use std::str::FromStr; #[cfg(target_pointer_width = "32")] #[test] @@ -303,7 +304,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.sid, StreamId(String::from("coucou"))); assert_eq!(transport.dstaddr, None); @@ -334,14 +337,14 @@ mod tests { sid: StreamId(String::from("coucou")), dstaddr: None, mode: Mode::Tcp, - payload: TransportPayload::Candidates(vec!(Candidate { + payload: TransportPayload::Candidates(vec![Candidate { cid: CandidateId(String::from("coucou")), host: IpAddr::from_str("127.0.0.1").unwrap(), jid: Jid::from_str("coucou@coucou").unwrap(), port: None, priority: 0u32, type_: Type::Direct, - })), + }]), }; let elem2: Element = transport.into(); assert!(elem.compare_to(&elem2)); diff --git a/src/lib.rs b/src/lib.rs index 54994e19ecadd6053b16bd3f7075362246809cfa..f35d5b956c691bd6375dc9f1e44155bc952f77b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,15 +24,15 @@ #![deny(missing_docs)] -extern crate minidom; -extern crate jid; extern crate base64; +extern crate blake2; +extern crate chrono; extern crate digest; +extern crate jid; +extern crate minidom; extern crate sha1; extern crate sha2; extern crate sha3; -extern crate blake2; -extern crate chrono; extern crate try_from; pub use minidom::Element; @@ -52,20 +52,20 @@ mod macros; /// Namespace-aware comparison for tests mod compare_elements; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod bind; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod presence; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core -pub mod iq; +pub mod sasl; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stream; -/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core -pub mod sasl; -/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core -pub mod bind; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod roster; diff --git a/src/macros.rs b/src/macros.rs index da96b67a61b227820390b8df7dbfec6fef79b689..ac77ed557d59cc60dcfa734fc4792f95b99cd20e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -5,34 +5,40 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. macro_rules! get_attr { - ($elem:ident, $attr:tt, $type:tt) => ( + ($elem:ident, $attr:tt, $type:tt) => { get_attr!($elem, $attr, $type, value, value.parse()?) - ); - ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => ( + }; + ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => { match $elem.attr($attr) { Some("") => None, Some($value) => Some($func), None => None, } - ); - ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => ( + }; + ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => Some($func), None => None, } - ); - ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => ( + }; + ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, - None => return Err(crate::error::Error::ParseError(concat!("Required attribute '", $attr, "' missing."))), + None => { + return Err(crate::error::Error::ParseError(concat!( + "Required attribute '", + $attr, + "' missing." + ))) + } } - ); - ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => ( + }; + ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, None => ::std::default::Default::default(), } - ); + }; } macro_rules! generate_attribute { @@ -211,38 +217,54 @@ macro_rules! generate_attribute_enum { } macro_rules! check_self { - ($elem:ident, $name:tt, $ns:ident) => ( + ($elem:ident, $name:tt, $ns:ident) => { check_self!($elem, $name, $ns, $name); - ); - ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => ( + }; + ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => { if !$elem.is($name, crate::ns::$ns) { - return Err(crate::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element."))); + return Err(crate::error::Error::ParseError(concat!( + "This is not a ", + $pretty_name, + " element." + ))); } - ); + }; } macro_rules! check_ns_only { - ($elem:ident, $name:tt, $ns:ident) => ( + ($elem:ident, $name:tt, $ns:ident) => { if !$elem.has_ns(crate::ns::$ns) { - return Err(crate::error::Error::ParseError(concat!("This is not a ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!( + "This is not a ", + $name, + " element." + ))); } - ); + }; } macro_rules! check_no_children { - ($elem:ident, $name:tt) => ( + ($elem:ident, $name:tt) => { for _ in $elem.children() { - return Err(crate::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!( + "Unknown child in ", + $name, + " element." + ))); } - ); + }; } macro_rules! check_no_attributes { - ($elem:ident, $name:tt) => ( + ($elem:ident, $name:tt) => { for _ in $elem.attrs() { - return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(crate::error::Error::ParseError(concat!( + "Unknown attribute in ", + $name, + " element." + ))); } - ); + }; } macro_rules! check_no_unknown_attributes { @@ -351,76 +373,95 @@ macro_rules! start_decl { } macro_rules! start_parse_elem { - ($temp:ident: Vec) => ( + ($temp:ident: Vec) => { let mut $temp = Vec::new(); - ); - ($temp:ident: Option) => ( + }; + ($temp:ident: Option) => { let mut $temp = None; - ); - ($temp:ident: Required) => ( + }; + ($temp:ident: Required) => { let mut $temp = None; - ); + }; } macro_rules! do_parse { - ($elem:ident, Element) => ( + ($elem:ident, Element) => { $elem.clone() - ); - ($elem:ident, String) => ( + }; + ($elem:ident, String) => { $elem.text() - ); - ($elem:ident, $constructor:ident) => ( + }; + ($elem:ident, $constructor:ident) => { $constructor::try_from($elem.clone())? - ); + }; } macro_rules! do_parse_elem { - ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( + ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { $temp.push(do_parse!($elem, $constructor)); - ); - ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( + }; + ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { if $temp.is_some() { - return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); + return Err(crate::error::Error::ParseError(concat!( + "Element ", + $parent_name, + " must not have more than one ", + $name, + " child." + ))); } $temp = Some(do_parse!($elem, $constructor)); - ); - ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => ( + }; + ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { if $temp.is_some() { - return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child."))); + return Err(crate::error::Error::ParseError(concat!( + "Element ", + $parent_name, + " must not have more than one ", + $name, + " child." + ))); } $temp = Some(do_parse!($elem, $constructor)); - ); + }; } macro_rules! finish_parse_elem { - ($temp:ident: Vec = $name:tt, $parent_name:tt) => ( + ($temp:ident: Vec = $name:tt, $parent_name:tt) => { $temp - ); - ($temp:ident: Option = $name:tt, $parent_name:tt) => ( + }; + ($temp:ident: Option = $name:tt, $parent_name:tt) => { $temp - ); - ($temp:ident: Required = $name:tt, $parent_name:tt) => ( - $temp.ok_or(crate::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))? - ); + }; + ($temp:ident: Required = $name:tt, $parent_name:tt) => { + $temp.ok_or(crate::error::Error::ParseError(concat!( + "Missing child ", + $name, + " in ", + $parent_name, + " element." + )))? + }; } macro_rules! generate_serialiser { - ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => ( + ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => { ::minidom::Element::builder($name) .ns(crate::ns::$ns) .append($parent.$elem) .build() - ); - ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => ( - $parent.$elem.map(|elem| + }; + ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => { + $parent.$elem.map(|elem| { ::minidom::Element::builder($name) .ns(crate::ns::$ns) .append(elem) - .build()) - ); - ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => ( + .build() + }) + }; + ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => { $parent.$elem - ); + }; } macro_rules! generate_element { diff --git a/src/mam.rs b/src/mam.rs index ec88ead6fb654e6c0022cbe59975d38959ed1c80..8124be9feb760ef3132e9bf78b5d4bc81f45571f 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -4,21 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; -use jid::Jid; - -use crate::error::Error; - -use crate::message::MessagePayload; -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; use crate::data_forms::DataForm; -use crate::rsm::{SetQuery, SetResult}; +use crate::error::Error; use crate::forwarding::Forwarded; -use crate::pubsub::NodeName; - +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; +use crate::message::MessagePayload; use crate::ns; +use crate::pubsub::NodeName; +use crate::rsm::{SetQuery, SetResult}; +use jid::Jid; +use minidom::Element; +use try_from::TryFrom; generate_id!( /// An identifier matching a result message to the query requesting it. @@ -70,7 +66,9 @@ impl MessagePayload for Result_ {} generate_attribute!( /// True when the end of a MAM query has been reached. - Complete, "complete", bool + Complete, + "complete", + bool ); generate_element!( @@ -133,8 +131,8 @@ impl TryFrom for Prefs { fn try_from(elem: Element) -> Result { check_self!(elem, "prefs", MAM); check_no_unknown_attributes!(elem, "prefs", ["default"]); - let mut always = vec!(); - let mut never = vec!(); + let mut always = vec![]; + let mut never = vec![]; for child in elem.children() { if child.is("always", ns::MAM) { for jid_elem in child.children() { @@ -155,7 +153,11 @@ impl TryFrom for Prefs { } } let default_ = get_attr!(elem, "default", required); - Ok(Prefs { default_, always, never }) + Ok(Prefs { + default_, + always, + never, + }) } } @@ -163,26 +165,27 @@ fn serialise_jid_list(name: &str, jids: Vec) -> Option { if jids.is_empty() { None } else { - Some(Element::builder(name) - .ns(ns::MAM) - .append(jids.into_iter() - .map(|jid| Element::builder("jid") - .ns(ns::MAM) - .append(jid) - .build()) - .collect::>()) - .build()) + Some( + Element::builder(name) + .ns(ns::MAM) + .append( + jids.into_iter() + .map(|jid| Element::builder("jid").ns(ns::MAM).append(jid).build()) + .collect::>(), + ) + .build(), + ) } } impl From for Element { fn from(prefs: Prefs) -> Element { Element::builder("prefs") - .ns(ns::MAM) - .attr("default", prefs.default_) - .append(serialise_jid_list("always", prefs.always)) - .append(serialise_jid_list("never", prefs.never)) - .build() + .ns(ns::MAM) + .attr("default", prefs.default_) + .append(serialise_jid_list("always", prefs.always)) + .append(serialise_jid_list("never", prefs.never)) + .build() } } @@ -233,7 +236,9 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); #[cfg(feature = "component")] let elem: Element = r#" @@ -257,7 +262,9 @@ mod tests { 09af3-cc343-b409f -"#.parse().unwrap(); +"# + .parse() + .unwrap(); Fin::try_from(elem).unwrap(); } @@ -274,7 +281,9 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); Query::try_from(elem).unwrap(); } @@ -294,13 +303,17 @@ mod tests { 10 -"#.parse().unwrap(); +"# + .parse() + .unwrap(); Query::try_from(elem).unwrap(); } #[test] fn test_prefs_get() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let prefs = Prefs::try_from(elem).unwrap(); assert_eq!(prefs.always, vec!()); assert_eq!(prefs.never, vec!()); @@ -310,7 +323,9 @@ mod tests { -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let prefs = Prefs::try_from(elem).unwrap(); assert_eq!(prefs.always, vec!()); assert_eq!(prefs.never, vec!()); @@ -327,10 +342,18 @@ mod tests { montague@montague.lit -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let prefs = Prefs::try_from(elem).unwrap(); - assert_eq!(prefs.always, vec!(Jid::from_str("romeo@montague.lit").unwrap())); - assert_eq!(prefs.never, vec!(Jid::from_str("montague@montague.lit").unwrap())); + assert_eq!( + prefs.always, + vec!(Jid::from_str("romeo@montague.lit").unwrap()) + ); + assert_eq!( + prefs.never, + vec!(Jid::from_str("montague@montague.lit").unwrap()) + ); let elem2 = Element::from(prefs.clone()); println!("{:?}", elem2); @@ -342,7 +365,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Query::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -354,7 +379,12 @@ mod tests { #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); - let replace = Query { queryid: None, node: None, form: None, set: None }; + let replace = Query { + queryid: None, + node: None, + form: None, + set: None, + }; let elem2 = replace.into(); assert_eq!(elem, elem2); } diff --git a/src/media_element.rs b/src/media_element.rs index 2bc512d8753ee61814a59d65f32197768b6f3188..f3a623870c06c53da518519365e23857d804e378 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -45,11 +45,11 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; - use crate::error::Error; use crate::data_forms::DataForm; + use crate::error::Error; + use minidom::Element; use std::error::Error as StdError; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -76,7 +76,9 @@ mod tests { #[test] fn test_width_height() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let media = MediaElement::try_from(elem).unwrap(); assert_eq!(media.width.unwrap(), 32); assert_eq!(media.height.unwrap(), 32); @@ -93,15 +95,22 @@ mod tests { #[test] fn test_invalid_width_height() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, _ => panic!(), }; - assert_eq!(error.description(), "cannot parse integer from empty string"); + assert_eq!( + error.description(), + "cannot parse integer from empty string" + ); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, @@ -109,15 +118,22 @@ mod tests { }; assert_eq!(error.description(), "invalid digit found in string"); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, _ => panic!(), }; - assert_eq!(error.description(), "cannot parse integer from empty string"); + assert_eq!( + error.description(), + "cannot parse integer from empty string" + ); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, @@ -128,7 +144,9 @@ mod tests { #[test] fn test_unknown_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -139,7 +157,10 @@ mod tests { #[test] fn test_bad_uri() { - let elem: Element = "https://example.org/".parse().unwrap(); + let elem: Element = + "https://example.org/" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -147,7 +168,9 @@ mod tests { }; assert_eq!(message, "Required attribute 'type' missing."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -169,17 +192,28 @@ mod tests { http://victim.example.com/challenges/speech.mp3?F3A6292C -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let media = MediaElement::try_from(elem).unwrap(); assert!(media.width.is_none()); assert!(media.height.is_none()); assert_eq!(media.uris.len(), 3); assert_eq!(media.uris[0].type_, "audio/x-wav"); - assert_eq!(media.uris[0].uri, "http://victim.example.com/challenges/speech.wav?F3A6292C"); + assert_eq!( + media.uris[0].uri, + "http://victim.example.com/challenges/speech.wav?F3A6292C" + ); assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex"); - assert_eq!(media.uris[1].uri, "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org"); + assert_eq!( + media.uris[1].uri, + "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org" + ); assert_eq!(media.uris[2].type_, "audio/mpeg"); - assert_eq!(media.uris[2].uri, "http://victim.example.com/challenges/speech.mp3?F3A6292C"); + assert_eq!( + media.uris[2].uri, + "http://victim.example.com/challenges/speech.mp3?F3A6292C" + ); } #[test] @@ -200,15 +234,23 @@ mod tests { [ ... ] -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let form = DataForm::try_from(elem).unwrap(); assert_eq!(form.fields.len(), 1); assert_eq!(form.fields[0].var, "ocr"); assert_eq!(form.fields[0].media[0].width, Some(290)); assert_eq!(form.fields[0].media[0].height, Some(80)); assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg"); - assert_eq!(form.fields[0].media[0].uris[0].uri, "http://www.victim.com/challenges/ocr.jpeg?F3A6292C"); + assert_eq!( + form.fields[0].media[0].uris[0].uri, + "http://www.victim.com/challenges/ocr.jpeg?F3A6292C" + ); assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg"); - assert_eq!(form.fields[0].media[0].uris[1].uri, "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org"); + assert_eq!( + form.fields[0].media[0].uris[1].uri, + "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org" + ); } } diff --git a/src/message.rs b/src/message.rs index caae40ab7c81107ea153bd8243b062df9fc1c3ae..159bcd2d8fe76492cb2a92236e24226ddf837239 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,16 +4,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use std::collections::BTreeMap; - -use minidom::Element; - -use jid::Jid; - use crate::error::Error; - use crate::ns; +use jid::Jid; +use minidom::Element; +use std::collections::BTreeMap; +use try_from::TryFrom; /// Should be implemented on every known payload of a ``. pub trait MessagePayload: TryFrom + Into {} @@ -44,18 +40,24 @@ type Lang = String; generate_elem_id!( /// Represents one `` element, that is the free form text content of /// a message. - Body, "body", DEFAULT_NS + Body, + "body", + DEFAULT_NS ); generate_elem_id!( /// Defines the subject of a room, or of an email-like normal message. - Subject, "subject", DEFAULT_NS + Subject, + "subject", + DEFAULT_NS ); generate_elem_id!( /// A thread identifier, so that other people can specify to which message /// they are replying. - Thread, "thread", DEFAULT_NS + Thread, + "thread", + DEFAULT_NS ); /// The main structure representing the `` stanza. @@ -102,11 +104,14 @@ impl Message { bodies: BTreeMap::new(), subjects: BTreeMap::new(), thread: None, - payloads: vec!(), + payloads: vec![], } } - fn get_best<'a, T>(map: &'a BTreeMap, preferred_langs: Vec<&str>) -> Option<(Lang, &'a T)> { + fn get_best<'a, T>( + map: &'a BTreeMap, + preferred_langs: Vec<&str>, + ) -> Option<(Lang, &'a T)> { if map.is_empty() { return None; } @@ -156,21 +161,25 @@ impl TryFrom for Message { let mut bodies = BTreeMap::new(); let mut subjects = BTreeMap::new(); let mut thread = None; - let mut payloads = vec!(); + let mut payloads = vec![]; for elem in root.children() { if elem.is("body", ns::DEFAULT_NS) { check_no_children!(elem, "body"); let lang = get_attr!(elem, "xml:lang", default); let body = Body(elem.text()); if bodies.insert(lang, body).is_some() { - return Err(Error::ParseError("Body element present twice for the same xml:lang.")); + return Err(Error::ParseError( + "Body element present twice for the same xml:lang.", + )); } } else if elem.is("subject", ns::DEFAULT_NS) { check_no_children!(elem, "subject"); let lang = get_attr!(elem, "xml:lang", default); let subject = Subject(elem.text()); if subjects.insert(lang, subject).is_some() { - return Err(Error::ParseError("Subject element present twice for the same xml:lang.")); + return Err(Error::ParseError( + "Subject element present twice for the same xml:lang.", + )); } } else if elem.is("thread", ns::DEFAULT_NS) { if thread.is_some() { @@ -198,41 +207,55 @@ impl TryFrom for Message { impl From for Element { fn from(message: Message) -> Element { Element::builder("message") - .ns(ns::DEFAULT_NS) - .attr("from", message.from) - .attr("to", message.to) - .attr("id", message.id) - .attr("type", message.type_) - .append(message.subjects.into_iter() - .map(|(lang, subject)| { - let mut subject = Element::from(subject); - subject.set_attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }); - subject - }) - .collect::>()) - .append(message.bodies.into_iter() - .map(|(lang, body)| { - let mut body = Element::from(body); - body.set_attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }); - body - }) - .collect::>()) - .append(message.payloads) - .build() + .ns(ns::DEFAULT_NS) + .attr("from", message.from) + .attr("to", message.to) + .attr("id", message.id) + .attr("type", message.type_) + .append( + message + .subjects + .into_iter() + .map(|(lang, subject)| { + let mut subject = Element::from(subject); + subject.set_attr( + "xml:lang", + match lang.as_ref() { + "" => None, + lang => Some(lang), + }, + ); + subject + }) + .collect::>(), + ) + .append( + message + .bodies + .into_iter() + .map(|(lang, body)| { + let mut body = Element::from(body); + body.set_attr( + "xml:lang", + match lang.as_ref() { + "" => None, + lang => Some(lang), + }, + ); + body + }) + .collect::>(), + ) + .append(message.payloads) + .build() } } #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; use crate::compare_elements::NamespaceAwareCompare; + use std::str::FromStr; #[cfg(target_pointer_width = "32")] #[test] @@ -259,7 +282,9 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let message = Message::try_from(elem).unwrap(); assert_eq!(message.from, None); assert_eq!(message.to, None); @@ -273,7 +298,9 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let mut message = Message::new(None); message.type_ = MessageType::Normal; let elem2 = message.into(); @@ -291,7 +318,7 @@ mod tests { assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); { - let (lang, body) = message.get_best_body(vec!("en")).unwrap(); + let (lang, body) = message.get_best_body(vec!["en"]).unwrap(); assert_eq!(lang, ""); assert_eq!(body, &Body::from_str("Hello world!").unwrap()); } @@ -307,7 +334,9 @@ mod tests { #[cfg(feature = "component")] let elem: Element = "Hello world!".parse().unwrap(); let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); - message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap()); + message + .bodies + .insert(String::from(""), Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); assert!(elem.compare_to(&elem2)); } @@ -320,10 +349,13 @@ mod tests { let elem: Element = "Hello world!".parse().unwrap(); let elem1 = elem.clone(); let message = Message::try_from(elem).unwrap(); - assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); + assert_eq!( + message.subjects[""], + Subject::from_str("Hello world!").unwrap() + ); { - let (lang, subject) = message.get_best_subject(vec!("en")).unwrap(); + let (lang, subject) = message.get_best_subject(vec!["en"]).unwrap(); assert_eq!(lang, ""); assert_eq!(subject, &Subject::from_str("Hello world!").unwrap()); } @@ -342,28 +374,28 @@ mod tests { // Tests basic feature. { - let (lang, body) = message.get_best_body(vec!("fr")).unwrap(); + let (lang, body) = message.get_best_body(vec!["fr"]).unwrap(); assert_eq!(lang, "fr"); assert_eq!(body, &Body::from_str("Salut le monde !").unwrap()); } // Tests order. { - let (lang, body) = message.get_best_body(vec!("en", "de")).unwrap(); + let (lang, body) = message.get_best_body(vec!["en", "de"]).unwrap(); assert_eq!(lang, "de"); assert_eq!(body, &Body::from_str("Hallo Welt!").unwrap()); } // Tests fallback. { - let (lang, body) = message.get_best_body(vec!()).unwrap(); + let (lang, body) = message.get_best_body(vec![]).unwrap(); assert_eq!(lang, ""); assert_eq!(body, &Body::from_str("Hello world!").unwrap()); } // Tests fallback. { - let (lang, body) = message.get_best_body(vec!("ja")).unwrap(); + let (lang, body) = message.get_best_body(vec!["ja"]).unwrap(); assert_eq!(lang, ""); assert_eq!(body, &Body::from_str("Hello world!").unwrap()); } diff --git a/src/message_correct.rs b/src/message_correct.rs index 24b56919132099984fa576f9bd4939c08ff066c7..9e7a08f726c1e789b2794949a3888ec4d8524eb8 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -21,9 +21,9 @@ impl MessagePayload for Replace {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -39,13 +39,17 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); Replace::try_from(elem).unwrap(); } #[test] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Replace::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -56,7 +60,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Replace::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -67,7 +73,9 @@ mod tests { #[test] fn test_invalid_id() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Replace::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -78,8 +86,12 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); - let replace = Replace { id: String::from("coucou") }; + let elem: Element = "" + .parse() + .unwrap(); + let replace = Replace { + id: String::from("coucou"), + }; let elem2 = replace.into(); assert_eq!(elem, elem2); } diff --git a/src/mood.rs b/src/mood.rs index 59204322be515e7f3ea7ff4bbba16f280e45e5d3..da07bf50a296d6a5190c56b6bc24cab306eb11ab 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -263,14 +263,16 @@ generate_element_enum!( generate_elem_id!( /// Free-form text description of the mood. - Text, "text", MOOD + Text, + "text", + MOOD ); #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -288,14 +290,18 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let mood = MoodEnum::try_from(elem).unwrap(); assert_eq!(mood, MoodEnum::Happy); } #[test] fn test_text() { - let elem: Element = "Yay!".parse().unwrap(); + let elem: Element = "Yay!" + .parse() + .unwrap(); let text = Text::try_from(elem).unwrap(); assert_eq!(text.0, String::from("Yay!")); } diff --git a/src/muc/muc.rs b/src/muc/muc.rs index a49c1bb85656d8fc11f222e8b515cd18d4a8ede2..32df468d7e46f8548656f6a9503daef514dc610f 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -5,8 +5,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::presence::PresencePayload; use crate::date::DateTime; +use crate::presence::PresencePayload; generate_element!( /// Represents the query for messages before our join. @@ -102,21 +102,25 @@ impl Muc { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; + use crate::compare_elements::NamespaceAwareCompare; use crate::error::Error; + use minidom::Element; use std::str::FromStr; - use crate::compare_elements::NamespaceAwareCompare; + use try_from::TryFrom; #[test] fn test_muc_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); Muc::try_from(elem).unwrap(); } #[test] fn test_muc_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Muc::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -127,7 +131,9 @@ mod tests { #[test] fn test_muc_serialise() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let muc = Muc { password: None, history: None, @@ -138,7 +144,9 @@ mod tests { #[test] fn test_muc_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Muc::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -153,7 +161,8 @@ mod tests { coucou " - .parse().unwrap(); + .parse() + .unwrap(); let elem1 = elem.clone(); let muc = Muc::try_from(elem).unwrap(); assert_eq!(muc.password, Some("coucou".to_owned())); @@ -168,7 +177,8 @@ mod tests { " - .parse().unwrap(); + .parse() + .unwrap(); let muc = Muc::try_from(elem).unwrap(); let muc2 = Muc::new().with_history(History::new().with_maxstanzas(0)); assert_eq!(muc, muc2); @@ -183,8 +193,12 @@ mod tests { " - .parse().unwrap(); + .parse() + .unwrap(); let muc = Muc::try_from(elem).unwrap(); - assert_eq!(muc.history.unwrap().since.unwrap(), DateTime::from_str("1970-01-01T00:00:00+00:00").unwrap()); + assert_eq!( + muc.history.unwrap().since.unwrap(), + DateTime::from_str("1970-01-01T00:00:00+00:00").unwrap() + ); } } diff --git a/src/muc/user.rs b/src/muc/user.rs index 7d114abd63c82b2d33ccf82c4ed9e8a40c3fab6c..3f058a1740803f879dcec7a873de4cbaccd9ff0c 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -5,15 +5,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; - -use jid::Jid; - use crate::error::Error; - use crate::ns; +use jid::Jid; +use minidom::Element; +use try_from::TryFrom; generate_attribute_enum!( /// Lists all of the possible status codes used in MUC presences. @@ -103,9 +99,11 @@ impl TryFrom for Actor { let nick = get_attr!(elem, "nick", optional); match (jid, nick) { - (Some(_), Some(_)) - | (None, None) => - return Err(Error::ParseError("Either 'jid' or 'nick' attribute is required.")), + (Some(_), Some(_)) | (None, None) => { + return Err(Error::ParseError( + "Either 'jid' or 'nick' attribute is required.", + )) + } (Some(jid), _) => Ok(Actor::Jid(jid)), (_, Some(nick)) => Ok(Actor::Nick(nick)), } @@ -119,7 +117,8 @@ impl From for Element { (match actor { Actor::Jid(jid) => elem.attr("jid", jid), Actor::Nick(nick) => elem.attr("nick", nick), - }).build() + }) + .build() } } @@ -135,7 +134,9 @@ generate_element!( generate_elem_id!( /// A reason for inviting, declining, etc. a request. - Reason, "reason", MUC_USER + Reason, + "reason", + MUC_USER ); generate_attribute!( @@ -237,14 +238,16 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use std::error::Error as StdError; use crate::compare_elements::NamespaceAwareCompare; + use std::error::Error as StdError; #[test] fn test_simple() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); MucUser::try_from(elem).unwrap(); } @@ -256,7 +259,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let muc_user = MucUser::try_from(elem).unwrap(); assert_eq!(muc_user.status.len(), 2); assert_eq!(muc_user.status[0], Status::AffiliationChange); @@ -272,7 +277,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = MucUser::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -285,8 +292,13 @@ mod tests { fn test_serialise() { let elem: Element = " - ".parse().unwrap(); - let muc = MucUser { status: vec!(), items: vec!() }; + " + .parse() + .unwrap(); + let muc = MucUser { + status: vec![], + items: vec![], + }; let elem2 = muc.into(); assert!(elem.compare_to(&elem2)); } @@ -295,7 +307,9 @@ mod tests { fn test_invalid_attribute() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = MucUser::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -308,7 +322,9 @@ mod tests { fn test_status_simple() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); Status::try_from(elem).unwrap(); } @@ -316,7 +332,9 @@ mod tests { fn test_status_invalid() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -331,7 +349,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -344,7 +364,9 @@ mod tests { fn test_status_simple_code() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let status = Status::try_from(elem).unwrap(); assert_eq!(status, Status::Kicked); } @@ -353,7 +375,9 @@ mod tests { fn test_status_invalid_code() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -366,7 +390,9 @@ mod tests { fn test_status_invalid_code2() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, @@ -379,7 +405,9 @@ mod tests { fn test_actor_required_attributes() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Actor::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -394,7 +422,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Actor::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -408,7 +438,9 @@ mod tests { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let actor = Actor::try_from(elem).unwrap(); let jid = match actor { Actor::Jid(jid) => jid, @@ -421,7 +453,9 @@ mod tests { fn test_actor_nick() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let actor = Actor::try_from(elem).unwrap(); let nick = match actor { Actor::Nick(nick) => nick, @@ -434,7 +468,9 @@ mod tests { fn test_continue_simple() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); Continue::try_from(elem).unwrap(); } @@ -443,7 +479,9 @@ mod tests { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let continue_ = Continue::try_from(elem).unwrap(); assert_eq!(continue_.thread, Some("foo".to_owned())); } @@ -454,7 +492,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let continue_ = Continue::try_from(elem).unwrap_err(); let message = match continue_ { Error::ParseError(string) => string, @@ -467,7 +507,8 @@ mod tests { fn test_reason_simple() { let elem: Element = " Reason" - .parse().unwrap(); + .parse() + .unwrap(); let reason = Reason::try_from(elem).unwrap(); assert_eq!(reason.0, "Reason".to_owned()); } @@ -476,7 +517,9 @@ mod tests { fn test_reason_invalid_attribute() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Reason::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -491,7 +534,9 @@ mod tests { - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Reason::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -501,11 +546,13 @@ mod tests { } #[test] - fn test_item_invalid_attr(){ + fn test_item_invalid_attr() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -515,21 +562,25 @@ mod tests { } #[test] - fn test_item_affiliation_role_attr(){ + fn test_item_affiliation_role_attr() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); Item::try_from(elem).unwrap(); } #[test] - fn test_item_affiliation_role_invalid_attr(){ + fn test_item_affiliation_role_invalid_attr() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -539,13 +590,15 @@ mod tests { } #[test] - fn test_item_nick_attr(){ + fn test_item_nick_attr() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); match item { Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())), @@ -553,65 +606,79 @@ mod tests { } #[test] - fn test_item_affiliation_role_invalid_attr2(){ + fn test_item_affiliation_role_invalid_attr2() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Required attribute 'affiliation' missing.".to_owned()); + assert_eq!( + message, + "Required attribute 'affiliation' missing.".to_owned() + ); } #[test] - fn test_item_role_actor_child(){ + fn test_item_role_actor_child() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); match item { - Item { actor, .. } => - assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))), + Item { actor, .. } => assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))), } } #[test] - fn test_item_role_continue_child(){ + fn test_item_role_continue_child() { let elem: Element = " - ".parse().unwrap(); + " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); - let continue_1 = Continue { thread: Some("foobar".to_owned()) }; + let continue_1 = Continue { + thread: Some("foobar".to_owned()), + }; match item { - Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2.thread, continue_1.thread), + Item { + continue_: Some(continue_2), + .. + } => assert_eq!(continue_2.thread, continue_1.thread), _ => panic!(), } } #[test] - fn test_item_role_reason_child(){ + fn test_item_role_reason_child() { let elem: Element = " foobar - ".parse().unwrap(); + " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); match item { - Item { reason, .. } => - assert_eq!(reason, Some(Reason("foobar".to_owned()))), + Item { reason, .. } => assert_eq!(reason, Some(Reason("foobar".to_owned()))), } } } diff --git a/src/nick.rs b/src/nick.rs index 360348bd8761074ce8ff9fa1bad2b51dfaf88982..edf8dcacce17b23e54ac39fcbea32b87aad8d5a2 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -6,15 +6,17 @@ generate_elem_id!( /// Represents a global, memorable, friendly or informal name chosen by a user. - Nick, "nick", NICK + Nick, + "nick", + NICK ); #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -30,7 +32,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "Link Mauve".parse().unwrap(); + let elem: Element = "Link Mauve" + .parse() + .unwrap(); let nick = Nick::try_from(elem).unwrap(); assert_eq!(&nick.0, "Link Mauve"); } @@ -38,13 +42,17 @@ mod tests { #[test] fn test_serialise() { let elem1 = Element::from(Nick(String::from("Link Mauve"))); - let elem2: Element = "Link Mauve".parse().unwrap(); + let elem2: Element = "Link Mauve" + .parse() + .unwrap(); assert_eq!(elem1, elem2); } #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Nick::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -55,7 +63,9 @@ mod tests { #[test] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Nick::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/ping.rs b/src/ping.rs index ddb636d3f3fa91d6b7c7883202acd83e14f8bc24..4cb25f649c453db84f9879f5871c743df187898f 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -10,7 +10,9 @@ use crate::iq::IqGetPayload; generate_empty_element!( /// Represents a ping to the recipient, which must be answered with an /// empty `` or with an error. - Ping, "ping", PING + Ping, + "ping", + PING ); impl IqGetPayload for Ping {} @@ -18,9 +20,9 @@ impl IqGetPayload for Ping {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; + use try_from::TryFrom; #[test] fn test_size() { @@ -42,7 +44,9 @@ mod tests { #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Ping::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/presence.rs b/src/presence.rs index aece0cff7c25c83db0b216c5f5a2e779d20d422e..8f3d2cf109a806ab68600cb12caa159412255553 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,11 +5,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; -use std::str::FromStr; use std::collections::BTreeMap; +use std::str::FromStr; +use try_from::TryFrom; -use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter}; +use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; use jid::Jid; @@ -68,14 +68,15 @@ impl IntoElements for Show { } emitter.append_child( Element::builder("show") - .append(match self { - Show::None => unreachable!(), - Show::Away => Some("away"), - Show::Chat => Some("chat"), - Show::Dnd => Some("dnd"), - Show::Xa => Some("xa"), - }) - .build()) + .append(match self { + Show::None => unreachable!(), + Show::Away => Some("away"), + Show::Chat => Some("chat"), + Show::Dnd => Some("dnd"), + Show::Xa => Some("xa"), + }) + .build(), + ) } } @@ -84,7 +85,7 @@ type Status = String; type Priority = i8; -/// +/// #[derive(Debug, Clone, PartialEq)] pub enum Type { /// This value is not an acceptable 'type' attribute, it is only used @@ -136,24 +137,31 @@ impl FromStr for Type { "unsubscribe" => Type::Unsubscribe, "unsubscribed" => Type::Unsubscribed, - _ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")), + _ => { + return Err(Error::ParseError( + "Invalid 'type' attribute on presence element.", + )) + } }) } } impl IntoAttributeValue for Type { fn into_attribute_value(self) -> Option { - Some(match self { - Type::None => return None, - - Type::Error => "error", - Type::Probe => "probe", - Type::Subscribe => "subscribe", - Type::Subscribed => "subscribed", - Type::Unavailable => "unavailable", - Type::Unsubscribe => "unsubscribe", - Type::Unsubscribed => "unsubscribed", - }.to_owned()) + Some( + match self { + Type::None => return None, + + Type::Error => "error", + Type::Probe => "probe", + Type::Subscribe => "subscribe", + Type::Subscribed => "subscribed", + Type::Unavailable => "unavailable", + Type::Unsubscribe => "unsubscribe", + Type::Unsubscribed => "unsubscribed", + } + .to_owned(), + ) } } @@ -197,7 +205,7 @@ impl Presence { show: Show::None, statuses: BTreeMap::new(), priority: 0i8, - payloads: vec!(), + payloads: vec![], } } @@ -266,12 +274,14 @@ impl TryFrom for Presence { show: Show::None, statuses: BTreeMap::new(), priority: 0i8, - payloads: vec!(), + payloads: vec![], }; for elem in root.children() { if elem.is("show", ns::DEFAULT_NS) { if show.is_some() { - return Err(Error::ParseError("More than one show element in a presence.")); + return Err(Error::ParseError( + "More than one show element in a presence.", + )); } check_no_attributes!(elem, "show"); check_no_children!(elem, "show"); @@ -281,11 +291,15 @@ impl TryFrom for Presence { check_no_children!(elem, "status"); let lang = get_attr!(elem, "xml:lang", default); if presence.statuses.insert(lang, elem.text()).is_some() { - return Err(Error::ParseError("Status element present twice for the same xml:lang.")); + return Err(Error::ParseError( + "Status element present twice for the same xml:lang.", + )); } } else if elem.is("priority", ns::DEFAULT_NS) { if priority.is_some() { - return Err(Error::ParseError("More than one priority element in a presence.")); + return Err(Error::ParseError( + "More than one priority element in a presence.", + )); } check_no_attributes!(elem, "priority"); check_no_children!(elem, "priority"); @@ -307,24 +321,37 @@ impl TryFrom for Presence { impl From for Element { fn from(presence: Presence) -> Element { Element::builder("presence") - .ns(ns::DEFAULT_NS) - .attr("from", presence.from) - .attr("to", presence.to) - .attr("id", presence.id) - .attr("type", presence.type_) - .append(presence.show) - .append(presence.statuses.into_iter().map(|(lang, status)| { - Element::builder("status") - .attr("xml:lang", match lang.as_ref() { - "" => None, - lang => Some(lang), - }) - .append(status) - .build() - }).collect::>()) - .append(if presence.priority == 0 { None } else { Some(format!("{}", presence.priority)) }) - .append(presence.payloads) - .build() + .ns(ns::DEFAULT_NS) + .attr("from", presence.from) + .attr("to", presence.to) + .attr("id", presence.id) + .attr("type", presence.type_) + .append(presence.show) + .append( + presence + .statuses + .into_iter() + .map(|(lang, status)| { + Element::builder("status") + .attr( + "xml:lang", + match lang.as_ref() { + "" => None, + lang => Some(lang), + }, + ) + .append(status) + .build() + }) + .collect::>(), + ) + .append(if presence.priority == 0 { + None + } else { + Some(format!("{}", presence.priority)) + }) + .append(presence.payloads) + .build() } } @@ -354,7 +381,9 @@ mod tests { #[cfg(not(feature = "component"))] let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.from, None); assert_eq!(presence.to, None); @@ -366,9 +395,13 @@ mod tests { #[test] fn test_serialise() { #[cfg(not(feature = "component"))] - let elem: Element = "/>".parse().unwrap(); + let elem: Element = "/>" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "/>".parse().unwrap(); + let elem: Element = "/>" + .parse() + .unwrap(); let presence = Presence::new(Type::Unavailable); let elem2 = presence.into(); assert!(elem.compare_to(&elem2)); @@ -377,9 +410,14 @@ mod tests { #[test] fn test_show() { #[cfg(not(feature = "component"))] - let elem: Element = "chat".parse().unwrap(); + let elem: Element = "chat" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "chat".parse().unwrap(); + let elem: Element = + "chat" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.show, Show::Chat); @@ -388,9 +426,13 @@ mod tests { #[test] fn test_missing_show_value() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -403,9 +445,14 @@ mod tests { fn test_invalid_show() { // "online" used to be a pretty common mistake. #[cfg(not(feature = "component"))] - let elem: Element = "online".parse().unwrap(); + let elem: Element = "online" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "online".parse().unwrap(); + let elem: Element = + "online" + .parse() + .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -417,9 +464,13 @@ mod tests { #[test] fn test_empty_status() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); @@ -429,9 +480,14 @@ mod tests { #[test] fn test_status() { #[cfg(not(feature = "component"))] - let elem: Element = "Here!".parse().unwrap(); + let elem: Element = "Here!" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "Here!".parse().unwrap(); + let elem: Element = + "Here!" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.statuses.len(), 1); @@ -462,15 +518,23 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Status element present twice for the same xml:lang."); + assert_eq!( + message, + "Status element present twice for the same xml:lang." + ); } #[test] fn test_priority() { #[cfg(not(feature = "component"))] - let elem: Element = "-1".parse().unwrap(); + let elem: Element = "-1" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "-1".parse().unwrap(); + let elem: Element = + "-1" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); assert_eq!(presence.priority, -1i8); @@ -479,9 +543,14 @@ mod tests { #[test] fn test_invalid_priority() { #[cfg(not(feature = "component"))] - let elem: Element = "128".parse().unwrap(); + let elem: Element = "128" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "128".parse().unwrap(); + let elem: Element = + "128" + .parse() + .unwrap(); let error = Presence::try_from(elem).unwrap_err(); match error { Error::ParseIntError(_) => (), @@ -492,9 +561,14 @@ mod tests { #[test] fn test_unknown_child() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let presence = Presence::try_from(elem).unwrap(); let payload = &presence.payloads[0]; assert!(payload.is("test", "invalid")); @@ -503,9 +577,14 @@ mod tests { #[test] fn test_invalid_status_child() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -517,9 +596,14 @@ mod tests { #[test] fn test_invalid_attribute() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index c1ccace3983e20bbf0da4fcc4a4898ef90c42c43..b8f2bc0066a17dc74c93e345b3a1f035f22d1ad5 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -4,19 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; -use jid::Jid; +use crate::data_forms::DataForm; use crate::date::DateTime; - use crate::error::Error; - use crate::ns; - -use crate::data_forms::DataForm; - -use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; +use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; +use jid::Jid; +use minidom::Element; +use try_from::TryFrom; /// One PubSub item from a node. #[derive(Debug, Clone)] @@ -40,7 +35,9 @@ impl TryFrom for Item { let mut payloads = elem.children().cloned().collect::>(); let payload = payloads.pop(); if !payloads.is_empty() { - return Err(Error::ParseError("More than a single payload in item element.")); + return Err(Error::ParseError( + "More than a single payload in item element.", + )); } Ok(Item { payload, @@ -53,11 +50,11 @@ impl TryFrom for Item { impl From for Element { fn from(item: Item) -> Element { Element::builder("item") - .ns(ns::PUBSUB_EVENT) - .attr("id", item.id) - .attr("publisher", item.publisher) - .append(item.payload) - .build() + .ns(ns::PUBSUB_EVENT) + .attr("id", item.id) + .attr("publisher", item.publisher) + .append(item.payload) + .build() } } @@ -131,21 +128,29 @@ pub enum PubSubEvent { fn parse_items(elem: Element, node: NodeName) -> Result { let mut is_retract = None; - let mut items = vec!(); - let mut retracts = vec!(); + let mut items = vec![]; + let mut retracts = vec![]; for child in elem.children() { if child.is("item", ns::PUBSUB_EVENT) { match is_retract { None => is_retract = Some(false), Some(false) => (), - Some(true) => return Err(Error::ParseError("Mix of item and retract in items element.")), + Some(true) => { + return Err(Error::ParseError( + "Mix of item and retract in items element.", + )) + } } items.push(Item::try_from(child.clone())?); } else if child.is("retract", ns::PUBSUB_EVENT) { match is_retract { None => is_retract = Some(true), Some(true) => (), - Some(false) => return Err(Error::ParseError("Mix of item and retract in items element.")), + Some(false) => { + return Err(Error::ParseError( + "Mix of item and retract in items element.", + )) + } } check_no_children!(child, "retract"); check_no_unknown_attributes!(child, "retract", ["id"]); @@ -157,7 +162,10 @@ fn parse_items(elem: Element, node: NodeName) -> Result { } Ok(match is_retract { Some(false) => PubSubEvent::PublishedItems { node, items }, - Some(true) => PubSubEvent::RetractedItems { node, items: retracts }, + Some(true) => PubSubEvent::RetractedItems { + node, + items: retracts, + }, None => return Err(Error::ParseError("Missing children in items element.")), }) } @@ -176,7 +184,9 @@ impl TryFrom for PubSubEvent { let mut payloads = child.children().cloned().collect::>(); let item = payloads.pop(); if !payloads.is_empty() { - return Err(Error::ParseError("More than a single payload in configuration element.")); + return Err(Error::ParseError( + "More than a single payload in configuration element.", + )); } let form = match item { None => None, @@ -188,7 +198,9 @@ impl TryFrom for PubSubEvent { for item in child.children() { if item.is("redirect", ns::PUBSUB_EVENT) { if redirect.is_some() { - return Err(Error::ParseError("More than one redirect in delete element.")); + return Err(Error::ParseError( + "More than one redirect in delete element.", + )); } let uri = get_attr!(item, "uri", required); redirect = Some(uri); @@ -222,77 +234,79 @@ impl TryFrom for PubSubEvent { impl From for Element { fn from(event: PubSubEvent) -> Element { let payload = match event { - PubSubEvent::Configuration { node, form } => { - Element::builder("configuration") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .append(form) - .build() - }, - PubSubEvent::Delete { node, redirect } => { - Element::builder("purge") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .append(redirect.map(|redirect| { - Element::builder("redirect") - .ns(ns::PUBSUB_EVENT) - .attr("uri", redirect) - .build() - })) - .build() - }, - PubSubEvent::PublishedItems { node, items } => { - Element::builder("items") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .append(items) - .build() - }, - PubSubEvent::RetractedItems { node, items } => { - Element::builder("items") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .append(items.into_iter().map(|id| { - Element::builder("retract") - .ns(ns::PUBSUB_EVENT) - .attr("id", id) - .build() - }).collect::>()) - .build() - }, - PubSubEvent::Purge { node } => { - Element::builder("purge") - .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .build() - }, - PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => { - Element::builder("subscription") + PubSubEvent::Configuration { node, form } => Element::builder("configuration") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(form) + .build(), + PubSubEvent::Delete { node, redirect } => Element::builder("purge") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(redirect.map(|redirect| { + Element::builder("redirect") .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .attr("expiry", expiry) - .attr("jid", jid) - .attr("subid", subid) - .attr("subscription", subscription) + .attr("uri", redirect) .build() - }, + })) + .build(), + PubSubEvent::PublishedItems { node, items } => Element::builder("items") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append(items) + .build(), + PubSubEvent::RetractedItems { node, items } => Element::builder("items") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .append( + items + .into_iter() + .map(|id| { + Element::builder("retract") + .ns(ns::PUBSUB_EVENT) + .attr("id", id) + .build() + }) + .collect::>(), + ) + .build(), + PubSubEvent::Purge { node } => Element::builder("purge") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .build(), + PubSubEvent::Subscription { + node, + expiry, + jid, + subid, + subscription, + } => Element::builder("subscription") + .ns(ns::PUBSUB_EVENT) + .attr("node", node) + .attr("expiry", expiry) + .attr("jid", jid) + .attr("subid", subid) + .attr("subscription", subscription) + .build(), }; Element::builder("event") - .ns(ns::PUBSUB_EVENT) - .append(payload) - .build() + .ns(ns::PUBSUB_EVENT) + .append(payload) + .build() } } #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; use crate::compare_elements::NamespaceAwareCompare; + use std::str::FromStr; #[test] fn missing_items() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = PubSubEvent::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -309,9 +323,12 @@ mod tests { PubSubEvent::PublishedItems { node, items } => { assert_eq!(node, NodeName(String::from("coucou"))); assert_eq!(items[0].id, Some(ItemId(String::from("test")))); - assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap())); + assert_eq!( + items[0].publisher, + Some(Jid::from_str("test@coucou").unwrap()) + ); assert_eq!(items[0].payload, None); - }, + } _ => panic!(), } } @@ -329,7 +346,7 @@ mod tests { Some(ref elem) => assert!(elem.is("foreign", "example:namespace")), _ => panic!(), } - }, + } _ => panic!(), } } @@ -343,7 +360,7 @@ mod tests { assert_eq!(node, NodeName(String::from("something"))); assert_eq!(items[0], ItemId(String::from("coucou"))); assert_eq!(items[1], ItemId(String::from("test"))); - }, + } _ => panic!(), } } @@ -356,19 +373,22 @@ mod tests { PubSubEvent::Delete { node, redirect } => { assert_eq!(node, NodeName(String::from("coucou"))); assert_eq!(redirect, Some(String::from("hello"))); - }, + } _ => panic!(), } } #[test] fn test_simple_purge() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let event = PubSubEvent::try_from(elem).unwrap(); match event { PubSubEvent::Purge { node } => { assert_eq!(node, NodeName(String::from("coucou"))); - }, + } _ => panic!(), } } @@ -381,14 +401,17 @@ mod tests { PubSubEvent::Configuration { node, form: _ } => { assert_eq!(node, NodeName(String::from("coucou"))); //assert_eq!(form.type_, Result_); - }, + } _ => panic!(), } } #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = PubSubEvent::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -399,7 +422,9 @@ mod tests { #[test] fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = PubSubEvent::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -419,16 +444,29 @@ mod tests { subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3' subscription='subscribed'/> -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let event = PubSubEvent::try_from(elem.clone()).unwrap(); match event.clone() { - PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => { + PubSubEvent::Subscription { + node, + expiry, + jid, + subid, + subscription, + } => { assert_eq!(node, NodeName(String::from("princely_musings"))); - assert_eq!(subid, Some(SubscriptionId(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3")))); + assert_eq!( + subid, + Some(SubscriptionId(String::from( + "ba49252aaa4f5d320c24d3766f0bdcade78c78d3" + ))) + ); assert_eq!(subscription, Some(Subscription::Subscribed)); assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap())); assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap())); - }, + } _ => panic!(), } diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index cf3d980396cf1efc200985de342927e84b2c7c42..e406d867a0eb126ea0c4c901e2c48ef53fc883b3 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -4,19 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; -use jid::Jid; - +use crate::data_forms::DataForm; use crate::error::Error; - +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; - -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; -use crate::data_forms::DataForm; - -use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId}; +use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; +use jid::Jid; +use minidom::Element; +use try_from::TryFrom; // TODO: a better solution would be to split this into a query and a result elements, like for // XEP-0030. @@ -137,7 +132,9 @@ impl TryFrom for Item { let mut payloads = elem.children().cloned().collect::>(); let payload = payloads.pop(); if !payloads.is_empty() { - return Err(Error::ParseError("More than a single payload in item element.")); + return Err(Error::ParseError( + "More than a single payload in item element.", + )); } Ok(Item { payload, @@ -149,10 +146,10 @@ impl TryFrom for Item { impl From for Element { fn from(item: Item) -> Element { Element::builder("item") - .ns(ns::PUBSUB) - .attr("id", item.id) - .append(item.payload) - .build() + .ns(ns::PUBSUB) + .attr("id", item.id) + .append(item.payload) + .build() } } @@ -199,7 +196,9 @@ generate_element!( generate_attribute!( /// Whether a retract request should notify subscribers or not. - Notify, "notify", bool + Notify, + "notify", + bool ); generate_element!( @@ -235,11 +234,15 @@ impl TryFrom for SubscribeOptions { for child in elem.children() { if child.is("required", ns::PUBSUB) { if required { - return Err(Error::ParseError("More than one required element in subscribe-options.")); + return Err(Error::ParseError( + "More than one required element in subscribe-options.", + )); } required = true; } else { - return Err(Error::ParseError("Unknown child in subscribe-options element.")); + return Err(Error::ParseError( + "Unknown child in subscribe-options element.", + )); } } Ok(SubscribeOptions { required }) @@ -251,12 +254,10 @@ impl From for Element { Element::builder("subscribe-options") .ns(ns::PUBSUB) .append(if subscribe_options.required { - vec!(Element::builder("required") - .ns(ns::PUBSUB) - .build()) - } else { - vec!() - }) + vec![Element::builder("required").ns(ns::PUBSUB).build()] + } else { + vec![] + }) .build() } } @@ -334,7 +335,7 @@ pub enum PubSub { create: Create, /// The configure request for the new node. - configure: Option + configure: Option, }, /// Request to publish items to a node, with optional options. @@ -343,7 +344,7 @@ pub enum PubSub { publish: Publish, /// The options related to this publish request. - publish_options: Option + publish_options: Option, }, /// A list of affiliations you have on a service, or on a node. @@ -383,75 +384,114 @@ impl TryFrom for PubSub { for child in elem.children() { if child.is("create", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let create = Create::try_from(child.clone())?; - payload = Some(PubSub::Create { create, configure: None }); + payload = Some(PubSub::Create { + create, + configure: None, + }); } else if child.is("configure", ns::PUBSUB) { if let Some(PubSub::Create { create, configure }) = payload { if configure.is_some() { - return Err(Error::ParseError("Configure is already defined in pubsub element.")); + return Err(Error::ParseError( + "Configure is already defined in pubsub element.", + )); } let configure = Some(Configure::try_from(child.clone())?); payload = Some(PubSub::Create { create, configure }); } else { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } } else if child.is("publish", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let publish = Publish::try_from(child.clone())?; - payload = Some(PubSub::Publish { publish, publish_options: None }); + payload = Some(PubSub::Publish { + publish, + publish_options: None, + }); } else if child.is("publish-options", ns::PUBSUB) { - if let Some(PubSub::Publish { publish, publish_options }) = payload { + if let Some(PubSub::Publish { + publish, + publish_options, + }) = payload + { if publish_options.is_some() { - return Err(Error::ParseError("Publish-options are already defined in pubsub element.")); + return Err(Error::ParseError( + "Publish-options are already defined in pubsub element.", + )); } let publish_options = Some(PublishOptions::try_from(child.clone())?); - payload = Some(PubSub::Publish { publish, publish_options }); + payload = Some(PubSub::Publish { + publish, + publish_options, + }); } else { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } } else if child.is("affiliations", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let affiliations = Affiliations::try_from(child.clone())?; payload = Some(PubSub::Affiliations(affiliations)); } else if child.is("default", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let default = Default::try_from(child.clone())?; payload = Some(PubSub::Default(default)); } else if child.is("items", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let items = Items::try_from(child.clone())?; payload = Some(PubSub::Items(items)); } else if child.is("retract", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let retract = Retract::try_from(child.clone())?; payload = Some(PubSub::Retract(retract)); } else if child.is("subscription", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let subscription = SubscriptionElem::try_from(child.clone())?; payload = Some(PubSub::Subscription(subscription)); } else if child.is("subscriptions", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let subscriptions = Subscriptions::try_from(child.clone())?; payload = Some(PubSub::Subscriptions(subscriptions)); } else if child.is("unsubscribe", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError("Payload is already defined in pubsub element.")); + return Err(Error::ParseError( + "Payload is already defined in pubsub element.", + )); } let unsubscribe = Unsubscribe::try_from(child.clone())?; payload = Some(PubSub::Unsubscribe(unsubscribe)); @@ -468,28 +508,31 @@ impl From for Element { Element::builder("pubsub") .ns(ns::PUBSUB) .append(match pubsub { - PubSub::Create { create, configure } => { - let mut elems = vec!(Element::from(create)); - if let Some(configure) = configure { - elems.push(Element::from(configure)); - } - elems - }, - PubSub::Publish { publish, publish_options } => { - let mut elems = vec!(Element::from(publish)); - if let Some(publish_options) = publish_options { - elems.push(Element::from(publish_options)); - } - elems - }, - PubSub::Affiliations(affiliations) => vec!(Element::from(affiliations)), - PubSub::Default(default) => vec!(Element::from(default)), - PubSub::Items(items) => vec!(Element::from(items)), - PubSub::Retract(retract) => vec!(Element::from(retract)), - PubSub::Subscription(subscription) => vec!(Element::from(subscription)), - PubSub::Subscriptions(subscriptions) => vec!(Element::from(subscriptions)), - PubSub::Unsubscribe(unsubscribe) => vec!(Element::from(unsubscribe)), - }) + PubSub::Create { create, configure } => { + let mut elems = vec![Element::from(create)]; + if let Some(configure) = configure { + elems.push(Element::from(configure)); + } + elems + } + PubSub::Publish { + publish, + publish_options, + } => { + let mut elems = vec![Element::from(publish)]; + if let Some(publish_options) = publish_options { + elems.push(Element::from(publish_options)); + } + elems + } + PubSub::Affiliations(affiliations) => vec![Element::from(affiliations)], + PubSub::Default(default) => vec![Element::from(default)], + PubSub::Items(items) => vec![Element::from(items)], + PubSub::Retract(retract) => vec![Element::from(retract)], + PubSub::Subscription(subscription) => vec![Element::from(subscription)], + PubSub::Subscriptions(subscriptions) => vec![Element::from(subscriptions)], + PubSub::Unsubscribe(unsubscribe) => vec![Element::from(unsubscribe)], + }) .build() } } @@ -501,7 +544,9 @@ mod tests { #[test] fn create() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let elem1 = elem.clone(); let pubsub = PubSub::try_from(elem).unwrap(); match pubsub.clone() { @@ -515,7 +560,10 @@ mod tests { let elem2 = Element::from(pubsub); assert!(elem1.compare_to(&elem2)); - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let elem1 = elem.clone(); let pubsub = PubSub::try_from(elem).unwrap(); match pubsub.clone() { @@ -532,7 +580,10 @@ mod tests { #[test] fn create_and_configure() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let elem1 = elem.clone(); let pubsub = PubSub::try_from(elem).unwrap(); match pubsub.clone() { @@ -549,11 +600,17 @@ mod tests { #[test] fn publish() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let elem1 = elem.clone(); let pubsub = PubSub::try_from(elem).unwrap(); match pubsub.clone() { - PubSub::Publish { publish, publish_options } => { + PubSub::Publish { + publish, + publish_options, + } => { assert_eq!(&publish.node.0, "coucou"); assert!(publish_options.is_none()); } @@ -570,7 +627,10 @@ mod tests { let elem1 = elem.clone(); let pubsub = PubSub::try_from(elem).unwrap(); match pubsub.clone() { - PubSub::Publish { publish, publish_options } => { + PubSub::Publish { + publish, + publish_options, + } => { assert_eq!(&publish.node.0, "coucou"); assert!(publish_options.unwrap().form.is_none()); } @@ -583,7 +643,9 @@ mod tests { #[test] fn invalid_empty_pubsub() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = PubSub::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -596,12 +658,17 @@ mod tests { fn publish_option() { let elem: Element = "http://jabber.org/protocol/pubsub#publish-options".parse().unwrap(); let publish_options = PublishOptions::try_from(elem).unwrap(); - assert_eq!(&publish_options.form.unwrap().form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options"); + assert_eq!( + &publish_options.form.unwrap().form_type.unwrap(), + "http://jabber.org/protocol/pubsub#publish-options" + ); } #[test] fn subscribe_options() { - let elem1: Element = "".parse().unwrap(); + let elem1: Element = "" + .parse() + .unwrap(); let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap(); assert_eq!(subscribe_options1.required, false); diff --git a/src/receipts.rs b/src/receipts.rs index 61877f576d2ebbb2d11aef84f53de0b39c20dec9..c97d3e118358630758fa253d5e0153fbea3cf8cc 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -9,7 +9,9 @@ use crate::message::MessagePayload; generate_empty_element!( /// Requests that this message is acked by the final recipient once /// received. - Request, "request", RECEIPTS + Request, + "request", + RECEIPTS ); impl MessagePayload for Request {} @@ -29,9 +31,9 @@ impl MessagePayload for Received {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::ns; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -55,7 +57,9 @@ mod tests { let elem: Element = "".parse().unwrap(); Received::try_from(elem).unwrap(); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); Received::try_from(elem).unwrap(); } diff --git a/src/roster.rs b/src/roster.rs index 86d023098767cb2a530bb2f16cc84d4a8276eaca..40cc7a25dc7f8a7f935306df4507e3cff8d4862e 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -4,12 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use jid::Jid; -use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload}; generate_elem_id!( /// Represents a group a contact is part of. - Group, "group", ROSTER + Group, + "group", + ROSTER ); generate_attribute!( @@ -79,11 +81,11 @@ impl IqResultPayload for Roster {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; + use crate::compare_elements::NamespaceAwareCompare; use crate::error::Error; + use minidom::Element; use std::str::FromStr; - use crate::compare_elements::NamespaceAwareCompare; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -122,7 +124,9 @@ mod tests { let roster2 = Roster::try_from(elem2).unwrap(); assert_eq!(roster.items, roster2.items); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert_eq!(roster.ver, Some(String::from("ver9"))); assert!(roster.items.is_empty()); @@ -141,14 +145,22 @@ mod tests { name='Benvolio' subscription='both'/> -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert_eq!(roster.ver, Some(String::from("ver11"))); assert_eq!(roster.items.len(), 3); - assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap()); + assert_eq!( + roster.items[0].jid, + Jid::from_str("romeo@example.net").unwrap() + ); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); assert_eq!(roster.items[0].subscription, Subscription::Both); - assert_eq!(roster.items[0].groups, vec!(Group::from_str("Friends").unwrap())); + assert_eq!( + roster.items[0].groups, + vec!(Group::from_str("Friends").unwrap()) + ); } #[test] @@ -160,12 +172,17 @@ mod tests { B -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let elem1 = elem.clone(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); - assert_eq!(roster.items[0].jid, Jid::from_str("test@example.org").unwrap()); + assert_eq!( + roster.items[0].jid, + Jid::from_str("test@example.org").unwrap() + ); assert_eq!(roster.items[0].name, None); assert_eq!(roster.items[0].groups.len(), 2); assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap()); @@ -176,7 +193,10 @@ mod tests { #[test] fn test_set() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); @@ -188,25 +208,38 @@ mod tests { Servants -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); - assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); + assert_eq!( + roster.items[0].jid, + Jid::from_str("nurse@example.com").unwrap() + ); assert_eq!(roster.items[0].name, Some(String::from("Nurse"))); assert_eq!(roster.items[0].groups.len(), 1); - assert_eq!(roster.items[0].groups[0], Group::from_str("Servants").unwrap()); + assert_eq!( + roster.items[0].groups[0], + Group::from_str("Servants").unwrap() + ); let elem: Element = r#" -"#.parse().unwrap(); +"# + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); - assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap()); + assert_eq!( + roster.items[0].jid, + Jid::from_str("nurse@example.com").unwrap() + ); assert!(roster.items[0].name.is_none()); assert!(roster.items[0].groups.is_empty()); assert_eq!(roster.items[0].subscription, Subscription::Remove); @@ -214,7 +247,9 @@ mod tests { #[test] fn test_invalid() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -222,7 +257,9 @@ mod tests { }; assert_eq!(message, "Unknown child in query element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -233,7 +270,9 @@ mod tests { #[test] fn test_invalid_item() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -251,7 +290,10 @@ mod tests { assert_eq!(error.description(), "Invalid JID, I guess?"); */ - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/rsm.rs b/src/rsm.rs index 6bb02ba736f23d2be3ae3986907d50fb411b51de..afa08be66f6361eaee0aa543116c791092bc3c76 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -74,12 +74,30 @@ impl TryFrom for SetQuery { impl From for Element { fn from(set: SetQuery) -> Element { Element::builder("set") - .ns(ns::RSM) - .append(set.max.map(|max| Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build())) - .append(set.after.map(|after| Element::builder("after").ns(ns::RSM).append(after).build())) - .append(set.before.map(|before| Element::builder("before").ns(ns::RSM).append(before).build())) - .append(set.index.map(|index| Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build())) - .build() + .ns(ns::RSM) + .append(set.max.map(|max| { + Element::builder("max") + .ns(ns::RSM) + .append(format!("{}", max)) + .build() + })) + .append( + set.after + .map(|after| Element::builder("after").ns(ns::RSM).append(after).build()), + ) + .append(set.before.map(|before| { + Element::builder("before") + .ns(ns::RSM) + .append(before) + .build() + })) + .append(set.index.map(|index| { + Element::builder("index") + .ns(ns::RSM) + .append(format!("{}", index)) + .build() + })) + .build() } } @@ -138,18 +156,27 @@ impl TryFrom for SetResult { impl From for Element { fn from(set: SetResult) -> Element { - let first = set.first.clone() - .map(|first| Element::builder("first") - .ns(ns::RSM) - .attr("index", set.first_index) - .append(first) - .build()); - Element::builder("set") + let first = set.first.clone().map(|first| { + Element::builder("first") .ns(ns::RSM) + .attr("index", set.first_index) .append(first) - .append(set.last.map(|last| Element::builder("last").ns(ns::RSM).append(last).build())) - .append(set.count.map(|count| Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build())) .build() + }); + Element::builder("set") + .ns(ns::RSM) + .append(first) + .append( + set.last + .map(|last| Element::builder("last").ns(ns::RSM).append(last).build()), + ) + .append(set.count.map(|count| { + Element::builder("count") + .ns(ns::RSM) + .append(format!("{}", count)) + .build() + })) + .build() } } @@ -174,14 +201,18 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let set = SetQuery::try_from(elem).unwrap(); assert_eq!(set.max, None); assert_eq!(set.after, None); assert_eq!(set.before, None); assert_eq!(set.index, None); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let set = SetResult::try_from(elem).unwrap(); match set.first { Some(_) => panic!(), @@ -193,7 +224,9 @@ mod tests { #[test] fn test_unknown() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = SetQuery::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -201,7 +234,9 @@ mod tests { }; assert_eq!(message, "This is not a RSM set element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = SetResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -212,7 +247,9 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = SetQuery::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -220,7 +257,9 @@ mod tests { }; assert_eq!(message, "Unknown child in set element."); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = SetResult::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -231,7 +270,9 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let rsm = SetQuery { max: None, after: None, @@ -241,7 +282,9 @@ mod tests { let elem2 = rsm.into(); assert_eq!(elem, elem2); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let rsm = SetResult { first: None, first_index: None, @@ -254,7 +297,10 @@ mod tests { #[test] fn test_first_index() { - let elem: Element = "coucou".parse().unwrap(); + let elem: Element = + "coucou" + .parse() + .unwrap(); let elem1 = elem.clone(); let set = SetResult::try_from(elem).unwrap(); assert_eq!(set.first, Some(String::from("coucou"))); diff --git a/src/sasl.rs b/src/sasl.rs index 30fa653a6baca3df08534c76befbd3ad905a8de7..3ae0c7adfd8a076d5d370a63f33c78de3884d8fd 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -6,10 +6,10 @@ use std::collections::BTreeMap; -use try_from::TryFrom; -use minidom::Element; use crate::error::Error; use crate::ns; +use minidom::Element; +use try_from::TryFrom; use crate::helpers::Base64; @@ -84,7 +84,9 @@ generate_element!( generate_empty_element!( /// Sent by the client at any point after [auth](struct.Auth.html) if it /// wants to cancel the current authentication process. - Abort, "abort", SASL + Abort, + "abort", + SASL ); generate_element!( @@ -166,11 +168,15 @@ impl TryFrom for Failure { check_no_children!(child, "text"); let lang = get_attr!(child, "xml:lang", default); if texts.insert(lang, child.text()).is_some() { - return Err(Error::ParseError("Text element present twice for the same xml:lang in failure element.")); + return Err(Error::ParseError( + "Text element present twice for the same xml:lang in failure element.", + )); } } else if child.has_ns(ns::SASL) { if defined_condition.is_some() { - return Err(Error::ParseError("Failure must not have more than one defined-condition.")); + return Err(Error::ParseError( + "Failure must not have more than one defined-condition.", + )); } check_no_attributes!(child, "defined-condition"); check_no_children!(child, "defined-condition"); @@ -184,7 +190,8 @@ impl TryFrom for Failure { return Err(Error::ParseError("Unknown element in Failure.")); } } - let defined_condition = defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?; + let defined_condition = + defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?; Ok(Failure { defined_condition: defined_condition, @@ -196,24 +203,30 @@ impl TryFrom for Failure { impl From for Element { fn from(failure: Failure) -> Element { Element::builder("failure") - .ns(ns::SASL) - .append(failure.defined_condition) - .append(failure.texts.into_iter().map(|(lang, text)| { - Element::builder("text") - .ns(ns::SASL) - .attr("xml:lang", lang) - .append(text) - .build() - }).collect::>()) - .build() + .ns(ns::SASL) + .append(failure.defined_condition) + .append( + failure + .texts + .into_iter() + .map(|(lang, text)| { + Element::builder("text") + .ns(ns::SASL) + .attr("xml:lang", lang) + .append(text) + .build() + }) + .collect::>(), + ) + .build() } } #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -243,7 +256,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let auth = Auth::try_from(elem).unwrap(); assert_eq!(auth.mechanism, Mechanism::Plain); assert!(auth.data.is_empty()); @@ -251,7 +266,10 @@ mod tests { #[test] fn section_6_5_1() { - let elem: Element = "".parse().unwrap(); + let elem: Element = + "" + .parse() + .unwrap(); let failure = Failure::try_from(elem).unwrap(); assert_eq!(failure.defined_condition, DefinedCondition::Aborted); assert!(failure.texts.is_empty()); @@ -262,9 +280,14 @@ mod tests { let elem: Element = " Call 212-555-1212 for assistance. - ".parse().unwrap(); + " + .parse() + .unwrap(); let failure = Failure::try_from(elem).unwrap(); assert_eq!(failure.defined_condition, DefinedCondition::AccountDisabled); - assert_eq!(failure.texts["en"], String::from("Call 212-555-1212 for assistance.")); + assert_eq!( + failure.texts["en"], + String::from("Call 212-555-1212 for assistance.") + ); } } diff --git a/src/sm.rs b/src/sm.rs index b0d08d3e62cca65162dab61c306206b3a8715c73..fc8e7377609a0db349799f2cc9bf033fafd19fe8 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -24,7 +24,9 @@ impl A { generate_attribute!( /// Whether to allow resumption of a previous stream. - ResumeAttr, "resume", bool + ResumeAttr, + "resume", + bool ); generate_element!( @@ -103,7 +105,9 @@ generate_element!( generate_empty_element!( /// Requests the currently received stanzas by the other party. - R, "r", SM + R, + "r", + SM ); generate_element!( @@ -135,14 +139,16 @@ generate_element!( // TODO: add support for optional and required. generate_empty_element!( /// Represents availability of Stream Management in ``. - StreamManagement, "sm", SM + StreamManagement, + "sm", + SM ); #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -189,12 +195,16 @@ mod tests { #[test] fn resume() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let enable = Enable::try_from(elem).unwrap(); assert_eq!(enable.max, None); assert_eq!(enable.resume, ResumeAttr::True); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let enabled = Enabled::try_from(elem).unwrap(); let previd = enabled.id.unwrap(); assert_eq!(enabled.resume, ResumeAttr::True); @@ -202,12 +212,16 @@ mod tests { assert_eq!(enabled.max, Some(600)); assert_eq!(enabled.location, None); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let resume = Resume::try_from(elem).unwrap(); assert_eq!(resume.h, 5); assert_eq!(resume.previd, previd); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let resumed = Resumed::try_from(elem).unwrap(); assert_eq!(resumed.h, 5); assert_eq!(resumed.previd, previd); diff --git a/src/stanza_error.rs b/src/stanza_error.rs index ec31ecb1d5d23d7562b580af934eef458c6bd11c..a0848eec439f6cc8e143a3c0ca8d171b5b99a1bf 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,16 +4,16 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; use std::collections::BTreeMap; +use try_from::TryFrom; use minidom::Element; +use crate::error::Error; use crate::message::MessagePayload; +use crate::ns; use crate::presence::PresencePayload; -use crate::error::Error; use jid::Jid; -use crate::ns; generate_attribute!( /// The type of the error. @@ -234,23 +234,30 @@ impl TryFrom for StanzaError { check_no_children!(child, "text"); let lang = get_attr!(elem, "xml:lang", default); if texts.insert(lang, child.text()).is_some() { - return Err(Error::ParseError("Text element present twice for the same xml:lang.")); + return Err(Error::ParseError( + "Text element present twice for the same xml:lang.", + )); } } else if child.has_ns(ns::XMPP_STANZAS) { if defined_condition.is_some() { - return Err(Error::ParseError("Error must not have more than one defined-condition.")); + return Err(Error::ParseError( + "Error must not have more than one defined-condition.", + )); } check_no_children!(child, "defined-condition"); let condition = DefinedCondition::try_from(child.clone())?; defined_condition = Some(condition); } else { if other.is_some() { - return Err(Error::ParseError("Error must not have more than one other element.")); + return Err(Error::ParseError( + "Error must not have more than one other element.", + )); } other = Some(child.clone()); } } - let defined_condition = defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; + let defined_condition = + defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; Ok(StanzaError { type_: type_, @@ -265,17 +272,17 @@ impl TryFrom for StanzaError { impl From for Element { fn from(err: StanzaError) -> Element { let mut root = Element::builder("error") - .ns(ns::DEFAULT_NS) - .attr("type", err.type_) - .attr("by", err.by) - .append(err.defined_condition) - .build(); + .ns(ns::DEFAULT_NS) + .attr("type", err.type_) + .attr("by", err.by) + .append(err.defined_condition) + .build(); for (lang, text) in err.texts { let elem = Element::builder("text") - .ns(ns::XMPP_STANZAS) - .attr("xml:lang", lang) - .append(text) - .build(); + .ns(ns::XMPP_STANZAS) + .attr("xml:lang", lang) + .append(text) + .build(); root.append_child(elem); } if let Some(other) = err.other { @@ -313,7 +320,10 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap(); assert_eq!(error.type_, ErrorType::Cancel); - assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition); + assert_eq!( + error.defined_condition, + DefinedCondition::UndefinedCondition + ); } #[test] @@ -330,9 +340,13 @@ mod tests { assert_eq!(message, "Required attribute 'type' missing."); #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -344,9 +358,13 @@ mod tests { #[test] fn test_invalid_condition() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 87595d4324bc9a26ba8b13f6a76ff3f3ebfc0087..2bac2de3e1d7be592ebc053bf1a84d30edb6187a 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -37,10 +37,10 @@ impl MessagePayload for OriginId {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::error::Error; + use minidom::Element; use std::str::FromStr; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -58,19 +58,25 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let stanza_id = StanzaId::try_from(elem).unwrap(); assert_eq!(stanza_id.id, String::from("coucou")); assert_eq!(stanza_id.by, Jid::from_str("coucou@coucou").unwrap()); - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let origin_id = OriginId::try_from(elem).unwrap(); assert_eq!(origin_id.id, String::from("coucou")); } #[test] fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -92,7 +98,9 @@ mod tests { #[test] fn test_invalid_by() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -103,8 +111,13 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = "".parse().unwrap(); - let stanza_id = StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; + let elem: Element = "" + .parse() + .unwrap(); + let stanza_id = StanzaId { + id: String::from("coucou"), + by: Jid::from_str("coucou@coucou").unwrap(), + }; let elem2 = stanza_id.into(); assert_eq!(elem, elem2); } diff --git a/src/stream.rs b/src/stream.rs index ad8ec4f1cf8371565b31f9ac72a4dfe7c572104d..fa416fd80dd7e361169cd909eb175e3a720de6f4 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -73,8 +73,8 @@ impl Stream { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/version.rs b/src/version.rs index 4e969f4a2004dfa6457b543de1ffcfc593b4acea..c9e08c32199060179e7be0ab3b3229c3ce9a51d0 100644 --- a/src/version.rs +++ b/src/version.rs @@ -11,7 +11,9 @@ generate_empty_element!( /// /// It should only be used in an ``, as it can only /// represent the request, and not a result. - VersionQuery, "query", VERSION + VersionQuery, + "query", + VERSION ); impl IqGetPayload for VersionQuery {} @@ -39,9 +41,9 @@ impl IqResultPayload for VersionResult {} #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; - use minidom::Element; use crate::compare_elements::NamespaceAwareCompare; + use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -59,7 +61,10 @@ mod tests { #[test] fn simple() { - let elem: Element = "xmpp-rs0.3.0".parse().unwrap(); + let elem: Element = + "xmpp-rs0.3.0" + .parse() + .unwrap(); let version = VersionResult::try_from(elem).unwrap(); assert_eq!(version.name, String::from("xmpp-rs")); assert_eq!(version.version, String::from("0.3.0")); @@ -74,7 +79,10 @@ mod tests { os: None, }; let elem1 = Element::from(version); - let elem2: Element = "xmpp-rs0.3.0".parse().unwrap(); + let elem2: Element = + "xmpp-rs0.3.0" + .parse() + .unwrap(); println!("{:?}", elem1); assert!(elem1.compare_to(&elem2)); } diff --git a/src/websocket.rs b/src/websocket.rs index f2d28ebfe2892ff84787a67375b53de73134822c..10693ca7d4bee4f9c6278e65c5a8815791a0123b 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -72,8 +72,8 @@ impl Open { #[cfg(test)] mod tests { use super::*; - use try_from::TryFrom; use minidom::Element; + use try_from::TryFrom; #[cfg(target_pointer_width = "32")] #[test] @@ -89,7 +89,9 @@ mod tests { #[test] fn test_simple() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let open = Open::try_from(elem).unwrap(); assert_eq!(open.from, None); assert_eq!(open.to, None); From 83f7c67870867c0cfb20f59f125b693c56cf6999 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:39:37 +0100 Subject: [PATCH 0757/1020] Remove now-useless "extern crate"s. --- src/lib.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f35d5b956c691bd6375dc9f1e44155bc952f77b9..36eb788250d34834804e416b93a55a1c839e45fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,17 +24,6 @@ #![deny(missing_docs)] -extern crate base64; -extern crate blake2; -extern crate chrono; -extern crate digest; -extern crate jid; -extern crate minidom; -extern crate sha1; -extern crate sha2; -extern crate sha3; -extern crate try_from; - pub use minidom::Element; /// Error type returned by every parser on failure. From 5e64dee0edb4a1cee0ed8d46a97f634adafbc10e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:43:49 +0100 Subject: [PATCH 0758/1020] Only define assert_size!() macro when testing. --- src/macros.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/macros.rs b/src/macros.rs index ac77ed557d59cc60dcfa734fc4792f95b99cd20e..d1a407406be2923e1d432eed1ebc6afada805206 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -552,6 +552,7 @@ macro_rules! generate_element { ); } +#[cfg(test)] macro_rules! assert_size ( ($t:ty, $sz:expr) => ( assert_eq!(::std::mem::size_of::<$t>(), $sz); From 7a204cd18267d0022b0a20bb0b162ec1cc2ac397 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 15:44:07 +0100 Subject: [PATCH 0759/1020] Also test the size of IBB StreamId. --- src/ibb.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ibb.rs b/src/ibb.rs index f778ddf1aa3aebdb55c3e778ec4946dcc3419947..f2aff83efe950cbd8475382dc054464d09393330 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -80,6 +80,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { + assert_size!(StreamId, 12); assert_size!(Stanza, 1); assert_size!(Open, 16); assert_size!(Data, 28); @@ -89,6 +90,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { + assert_size!(StreamId, 24); assert_size!(Stanza, 1); assert_size!(Open, 32); assert_size!(Data, 56); From 090a16953b2b09e1b815ee0b640d501151be4ee5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 16:00:25 +0100 Subject: [PATCH 0760/1020] bind: Add forgotten test for attributes. --- src/bind.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/bind.rs b/src/bind.rs index ddda0392b67b2105977923cf0f47e62a4dbf8e75..fe9472b2cc3c2192c959cd7c076dc87d9adc2786 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -55,9 +55,11 @@ impl TryFrom for Bind { return Err(Error::ParseError("Bind can only have one child.")); } if child.is("resource", ns::BIND) { + check_no_attributes!(child, "resource"); check_no_children!(child, "resource"); bind = Bind::Resource(child.text()); } else if child.is("jid", ns::BIND) { + check_no_attributes!(child, "jid"); check_no_children!(child, "jid"); bind = Bind::Jid(Jid::from_str(&child.text())?); } else { @@ -109,4 +111,27 @@ mod tests { let bind = Bind::try_from(elem).unwrap(); assert_eq!(bind, Bind::None); } + + #[test] + fn test_invalid_resource() { + let elem: Element = "resource" + .parse() + .unwrap(); + let error = Bind::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in resource element."); + + let elem: Element = "resource" + .parse() + .unwrap(); + let error = Bind::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in resource element."); + } } From 376ccc1c063937c60d841c5ed037db2d30b0988b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 16:07:46 +0100 Subject: [PATCH 0761/1020] Run `cargo fmt` on some more files. --- src/presence.rs | 12 ++++-------- src/rsm.rs | 7 ++----- src/sasl.rs | 6 ++---- src/stanza_error.rs | 8 +++----- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 8f3d2cf109a806ab68600cb12caa159412255553..765e680d87d7ef36aab84f85dd178252aa3467ff 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,18 +5,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use crate::error::Error; +use crate::ns; +use jid::Jid; +use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; use std::collections::BTreeMap; use std::str::FromStr; use try_from::TryFrom; -use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; - -use jid::Jid; - -use crate::error::Error; - -use crate::ns; - /// Should be implemented on every known payload of a ``. pub trait PresencePayload: TryFrom + Into {} diff --git a/src/rsm.rs b/src/rsm.rs index afa08be66f6361eaee0aa543116c791092bc3c76..0cd048ec854d5c4bb11c16906093356c0498aaff 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -4,13 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use try_from::TryFrom; - -use minidom::Element; - use crate::error::Error; - use crate::ns; +use minidom::Element; +use try_from::TryFrom; /// Requests paging through a potentially big set of items (represented by an /// UID). diff --git a/src/sasl.rs b/src/sasl.rs index 3ae0c7adfd8a076d5d370a63f33c78de3884d8fd..05dd250aba849569c445349720e024dee0e0179a 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,15 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::collections::BTreeMap; - use crate::error::Error; +use crate::helpers::Base64; use crate::ns; use minidom::Element; +use std::collections::BTreeMap; use try_from::TryFrom; -use crate::helpers::Base64; - generate_attribute!( /// The list of available SASL mechanisms. Mechanism, "mechanism", { diff --git a/src/stanza_error.rs b/src/stanza_error.rs index a0848eec439f6cc8e143a3c0ca8d171b5b99a1bf..c5e62026848b668e5d269d0fdeb8c4f1e3a7eeef 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,16 +4,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::collections::BTreeMap; -use try_from::TryFrom; - -use minidom::Element; - use crate::error::Error; use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; use jid::Jid; +use minidom::Element; +use std::collections::BTreeMap; +use try_from::TryFrom; generate_attribute!( /// The type of the error. From d48473648be4c48b2ffa49c41ff76b4fe7219904 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 16:35:08 +0100 Subject: [PATCH 0762/1020] Bump minidom dependency to 0.10. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e5f77215f2501f0a3e294c1154ee9ae478e7a048..9d1aeed54e06c42cdf43191af739fce6c2329301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,4 +20,4 @@ gitlab = { repository = "xmpp-rs/jid-rs" } [dependencies] failure = "0.1.1" failure_derive = "0.1.1" -minidom = { version = "0.9.1", optional = true } +minidom = { version = "0.10", optional = true } From 316268d3a115f6466f71ad7fe794be8fdc6880fe Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 16:38:14 +0100 Subject: [PATCH 0763/1020] Use edition 2018. --- Cargo.toml | 1 + src/lib.rs | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9d1aeed54e06c42cdf43191af739fce6c2329301..d309aef6d50618c86f423581201e7404609af509 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ documentation = "https://docs.rs/jid" readme = "README.md" keywords = ["xmpp", "jid"] license = "LGPL-3.0+" +edition = "2018" [badges] gitlab = { repository = "xmpp-rs/jid-rs" } diff --git a/src/lib.rs b/src/lib.rs index 293c4a0ccfc04c97fbb17de4bfa8bb3b3656cbe6..2861ef6eb4cdd1a71bb02498152292ea441f6e18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,6 @@ //! //! For usage, check the documentation on the `Jid` struct. -extern crate failure; #[macro_use] extern crate failure_derive; use std::fmt; @@ -381,9 +380,6 @@ impl Jid { } -#[cfg(feature = "minidom")] -extern crate minidom; - #[cfg(feature = "minidom")] use minidom::{IntoAttributeValue, IntoElements, ElementEmitter}; From fa0894daa06f88db0531e542875bbce332bb83d0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 16:40:44 +0100 Subject: [PATCH 0764/1020] Run `cargo fmt`. --- src/lib.rs | 80 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2861ef6eb4cdd1a71bb02498152292ea441f6e18..dd9a5f1b0bb1d82e8fe17232f9ec18d6312f9520 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,11 @@ //! //! For usage, check the documentation on the `Jid` struct. -#[macro_use] extern crate failure_derive; - -use std::fmt; +#[macro_use] +extern crate failure_derive; use std::convert::Into; - +use std::fmt; use std::str::FromStr; /// An error that signifies that a `Jid` cannot be parsed from a string. @@ -77,7 +76,7 @@ impl fmt::Display for Jid { enum ParserState { Node, Domain, - Resource + Resource, } impl FromStr for Jid { @@ -102,7 +101,7 @@ impl FromStr for Jid { state = ParserState::Domain; node = Some(buf.clone()); // TODO: performance tweaks, do not need to copy it buf.clear(); - }, + } '/' => { if buf == "" { return Err(JidParseError::NoDomain); @@ -110,12 +109,12 @@ impl FromStr for Jid { state = ParserState::Resource; domain = Some(buf.clone()); // TODO: performance tweaks buf.clear(); - }, + } c => { buf.push(c); - }, + } } - }, + } ParserState::Domain => { match c { '/' => { @@ -125,28 +124,28 @@ impl FromStr for Jid { state = ParserState::Resource; domain = Some(buf.clone()); // TODO: performance tweaks buf.clear(); - }, + } c => { buf.push(c); - }, + } } - }, + } ParserState::Resource => { buf.push(c); - }, + } } } if !buf.is_empty() { match state { ParserState::Node => { domain = Some(buf); - }, + } ParserState::Domain => { domain = Some(buf); - }, + } ParserState::Resource => { resource = Some(buf); - }, + } } } else if let ParserState::Resource = state { return Err(JidParseError::EmptyResource); @@ -176,9 +175,11 @@ impl Jid { /// assert_eq!(jid.resource, Some("resource".to_owned())); /// ``` pub fn full(node: NS, domain: DS, resource: RS) -> Jid - where NS: Into - , DS: Into - , RS: Into { + where + NS: Into, + DS: Into, + RS: Into, + { Jid { node: Some(node.into()), domain: domain.into(), @@ -202,8 +203,10 @@ impl Jid { /// assert_eq!(jid.resource, None); /// ``` pub fn bare(node: NS, domain: DS) -> Jid - where NS: Into - , DS: Into { + where + NS: Into, + DS: Into, + { Jid { node: Some(node.into()), domain: domain.into(), @@ -250,7 +253,9 @@ impl Jid { /// assert_eq!(jid.resource, None); /// ``` pub fn domain(domain: DS) -> Jid - where DS: Into { + where + DS: Into, + { Jid { node: None, domain: domain.into(), @@ -297,8 +302,10 @@ impl Jid { /// assert_eq!(jid.resource, Some("resource".to_owned())); /// ``` pub fn domain_with_resource(domain: DS, resource: RS) -> Jid - where DS: Into - , RS: Into { + where + DS: Into, + RS: Into, + { Jid { node: None, domain: domain.into(), @@ -322,7 +329,9 @@ impl Jid { /// assert_eq!(new_jid.node, Some("node".to_owned())); /// ``` pub fn with_node(&self, node: S) -> Jid - where S: Into { + where + S: Into, + { Jid { node: Some(node.into()), domain: self.domain.clone(), @@ -346,7 +355,9 @@ impl Jid { /// assert_eq!(new_jid.domain, "new_domain"); /// ``` pub fn with_domain(&self, domain: S) -> Jid - where S: Into { + where + S: Into, + { Jid { node: self.node.clone(), domain: domain.into(), @@ -370,18 +381,19 @@ impl Jid { /// assert_eq!(new_jid.resource, Some("resource".to_owned())); /// ``` pub fn with_resource(&self, resource: S) -> Jid - where S: Into { + where + S: Into, + { Jid { node: self.node.clone(), domain: self.domain.clone(), resource: Some(resource.into()), } } - } #[cfg(feature = "minidom")] -use minidom::{IntoAttributeValue, IntoElements, ElementEmitter}; +use minidom::{ElementEmitter, IntoAttributeValue, IntoElements}; #[cfg(feature = "minidom")] impl IntoAttributeValue for Jid { @@ -408,12 +420,18 @@ mod tests { assert_eq!(Jid::from_str("a@b.c/d"), Ok(Jid::full("a", "b.c", "d"))); assert_eq!(Jid::from_str("a@b.c"), Ok(Jid::bare("a", "b.c"))); assert_eq!(Jid::from_str("b.c"), Ok(Jid::domain("b.c"))); - assert_eq!(Jid::from_str("a/b@c"), Ok(Jid::domain_with_resource("a", "b@c"))); + assert_eq!( + Jid::from_str("a/b@c"), + Ok(Jid::domain_with_resource("a", "b@c")) + ); } #[test] fn serialise() { - assert_eq!(String::from(Jid::full("a", "b", "c")), String::from("a@b/c")); + assert_eq!( + String::from(Jid::full("a", "b", "c")), + String::from("a@b/c") + ); } #[test] From 24b2ff185107d59db0e8cfdb02c296e96122a43a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 18:29:31 +0100 Subject: [PATCH 0765/1020] Move to edition 2018. --- Cargo.toml | 1 + src/client/auth.rs | 8 ++++---- src/client/bind.rs | 6 +++--- src/component/auth.rs | 6 +++--- src/happy_eyeballs.rs | 2 +- src/lib.rs | 10 +++++----- src/starttls.rs | 6 +++--- src/stream_start.rs | 6 +++--- src/xmpp_codec.rs | 2 +- src/xmpp_stream.rs | 4 ++-- 10 files changed, 26 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 09a0343714303b99bcd4d454dd3f300666d8243d..08ff17c5cb6fd35a4aca21d6e9eb933530257493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ repository = "https://github.com/astro/tokio-xmpp" documentation = "https://docs.rs/tokio-xmpp" categories = ["asynchronous", "network-programming"] keywords = ["xmpp", "tokio"] +edition = "2018" [dependencies] futures = "0.1" diff --git a/src/client/auth.rs b/src/client/auth.rs index 55dfe2d2ac6a91c4137af9db756edcafd6ac904e..2efb7071ae1a7ef958dc64c7a51e490a824b73e7 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -10,10 +10,10 @@ use minidom::Element; use xmpp_parsers::sasl::{Auth, Challenge, Response, Success, Failure, Mechanism as XMPPMechanism}; use try_from::TryFrom; -use xmpp_codec::Packet; -use xmpp_stream::XMPPStream; -use stream_start::StreamStart; -use {Error, AuthError, ProtocolError}; +use crate::xmpp_codec::Packet; +use crate::xmpp_stream::XMPPStream; +use crate::stream_start::StreamStart; +use crate::{Error, AuthError, ProtocolError}; const NS_XMPP_SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; diff --git a/src/client/bind.rs b/src/client/bind.rs index b0268e6eb777d6b66a6e8ee905a0a71975da682f..bfcca2bee78201a5cee33d8afd39ecdf7058bcf5 100644 --- a/src/client/bind.rs +++ b/src/client/bind.rs @@ -5,9 +5,9 @@ use xmpp_parsers::iq::{Iq, IqType}; use xmpp_parsers::bind::Bind; use try_from::TryFrom; -use xmpp_codec::Packet; -use xmpp_stream::XMPPStream; -use {Error, ProtocolError}; +use crate::xmpp_codec::Packet; +use crate::xmpp_stream::XMPPStream; +use crate::{Error, ProtocolError}; const NS_XMPP_BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind"; const BIND_REQ_ID: &str = "resource-bind"; diff --git a/src/component/auth.rs b/src/component/auth.rs index b108ef3ffffe120ca7c038873677b070055cebc0..ce55b3a9b2b9f365427721282872466beb5558ac 100644 --- a/src/component/auth.rs +++ b/src/component/auth.rs @@ -3,9 +3,9 @@ use futures::{Future, Poll, Async, sink, Stream}; use tokio_io::{AsyncRead, AsyncWrite}; use xmpp_parsers::component::Handshake; -use xmpp_codec::Packet; -use xmpp_stream::XMPPStream; -use {Error, AuthError}; +use crate::xmpp_codec::Packet; +use crate::xmpp_stream::XMPPStream; +use crate::{Error, AuthError}; const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 6e7f02757fc0bd4711af1b9687ebc45e02217a07..7bdab7eeca16c999e75d9fb1cd1d90f9b9058759 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -12,7 +12,7 @@ use trust_dns_resolver::lookup::SrvLookupFuture; use trust_dns_resolver::lookup_ip::LookupIpFuture; use trust_dns_resolver::system_conf; use trust_dns_resolver::config::LookupIpStrategy; -use {Error, ConnecterError}; +use crate::{Error, ConnecterError}; enum State { AwaitResolver(Box + Send>), diff --git a/src/lib.rs b/src/lib.rs index bed95b0722ccc7eecfe9b6bc4397d94343f136e1..f874315f8d1a4286d6d3e3e4171a4a802bb2c75b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,13 +26,13 @@ pub mod xmpp_codec; pub mod xmpp_stream; mod stream_start; mod starttls; -pub use starttls::StartTlsClient; +pub use crate::starttls::StartTlsClient; mod happy_eyeballs; mod event; -pub use event::Event; +pub use crate::event::Event; mod client; -pub use client::Client; +pub use crate::client::Client; mod component; -pub use component::Component; +pub use crate::component::Component; mod error; -pub use error::{Error, ProtocolError, AuthError, ConnecterError, ParseError, ParserError}; +pub use crate::error::{Error, ProtocolError, AuthError, ConnecterError, ParseError, ParserError}; diff --git a/src/starttls.rs b/src/starttls.rs index 5acfcb15b9ea99734284ba54a9186bac859c2100..a7f7fd392bc19c80f1be419441aa5d6e0e566697 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -8,9 +8,9 @@ use native_tls::TlsConnector as NativeTlsConnector; use minidom::Element; use jid::Jid; -use xmpp_codec::Packet; -use xmpp_stream::XMPPStream; -use Error; +use crate::xmpp_codec::Packet; +use crate::xmpp_stream::XMPPStream; +use crate::Error; /// XMPP TLS XML namespace pub const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; diff --git a/src/stream_start.rs b/src/stream_start.rs index ce9db27a8128eb42ac1296405c125379b5343cee..9aef117da7a622d2419cecd03256d4d9f31908bf 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -5,9 +5,9 @@ use tokio_codec::Framed; use jid::Jid; use minidom::Element; -use xmpp_codec::{XMPPCodec, Packet}; -use xmpp_stream::XMPPStream; -use {Error, ProtocolError}; +use crate::xmpp_codec::{XMPPCodec, Packet}; +use crate::xmpp_stream::XMPPStream; +use crate::{Error, ProtocolError}; const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 37e5a78c8dccb8d9d43ac6e2dc8bbc4d49205603..4088552c9c9fea4fb4b0690b22cccc54fb5f2b02 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -16,7 +16,7 @@ use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind}; use xml5ever::interface::Attribute; use bytes::{BytesMut, BufMut}; use quick_xml::Writer as EventWriter; -use {ParserError, ParseError}; +use crate::{ParserError, ParseError}; /// Anything that can be sent or received on an XMPP/XML stream #[derive(Debug)] diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index c5443a98e54242bf100cefbb1fa9dad471c8fb8e..d8e878d5a2bf52451547808a090889cc2119c3bc 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -7,8 +7,8 @@ use tokio_codec::Framed; use minidom::Element; use jid::Jid; -use xmpp_codec::{XMPPCodec, Packet}; -use stream_start::StreamStart; +use crate::xmpp_codec::{XMPPCodec, Packet}; +use crate::stream_start::StreamStart; /// namespace pub const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; From 3aba4e707043c0b86ce1deebc27a9a8025564753 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 19:04:02 +0100 Subject: [PATCH 0766/1020] Remove now-useless "extern crate"s. --- examples/echo_bot.rs | 8 -------- examples/echo_component.rs | 8 -------- src/happy_eyeballs.rs | 2 +- src/lib.rs | 17 ----------------- 4 files changed, 1 insertion(+), 34 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 138222609515f49ee5cc68c24b98c2c94f40f409..67cbfbb35f98097ac7f7750a4cce612fb27b0967 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,11 +1,3 @@ -extern crate futures; -extern crate tokio; -extern crate tokio_xmpp; -extern crate jid; -extern crate minidom; -extern crate xmpp_parsers; -extern crate try_from; - use std::env::args; use std::process::exit; use try_from::TryFrom; diff --git a/examples/echo_component.rs b/examples/echo_component.rs index 1239354aa0fceb19cebfb8e039bd298da548d7fe..b9e24265dafb60473b32af6a00ae35f84ff8cf33 100644 --- a/examples/echo_component.rs +++ b/examples/echo_component.rs @@ -1,11 +1,3 @@ -extern crate futures; -extern crate tokio; -extern crate tokio_xmpp; -extern crate jid; -extern crate minidom; -extern crate xmpp_parsers; -extern crate try_from; - use std::env::args; use std::process::exit; use std::str::FromStr; diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 7bdab7eeca16c999e75d9fb1cd1d90f9b9058759..451f9c73499a9e6137c3f402b73de41fd14fd551 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -115,7 +115,7 @@ impl Future for Connecter { Ok(Async::NotReady) } Ok(Async::Ready(srv_result)) => { - let mut srv_map: BTreeMap<_, _> = + let srv_map: BTreeMap<_, _> = srv_result.iter() .map(|srv| (srv.priority(), (srv.target().clone(), srv.port()))) .collect(); diff --git a/src/lib.rs b/src/lib.rs index f874315f8d1a4286d6d3e3e4171a4a802bb2c75b..b1942119211f44ce2b68f0d30b05460875077cd0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,23 +2,6 @@ //! XMPP implemeentation with asynchronous I/O using Tokio. -extern crate futures; -extern crate tokio; -extern crate tokio_io; -extern crate tokio_codec; -extern crate bytes; -extern crate xml5ever; -extern crate quick_xml; -extern crate minidom; -extern crate native_tls; -extern crate tokio_tls; -extern crate sasl; -extern crate jid; -extern crate trust_dns_resolver; -extern crate trust_dns_proto; -extern crate idna; -extern crate xmpp_parsers; -extern crate try_from; #[macro_use] extern crate derive_error; From 51bae9b0e5f97d0b36dc66f0306252b878fe17c8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 18 Dec 2018 19:04:31 +0100 Subject: [PATCH 0767/1020] Run `cargo fmt` on the entire project. --- examples/echo_bot.rs | 24 ++--- examples/echo_component.rs | 35 +++++--- src/client/auth.rs | 130 +++++++++++++-------------- src/client/bind.rs | 107 +++++++++++------------ src/client/mod.rs | 142 +++++++++++++++--------------- src/component/auth.rs | 79 ++++++++--------- src/component/mod.rs | 110 +++++++++++------------ src/error.rs | 10 +-- src/happy_eyeballs.rs | 106 +++++++++++----------- src/lib.rs | 8 +- src/starttls.rs | 112 ++++++++++++------------ src/stream_start.rs | 133 +++++++++++++++------------- src/xmpp_codec.rs | 175 +++++++++++++++++-------------------- src/xmpp_stream.rs | 27 +++--- 14 files changed, 591 insertions(+), 607 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 67cbfbb35f98097ac7f7750a4cce612fb27b0967..80ffc97fcc142e4e035d3488abb05379a1c53008 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,13 +1,13 @@ +use futures::{future, Sink, Stream}; +use jid::Jid; +use minidom::Element; use std::env::args; use std::process::exit; -use try_from::TryFrom; -use futures::{Stream, Sink, future}; use tokio::runtime::current_thread::Runtime; use tokio_xmpp::Client; -use minidom::Element; -use xmpp_parsers::presence::{Presence, Type as PresenceType, Show as PresenceShow}; -use xmpp_parsers::message::{Message, MessageType, Body}; -use jid::Jid; +use try_from::TryFrom; +use xmpp_parsers::message::{Body, Message, MessageType}; +use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType}; fn main() { let args: Vec = args().collect(); @@ -38,16 +38,18 @@ fn main() { let presence = make_presence(); send(presence); - } else if let Some(message) = event.into_stanza() + } else if let Some(message) = event + .into_stanza() .and_then(|stanza| Message::try_from(stanza).ok()) { // This is a message we'll echo match (message.from, message.bodies.get("")) { - (Some(from), Some(body)) => + (Some(from), Some(body)) => { if message.type_ != MessageType::Error { let reply = make_reply(from, &body.0); send(reply); - }, + } + } _ => (), } } @@ -69,7 +71,9 @@ fn main() { fn make_presence() -> Element { let mut presence = Presence::new(PresenceType::None); presence.show = PresenceShow::Chat; - presence.statuses.insert(String::from("en"), String::from("Echoing messages.")); + presence + .statuses + .insert(String::from("en"), String::from("Echoing messages.")); presence.into() } diff --git a/examples/echo_component.rs b/examples/echo_component.rs index b9e24265dafb60473b32af6a00ae35f84ff8cf33..348b06a4d2d812303943b996e514a966b945f610 100644 --- a/examples/echo_component.rs +++ b/examples/echo_component.rs @@ -1,14 +1,14 @@ +use futures::{future, Sink, Stream}; +use jid::Jid; +use minidom::Element; use std::env::args; use std::process::exit; use std::str::FromStr; -use try_from::TryFrom; use tokio::runtime::current_thread::Runtime; -use futures::{Stream, Sink, future}; use tokio_xmpp::Component; -use minidom::Element; -use xmpp_parsers::presence::{Presence, Type as PresenceType, Show as PresenceShow}; -use xmpp_parsers::message::{Message, MessageType, Body}; -use jid::Jid; +use try_from::TryFrom; +use xmpp_parsers::message::{Body, Message, MessageType}; +use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType}; fn main() { let args: Vec = args().collect(); @@ -18,7 +18,11 @@ fn main() { } let jid = &args[1]; let password = &args[2]; - let server = &args.get(3).unwrap().parse().unwrap_or("127.0.0.1".to_owned()); + let server = &args + .get(3) + .unwrap() + .parse() + .unwrap_or("127.0.0.1".to_owned()); let port: u16 = args.get(4).unwrap().parse().unwrap_or(5347u16); // tokio_core context @@ -42,18 +46,23 @@ fn main() { println!("Online!"); // TODO: replace these hardcoded JIDs - let presence = make_presence(Jid::from_str("test@component.linkmauve.fr/coucou").unwrap(), Jid::from_str("linkmauve@linkmauve.fr").unwrap()); + let presence = make_presence( + Jid::from_str("test@component.linkmauve.fr/coucou").unwrap(), + Jid::from_str("linkmauve@linkmauve.fr").unwrap(), + ); send(presence); - } else if let Some(message) = event.into_stanza() + } else if let Some(message) = event + .into_stanza() .and_then(|stanza| Message::try_from(stanza).ok()) { // This is a message we'll echo match (message.from, message.bodies.get("")) { - (Some(from), Some(body)) => + (Some(from), Some(body)) => { if message.type_ != MessageType::Error { let reply = make_reply(from, &body.0); send(reply); - }, + } + } _ => (), } } @@ -77,7 +86,9 @@ fn make_presence(from: Jid, to: Jid) -> Element { presence.from = Some(from); presence.to = Some(to); presence.show = PresenceShow::Chat; - presence.statuses.insert(String::from("en"), String::from("Echoing messages.")); + presence + .statuses + .insert(String::from("en"), String::from("Echoing messages.")); presence.into() } diff --git a/src/client/auth.rs b/src/client/auth.rs index 2efb7071ae1a7ef958dc64c7a51e490a824b73e7..eadceb02a63c873d607cd6b5efc25cecb3dbba6d 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -1,23 +1,22 @@ +use futures::{sink, Async, Future, Poll, Stream}; +use minidom::Element; +use sasl::client::mechanisms::{Anonymous, Plain, Scram}; +use sasl::client::Mechanism; +use sasl::common::scram::{Sha1, Sha256}; +use sasl::common::Credentials; use std::mem::replace; use std::str::FromStr; -use futures::{Future, Poll, Async, sink, Stream}; use tokio_io::{AsyncRead, AsyncWrite}; -use sasl::common::Credentials; -use sasl::common::scram::{Sha1, Sha256}; -use sasl::client::Mechanism; -use sasl::client::mechanisms::{Scram, Plain, Anonymous}; -use minidom::Element; -use xmpp_parsers::sasl::{Auth, Challenge, Response, Success, Failure, Mechanism as XMPPMechanism}; use try_from::TryFrom; +use xmpp_parsers::sasl::{Auth, Challenge, Failure, Mechanism as XMPPMechanism, Response, Success}; +use crate::stream_start::StreamStart; use crate::xmpp_codec::Packet; use crate::xmpp_stream::XMPPStream; -use crate::stream_start::StreamStart; -use crate::{Error, AuthError, ProtocolError}; +use crate::{AuthError, Error, ProtocolError}; const NS_XMPP_SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; - pub struct ClientAuth { state: ClientAuthState, mechanism: Box, @@ -39,8 +38,9 @@ impl ClientAuth { Box::new(Anonymous::new()), ]; - let mech_names: Vec = - stream.stream_features.get_child("mechanisms", NS_XMPP_SASL) + let mech_names: Vec = stream + .stream_features + .get_child("mechanisms", NS_XMPP_SASL) .ok_or(AuthError::NoMechanism)? .children() .filter(|child| child.is("mechanism", NS_XMPP_SASL)) @@ -52,20 +52,18 @@ impl ClientAuth { let name = mech.name().to_owned(); if mech_names.iter().any(|name1| *name1 == name) { // println!("SASL mechanism selected: {:?}", name); - let initial = mech.initial() - .map_err(AuthError::Sasl)?; + let initial = mech.initial().map_err(AuthError::Sasl)?; let mut this = ClientAuth { state: ClientAuthState::Invalid, mechanism: mech, }; - let mechanism = XMPPMechanism::from_str(&name) - .map_err(ProtocolError::Parsers)?; + let mechanism = XMPPMechanism::from_str(&name).map_err(ProtocolError::Parsers)?; this.send( stream, Auth { mechanism, data: initial, - } + }, ); return Ok(this); } @@ -89,61 +87,55 @@ impl Future for ClientAuth { let state = replace(&mut self.state, ClientAuthState::Invalid); match state { - ClientAuthState::WaitSend(mut send) => - match send.poll() { - Ok(Async::Ready(stream)) => { - self.state = ClientAuthState::WaitRecv(stream); + ClientAuthState::WaitSend(mut send) => match send.poll() { + Ok(Async::Ready(stream)) => { + self.state = ClientAuthState::WaitRecv(stream); + self.poll() + } + Ok(Async::NotReady) => { + self.state = ClientAuthState::WaitSend(send); + Ok(Async::NotReady) + } + Err(e) => Err(e)?, + }, + ClientAuthState::WaitRecv(mut stream) => match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { + if let Ok(challenge) = Challenge::try_from(stanza.clone()) { + let response = self + .mechanism + .response(&challenge.data) + .map_err(AuthError::Sasl)?; + self.send(stream, Response { data: response }); self.poll() - }, - Ok(Async::NotReady) => { - self.state = ClientAuthState::WaitSend(send); - Ok(Async::NotReady) - }, - Err(e) => - Err(e)?, - }, - ClientAuthState::WaitRecv(mut stream) => - match stream.poll() { - Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { - if let Ok(challenge) = Challenge::try_from(stanza.clone()) { - let response = self.mechanism.response(&challenge.data) - .map_err(AuthError::Sasl)?; - self.send(stream, Response { data: response }); - self.poll() - } else if let Ok(_) = Success::try_from(stanza.clone()) { - let start = stream.restart(); - self.state = ClientAuthState::Start(start); - self.poll() - } else if let Ok(failure) = Failure::try_from(stanza) { - Err(AuthError::Fail(failure.defined_condition))? - } else { - Ok(Async::NotReady) - } - } - Ok(Async::Ready(_event)) => { - // println!("ClientAuth ignore {:?}", _event); - Ok(Async::NotReady) - }, - Ok(_) => { - self.state = ClientAuthState::WaitRecv(stream); - Ok(Async::NotReady) - }, - Err(e) => - Err(ProtocolError::Parser(e))? - }, - ClientAuthState::Start(mut start) => - match start.poll() { - Ok(Async::Ready(stream)) => - Ok(Async::Ready(stream)), - Ok(Async::NotReady) => { + } else if let Ok(_) = Success::try_from(stanza.clone()) { + let start = stream.restart(); self.state = ClientAuthState::Start(start); + self.poll() + } else if let Ok(failure) = Failure::try_from(stanza) { + Err(AuthError::Fail(failure.defined_condition))? + } else { Ok(Async::NotReady) - }, - Err(e) => - Err(e) - }, - ClientAuthState::Invalid => - unreachable!(), + } + } + Ok(Async::Ready(_event)) => { + // println!("ClientAuth ignore {:?}", _event); + Ok(Async::NotReady) + } + Ok(_) => { + self.state = ClientAuthState::WaitRecv(stream); + Ok(Async::NotReady) + } + Err(e) => Err(ProtocolError::Parser(e))?, + }, + ClientAuthState::Start(mut start) => match start.poll() { + Ok(Async::Ready(stream)) => Ok(Async::Ready(stream)), + Ok(Async::NotReady) => { + self.state = ClientAuthState::Start(start); + Ok(Async::NotReady) + } + Err(e) => Err(e), + }, + ClientAuthState::Invalid => unreachable!(), } } } diff --git a/src/client/bind.rs b/src/client/bind.rs index bfcca2bee78201a5cee33d8afd39ecdf7058bcf5..3758a563708083650694d4419f14bd186ba3d15d 100644 --- a/src/client/bind.rs +++ b/src/client/bind.rs @@ -1,9 +1,9 @@ +use futures::{sink, Async, Future, Poll, Stream}; use std::mem::replace; -use futures::{Future, Poll, Async, sink, Stream}; use tokio_io::{AsyncRead, AsyncWrite}; -use xmpp_parsers::iq::{Iq, IqType}; -use xmpp_parsers::bind::Bind; use try_from::TryFrom; +use xmpp_parsers::bind::Bind; +use xmpp_parsers::iq::{Iq, IqType}; use crate::xmpp_codec::Packet; use crate::xmpp_stream::XMPPStream; @@ -26,16 +26,17 @@ impl ClientBind { pub fn new(stream: XMPPStream) -> Self { match stream.stream_features.get_child("bind", NS_XMPP_BIND) { None => - // No resource binding available, - // return the (probably // usable) stream immediately - ClientBind::Unsupported(stream), + // No resource binding available, + // return the (probably // usable) stream immediately + { + ClientBind::Unsupported(stream) + } Some(_) => { let resource = stream.jid.resource.clone(); - let iq = Iq::from_set(Bind::new(resource)) - .with_id(BIND_REQ_ID.to_string()); + let iq = Iq::from_set(Bind::new(resource)).with_id(BIND_REQ_ID.to_string()); let send = stream.send_stanza(iq); ClientBind::WaitSend(send) - }, + } } } } @@ -48,59 +49,51 @@ impl Future for ClientBind { let state = replace(self, ClientBind::Invalid); match state { - ClientBind::Unsupported(stream) => - Ok(Async::Ready(stream)), - ClientBind::WaitSend(mut send) => { - match send.poll() { - Ok(Async::Ready(stream)) => { - replace(self, ClientBind::WaitRecv(stream)); - self.poll() - }, - Ok(Async::NotReady) => { - replace(self, ClientBind::WaitSend(send)); - Ok(Async::NotReady) - }, - Err(e) => - Err(e)? + ClientBind::Unsupported(stream) => Ok(Async::Ready(stream)), + ClientBind::WaitSend(mut send) => match send.poll() { + Ok(Async::Ready(stream)) => { + replace(self, ClientBind::WaitRecv(stream)); + self.poll() + } + Ok(Async::NotReady) => { + replace(self, ClientBind::WaitSend(send)); + Ok(Async::NotReady) } + Err(e) => Err(e)?, }, - ClientBind::WaitRecv(mut stream) => { - match stream.poll() { - Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => - match Iq::try_from(stanza) { - Ok(iq) => if iq.id == Some(BIND_REQ_ID.to_string()) { - match iq.payload { - IqType::Result(payload) => { - payload - .and_then(|payload| Bind::try_from(payload).ok()) - .map(|bind| match bind { - Bind::Jid(jid) => stream.jid = jid, - _ => {} - }); - Ok(Async::Ready(stream)) - }, - _ => - Err(ProtocolError::InvalidBindResponse)?, + ClientBind::WaitRecv(mut stream) => match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => match Iq::try_from(stanza) { + Ok(iq) => { + if iq.id == Some(BIND_REQ_ID.to_string()) { + match iq.payload { + IqType::Result(payload) => { + payload + .and_then(|payload| Bind::try_from(payload).ok()) + .map(|bind| match bind { + Bind::Jid(jid) => stream.jid = jid, + _ => {} + }); + Ok(Async::Ready(stream)) } - } else { - Ok(Async::NotReady) - }, - _ => Ok(Async::NotReady), - }, - Ok(Async::Ready(_)) => { - replace(self, ClientBind::WaitRecv(stream)); - self.poll() - }, - Ok(Async::NotReady) => { - replace(self, ClientBind::WaitRecv(stream)); - Ok(Async::NotReady) - }, - Err(e) => - Err(e)?, + _ => Err(ProtocolError::InvalidBindResponse)?, + } + } else { + Ok(Async::NotReady) + } + } + _ => Ok(Async::NotReady), + }, + Ok(Async::Ready(_)) => { + replace(self, ClientBind::WaitRecv(stream)); + self.poll() + } + Ok(Async::NotReady) => { + replace(self, ClientBind::WaitRecv(stream)); + Ok(Async::NotReady) } + Err(e) => Err(e)?, }, - ClientBind::Invalid => - unreachable!(), + ClientBind::Invalid => unreachable!(), } } } diff --git a/src/client/mod.rs b/src/client/mod.rs index 72634a2ee02c93077f967a7b00b30e908d8c11c4..9e62373dc4f276d059b9203592ede8886139bfd4 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,19 +1,19 @@ +use futures::{done, Async, AsyncSink, Future, Poll, Sink, StartSend, Stream}; +use idna; +use jid::{Jid, JidParseError}; +use minidom::Element; +use sasl::common::{ChannelBinding, Credentials}; use std::mem::replace; use std::str::FromStr; use tokio::net::TcpStream; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_tls::TlsStream; -use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink, done}; -use minidom::Element; -use jid::{Jid, JidParseError}; -use sasl::common::{Credentials, ChannelBinding}; -use idna; +use super::event::Event; +use super::happy_eyeballs::Connecter; +use super::starttls::{StartTlsClient, NS_XMPP_TLS}; use super::xmpp_codec::Packet; use super::xmpp_stream; -use super::starttls::{NS_XMPP_TLS, StartTlsClient}; -use super::happy_eyeballs::Connecter; -use super::event::Event; use super::{Error, ProtocolError}; mod auth; @@ -34,7 +34,7 @@ const NS_JABBER_CLIENT: &str = "jabber:client"; enum ClientState { Invalid, Disconnected, - Connecting(Box>), + Connecting(Box>), Connected(XMPPStream), } @@ -53,51 +53,62 @@ impl Client { }) } - fn make_connect(jid: Jid, password: String) -> impl Future { + fn make_connect(jid: Jid, password: String) -> impl Future { let username = jid.node.as_ref().unwrap().to_owned(); let jid1 = jid.clone(); let jid2 = jid.clone(); let password = password; done(idna::domain_to_ascii(&jid.domain)) .map_err(|_| Error::Idna) - .and_then(|domain| - done(Connecter::from_lookup(&domain, Some("_xmpp-client._tcp"), 5222)) - ) + .and_then(|domain| { + done(Connecter::from_lookup( + &domain, + Some("_xmpp-client._tcp"), + 5222, + )) + }) .flatten() - .and_then(move |tcp_stream| - xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_CLIENT.to_owned()) - ).and_then(|xmpp_stream| { + .and_then(move |tcp_stream| { + xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_CLIENT.to_owned()) + }) + .and_then(|xmpp_stream| { if Self::can_starttls(&xmpp_stream) { Ok(Self::starttls(xmpp_stream)) } else { Err(Error::Protocol(ProtocolError::NoTls)) } - }).flatten() - .and_then(|tls_stream| - XMPPStream::start(tls_stream, jid2, NS_JABBER_CLIENT.to_owned()) - ).and_then(move |xmpp_stream| - done(Self::auth(xmpp_stream, username, password)) - // TODO: flatten? - ).and_then(|auth| auth) + }) + .flatten() + .and_then(|tls_stream| XMPPStream::start(tls_stream, jid2, NS_JABBER_CLIENT.to_owned())) + .and_then( + move |xmpp_stream| done(Self::auth(xmpp_stream, username, password)), // TODO: flatten? + ) + .and_then(|auth| auth) + .and_then(|xmpp_stream| Self::bind(xmpp_stream)) .and_then(|xmpp_stream| { - Self::bind(xmpp_stream) - }).and_then(|xmpp_stream| { // println!("Bound to {}", xmpp_stream.jid); Ok(xmpp_stream) }) } fn can_starttls(stream: &xmpp_stream::XMPPStream) -> bool { - stream.stream_features + stream + .stream_features .get_child("starttls", NS_XMPP_TLS) .is_some() } - fn starttls(stream: xmpp_stream::XMPPStream) -> StartTlsClient { + fn starttls( + stream: xmpp_stream::XMPPStream, + ) -> StartTlsClient { StartTlsClient::from_stream(stream) } - fn auth(stream: xmpp_stream::XMPPStream, username: String, password: String) -> Result, Error> { + fn auth( + stream: xmpp_stream::XMPPStream, + username: String, + password: String, + ) -> Result, Error> { let creds = Credentials::default() .with_username(username) .with_password(password) @@ -118,31 +129,25 @@ impl Stream for Client { let state = replace(&mut self.state, ClientState::Invalid); match state { - ClientState::Invalid => - Err(Error::InvalidState), - ClientState::Disconnected => - Ok(Async::Ready(None)), - ClientState::Connecting(mut connect) => { - match connect.poll() { - Ok(Async::Ready(stream)) => { - self.state = ClientState::Connected(stream); - Ok(Async::Ready(Some(Event::Online))) - }, - Ok(Async::NotReady) => { - self.state = ClientState::Connecting(connect); - Ok(Async::NotReady) - }, - Err(e) => - Err(e), + ClientState::Invalid => Err(Error::InvalidState), + ClientState::Disconnected => Ok(Async::Ready(None)), + ClientState::Connecting(mut connect) => match connect.poll() { + Ok(Async::Ready(stream)) => { + self.state = ClientState::Connected(stream); + Ok(Async::Ready(Some(Event::Online))) + } + Ok(Async::NotReady) => { + self.state = ClientState::Connecting(connect); + Ok(Async::NotReady) } + Err(e) => Err(e), }, ClientState::Connected(mut stream) => { // Poll sink match stream.poll_complete() { Ok(Async::NotReady) => (), Ok(Async::Ready(())) => (), - Err(e) => - return Err(e)?, + Err(e) => return Err(e)?, }; // Poll stream @@ -151,20 +156,18 @@ impl Stream for Client { // EOF self.state = ClientState::Disconnected; Ok(Async::Ready(Some(Event::Disconnected))) - }, + } Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { self.state = ClientState::Connected(stream); Ok(Async::Ready(Some(Event::Stanza(stanza)))) - }, - Ok(Async::NotReady) | - Ok(Async::Ready(_)) => { + } + Ok(Async::NotReady) | Ok(Async::Ready(_)) => { self.state = ClientState::Connected(stream); Ok(Async::NotReady) - }, - Err(e) => - Err(e)?, + } + Err(e) => Err(e)?, } - }, + } } } } @@ -175,30 +178,23 @@ impl Sink for Client { fn start_send(&mut self, item: Self::SinkItem) -> StartSend { match self.state { - ClientState::Connected(ref mut stream) => - match stream.start_send(Packet::Stanza(item)) { - Ok(AsyncSink::NotReady(Packet::Stanza(stanza))) => - Ok(AsyncSink::NotReady(stanza)), - Ok(AsyncSink::NotReady(_)) => - panic!("Client.start_send with stanza but got something else back"), - Ok(AsyncSink::Ready) => { - Ok(AsyncSink::Ready) - }, - Err(e) => - Err(e)?, - }, - _ => - Ok(AsyncSink::NotReady(item)), + ClientState::Connected(ref mut stream) => match stream.start_send(Packet::Stanza(item)) + { + Ok(AsyncSink::NotReady(Packet::Stanza(stanza))) => Ok(AsyncSink::NotReady(stanza)), + Ok(AsyncSink::NotReady(_)) => { + panic!("Client.start_send with stanza but got something else back") + } + Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready), + Err(e) => Err(e)?, + }, + _ => Ok(AsyncSink::NotReady(item)), } } fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { match self.state { - ClientState::Connected(ref mut stream) => - stream.poll_complete() - .map_err(|e| e.into()), - _ => - Ok(Async::Ready(())), + ClientState::Connected(ref mut stream) => stream.poll_complete().map_err(|e| e.into()), + _ => Ok(Async::Ready(())), } } } diff --git a/src/component/auth.rs b/src/component/auth.rs index ce55b3a9b2b9f365427721282872466beb5558ac..92ae0903cd8ab4aa93a0658cd8a7486f521878f8 100644 --- a/src/component/auth.rs +++ b/src/component/auth.rs @@ -1,11 +1,11 @@ +use futures::{sink, Async, Future, Poll, Stream}; use std::mem::replace; -use futures::{Future, Poll, Async, sink, Stream}; use tokio_io::{AsyncRead, AsyncWrite}; use xmpp_parsers::component::Handshake; use crate::xmpp_codec::Packet; use crate::xmpp_stream::XMPPStream; -use crate::{Error, AuthError}; +use crate::{AuthError, Error}; const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; @@ -29,7 +29,7 @@ impl ComponentAuth { }; this.send( stream, - Handshake::from_password_and_stream_id(&password, &sid) + Handshake::from_password_and_stream_id(&password, &sid), ); Ok(this) } @@ -50,45 +50,40 @@ impl Future for ComponentAuth { let state = replace(&mut self.state, ComponentAuthState::Invalid); match state { - ComponentAuthState::WaitSend(mut send) => - match send.poll() { - Ok(Async::Ready(stream)) => { - self.state = ComponentAuthState::WaitRecv(stream); - self.poll() - }, - Ok(Async::NotReady) => { - self.state = ComponentAuthState::WaitSend(send); - Ok(Async::NotReady) - }, - Err(e) => - Err(e)? - }, - ComponentAuthState::WaitRecv(mut stream) => - match stream.poll() { - Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.is("handshake", NS_JABBER_COMPONENT_ACCEPT) => - { - self.state = ComponentAuthState::Invalid; - Ok(Async::Ready(stream)) - }, - Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.is("error", "http://etherx.jabber.org/streams") => - { - Err(AuthError::ComponentFail.into()) - }, - Ok(Async::Ready(event)) => { - println!("ComponentAuth ignore {:?}", event); - Ok(Async::NotReady) - }, - Ok(_) => { - self.state = ComponentAuthState::WaitRecv(stream); - Ok(Async::NotReady) - }, - Err(e) => - Err(e)? - }, - ComponentAuthState::Invalid => - unreachable!(), + ComponentAuthState::WaitSend(mut send) => match send.poll() { + Ok(Async::Ready(stream)) => { + self.state = ComponentAuthState::WaitRecv(stream); + self.poll() + } + Ok(Async::NotReady) => { + self.state = ComponentAuthState::WaitSend(send); + Ok(Async::NotReady) + } + Err(e) => Err(e)?, + }, + ComponentAuthState::WaitRecv(mut stream) => match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.is("handshake", NS_JABBER_COMPONENT_ACCEPT) => + { + self.state = ComponentAuthState::Invalid; + Ok(Async::Ready(stream)) + } + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.is("error", "http://etherx.jabber.org/streams") => + { + Err(AuthError::ComponentFail.into()) + } + Ok(Async::Ready(event)) => { + println!("ComponentAuth ignore {:?}", event); + Ok(Async::NotReady) + } + Ok(_) => { + self.state = ComponentAuthState::WaitRecv(stream); + Ok(Async::NotReady) + } + Err(e) => Err(e)?, + }, + ComponentAuthState::Invalid => unreachable!(), } } } diff --git a/src/component/mod.rs b/src/component/mod.rs index ce55e7ecdba1c7548c62c2b1e3c15fe4e4197c9e..aac66ab4e5e64e40eac3a5233b9cf7cc4831bf0e 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -1,18 +1,18 @@ //! Components in XMPP are services/gateways that are logged into an //! XMPP server under a JID consisting of just a domain name. They are //! allowed to use any user and resource identifiers in their stanzas. +use futures::{done, Async, AsyncSink, Future, Poll, Sink, StartSend, Stream}; +use jid::{Jid, JidParseError}; +use minidom::Element; use std::mem::replace; use std::str::FromStr; use tokio::net::TcpStream; use tokio_io::{AsyncRead, AsyncWrite}; -use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink, done}; -use minidom::Element; -use jid::{Jid, JidParseError}; +use super::event::Event; +use super::happy_eyeballs::Connecter; use super::xmpp_codec::Packet; use super::xmpp_stream; -use super::happy_eyeballs::Connecter; -use super::event::Event; use super::Error; mod auth; @@ -31,7 +31,7 @@ const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; enum ComponentState { Invalid, Disconnected, - Connecting(Box>), + Connecting(Box>), Connected(XMPPStream), } @@ -50,19 +50,30 @@ impl Component { }) } - fn make_connect(jid: Jid, password: String, server: &str, port: u16) -> impl Future { + fn make_connect( + jid: Jid, + password: String, + server: &str, + port: u16, + ) -> impl Future { let jid1 = jid.clone(); let password = password; done(Connecter::from_lookup(server, None, port)) .flatten() .and_then(move |tcp_stream| { - xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_COMPONENT_ACCEPT.to_owned()) - }).and_then(move |xmpp_stream| { - Self::auth(xmpp_stream, password).expect("auth") + xmpp_stream::XMPPStream::start( + tcp_stream, + jid1, + NS_JABBER_COMPONENT_ACCEPT.to_owned(), + ) }) + .and_then(move |xmpp_stream| Self::auth(xmpp_stream, password).expect("auth")) } - fn auth(stream: xmpp_stream::XMPPStream, password: String) -> Result, Error> { + fn auth( + stream: xmpp_stream::XMPPStream, + password: String, + ) -> Result, Error> { ComponentAuth::new(stream, password) } } @@ -75,31 +86,25 @@ impl Stream for Component { let state = replace(&mut self.state, ComponentState::Invalid); match state { - ComponentState::Invalid => - Err(Error::InvalidState), - ComponentState::Disconnected => - Ok(Async::Ready(None)), - ComponentState::Connecting(mut connect) => { - match connect.poll() { - Ok(Async::Ready(stream)) => { - self.state = ComponentState::Connected(stream); - Ok(Async::Ready(Some(Event::Online))) - }, - Ok(Async::NotReady) => { - self.state = ComponentState::Connecting(connect); - Ok(Async::NotReady) - }, - Err(e) => - Err(e), + ComponentState::Invalid => Err(Error::InvalidState), + ComponentState::Disconnected => Ok(Async::Ready(None)), + ComponentState::Connecting(mut connect) => match connect.poll() { + Ok(Async::Ready(stream)) => { + self.state = ComponentState::Connected(stream); + Ok(Async::Ready(Some(Event::Online))) + } + Ok(Async::NotReady) => { + self.state = ComponentState::Connecting(connect); + Ok(Async::NotReady) } + Err(e) => Err(e), }, ComponentState::Connected(mut stream) => { // Poll sink match stream.poll_complete() { Ok(Async::NotReady) => (), Ok(Async::Ready(())) => (), - Err(e) => - return Err(e)?, + Err(e) => return Err(e)?, }; // Poll stream @@ -107,24 +112,23 @@ impl Stream for Component { Ok(Async::NotReady) => { self.state = ComponentState::Connected(stream); Ok(Async::NotReady) - }, + } Ok(Async::Ready(None)) => { // EOF self.state = ComponentState::Disconnected; Ok(Async::Ready(Some(Event::Disconnected))) - }, + } Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { self.state = ComponentState::Connected(stream); Ok(Async::Ready(Some(Event::Stanza(stanza)))) - }, + } Ok(Async::Ready(_)) => { self.state = ComponentState::Connected(stream); Ok(Async::NotReady) - }, - Err(e) => - Err(e)?, + } + Err(e) => Err(e)?, } - }, + } } } } @@ -135,30 +139,26 @@ impl Sink for Component { fn start_send(&mut self, item: Self::SinkItem) -> StartSend { match self.state { - ComponentState::Connected(ref mut stream) => - match stream.start_send(Packet::Stanza(item)) { - Ok(AsyncSink::NotReady(Packet::Stanza(stanza))) => - Ok(AsyncSink::NotReady(stanza)), - Ok(AsyncSink::NotReady(_)) => - panic!("Component.start_send with stanza but got something else back"), - Ok(AsyncSink::Ready) => { - Ok(AsyncSink::Ready) - }, - Err(e) => - Err(e)?, - }, - _ => - Ok(AsyncSink::NotReady(item)), + ComponentState::Connected(ref mut stream) => match stream + .start_send(Packet::Stanza(item)) + { + Ok(AsyncSink::NotReady(Packet::Stanza(stanza))) => Ok(AsyncSink::NotReady(stanza)), + Ok(AsyncSink::NotReady(_)) => { + panic!("Component.start_send with stanza but got something else back") + } + Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready), + Err(e) => Err(e)?, + }, + _ => Ok(AsyncSink::NotReady(item)), } } fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { match &mut self.state { - &mut ComponentState::Connected(ref mut stream) => - stream.poll_complete() - .map_err(|e| e.into()), - _ => - Ok(Async::Ready(())), + &mut ComponentState::Connected(ref mut stream) => { + stream.poll_complete().map_err(|e| e.into()) + } + _ => Ok(Async::Ready(())), } } } diff --git a/src/error.rs b/src/error.rs index 43f5af1480b34de631a5a9dcd3679bb87d305611..26d794495ebff0562e894f022a955b1ea44e32c0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,11 +1,11 @@ -use std::io::Error as IoError; -use std::error::Error as StdError; -use std::str::Utf8Error; +use native_tls::Error as TlsError; use std::borrow::Cow; +use std::error::Error as StdError; use std::fmt; -use native_tls::Error as TlsError; -use trust_dns_resolver::error::ResolveError; +use std::io::Error as IoError; +use std::str::Utf8Error; use trust_dns_proto::error::ProtoError; +use trust_dns_resolver::error::ResolveError; use xmpp_parsers::error::Error as ParsersError; use xmpp_parsers::sasl::DefinedCondition as SaslDefinedCondition; diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 451f9c73499a9e6137c3f402b73de41fd14fd551..19dbe4939d4e7e51d84fa8843646ddd10cef5eac 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -1,18 +1,18 @@ -use std::mem; -use std::io::Error as IoError; -use std::net::SocketAddr; +use crate::{ConnecterError, Error}; +use futures::{Async, Future, Poll}; +use std::cell::RefCell; use std::collections::BTreeMap; use std::collections::VecDeque; -use std::cell::RefCell; -use futures::{Future, Poll, Async}; -use tokio::net::TcpStream; +use std::io::Error as IoError; +use std::mem; +use std::net::SocketAddr; use tokio::net::tcp::ConnectFuture; -use trust_dns_resolver::{IntoName, Name, ResolverFuture, error::ResolveError}; +use tokio::net::TcpStream; +use trust_dns_resolver::config::LookupIpStrategy; use trust_dns_resolver::lookup::SrvLookupFuture; use trust_dns_resolver::lookup_ip::LookupIpFuture; use trust_dns_resolver::system_conf; -use trust_dns_resolver::config::LookupIpStrategy; -use crate::{Error, ConnecterError}; +use trust_dns_resolver::{error::ResolveError, IntoName, Name, ResolverFuture}; enum State { AwaitResolver(Box + Send>), @@ -31,23 +31,26 @@ pub struct Connecter { error: Option, } -fn resolver_future() -> Result + Send>, IoError> { +fn resolver_future( +) -> Result + Send>, IoError> { let (conf, mut opts) = system_conf::read_system_conf()?; opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6; Ok(ResolverFuture::new(conf, opts)) } impl Connecter { - pub fn from_lookup(domain: &str, srv: Option<&str>, fallback_port: u16) -> Result { + pub fn from_lookup( + domain: &str, + srv: Option<&str>, + fallback_port: u16, + ) -> Result { if let Ok(ip) = domain.parse() { // use specified IP address, not domain name, skip the whole dns part - let connect = - RefCell::new(TcpStream::connect(&SocketAddr::new(ip, fallback_port))); + let connect = RefCell::new(TcpStream::connect(&SocketAddr::new(ip, fallback_port))); return Ok(Connecter { fallback_port, srv_domain: None, - domain: "nohost".into_name() - .map_err(ConnecterError::Dns)?, + domain: "nohost".into_name().map_err(ConnecterError::Dns)?, state: State::Connecting(None, vec![connect]), targets: VecDeque::new(), error: None, @@ -56,20 +59,18 @@ impl Connecter { let state = State::AwaitResolver(resolver_future()?); let srv_domain = match srv { - Some(srv) => - Some(format!("{}.{}.", srv, domain) - .into_name() - .map_err(ConnecterError::Dns)? - ), - None => - None, + Some(srv) => Some( + format!("{}.{}.", srv, domain) + .into_name() + .map_err(ConnecterError::Dns)?, + ), + None => None, }; Ok(Connecter { fallback_port, srv_domain, - domain: domain.into_name() - .map_err(ConnecterError::Dns)?, + domain: domain.into_name().map_err(ConnecterError::Dns)?, state, targets: VecDeque::new(), error: None, @@ -97,8 +98,8 @@ impl Future for Connecter { self.state = State::ResolveSrv(resolver, srv_lookup); } None => { - self.targets = - [(self.domain.clone(), self.fallback_port)].into_iter() + self.targets = [(self.domain.clone(), self.fallback_port)] + .into_iter() .cloned() .collect(); self.state = State::Connecting(Some(resolver), vec![]); @@ -115,22 +116,19 @@ impl Future for Connecter { Ok(Async::NotReady) } Ok(Async::Ready(srv_result)) => { - let srv_map: BTreeMap<_, _> = - srv_result.iter() + let srv_map: BTreeMap<_, _> = srv_result + .iter() .map(|srv| (srv.priority(), (srv.target().clone(), srv.port()))) .collect(); - let targets = - srv_map.into_iter() - .map(|(_, tp)| tp) - .collect(); + let targets = srv_map.into_iter().map(|(_, tp)| tp).collect(); self.targets = targets; self.state = State::Connecting(Some(resolver), vec![]); self.poll() } Err(_) => { // ignore, fallback - self.targets = - [(self.domain.clone(), self.fallback_port)].into_iter() + self.targets = [(self.domain.clone(), self.fallback_port)] + .into_iter() .cloned() .collect(); self.state = State::Connecting(Some(resolver), vec![]); @@ -147,36 +145,31 @@ impl Future for Connecter { self.poll() } else if connects.len() > 0 { let mut success = None; - connects.retain(|connect| { - match connect.borrow_mut().poll() { - Ok(Async::NotReady) => true, - Ok(Async::Ready(connection)) => { - success = Some(connection); - false + connects.retain(|connect| match connect.borrow_mut().poll() { + Ok(Async::NotReady) => true, + Ok(Async::Ready(connection)) => { + success = Some(connection); + false + } + Err(e) => { + if self.error.is_none() { + self.error = Some(e.into()); } - Err(e) => { - if self.error.is_none() { - self.error = Some(e.into()); - } - false - }, + false } }); match success { - Some(connection) => - Ok(Async::Ready(connection)), + Some(connection) => Ok(Async::Ready(connection)), None => { self.state = State::Connecting(resolver, connects); Ok(Async::NotReady) - }, + } } } else { // All targets tried match self.error.take() { - None => - Err(ConnecterError::AllFailed.into()), - Some(e) => - Err(e), + None => Err(ConnecterError::AllFailed.into()), + Some(e) => Err(e), } } } @@ -187,8 +180,8 @@ impl Future for Connecter { Ok(Async::NotReady) } Ok(Async::Ready(ip_result)) => { - let connects = - ip_result.iter() + let connects = ip_result + .iter() .map(|ip| RefCell::new(TcpStream::connect(&SocketAddr::new(ip, port)))) .collect(); self.state = State::Connecting(Some(resolver), connects); @@ -204,8 +197,7 @@ impl Future for Connecter { } } } - _ => panic!("") + _ => panic!(""), } } } - diff --git a/src/lib.rs b/src/lib.rs index b1942119211f44ce2b68f0d30b05460875077cd0..39876b1917baaed46289aa35a5fd7e5d7997d36a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,17 +5,17 @@ #[macro_use] extern crate derive_error; +mod starttls; +mod stream_start; pub mod xmpp_codec; pub mod xmpp_stream; -mod stream_start; -mod starttls; pub use crate::starttls::StartTlsClient; -mod happy_eyeballs; mod event; +mod happy_eyeballs; pub use crate::event::Event; mod client; pub use crate::client::Client; mod component; pub use crate::component::Component; mod error; -pub use crate::error::{Error, ProtocolError, AuthError, ConnecterError, ParseError, ParserError}; +pub use crate::error::{AuthError, ConnecterError, Error, ParseError, ParserError, ProtocolError}; diff --git a/src/starttls.rs b/src/starttls.rs index a7f7fd392bc19c80f1be419441aa5d6e0e566697..e48d1824a1220f98b70b015098cc9c93ec9a9b0a 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -1,12 +1,12 @@ -use std::mem::replace; -use futures::{Future, Sink, Poll, Async}; -use futures::stream::Stream; use futures::sink; -use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_tls::{TlsStream, TlsConnector, Connect}; -use native_tls::TlsConnector as NativeTlsConnector; -use minidom::Element; +use futures::stream::Stream; +use futures::{Async, Future, Poll, Sink}; use jid::Jid; +use minidom::Element; +use native_tls::TlsConnector as NativeTlsConnector; +use std::mem::replace; +use tokio_io::{AsyncRead, AsyncWrite}; +use tokio_tls::{Connect, TlsConnector, TlsStream}; use crate::xmpp_codec::Packet; use crate::xmpp_stream::XMPPStream; @@ -15,7 +15,6 @@ use crate::Error; /// XMPP TLS XML namespace pub const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; - /// XMPP stream that switches to TLS if available in received features pub struct StartTlsClient { state: StartTlsClientState, @@ -34,9 +33,7 @@ impl StartTlsClient { pub fn from_stream(xmpp_stream: XMPPStream) -> Self { let jid = xmpp_stream.jid.clone(); - let nonza = Element::builder("starttls") - .ns(NS_XMPP_TLS) - .build(); + let nonza = Element::builder("starttls").ns(NS_XMPP_TLS).build(); let packet = Packet::Stanza(nonza); let send = xmpp_stream.send(packet); @@ -56,51 +53,56 @@ impl Future for StartTlsClient { let mut retry = false; let (new_state, result) = match old_state { - StartTlsClientState::SendStartTls(mut send) => - match send.poll() { - Ok(Async::Ready(xmpp_stream)) => { - let new_state = StartTlsClientState::AwaitProceed(xmpp_stream); - retry = true; - (new_state, Ok(Async::NotReady)) - }, - Ok(Async::NotReady) => - (StartTlsClientState::SendStartTls(send), Ok(Async::NotReady)), - Err(e) => - (StartTlsClientState::SendStartTls(send), Err(e.into())), - }, - StartTlsClientState::AwaitProceed(mut xmpp_stream) => - match xmpp_stream.poll() { - Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) - if stanza.name() == "proceed" => - { - let stream = xmpp_stream.stream.into_inner(); - let connect = TlsConnector::from(NativeTlsConnector::builder() - .build().unwrap()) + StartTlsClientState::SendStartTls(mut send) => match send.poll() { + Ok(Async::Ready(xmpp_stream)) => { + let new_state = StartTlsClientState::AwaitProceed(xmpp_stream); + retry = true; + (new_state, Ok(Async::NotReady)) + } + Ok(Async::NotReady) => { + (StartTlsClientState::SendStartTls(send), Ok(Async::NotReady)) + } + Err(e) => (StartTlsClientState::SendStartTls(send), Err(e.into())), + }, + StartTlsClientState::AwaitProceed(mut xmpp_stream) => match xmpp_stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.name() == "proceed" => + { + let stream = xmpp_stream.stream.into_inner(); + let connect = + TlsConnector::from(NativeTlsConnector::builder().build().unwrap()) .connect(&self.jid.domain, stream); - let new_state = StartTlsClientState::StartingTls(connect); - retry = true; - (new_state, Ok(Async::NotReady)) - }, - Ok(Async::Ready(value)) => { - println!("StartTlsClient ignore {:?}", value); - (StartTlsClientState::AwaitProceed(xmpp_stream), Ok(Async::NotReady)) - }, - Ok(_) => - (StartTlsClientState::AwaitProceed(xmpp_stream), Ok(Async::NotReady)), - Err(e) => - (StartTlsClientState::AwaitProceed(xmpp_stream), Err(Error::Protocol(e.into()))), - }, - StartTlsClientState::StartingTls(mut connect) => - match connect.poll() { - Ok(Async::Ready(tls_stream)) => - (StartTlsClientState::Invalid, Ok(Async::Ready(tls_stream))), - Ok(Async::NotReady) => - (StartTlsClientState::StartingTls(connect), Ok(Async::NotReady)), - Err(e) => - (StartTlsClientState::Invalid, Err(e.into())), - }, - StartTlsClientState::Invalid => - unreachable!(), + let new_state = StartTlsClientState::StartingTls(connect); + retry = true; + (new_state, Ok(Async::NotReady)) + } + Ok(Async::Ready(value)) => { + println!("StartTlsClient ignore {:?}", value); + ( + StartTlsClientState::AwaitProceed(xmpp_stream), + Ok(Async::NotReady), + ) + } + Ok(_) => ( + StartTlsClientState::AwaitProceed(xmpp_stream), + Ok(Async::NotReady), + ), + Err(e) => ( + StartTlsClientState::AwaitProceed(xmpp_stream), + Err(Error::Protocol(e.into())), + ), + }, + StartTlsClientState::StartingTls(mut connect) => match connect.poll() { + Ok(Async::Ready(tls_stream)) => { + (StartTlsClientState::Invalid, Ok(Async::Ready(tls_stream))) + } + Ok(Async::NotReady) => ( + StartTlsClientState::StartingTls(connect), + Ok(Async::NotReady), + ), + Err(e) => (StartTlsClientState::Invalid, Err(e.into())), + }, + StartTlsClientState::Invalid => unreachable!(), }; self.state = new_state; diff --git a/src/stream_start.rs b/src/stream_start.rs index 9aef117da7a622d2419cecd03256d4d9f31908bf..0a5945b7bda5e6d8dae908f23d20c59b3b5bb4e6 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -1,11 +1,11 @@ -use std::mem::replace; -use futures::{Future, Async, Poll, Stream, sink, Sink}; -use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_codec::Framed; +use futures::{sink, Async, Future, Poll, Sink, Stream}; use jid::Jid; use minidom::Element; +use std::mem::replace; +use tokio_codec::Framed; +use tokio_io::{AsyncRead, AsyncWrite}; -use crate::xmpp_codec::{XMPPCodec, Packet}; +use crate::xmpp_codec::{Packet, XMPPCodec}; use crate::xmpp_stream::XMPPStream; use crate::{Error, ProtocolError}; @@ -26,11 +26,15 @@ enum StreamStartState { impl StreamStart { pub fn from_stream(stream: Framed, jid: Jid, ns: String) -> Self { - let attrs = [("to".to_owned(), jid.domain.clone()), - ("version".to_owned(), "1.0".to_owned()), - ("xmlns".to_owned(), ns.clone()), - ("xmlns:stream".to_owned(), NS_XMPP_STREAM.to_owned()), - ].iter().cloned().collect(); + let attrs = [ + ("to".to_owned(), jid.domain.clone()), + ("version".to_owned(), "1.0".to_owned()), + ("xmlns".to_owned(), ns.clone()), + ("xmlns:stream".to_owned(), NS_XMPP_STREAM.to_owned()), + ] + .iter() + .cloned() + .collect(); let send = stream.send(Packet::StreamStart(attrs)); StreamStart { @@ -50,59 +54,66 @@ impl Future for StreamStart { let mut retry = false; let (new_state, result) = match old_state { - StreamStartState::SendStart(mut send) => - match send.poll() { - Ok(Async::Ready(stream)) => { + StreamStartState::SendStart(mut send) => match send.poll() { + Ok(Async::Ready(stream)) => { + retry = true; + (StreamStartState::RecvStart(stream), Ok(Async::NotReady)) + } + Ok(Async::NotReady) => (StreamStartState::SendStart(send), Ok(Async::NotReady)), + Err(e) => (StreamStartState::Invalid, Err(e.into())), + }, + StreamStartState::RecvStart(mut stream) => match stream.poll() { + Ok(Async::Ready(Some(Packet::StreamStart(stream_attrs)))) => { + let stream_ns = stream_attrs + .get("xmlns") + .ok_or(ProtocolError::NoStreamNamespace)? + .clone(); + if self.ns == "jabber:client" { retry = true; - (StreamStartState::RecvStart(stream), Ok(Async::NotReady)) - }, - Ok(Async::NotReady) => - (StreamStartState::SendStart(send), Ok(Async::NotReady)), - Err(e) => - (StreamStartState::Invalid, Err(e.into())), - }, - StreamStartState::RecvStart(mut stream) => - match stream.poll() { - Ok(Async::Ready(Some(Packet::StreamStart(stream_attrs)))) => { - let stream_ns = stream_attrs.get("xmlns") - .ok_or(ProtocolError::NoStreamNamespace)? + // TODO: skip RecvFeatures for version < 1.0 + ( + StreamStartState::RecvFeatures(stream, stream_ns), + Ok(Async::NotReady), + ) + } else { + let id = stream_attrs + .get("id") + .ok_or(ProtocolError::NoStreamId)? .clone(); - if self.ns == "jabber:client" { - retry = true; - // TODO: skip RecvFeatures for version < 1.0 - (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)) - } else { - let id = stream_attrs.get("id") - .ok_or(ProtocolError::NoStreamId)? - .clone(); - // FIXME: huge hack, shouldn’t be an element! - let stream = XMPPStream::new(self.jid.clone(), stream, self.ns.clone(), Element::builder(id).build()); - (StreamStartState::Invalid, Ok(Async::Ready(stream))) - } - }, - Ok(Async::Ready(_)) => - return Err(ProtocolError::InvalidToken.into()), - Ok(Async::NotReady) => - (StreamStartState::RecvStart(stream), Ok(Async::NotReady)), - Err(e) => - return Err(ProtocolError::from(e).into()), - }, - StreamStartState::RecvFeatures(mut stream, stream_ns) => - match stream.poll() { - Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => - if stanza.is("features", NS_XMPP_STREAM) { - let stream = XMPPStream::new(self.jid.clone(), stream, self.ns.clone(), stanza); - (StreamStartState::Invalid, Ok(Async::Ready(stream))) - } else { - (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)) - }, - Ok(Async::Ready(_)) | Ok(Async::NotReady) => - (StreamStartState::RecvFeatures(stream, stream_ns), Ok(Async::NotReady)), - Err(e) => - return Err(ProtocolError::from(e).into()), - }, - StreamStartState::Invalid => - unreachable!(), + // FIXME: huge hack, shouldn’t be an element! + let stream = XMPPStream::new( + self.jid.clone(), + stream, + self.ns.clone(), + Element::builder(id).build(), + ); + (StreamStartState::Invalid, Ok(Async::Ready(stream))) + } + } + Ok(Async::Ready(_)) => return Err(ProtocolError::InvalidToken.into()), + Ok(Async::NotReady) => (StreamStartState::RecvStart(stream), Ok(Async::NotReady)), + Err(e) => return Err(ProtocolError::from(e).into()), + }, + StreamStartState::RecvFeatures(mut stream, stream_ns) => match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { + if stanza.is("features", NS_XMPP_STREAM) { + let stream = + XMPPStream::new(self.jid.clone(), stream, self.ns.clone(), stanza); + (StreamStartState::Invalid, Ok(Async::Ready(stream))) + } else { + ( + StreamStartState::RecvFeatures(stream, stream_ns), + Ok(Async::NotReady), + ) + } + } + Ok(Async::Ready(_)) | Ok(Async::NotReady) => ( + StreamStartState::RecvFeatures(stream, stream_ns), + Ok(Async::NotReady), + ), + Err(e) => return Err(ProtocolError::from(e).into()), + }, + StreamStartState::Invalid => unreachable!(), }; self.state = new_state; diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 4088552c9c9fea4fb4b0690b22cccc54fb5f2b02..e8c4e0538e1b47d308e947bc25d548e9a1ec83b6 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -1,22 +1,22 @@ //! XML stream parser for XMPP +use crate::{ParseError, ParserError}; +use bytes::{BufMut, BytesMut}; +use minidom::Element; +use quick_xml::Writer as EventWriter; use std; +use std::cell::RefCell; +use std::collections::vec_deque::VecDeque; +use std::collections::HashMap; use std::default::Default; +use std::fmt::Write; +use std::io; use std::iter::FromIterator; -use std::cell::RefCell; use std::rc::Rc; -use std::fmt::Write; use std::str::from_utf8; -use std::io; -use std::collections::HashMap; -use std::collections::vec_deque::VecDeque; -use tokio_codec::{Encoder, Decoder}; -use minidom::Element; -use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind}; +use tokio_codec::{Decoder, Encoder}; use xml5ever::interface::Attribute; -use bytes::{BytesMut, BufMut}; -use quick_xml::Writer as EventWriter; -use crate::{ParserError, ParseError}; +use xml5ever::tokenizer::{Tag, TagKind, Token, TokenSink, XmlTokenizer}; /// Anything that can be sent or received on an XMPP/XML stream #[derive(Debug)] @@ -72,17 +72,21 @@ impl ParserSink { fn handle_start_tag(&mut self, tag: Tag) { let mut nss = HashMap::new(); - let is_prefix_xmlns = |attr: &Attribute| attr.name.prefix.as_ref() - .map(|prefix| prefix.eq_str_ignore_ascii_case("xmlns")) - .unwrap_or(false); + let is_prefix_xmlns = |attr: &Attribute| { + attr.name + .prefix + .as_ref() + .map(|prefix| prefix.eq_str_ignore_ascii_case("xmlns")) + .unwrap_or(false) + }; for attr in &tag.attrs { match attr.name.local.as_ref() { "xmlns" => { nss.insert(None, attr.value.as_ref().to_owned()); - }, + } prefix if is_prefix_xmlns(attr) => { - nss.insert(Some(prefix.to_owned()), attr.value.as_ref().to_owned()); - }, + nss.insert(Some(prefix.to_owned()), attr.value.as_ref().to_owned()); + } _ => (), } } @@ -90,10 +94,9 @@ impl ParserSink { let el = { let mut el_builder = Element::builder(tag.name.local.as_ref()); - if let Some(el_ns) = self.lookup_ns( - &tag.name.prefix.map(|prefix| - prefix.as_ref().to_owned()) - ) { + if let Some(el_ns) = + self.lookup_ns(&tag.name.prefix.map(|prefix| prefix.as_ref().to_owned())) + { el_builder = el_builder.ns(el_ns); } for attr in &tag.attrs { @@ -101,21 +104,20 @@ impl ParserSink { "xmlns" => (), _ if is_prefix_xmlns(attr) => (), _ => { - el_builder = el_builder.attr( - attr.name.local.as_ref(), - attr.value.as_ref() - ); - }, + el_builder = el_builder.attr(attr.name.local.as_ref(), attr.value.as_ref()); + } } } el_builder.build() }; if self.stack.is_empty() { - let attrs = HashMap::from_iter( - tag.attrs.iter() - .map(|attr| (attr.name.local.as_ref().to_owned(), attr.value.as_ref().to_owned())) - ); + let attrs = HashMap::from_iter(tag.attrs.iter().map(|attr| { + ( + attr.name.local.as_ref().to_owned(), + attr.value.as_ref().to_owned(), + ) + })); self.push_queue(Packet::StreamStart(attrs)); } @@ -128,15 +130,13 @@ impl ParserSink { match self.stack.len() { // - 0 => - self.push_queue(Packet::StreamEnd), + 0 => self.push_queue(Packet::StreamEnd), // - 1 => - self.push_queue(Packet::Stanza(el)), + 1 => self.push_queue(Packet::Stanza(el)), len => { let parent = &mut self.stack[len - 1]; parent.append_child(el); - }, + } } } } @@ -145,32 +145,26 @@ impl TokenSink for ParserSink { fn process_token(&mut self, token: Token) { match token { Token::TagToken(tag) => match tag.kind { - TagKind::StartTag => - self.handle_start_tag(tag), - TagKind::EndTag => - self.handle_end_tag(), + TagKind::StartTag => self.handle_start_tag(tag), + TagKind::EndTag => self.handle_end_tag(), TagKind::EmptyTag => { self.handle_start_tag(tag); self.handle_end_tag(); - }, - TagKind::ShortTag => - self.push_queue_error(ParserError::ShortTag), + } + TagKind::ShortTag => self.push_queue_error(ParserError::ShortTag), }, - Token::CharacterTokens(tendril) => - match self.stack.len() { - 0 | 1 => - self.push_queue(Packet::Text(tendril.into())), - len => { - let el = &mut self.stack[len - 1]; - el.append_text_node(tendril); - }, - }, - Token::EOFToken => - self.push_queue(Packet::StreamEnd), + Token::CharacterTokens(tendril) => match self.stack.len() { + 0 | 1 => self.push_queue(Packet::Text(tendril.into())), + len => { + let el = &mut self.stack[len - 1]; + el.append_text_node(tendril); + } + }, + Token::EOFToken => self.push_queue(Packet::StreamEnd), Token::ParseError(s) => { // println!("ParseError: {:?}", s); self.push_queue_error(ParserError::Parse(ParseError(s))); - }, + } _ => (), } } @@ -219,23 +213,22 @@ impl Decoder for XMPPCodec { type Error = ParserError; fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { - let buf1: Box> = - if ! self.buf.is_empty() && ! buf.is_empty() { - let mut prefix = std::mem::replace(&mut self.buf, vec![]); - prefix.extend_from_slice(buf.take().as_ref()); - Box::new(prefix) - } else { - Box::new(buf.take()) - }; + let buf1: Box> = if !self.buf.is_empty() && !buf.is_empty() { + let mut prefix = std::mem::replace(&mut self.buf, vec![]); + prefix.extend_from_slice(buf.take().as_ref()); + Box::new(prefix) + } else { + Box::new(buf.take()) + }; let buf1 = buf1.as_ref().as_ref(); match from_utf8(buf1) { Ok(s) => { - if ! s.is_empty() { + if !s.is_empty() { // println!("<< {}", s); let tendril = FromIterator::from_iter(s.chars()); self.parser.feed(tendril); } - }, + } // Remedies for truncated utf8 Err(e) if e.valid_up_to() >= buf1.len() - 3 => { // Prepare all the valid data @@ -249,17 +242,16 @@ impl Decoder for XMPPCodec { self.buf.extend_from_slice(&buf1[e.valid_up_to()..]); return result; - }, + } Err(e) => { // println!("error {} at {}/{} in {:?}", e, e.valid_up_to(), buf1.len(), buf1); return Err(ParserError::Utf8(e)); - }, + } } match self.queue.borrow_mut().pop_front() { None => Ok(None), - Some(result) => - result.map(|pkt| Some(pkt)), + Some(result) => result.map(|pkt| Some(pkt)), } } @@ -284,8 +276,7 @@ impl Encoder for XMPPCodec { let mut buf = String::new(); write!(buf, "\n").unwrap(); print!(">> {}", buf); - write!(dst, "{}", buf) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)) - }, + write!(dst, "{}", buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)) + } Packet::Stanza(stanza) => { - stanza.write_to_inner(&mut EventWriter::new(WriteBytes::new(dst))) + stanza + .write_to_inner(&mut EventWriter::new(WriteBytes::new(dst))) .and_then(|_| { // println!(">> {:?}", dst); Ok(()) }) .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, format!("{}", e))) - }, + } Packet::Text(text) => { write_text(&text, dst) .and_then(|_| { @@ -311,9 +302,9 @@ impl Encoder for XMPPCodec { Ok(()) }) .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, format!("{}", e))) - }, + } // TODO: Implement all - _ => Ok(()) + _ => Ok(()), } } } @@ -334,7 +325,7 @@ pub fn escape(input: &str) -> String { '>' => result.push_str(">"), '\'' => result.push_str("'"), '"' => result.push_str("""), - o => result.push(o) + o => result.push(o), } } result @@ -364,7 +355,6 @@ impl<'a> std::io::Write for WriteBytes<'a> { } } - #[cfg(test)] mod tests { use super::*; @@ -405,10 +395,7 @@ mod tests { b.put(r">"); let r = c.decode(&mut b); assert!(match r { - Ok(Some(Packet::Stanza(ref el))) - if el.name() == "test" - && el.text() == "ß" - => true, + Ok(Some(Packet::Stanza(ref el))) if el.name() == "test" && el.text() == "ß" => true, _ => false, }); } @@ -436,10 +423,7 @@ mod tests { b.put(&b"\x9f"[..]); let r = c.decode(&mut b); assert!(match r { - Ok(Some(Packet::Stanza(ref el))) - if el.name() == "test" - && el.text() == "ß" - => true, + Ok(Some(Packet::Stanza(ref el))) if el.name() == "test" && el.text() == "ß" => true, _ => false, }); } @@ -447,8 +431,8 @@ mod tests { /// By default, encode() only get's a BytesMut that has 8kb space reserved. #[test] fn test_large_stanza() { - use std::io::Cursor; use futures::{Future, Sink}; + use std::io::Cursor; use tokio_codec::FramedWrite; let framed = FramedWrite::new(Cursor::new(vec![]), XMPPCodec::new()); let mut text = "".to_owned(); @@ -456,15 +440,12 @@ mod tests { text = text + "A"; } let stanza = Element::builder("message") - .append( - Element::builder("body") - .append(&text) - .build() - ) + .append(Element::builder("body").append(&text).build()) .build(); - let framed = framed.send(Packet::Stanza(stanza)) - .wait() - .expect("send"); - assert_eq!(framed.get_ref().get_ref(), &("".to_owned() + &text + "").as_bytes()); + let framed = framed.send(Packet::Stanza(stanza)).wait().expect("send"); + assert_eq!( + framed.get_ref().get_ref(), + &("".to_owned() + &text + "").as_bytes() + ); } } diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index d8e878d5a2bf52451547808a090889cc2119c3bc..c67cc1efdd6ea8b53e84c784b2e88b4829fb6772 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -1,14 +1,14 @@ //! `XMPPStream` is the common container for all XMPP network connections -use futures::{Poll, Stream, Sink, StartSend}; use futures::sink::Send; -use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_codec::Framed; -use minidom::Element; +use futures::{Poll, Sink, StartSend, Stream}; use jid::Jid; +use minidom::Element; +use tokio_codec::Framed; +use tokio_io::{AsyncRead, AsyncWrite}; -use crate::xmpp_codec::{XMPPCodec, Packet}; use crate::stream_start::StreamStart; +use crate::xmpp_codec::{Packet, XMPPCodec}; /// namespace pub const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; @@ -30,11 +30,18 @@ pub struct XMPPStream { impl XMPPStream { /// Constructor - pub fn new(jid: Jid, - stream: Framed, - ns: String, - stream_features: Element) -> Self { - XMPPStream { jid, stream, stream_features, ns } + pub fn new( + jid: Jid, + stream: Framed, + ns: String, + stream_features: Element, + ) -> Self { + XMPPStream { + jid, + stream, + stream_features, + ns, + } } /// Send a `` start tag From cf0cfda6b50ed4a7727223c8ad46a858943f277f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Dec 2018 16:46:36 +0100 Subject: [PATCH 0768/1020] Link .hgignore to .gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 120000 .gitignore diff --git a/.gitignore b/.gitignore new file mode 120000 index 0000000000000000000000000000000000000000..913762c1ff668d96df2c6513e091ff77fbb4277c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.hgignore \ No newline at end of file From f6593aa74b9a0ff05990f0225cf0e7b91013e786 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Dec 2018 16:56:15 +0100 Subject: [PATCH 0769/1020] Setup CI for GitLab. --- .gitlab-ci.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..0b4e77f06d233074e4efced741958cc6a524498c --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,16 @@ +stages: + - build + +rust-latest: + stage: build + image: rust:latest + script: + - cargo build --verbose + - cargo test --verbose + +rust-nightly: + stage: build + image: rustlang/rust:nightly + script: + - cargo build --verbose + - cargo test --verbose From c7132493cf199099e0d32fdd4f1bec3be4af3566 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 20 Dec 2018 17:30:32 +0100 Subject: [PATCH 0770/1020] Cargo: update paths for move to gitlab.com --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 08ff17c5cb6fd35a4aca21d6e9eb933530257493..b6c86df71ea6a722313cfe86ea8378b44915c0ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ version = "0.2.0" authors = ["Astro ", "Emmanuel Gil Peyrot ", "pep ", "O01eg "] description = "Asynchronous XMPP for Rust with tokio" license = "MPL-2.0" -homepage = "https://github.com/astro/tokio-xmpp" -repository = "https://github.com/astro/tokio-xmpp" +homepage = "https://gitlab.com/xmpp-rs/tokio-xmpp" +repository = "https://gitlab.com/xmpp-rs/tokio-xmpp" documentation = "https://docs.rs/tokio-xmpp" categories = ["asynchronous", "network-programming"] keywords = ["xmpp", "tokio"] From bbadf75c010bcd0395ccad4ad2eb419c46ed8428 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 20 Dec 2018 17:35:05 +0100 Subject: [PATCH 0771/1020] move from travis-ci to gitlab-ci --- .gitlab-ci.yml | 14 ++++++++++++++ .travis.yml | 8 -------- 2 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 .gitlab-ci.yml delete mode 100644 .travis.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..ae087bde44b0e6eb8078985f13099bf76e212345 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,14 @@ +stages: + - build +rust-latest: + stage: build + image: rust:latest + script: + - cargo build --verbose + - cargo test --verbose +rust-nightly: + stage: build + image: rustlang/rust:nightly + script: + - cargo build --verbose + - cargo test --verbose diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9fdb54bc1da3ce5ba281caa0d4b8174685f26644..0000000000000000000000000000000000000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -sudo: false - -language: rust - -rust: - - stable - - beta - - nightly From 9c8da4a06329d72cd109f13bc5f11ae4bfded0f9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 20 Dec 2018 17:49:36 +0100 Subject: [PATCH 0772/1020] Use a working CI script, and test on both stable and nightly. --- .gitlab-ci.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index de843e5964b80dfc95c3fd7a9cb5aa061c1b993b..0b4e77f06d233074e4efced741958cc6a524498c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,16 @@ -image: "pitkley/rust:stable" +stages: + - build -test:cargo: +rust-latest: + stage: build + image: rust:latest script: - - rustc --version && cargo --version - - cargo test --verbose --jobs 1 --release + - cargo build --verbose + - cargo test --verbose + +rust-nightly: + stage: build + image: rustlang/rust:nightly + script: + - cargo build --verbose + - cargo test --verbose From ce039d767ebbc2a502b960d36d1cf2fee0065e92 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 20 Dec 2018 20:39:01 +0100 Subject: [PATCH 0773/1020] restructure auth code --- src/client/auth.rs | 154 +++++++++++++++++++-------------------------- src/client/mod.rs | 2 +- src/error.rs | 2 + src/xmpp_codec.rs | 2 +- 4 files changed, 70 insertions(+), 90 deletions(-) diff --git a/src/client/auth.rs b/src/client/auth.rs index eadceb02a63c873d607cd6b5efc25cecb3dbba6d..51bcaa115600ef96457b26b31327de6549d83c7c 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -1,37 +1,29 @@ -use futures::{sink, Async, Future, Poll, Stream}; +use std::mem::replace; +use std::str::FromStr; +use futures::{sink, Async, Future, Poll, Stream, future::{ok, err, IntoFuture}}; use minidom::Element; use sasl::client::mechanisms::{Anonymous, Plain, Scram}; use sasl::client::Mechanism; use sasl::common::scram::{Sha1, Sha256}; use sasl::common::Credentials; -use std::mem::replace; -use std::str::FromStr; use tokio_io::{AsyncRead, AsyncWrite}; use try_from::TryFrom; use xmpp_parsers::sasl::{Auth, Challenge, Failure, Mechanism as XMPPMechanism, Response, Success}; -use crate::stream_start::StreamStart; use crate::xmpp_codec::Packet; use crate::xmpp_stream::XMPPStream; use crate::{AuthError, Error, ProtocolError}; const NS_XMPP_SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; -pub struct ClientAuth { - state: ClientAuthState, - mechanism: Box, -} - -enum ClientAuthState { - WaitSend(sink::Send>), - WaitRecv(XMPPStream), - Start(StreamStart), - Invalid, +pub struct ClientAuth { + future: Box, Error = Error>>, } -impl ClientAuth { +impl ClientAuth { pub fn new(stream: XMPPStream, creds: Credentials) -> Result { let mechs: Vec> = vec![ + // TODO: Box::new(|| … Box::new(Scram::::from_credentials(creds.clone()).unwrap()), Box::new(Scram::::from_credentials(creds.clone()).unwrap()), Box::new(Plain::from_credentials(creds).unwrap()), @@ -46,36 +38,74 @@ impl ClientAuth { .filter(|child| child.is("mechanism", NS_XMPP_SASL)) .map(|mech_el| mech_el.text()) .collect(); + // TODO: iter instead of collect() // println!("SASL mechanisms offered: {:?}", mech_names); - for mut mech in mechs { - let name = mech.name().to_owned(); + for mut mechanism in mechs { + let name = mechanism.name().to_owned(); if mech_names.iter().any(|name1| *name1 == name) { // println!("SASL mechanism selected: {:?}", name); - let initial = mech.initial().map_err(AuthError::Sasl)?; - let mut this = ClientAuth { - state: ClientAuthState::Invalid, - mechanism: mech, - }; - let mechanism = XMPPMechanism::from_str(&name).map_err(ProtocolError::Parsers)?; - this.send( - stream, - Auth { - mechanism, - data: initial, - }, - ); - return Ok(this); + let initial = mechanism.initial().map_err(AuthError::Sasl)?; + let mechanism_name = XMPPMechanism::from_str(&name).map_err(ProtocolError::Parsers)?; + + let send_initial = Box::new(stream.send_stanza(Auth { + mechanism: mechanism_name, + data: initial, + })) + .map_err(Error::Io); + let future = Box::new(send_initial.and_then( + |stream| Self::handle_challenge(stream, mechanism) + ).and_then( + |stream| stream.restart() + )); + return Ok(ClientAuth { + future, + }); } } Err(AuthError::NoMechanism)? } - fn send>(&mut self, stream: XMPPStream, nonza: N) { - let send = stream.send_stanza(nonza); - - self.state = ClientAuthState::WaitSend(send); + fn handle_challenge(stream: XMPPStream, mut mechanism: Box) -> Box, Error = Error>> { + Box::new( + stream.into_future() + .map_err(|(e, _stream)| e.into()) + .and_then(|(stanza, stream)| { + match stanza { + Some(Packet::Stanza(stanza)) => { + if let Ok(challenge) = Challenge::try_from(stanza.clone()) { + let response = mechanism + .response(&challenge.data); + Box::new( + response + .map_err(|e| AuthError::Sasl(e).into()) + .into_future() + .and_then(|response| { + // Send response and loop + stream.send_stanza(Response { data: response }) + .map_err(Error::Io) + .and_then(|stream| Self::handle_challenge(stream, mechanism)) + }) + ) + } else if let Ok(_) = Success::try_from(stanza.clone()) { + Box::new(ok(stream)) + } else if let Ok(failure) = Failure::try_from(stanza.clone()) { + Box::new(err(Error::Auth(AuthError::Fail(failure.defined_condition)))) + } else { + // ignore and loop + println!("Ignore: {:?}", stanza); + Self::handle_challenge(stream, mechanism) + } + } + Some(_) => { + // ignore and loop + Self::handle_challenge(stream, mechanism) + } + None => Box::new(err(Error::Disconnected)) + } + }) + ) } } @@ -84,58 +114,6 @@ impl Future for ClientAuth { type Error = Error; fn poll(&mut self) -> Poll { - let state = replace(&mut self.state, ClientAuthState::Invalid); - - match state { - ClientAuthState::WaitSend(mut send) => match send.poll() { - Ok(Async::Ready(stream)) => { - self.state = ClientAuthState::WaitRecv(stream); - self.poll() - } - Ok(Async::NotReady) => { - self.state = ClientAuthState::WaitSend(send); - Ok(Async::NotReady) - } - Err(e) => Err(e)?, - }, - ClientAuthState::WaitRecv(mut stream) => match stream.poll() { - Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { - if let Ok(challenge) = Challenge::try_from(stanza.clone()) { - let response = self - .mechanism - .response(&challenge.data) - .map_err(AuthError::Sasl)?; - self.send(stream, Response { data: response }); - self.poll() - } else if let Ok(_) = Success::try_from(stanza.clone()) { - let start = stream.restart(); - self.state = ClientAuthState::Start(start); - self.poll() - } else if let Ok(failure) = Failure::try_from(stanza) { - Err(AuthError::Fail(failure.defined_condition))? - } else { - Ok(Async::NotReady) - } - } - Ok(Async::Ready(_event)) => { - // println!("ClientAuth ignore {:?}", _event); - Ok(Async::NotReady) - } - Ok(_) => { - self.state = ClientAuthState::WaitRecv(stream); - Ok(Async::NotReady) - } - Err(e) => Err(ProtocolError::Parser(e))?, - }, - ClientAuthState::Start(mut start) => match start.poll() { - Ok(Async::Ready(stream)) => Ok(Async::Ready(stream)), - Ok(Async::NotReady) => { - self.state = ClientAuthState::Start(start); - Ok(Async::NotReady) - } - Err(e) => Err(e), - }, - ClientAuthState::Invalid => unreachable!(), - } + self.future.poll() } } diff --git a/src/client/mod.rs b/src/client/mod.rs index 9e62373dc4f276d059b9203592ede8886139bfd4..37670131b72b44e078b99c79b01e1c7809dbf904 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -104,7 +104,7 @@ impl Client { StartTlsClient::from_stream(stream) } - fn auth( + fn auth( stream: xmpp_stream::XMPPStream, username: String, password: String, diff --git a/src/error.rs b/src/error.rs index 26d794495ebff0562e894f022a955b1ea44e32c0..e7fad549fbda79e50f73884b7c7b2cee3ca4b397 100644 --- a/src/error.rs +++ b/src/error.rs @@ -26,6 +26,8 @@ pub enum Error { Auth(AuthError), /// TLS error Tls(TlsError), + /// Connection closed + Disconnected, /// Shoud never happen InvalidState, } diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index e8c4e0538e1b47d308e947bc25d548e9a1ec83b6..9c59bac877829b228e9b9f6640475300815b1939 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -19,7 +19,7 @@ use xml5ever::interface::Attribute; use xml5ever::tokenizer::{Tag, TagKind, Token, TokenSink, XmlTokenizer}; /// Anything that can be sent or received on an XMPP/XML stream -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum Packet { /// `` start tag StreamStart(HashMap), From 11cc7f183a41be0a04e57ae80f4f826c658fe6d1 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 20 Dec 2018 21:09:13 +0100 Subject: [PATCH 0774/1020] auth: add work-around for xmpp-parsers pickyness --- src/client/auth.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/client/auth.rs b/src/client/auth.rs index 51bcaa115600ef96457b26b31327de6549d83c7c..9a56d04907dda55965de107a668d0cd763df1305 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -92,9 +92,11 @@ impl ClientAuth { Box::new(ok(stream)) } else if let Ok(failure) = Failure::try_from(stanza.clone()) { Box::new(err(Error::Auth(AuthError::Fail(failure.defined_condition)))) + } else if stanza.name() == "failure" { + // Workaround for https://gitlab.com/xmpp-rs/xmpp-parsers/merge_requests/1 + Box::new(err(Error::Auth(AuthError::Sasl("failure".to_string())))) } else { // ignore and loop - println!("Ignore: {:?}", stanza); Self::handle_challenge(stream, mechanism) } } From 78f74c633850b44fd4485325c81606dd9c1d360b Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 20 Dec 2018 21:17:56 +0100 Subject: [PATCH 0775/1020] auth: clarify + optimize --- src/client/auth.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/client/auth.rs b/src/client/auth.rs index 9a56d04907dda55965de107a668d0cd763df1305..fb4243948f92fdc05c25ad08401e23f05fea709a 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -1,5 +1,6 @@ use std::mem::replace; use std::str::FromStr; +use std::collections::HashSet; use futures::{sink, Async, Future, Poll, Stream, future::{ok, err, IntoFuture}}; use minidom::Element; use sasl::client::mechanisms::{Anonymous, Plain, Scram}; @@ -22,15 +23,14 @@ pub struct ClientAuth { impl ClientAuth { pub fn new(stream: XMPPStream, creds: Credentials) -> Result { - let mechs: Vec> = vec![ - // TODO: Box::new(|| … - Box::new(Scram::::from_credentials(creds.clone()).unwrap()), - Box::new(Scram::::from_credentials(creds.clone()).unwrap()), - Box::new(Plain::from_credentials(creds).unwrap()), - Box::new(Anonymous::new()), + let local_mechs: Vec Box>> = vec![ + Box::new(|| Box::new(Scram::::from_credentials(creds.clone()).unwrap())), + Box::new(|| Box::new(Scram::::from_credentials(creds.clone()).unwrap())), + Box::new(|| Box::new(Plain::from_credentials(creds.clone()).unwrap())), + Box::new(|| Box::new(Anonymous::new())), ]; - let mech_names: Vec = stream + let remote_mechs: HashSet = stream .stream_features .get_child("mechanisms", NS_XMPP_SASL) .ok_or(AuthError::NoMechanism)? @@ -38,15 +38,12 @@ impl ClientAuth { .filter(|child| child.is("mechanism", NS_XMPP_SASL)) .map(|mech_el| mech_el.text()) .collect(); - // TODO: iter instead of collect() - // println!("SASL mechanisms offered: {:?}", mech_names); - for mut mechanism in mechs { - let name = mechanism.name().to_owned(); - if mech_names.iter().any(|name1| *name1 == name) { - // println!("SASL mechanism selected: {:?}", name); + for local_mech in local_mechs { + let mut mechanism = local_mech(); + if remote_mechs.contains(mechanism.name()) { let initial = mechanism.initial().map_err(AuthError::Sasl)?; - let mechanism_name = XMPPMechanism::from_str(&name).map_err(ProtocolError::Parsers)?; + let mechanism_name = XMPPMechanism::from_str(mechanism.name()).map_err(ProtocolError::Parsers)?; let send_initial = Box::new(stream.send_stanza(Auth { mechanism: mechanism_name, From a6512c2b42e9e41204924bed34397d6018ba9480 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 20 Dec 2018 21:20:46 +0100 Subject: [PATCH 0776/1020] auth: delint --- src/client/auth.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/client/auth.rs b/src/client/auth.rs index fb4243948f92fdc05c25ad08401e23f05fea709a..588b1a73e14c757c356244f8884a6efe1680bc3c 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -1,8 +1,6 @@ -use std::mem::replace; use std::str::FromStr; use std::collections::HashSet; -use futures::{sink, Async, Future, Poll, Stream, future::{ok, err, IntoFuture}}; -use minidom::Element; +use futures::{Future, Poll, Stream, future::{ok, err, IntoFuture}}; use sasl::client::mechanisms::{Anonymous, Plain, Scram}; use sasl::client::Mechanism; use sasl::common::scram::{Sha1, Sha256}; From 7bb4bd1094ca11372b79fe19c9022b98530187da Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 20 Dec 2018 21:30:56 +0100 Subject: [PATCH 0777/1020] tokio-xmpp 0.2.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b6c86df71ea6a722313cfe86ea8378b44915c0ed..ee3b80a49deb900dc4a568777042806a21962afe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-xmpp" -version = "0.2.0" +version = "0.2.1" authors = ["Astro ", "Emmanuel Gil Peyrot ", "pep ", "O01eg "] description = "Asynchronous XMPP for Rust with tokio" license = "MPL-2.0" From c5c8dee20a951b6dc2b2aa9f22724a3044b5c376 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 23 Dec 2018 15:40:23 +0100 Subject: [PATCH 0778/1020] Get rid of `IntoElements`, replace it with `Into` and ` IntoIterator`. This is a backwards-incompatible change, but should require minimal changes. Doing this splits the `append` method in `ElementBuilder` into two methods. `append` now appends exactly one node, while `append_all` appends an iterator of nodes. Add `remove_child`, which removes the first child that has a specific name and namespace, then returns it if it exists. Add a lot of convenience methods on `Node`: `as_text_mut`, `as_element_mut`, `into_text`, `into_element`. --- src/convert.rs | 79 ----------------------- src/element.rs | 171 ++++++++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 2 +- 3 files changed, 162 insertions(+), 90 deletions(-) diff --git a/src/convert.rs b/src/convert.rs index 1a528111a7049651571368798d5609866e4b6173..cabe86d5be2dff5c82d13848e0cda318ad65200a 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -2,85 +2,6 @@ use element::{Element, ElementBuilder}; -/// A struct which is used for emitting `Element`s and text nodes into an `Element` without -/// exposing more functionality than necessary. -pub struct ElementEmitter<'a>(&'a mut Element); - -impl<'a> ElementEmitter<'a> { - /// Creates a new `ElementEmitter`. - pub fn new(root: &'a mut Element) -> ElementEmitter<'a> { - ElementEmitter(root) - } - - /// Appends an `Element` to the target. - pub fn append_child(&mut self, element: Element) { - self.0.append_child(element); - } - - /// Appends a text node to the target. - pub fn append_text_node(&mut self, text: String) { - self.0.append_text_node(text); - } -} - -/// A trait for types which can be converted to one or multiple `Element`s. -pub trait IntoElements { - /// Emits this as a sequence of text nodes and `Element`s. - fn into_elements(self, emitter: &mut ElementEmitter); -} - -impl IntoElements for Vec { - fn into_elements(self, emitter: &mut ElementEmitter) { - for elem in self { - elem.into_elements(emitter); - } - } -} - -impl<'a, T: IntoElements + Clone> IntoElements for &'a [T] { - fn into_elements(self, emitter: &mut ElementEmitter) { - self.to_vec().into_elements(emitter); - } -} - -impl IntoElements for Option { - fn into_elements(self, emitter: &mut ElementEmitter) { - if let Some(e) = self { - e.into_elements(emitter); - } - } -} - -impl IntoElements for T where T: Into { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - -impl IntoElements for ElementBuilder { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.build()); - } -} - -impl IntoElements for String { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(self); - } -} - -impl<'a> IntoElements for &'a String { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(self.to_owned()); - } -} - -impl<'a> IntoElements for &'a str { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(self.to_owned()); - } -} - /// A trait for types which can be converted to an attribute value. pub trait IntoAttributeValue { /// Turns this into an attribute string, or None if it shouldn't be added. diff --git a/src/element.rs b/src/element.rs index e147a27d7b4fccdb6990386b4cd66129f63e1f5f..88a048d12dd52dc104f51f6444c206e38c8c0a56 100644 --- a/src/element.rs +++ b/src/element.rs @@ -19,7 +19,7 @@ use std::str::FromStr; use std::slice; -use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; +use convert::IntoAttributeValue; use namespace_set::NamespaceSet; /// helper function to escape a `&[u8]` and replace all @@ -80,7 +80,8 @@ pub enum Node { } impl Node { - /// Turns this into an `Element` if possible, else returns None. + /// Turns this into a reference to an `Element` if this is an element node. + /// Else this returns `None`. /// /// # Examples /// @@ -101,7 +102,52 @@ impl Node { } } - /// Turns this into a `String` if possible, else returns None. + /// Turns this into a mutable reference of an `Element` if this is an element node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let mut elm = Node::Element("".parse().unwrap()); + /// let mut txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_element_mut().unwrap().name(), "meow"); + /// assert_eq!(txt.as_element_mut(), None); + /// ``` + pub fn as_element_mut(&mut self) -> Option<&mut Element> { + match *self { + Node::Element(ref mut e) => Some(e), + Node::Text(_) => None, + Node::Comment(_) => None, + } + } + + /// Turns this into an `Element`, consuming self, if this is an element node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.into_element().unwrap().name(), "meow"); + /// assert_eq!(txt.into_element(), None); + /// ``` + pub fn into_element(self) -> Option { + match self { + Node::Element(e) => Some(e), + Node::Text(_) => None, + Node::Comment(_) => None, + } + } + + /// Turns this into an `&str` if this is a text node. + /// Else this returns `None`. /// /// # Examples /// @@ -122,6 +168,56 @@ impl Node { } } + /// Turns this into an `&mut String` if this is a text node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let mut elm = Node::Element("".parse().unwrap()); + /// let mut txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_text_mut(), None); + /// { + /// let text_mut = txt.as_text_mut().unwrap(); + /// assert_eq!(text_mut, "meow"); + /// text_mut.push_str("zies"); + /// assert_eq!(text_mut, "meowzies"); + /// } + /// assert_eq!(txt.as_text().unwrap(), "meowzies"); + /// ``` + pub fn as_text_mut(&mut self) -> Option<&mut String> { + match *self { + Node::Element(_) => None, + Node::Text(ref mut s) => Some(s), + Node::Comment(_) => None, + } + } + + /// Turns this into an `String`, consuming self, if this is a text node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.into_text(), None); + /// assert_eq!(txt.into_text().unwrap(), "meow"); + /// ``` + pub fn into_text(self) -> Option { + match self { + Node::Element(_) => None, + Node::Text(s) => Some(s), + Node::Comment(_) => None, + } + } + fn write_to_inner(&self, writer: &mut EventWriter) -> Result<()>{ match *self { Node::Element(ref elmt) => elmt.write_to_inner(writer)?, @@ -139,6 +235,30 @@ impl Node { } } +impl From for Node { + fn from(elm: Element) -> Node { + Node::Element(elm) + } +} + +impl From for Node { + fn from(s: String) -> Node { + Node::Text(s) + } +} + +impl<'a> From<&'a str> for Node { + fn from(s: &'a str) -> Node { + Node::Text(s.to_owned()) + } +} + +impl From for Node { + fn from(builder: ElementBuilder) -> Node { + Node::Element(builder.build()) + } +} + #[derive(Clone, PartialEq, Eq, Debug)] /// A struct representing a DOM Element. pub struct Element { @@ -157,7 +277,6 @@ impl<'a> From<&'a Element> for String { } } - impl FromStr for Element { type Err = Error; @@ -736,6 +855,34 @@ impl Element { pub fn has_child, NS: AsRef>(&self, name: N, namespace: NS) -> bool { self.get_child(name, namespace).is_some() } + + /// Removes the first child with this name and namespace, if it exists, and returns an + /// `Option` containing this child if it succeeds. + /// Returns `None` if no child matches this name and namespace. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Element; + /// + /// let mut elem: Element = r#""#.parse().unwrap(); + /// + /// assert!(elem.remove_child("a", "ns").unwrap().is("a", "ns")); + /// assert!(elem.remove_child("a", "ns").is_none()); + /// assert!(elem.remove_child("inexistent", "inexistent").is_none()); + /// ``` + pub fn remove_child, NS: AsRef>(&mut self, name: N, namespace: NS) -> Option { + let name = name.as_ref(); + let namespace = namespace.as_ref(); + let idx = self.children.iter().position(|x| { + if let Node::Element(ref elm) = x { + elm.is(name, namespace) + } else { + false + } + })?; + self.children.remove(idx).into_element() + } } fn split_element_name>(s: S) -> Result<(Option, String)> { @@ -900,11 +1047,16 @@ impl ElementBuilder { self } - /// Appends anything implementing `IntoElements` into the tree. - pub fn append(mut self, into: T) -> ElementBuilder { - { - let mut emitter = ElementEmitter::new(&mut self.root); - into.into_elements(&mut emitter); + /// Appends anything implementing `Into` into the tree. + pub fn append>(mut self, node: T) -> ElementBuilder { + self.root.append_node(node.into()); + self + } + + /// Appends an iterator of things implementing `Into` into the tree. + pub fn append_all, I: IntoIterator>(mut self, iter: I) -> ElementBuilder { + for node in iter { + self.root.append_node(node.into()); } self } @@ -920,7 +1072,6 @@ impl ElementBuilder { e.namespaces.set_parent(Rc::clone(&element.namespaces)); } } - element } } diff --git a/src/lib.rs b/src/lib.rs index 32a18147e4d3ee9fbe19c5acde39b44ae1b381b1..20be49d3734e8f4a82c4ab4a1a6fae847a934ec3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,4 +77,4 @@ mod namespace_set; pub use error::{Error, Result}; pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder}; -pub use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; +pub use convert::IntoAttributeValue; From f0dd03d63327ce0cd48eccb94783fc11751b1f54 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 23 Dec 2018 15:59:13 +0100 Subject: [PATCH 0779/1020] Split `Node` off into its own module: `node.rs` --- src/convert.rs | 2 - src/element.rs | 193 +--------------------------------------------- src/lib.rs | 4 +- src/node.rs | 202 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 207 insertions(+), 194 deletions(-) create mode 100644 src/node.rs diff --git a/src/convert.rs b/src/convert.rs index cabe86d5be2dff5c82d13848e0cda318ad65200a..534073298b3d94fb206c68800a4e750d1558f28b 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -1,7 +1,5 @@ //! A module which exports a few traits for converting types to elements and attributes. -use element::{Element, ElementBuilder}; - /// A trait for types which can be converted to an attribute value. pub trait IntoAttributeValue { /// Turns this into an attribute string, or None if it shouldn't be added. diff --git a/src/element.rs b/src/element.rs index 88a048d12dd52dc104f51f6444c206e38c8c0a56..5db7727bec18581a013e339defad144279ed1c6e 100644 --- a/src/element.rs +++ b/src/element.rs @@ -11,7 +11,7 @@ use error::{Error, Result}; use quick_xml::Reader as EventReader; use quick_xml::Writer as EventWriter; -use quick_xml::events::{Event, BytesStart, BytesEnd, BytesText, BytesDecl}; +use quick_xml::events::{Event, BytesStart, BytesEnd, BytesDecl}; use std::io::BufRead; @@ -21,6 +21,7 @@ use std::slice; use convert::IntoAttributeValue; use namespace_set::NamespaceSet; +use node::Node; /// helper function to escape a `&[u8]` and replace all /// xml special characters (<, >, &, ', ") with their corresponding @@ -68,196 +69,6 @@ pub fn escape(raw: &[u8]) -> Cow<[u8]> { } } -/// A node in an element tree. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Node { - /// An `Element`. - Element(Element), - /// A text node. - Text(String), - /// A comment node. - Comment(String), -} - -impl Node { - /// Turns this into a reference to an `Element` if this is an element node. - /// Else this returns `None`. - /// - /// # Examples - /// - /// ```rust - /// use minidom::Node; - /// - /// let elm = Node::Element("".parse().unwrap()); - /// let txt = Node::Text("meow".to_owned()); - /// - /// assert_eq!(elm.as_element().unwrap().name(), "meow"); - /// assert_eq!(txt.as_element(), None); - /// ``` - pub fn as_element(&self) -> Option<&Element> { - match *self { - Node::Element(ref e) => Some(e), - Node::Text(_) => None, - Node::Comment(_) => None, - } - } - - /// Turns this into a mutable reference of an `Element` if this is an element node. - /// Else this returns `None`. - /// - /// # Examples - /// - /// ```rust - /// use minidom::Node; - /// - /// let mut elm = Node::Element("".parse().unwrap()); - /// let mut txt = Node::Text("meow".to_owned()); - /// - /// assert_eq!(elm.as_element_mut().unwrap().name(), "meow"); - /// assert_eq!(txt.as_element_mut(), None); - /// ``` - pub fn as_element_mut(&mut self) -> Option<&mut Element> { - match *self { - Node::Element(ref mut e) => Some(e), - Node::Text(_) => None, - Node::Comment(_) => None, - } - } - - /// Turns this into an `Element`, consuming self, if this is an element node. - /// Else this returns `None`. - /// - /// # Examples - /// - /// ```rust - /// use minidom::Node; - /// - /// let elm = Node::Element("".parse().unwrap()); - /// let txt = Node::Text("meow".to_owned()); - /// - /// assert_eq!(elm.into_element().unwrap().name(), "meow"); - /// assert_eq!(txt.into_element(), None); - /// ``` - pub fn into_element(self) -> Option { - match self { - Node::Element(e) => Some(e), - Node::Text(_) => None, - Node::Comment(_) => None, - } - } - - /// Turns this into an `&str` if this is a text node. - /// Else this returns `None`. - /// - /// # Examples - /// - /// ```rust - /// use minidom::Node; - /// - /// let elm = Node::Element("".parse().unwrap()); - /// let txt = Node::Text("meow".to_owned()); - /// - /// assert_eq!(elm.as_text(), None); - /// assert_eq!(txt.as_text().unwrap(), "meow"); - /// ``` - pub fn as_text(&self) -> Option<&str> { - match *self { - Node::Element(_) => None, - Node::Text(ref s) => Some(s), - Node::Comment(_) => None, - } - } - - /// Turns this into an `&mut String` if this is a text node. - /// Else this returns `None`. - /// - /// # Examples - /// - /// ```rust - /// use minidom::Node; - /// - /// let mut elm = Node::Element("".parse().unwrap()); - /// let mut txt = Node::Text("meow".to_owned()); - /// - /// assert_eq!(elm.as_text_mut(), None); - /// { - /// let text_mut = txt.as_text_mut().unwrap(); - /// assert_eq!(text_mut, "meow"); - /// text_mut.push_str("zies"); - /// assert_eq!(text_mut, "meowzies"); - /// } - /// assert_eq!(txt.as_text().unwrap(), "meowzies"); - /// ``` - pub fn as_text_mut(&mut self) -> Option<&mut String> { - match *self { - Node::Element(_) => None, - Node::Text(ref mut s) => Some(s), - Node::Comment(_) => None, - } - } - - /// Turns this into an `String`, consuming self, if this is a text node. - /// Else this returns `None`. - /// - /// # Examples - /// - /// ```rust - /// use minidom::Node; - /// - /// let elm = Node::Element("".parse().unwrap()); - /// let txt = Node::Text("meow".to_owned()); - /// - /// assert_eq!(elm.into_text(), None); - /// assert_eq!(txt.into_text().unwrap(), "meow"); - /// ``` - pub fn into_text(self) -> Option { - match self { - Node::Element(_) => None, - Node::Text(s) => Some(s), - Node::Comment(_) => None, - } - } - - fn write_to_inner(&self, writer: &mut EventWriter) -> Result<()>{ - match *self { - Node::Element(ref elmt) => elmt.write_to_inner(writer)?, - Node::Text(ref s) => { - writer.write_event(Event::Text(BytesText::from_plain_str(s)))?; - () - }, - Node::Comment(ref s) => { - writer.write_event(Event::Comment(BytesText::from_plain_str(s)))?; - () - }, - }; - - Ok(()) - } -} - -impl From for Node { - fn from(elm: Element) -> Node { - Node::Element(elm) - } -} - -impl From for Node { - fn from(s: String) -> Node { - Node::Text(s) - } -} - -impl<'a> From<&'a str> for Node { - fn from(s: &'a str) -> Node { - Node::Text(s.to_owned()) - } -} - -impl From for Node { - fn from(builder: ElementBuilder) -> Node { - Node::Element(builder.build()) - } -} #[derive(Clone, PartialEq, Eq, Debug)] /// A struct representing a DOM Element. diff --git a/src/lib.rs b/src/lib.rs index 20be49d3734e8f4a82c4ab4a1a6fae847a934ec3..5ea7e7063617163753b02f6550d957037be0fa13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,10 +71,12 @@ extern crate failure; pub mod error; pub mod element; pub mod convert; +pub mod node; mod namespace_set; #[cfg(test)] mod tests; pub use error::{Error, Result}; -pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder}; +pub use element::{Element, Children, ChildrenMut, ElementBuilder}; +pub use node::Node; pub use convert::IntoAttributeValue; diff --git a/src/node.rs b/src/node.rs new file mode 100644 index 0000000000000000000000000000000000000000..7fe5ddadc70ca316d807c87dc4c1936c4dd6a833 --- /dev/null +++ b/src/node.rs @@ -0,0 +1,202 @@ +//! Provides the `Node` struct, which represents a node in the DOM. + +use std::io::Write; + +use quick_xml::Writer as EventWriter; +use quick_xml::events::{Event, BytesText}; + +use error::Result; + +use element::{Element, ElementBuilder}; + +/// A node in an element tree. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Node { + /// An `Element`. + Element(Element), + /// A text node. + Text(String), + /// A comment node. + Comment(String), +} + +impl Node { + /// Turns this into a reference to an `Element` if this is an element node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_element().unwrap().name(), "meow"); + /// assert_eq!(txt.as_element(), None); + /// ``` + pub fn as_element(&self) -> Option<&Element> { + match *self { + Node::Element(ref e) => Some(e), + Node::Text(_) => None, + Node::Comment(_) => None, + } + } + + /// Turns this into a mutable reference of an `Element` if this is an element node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let mut elm = Node::Element("".parse().unwrap()); + /// let mut txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_element_mut().unwrap().name(), "meow"); + /// assert_eq!(txt.as_element_mut(), None); + /// ``` + pub fn as_element_mut(&mut self) -> Option<&mut Element> { + match *self { + Node::Element(ref mut e) => Some(e), + Node::Text(_) => None, + Node::Comment(_) => None, + } + } + + /// Turns this into an `Element`, consuming self, if this is an element node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.into_element().unwrap().name(), "meow"); + /// assert_eq!(txt.into_element(), None); + /// ``` + pub fn into_element(self) -> Option { + match self { + Node::Element(e) => Some(e), + Node::Text(_) => None, + Node::Comment(_) => None, + } + } + + /// Turns this into an `&str` if this is a text node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_text(), None); + /// assert_eq!(txt.as_text().unwrap(), "meow"); + /// ``` + pub fn as_text(&self) -> Option<&str> { + match *self { + Node::Element(_) => None, + Node::Text(ref s) => Some(s), + Node::Comment(_) => None, + } + } + + /// Turns this into an `&mut String` if this is a text node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let mut elm = Node::Element("".parse().unwrap()); + /// let mut txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_text_mut(), None); + /// { + /// let text_mut = txt.as_text_mut().unwrap(); + /// assert_eq!(text_mut, "meow"); + /// text_mut.push_str("zies"); + /// assert_eq!(text_mut, "meowzies"); + /// } + /// assert_eq!(txt.as_text().unwrap(), "meowzies"); + /// ``` + pub fn as_text_mut(&mut self) -> Option<&mut String> { + match *self { + Node::Element(_) => None, + Node::Text(ref mut s) => Some(s), + Node::Comment(_) => None, + } + } + + /// Turns this into an `String`, consuming self, if this is a text node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.into_text(), None); + /// assert_eq!(txt.into_text().unwrap(), "meow"); + /// ``` + pub fn into_text(self) -> Option { + match self { + Node::Element(_) => None, + Node::Text(s) => Some(s), + Node::Comment(_) => None, + } + } + + #[doc(hidden)] + pub(crate) fn write_to_inner(&self, writer: &mut EventWriter) -> Result<()>{ + match *self { + Node::Element(ref elmt) => elmt.write_to_inner(writer)?, + Node::Text(ref s) => { + writer.write_event(Event::Text(BytesText::from_plain_str(s)))?; + () + }, + Node::Comment(ref s) => { + writer.write_event(Event::Comment(BytesText::from_plain_str(s)))?; + () + }, + }; + + Ok(()) + } +} + +impl From for Node { + fn from(elm: Element) -> Node { + Node::Element(elm) + } +} + +impl From for Node { + fn from(s: String) -> Node { + Node::Text(s) + } +} + +impl<'a> From<&'a str> for Node { + fn from(s: &'a str) -> Node { + Node::Text(s.to_owned()) + } +} + +impl From for Node { + fn from(builder: ElementBuilder) -> Node { + Node::Element(builder.build()) + } +} From 96c8b056779122e4be173b202317f16f6902495a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 29 Dec 2018 18:29:11 +0100 Subject: [PATCH 0780/1020] Do some manual formatting in macros. --- src/macros.rs | 83 +++++++++++++++++++++++---------------------- src/muc/user.rs | 2 +- src/presence.rs | 2 +- src/pubsub/event.rs | 4 +-- 4 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index d1a407406be2923e1d432eed1ebc6afada805206..62c59736dc5a52435d988e88b30597f22a3f5097 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -29,7 +29,7 @@ macro_rules! get_attr { "Required attribute '", $attr, "' missing." - ))) + ))); } } }; @@ -151,8 +151,8 @@ macro_rules! generate_element_enum { #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( - $(#[$enum_meta])* - $enum + $(#[$enum_meta])* + $enum ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { @@ -169,10 +169,13 @@ macro_rules! generate_element_enum { } impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder(match elem { - $($elem::$enum => $enum_name,)+ - }).ns(crate::ns::$ns) - .build() + ::minidom::Element::builder( + match elem { + $($elem::$enum => $enum_name,)+ + } + ) + .ns(crate::ns::$ns) + .build() } } ); @@ -187,8 +190,8 @@ macro_rules! generate_attribute_enum { #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( - $(#[$enum_meta])* - $enum + $(#[$enum_meta])* + $enum ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { @@ -206,11 +209,11 @@ macro_rules! generate_attribute_enum { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - .attr($attr, match elem { - $($elem::$enum => $enum_name,)+ - }) - .build() + .ns(crate::ns::$ns) + .attr($attr, match elem { + $($elem::$enum => $enum_name,)+ + }) + .build() } } ); @@ -271,9 +274,9 @@ macro_rules! check_no_unknown_attributes { ($elem:ident, $name:tt, [$($attr:tt),*]) => ( for (_attr, _) in $elem.attrs() { $( - if _attr == $attr { - continue; - } + if _attr == $attr { + continue; + } )* return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } @@ -300,8 +303,8 @@ macro_rules! generate_empty_element { impl From<$elem> for ::minidom::Element { fn from(_: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - .build() + .ns(crate::ns::$ns) + .build() } } ); @@ -352,9 +355,9 @@ macro_rules! generate_elem_id { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - .append(elem.0) - .build() + .ns(crate::ns::$ns) + .append(elem.0) + .build() } } ); @@ -488,16 +491,16 @@ macro_rules! generate_element { #[derive(Debug, Clone)] pub struct $elem { $( - $(#[$attr_meta])* - pub $attr: $attr_type, + $(#[$attr_meta])* + pub $attr: $attr_type, )* $( - $(#[$child_meta])* - pub $child_ident: start_decl!($coucou, $child_type), + $(#[$child_meta])* + pub $child_ident: start_decl!($coucou, $child_type), )* $( - $(#[$text_meta])* - pub $text_ident: $text_type, + $(#[$text_meta])* + pub $text_ident: $text_type, )* } @@ -508,7 +511,7 @@ macro_rules! generate_element { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); $( - start_parse_elem!($child_ident: $coucou); + start_parse_elem!($child_ident: $coucou); )* for _child in elem.children() { $( @@ -521,13 +524,13 @@ macro_rules! generate_element { } Ok($elem { $( - $attr: get_attr!(elem, $attr_name, $attr_action), + $attr: get_attr!(elem, $attr_name, $attr_action), )* $( - $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name), + $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name), )* $( - $text_ident: $codec::decode(&elem.text())?, + $text_ident: $codec::decode(&elem.text())?, )* }) } @@ -536,17 +539,17 @@ macro_rules! generate_element { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - $( + .ns(crate::ns::$ns) + $( .attr($attr_name, elem.$attr) - )* - $( + )* + $( .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns))) - )* - $( + )* + $( .append($codec::encode(&elem.$text_ident)) - )* - .build() + )* + .build() } } ); diff --git a/src/muc/user.rs b/src/muc/user.rs index 3f058a1740803f879dcec7a873de4cbaccd9ff0c..2c1a6926a98b9099f207268bcb683ec9127070a1 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -102,7 +102,7 @@ impl TryFrom for Actor { (Some(_), Some(_)) | (None, None) => { return Err(Error::ParseError( "Either 'jid' or 'nick' attribute is required.", - )) + )); } (Some(jid), _) => Ok(Actor::Jid(jid)), (_, Some(nick)) => Ok(Actor::Nick(nick)), diff --git a/src/presence.rs b/src/presence.rs index 765e680d87d7ef36aab84f85dd178252aa3467ff..1f9846c2b7dc493b5f37d6c135327ebfcd125a68 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -136,7 +136,7 @@ impl FromStr for Type { _ => { return Err(Error::ParseError( "Invalid 'type' attribute on presence element.", - )) + )); } }) } diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index b8f2bc0066a17dc74c93e345b3a1f035f22d1ad5..fac8e2fe26b822297abf1438cfe6ae4332bd065d 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -138,7 +138,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { Some(true) => { return Err(Error::ParseError( "Mix of item and retract in items element.", - )) + )); } } items.push(Item::try_from(child.clone())?); @@ -149,7 +149,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { Some(false) => { return Err(Error::ParseError( "Mix of item and retract in items element.", - )) + )); } } check_no_children!(child, "retract"); From 1e85abd10cca63a7426eec56d1ed9d639c96437e Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 20 Dec 2018 20:58:13 +0100 Subject: [PATCH 0781/1020] sasl: Add test failure_with_non_prefixed_text_lang. --- src/sasl.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sasl.rs b/src/sasl.rs index 05dd250aba849569c445349720e024dee0e0179a..86e10760b996632ded068544d3e2c05ded68c544 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -288,4 +288,20 @@ mod tests { String::from("Call 212-555-1212 for assistance.") ); } + + #[test] + fn failure_with_non_prefixed_text_lang() { + let elem: Element = " + + Invalid username or password + " + .parse() + .unwrap(); + let failure = Failure::try_from(elem).unwrap(); + assert_eq!(failure.defined_condition, DefinedCondition::NotAuthorized); + assert_eq!( + failure.texts["en"], + String::from("Invalid username or password") + ); + } } From 95f4ade4ba6d8d86ffa84cd49bd4021dad7f2bf3 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 30 Dec 2018 00:36:29 +0100 Subject: [PATCH 0782/1020] compat mode that relaxes some of the check_* macros --- Cargo.toml | 2 ++ src/attention.rs | 3 +++ src/bind.rs | 1 + src/blocking.rs | 18 ++++++++++------ src/caps.rs | 1 + src/chatstates.rs | 2 ++ src/jingle_ft.rs | 49 +++++++++++++++++++++++++----------------- src/macros.rs | 3 +++ src/message_correct.rs | 1 + src/muc/muc.rs | 1 + src/muc/user.rs | 5 +++++ src/nick.rs | 3 +++ src/ping.rs | 3 +++ src/presence.rs | 2 ++ src/pubsub/event.rs | 1 + src/roster.rs | 1 + src/sasl.rs | 5 +---- 17 files changed, 70 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d3122cb9084fd17e1434003c591f8e2036d1454..65935bab1f028d897229ee24541225749156fd59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,5 @@ try_from = "0.3.2" [features] # Build xmpp-parsers to make components instead of clients. component = [] +# Compatibility mode +compat = [] diff --git a/src/attention.rs b/src/attention.rs index be33bb3b6f07d002163bfa2ee46d4f3a7bc6b6c3..c786f3137fce92ed8eb178329fda4ee4481a1da5 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -18,6 +18,7 @@ impl MessagePayload for Attention {} #[cfg(test)] mod tests { use super::*; + #[cfg(not(feature = "compat"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -33,6 +34,7 @@ mod tests { Attention::try_from(elem).unwrap(); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_child() { let elem: Element = "" @@ -46,6 +48,7 @@ mod tests { assert_eq!(message, "Unknown child in attention element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/bind.rs b/src/bind.rs index fe9472b2cc3c2192c959cd7c076dc87d9adc2786..bd09d4f3b36aa326f51790cc3f0ad0f905f6a2a0 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -112,6 +112,7 @@ mod tests { assert_eq!(bind, Bind::None); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_resource() { let elem: Element = "resource" diff --git a/src/blocking.rs b/src/blocking.rs index d34fee3d97a1935c5d825e7bfc836408e74885a2..b2fe70df28c9bf49cbd6448a79504baf8f08f0d5 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -155,13 +155,16 @@ mod tests { }, ]; - let request_elem = elem.clone(); - let error = BlocklistRequest::try_from(request_elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in blocklist element."); + #[cfg(not(feature = "compat"))] + { + let request_elem = elem.clone(); + let error = BlocklistRequest::try_from(request_elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in blocklist element."); + } let result_elem = elem.clone(); let result = BlocklistResult::try_from(result_elem).unwrap(); @@ -176,6 +179,7 @@ mod tests { assert_eq!(unblock.items, two_items); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid() { let elem: Element = "" diff --git a/src/caps.rs b/src/caps.rs index 72b04263f14bfd58a51276de57be39026c63d21e..3befdc9bc2541de8d5d401c362f4733124eecb03 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -233,6 +233,7 @@ mod tests { ); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_child() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=".parse().unwrap(); diff --git a/src/chatstates.rs b/src/chatstates.rs index 75d64d91b1549651793e2fd2c2775f811743f64a..9a972aa79a4e97a820c62ac1f76275936240b214 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -63,6 +63,7 @@ mod tests { assert_eq!(message, "This is not a chatstate element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_child() { let elem: Element = "" @@ -76,6 +77,7 @@ mod tests { assert_eq!(message, "Unknown child in chatstate element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 381eb21fa288b1c37ac00ef139d4f033750b454c..359cd8b07c17f86094696d201573b5c9169bdc2f 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -510,13 +510,16 @@ mod tests { }; assert_eq!(message, "Unknown child in received element."); - let elem: Element = "".parse().unwrap(); - let error = Received::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in received element."); + #[cfg(not(feature = "compat"))] + { + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in received element."); + } let elem: Element = "" @@ -575,13 +578,16 @@ mod tests { }; assert_eq!(message, "This is not a file element."); - let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); - let error = Checksum::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in checksum element."); + #[cfg(not(feature = "compat"))] + { + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in checksum element."); + } let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); let error = Checksum::try_from(elem).unwrap_err(); @@ -631,11 +637,14 @@ mod tests { let elem: Element = "" .parse() .unwrap(); - let error = Range::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in range element."); + #[cfg(not(feature = "compat"))] + { + let error = Range::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in range element."); + } } } diff --git a/src/macros.rs b/src/macros.rs index 62c59736dc5a52435d988e88b30597f22a3f5097..daccec5c676346e14468723f34b9151e044972a2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -248,6 +248,7 @@ macro_rules! check_ns_only { macro_rules! check_no_children { ($elem:ident, $name:tt) => { + #[cfg(not(feature = "compat"))] for _ in $elem.children() { return Err(crate::error::Error::ParseError(concat!( "Unknown child in ", @@ -260,6 +261,7 @@ macro_rules! check_no_children { macro_rules! check_no_attributes { ($elem:ident, $name:tt) => { + #[cfg(not(feature = "compat"))] for _ in $elem.attrs() { return Err(crate::error::Error::ParseError(concat!( "Unknown attribute in ", @@ -272,6 +274,7 @@ macro_rules! check_no_attributes { macro_rules! check_no_unknown_attributes { ($elem:ident, $name:tt, [$($attr:tt),*]) => ( + #[cfg(not(feature = "compat"))] for (_attr, _) in $elem.attrs() { $( if _attr == $attr { diff --git a/src/message_correct.rs b/src/message_correct.rs index 9e7a08f726c1e789b2794949a3888ec4d8524eb8..e0d22d6257322d3a31bccbb32d20ed6cfb44ce0c 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -45,6 +45,7 @@ mod tests { Replace::try_from(elem).unwrap(); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 32df468d7e46f8548656f6a9503daef514dc610f..40528383da094ad9438d492d6ec92f95cf291540 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -142,6 +142,7 @@ mod tests { assert_eq!(elem, elem2); } + #[cfg(not(feature = "compat"))] #[test] fn test_muc_invalid_attribute() { let elem: Element = "" diff --git a/src/muc/user.rs b/src/muc/user.rs index 2c1a6926a98b9099f207268bcb683ec9127070a1..a104e319f9104715a22655c4901631cdad49d1a9 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -303,6 +303,7 @@ mod tests { assert!(elem.compare_to(&elem2)); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = " @@ -343,6 +344,7 @@ mod tests { assert_eq!(message, "Required attribute 'code' missing."); } + #[cfg(not(feature = "compat"))] #[test] fn test_status_invalid_child() { let elem: Element = " @@ -513,6 +515,7 @@ mod tests { assert_eq!(reason.0, "Reason".to_owned()); } + #[cfg(not(feature = "compat"))] #[test] fn test_reason_invalid_attribute() { let elem: Element = " @@ -528,6 +531,7 @@ mod tests { assert_eq!(message, "Unknown attribute in reason element.".to_owned()); } + #[cfg(not(feature = "compat"))] #[test] fn test_reason_invalid() { let elem: Element = " @@ -545,6 +549,7 @@ mod tests { assert_eq!(message, "Unknown child in reason element.".to_owned()); } + #[cfg(not(feature = "compat"))] #[test] fn test_item_invalid_attr() { let elem: Element = " diff --git a/src/nick.rs b/src/nick.rs index edf8dcacce17b23e54ac39fcbea32b87aad8d5a2..67a45ff9c5342ea6c2216ec4a4f99865e0aae859 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -14,6 +14,7 @@ generate_elem_id!( #[cfg(test)] mod tests { use super::*; + #[cfg(not(feature = "compat"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -48,6 +49,7 @@ mod tests { assert_eq!(elem1, elem2); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid() { let elem: Element = "" @@ -61,6 +63,7 @@ mod tests { assert_eq!(message, "Unknown child in nick element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/ping.rs b/src/ping.rs index 4cb25f649c453db84f9879f5871c743df187898f..6f48dd5592941854555995db680452f7a901ccaf 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -20,6 +20,7 @@ impl IqGetPayload for Ping {} #[cfg(test)] mod tests { use super::*; + #[cfg(not(feature = "compat"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -42,6 +43,7 @@ mod tests { assert_eq!(elem1, elem2); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid() { let elem: Element = "" @@ -55,6 +57,7 @@ mod tests { assert_eq!(message, "Unknown child in ping element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); diff --git a/src/presence.rs b/src/presence.rs index 1f9846c2b7dc493b5f37d6c135327ebfcd125a68..20d63e879e48ac4af6f1a147b30010a785d215cd 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -570,6 +570,7 @@ mod tests { assert!(payload.is("test", "invalid")); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_status_child() { #[cfg(not(feature = "component"))] @@ -589,6 +590,7 @@ mod tests { assert_eq!(message, "Unknown child in status element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { #[cfg(not(feature = "component"))] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index fac8e2fe26b822297abf1438cfe6ae4332bd065d..0cad1cc0379fb9b7ad357821ad8f5a899057734b 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -420,6 +420,7 @@ mod tests { assert_eq!(message, "Unknown child in event element."); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/roster.rs b/src/roster.rs index 40cc7a25dc7f8a7f935306df4507e3cff8d4862e..c0d869d1d0079196f04dfa651fbdce888178d721 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -245,6 +245,7 @@ mod tests { assert_eq!(roster.items[0].subscription, Subscription::Remove); } + #[cfg(not(feature = "compat"))] #[test] fn test_invalid() { let elem: Element = "" diff --git a/src/sasl.rs b/src/sasl.rs index 86e10760b996632ded068544d3e2c05ded68c544..21e585722aaf113c0c1a391ae2c79d28aed00ed8 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -289,6 +289,7 @@ mod tests { ); } + #[cfg(feature = "compat")] #[test] fn failure_with_non_prefixed_text_lang() { let elem: Element = " @@ -299,9 +300,5 @@ mod tests { .unwrap(); let failure = Failure::try_from(elem).unwrap(); assert_eq!(failure.defined_condition, DefinedCondition::NotAuthorized); - assert_eq!( - failure.texts["en"], - String::from("Invalid username or password") - ); } } From 047649dbc839b4253df17bc01d357a7f731b48fe Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 30 Dec 2018 00:49:57 +0100 Subject: [PATCH 0783/1020] .gitlab-ci.yml: add a compat-rust-latest stage --- .gitlab-ci.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b4e77f06d233074e4efced741958cc6a524498c..ba1742248ffe71a3ce5d45053f47159030da4b50 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,16 +1,29 @@ stages: - build +variables: + FEATURES: "" + rust-latest: stage: build image: rust:latest script: - - cargo build --verbose - - cargo test --verbose + - cargo build --verbose --no-default-features --features=$FEATURES + - cargo test --verbose --no-default-features --features=$FEATURES rust-nightly: stage: build image: rustlang/rust:nightly script: - - cargo build --verbose - - cargo test --verbose + - cargo build --verbose --no-default-features --features=$FEATURES + - cargo test --verbose --no-default-features --features=$FEATURES + +"rust-latest with features=compat": + extends: rust-latest + variables: + FEATURES: "compat" + +"rust-nightly with features=compat": + extends: rust-nightly + variables: + FEATURES: "compat" From 5ebe92c2605d53234afcb1abaa07f391270630f6 Mon Sep 17 00:00:00 2001 From: O01eg Date: Mon, 7 Jan 2019 16:49:33 +0300 Subject: [PATCH 0784/1020] Add ask attribute from RFC3921. Fixes #1 --- src/roster.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/roster.rs b/src/roster.rs index 40cc7a25dc7f8a7f935306df4507e3cff8d4862e..a64a47932e14ee94020163bfb06f1c473ccf89de 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -36,6 +36,14 @@ generate_attribute!( }, Default = None ); +generate_attribute!( + /// The sub-state of subscription with a contact. + Ask, "ask", { + /// Pending sub-state of the 'none' subscription state + Subscribe => "subscribe", + } +); + generate_element!( /// Contact from the user’s contact list. #[derive(PartialEq)] @@ -48,7 +56,10 @@ generate_element!( name: Option = "name" => optional_empty, /// Subscription status of this contact. - subscription: Subscription = "subscription" => default + subscription: Subscription = "subscription" => default, + + /// + ask: Option = "ask" => optional_empty ], children: [ @@ -144,23 +155,42 @@ mod tests { + + MyBuddies + "# .parse() .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert_eq!(roster.ver, Some(String::from("ver11"))); - assert_eq!(roster.items.len(), 3); + assert_eq!(roster.items.len(), 4); assert_eq!( roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap() ); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); assert_eq!(roster.items[0].subscription, Subscription::Both); + assert_eq!(roster.items[0].ask, None); assert_eq!( roster.items[0].groups, vec!(Group::from_str("Friends").unwrap()) ); + + assert_eq!( + roster.items[3].jid, + Jid::from_str("contact@example.org").unwrap() + ); + assert_eq!(roster.items[3].name, Some(String::from("MyContact"))); + assert_eq!(roster.items[3].subscription, Subscription::None); + assert_eq!(roster.items[3].ask, Some(Ask::Subscribe)); + assert_eq!( + roster.items[3].groups, + vec!(Group::from_str("MyBuddies").unwrap()) + ); } #[test] From 983078f120a574bb0019afec44ac9beca52dd7ae Mon Sep 17 00:00:00 2001 From: O01eg Date: Tue, 8 Jan 2019 13:41:39 +0300 Subject: [PATCH 0785/1020] Add prefix support to decoder to accept xml:lang in presence statuses. --- src/xmpp_codec.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 9c59bac877829b228e9b9f6640475300815b1939..199627ba4ffedbd4983dda7451ae6e06e95c4747 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -104,7 +104,11 @@ impl ParserSink { "xmlns" => (), _ if is_prefix_xmlns(attr) => (), _ => { - el_builder = el_builder.attr(attr.name.local.as_ref(), attr.value.as_ref()); + if let Some(ref prefix) = attr.name.prefix { + el_builder = el_builder.attr(format!("{}:{}", prefix, attr.name.local), attr.value.as_ref()); + } else { + el_builder = el_builder.attr(attr.name.local.as_ref(), attr.value.as_ref()); + } } } } @@ -428,6 +432,28 @@ mod tests { }); } + /// test case for https://gitlab.com/xmpp-rs/tokio-xmpp/issues/3 + #[test] + fn test_atrribute_prefix() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + + b.clear(); + b.put(r"Test status"); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::Stanza(ref el))) if el.name() == "status" && el.text() == "Test status" && el.attr("xml:lang").map_or(false, |a| a == "en") => true, + _ => false, + }); + + } + /// By default, encode() only get's a BytesMut that has 8kb space reserved. #[test] fn test_large_stanza() { From ee511e653aa17a646391a991f7aef0af281080ba Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Jan 2019 20:41:12 +0100 Subject: [PATCH 0786/1020] sasl: Add back the assert, with the correct @xml:lang this time. --- src/sasl.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sasl.rs b/src/sasl.rs index 21e585722aaf113c0c1a391ae2c79d28aed00ed8..6f922901b2475da7cb72ecdcb6c55e47ddb30f17 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -300,5 +300,9 @@ mod tests { .unwrap(); let failure = Failure::try_from(elem).unwrap(); assert_eq!(failure.defined_condition, DefinedCondition::NotAuthorized); + assert_eq!( + failure.texts[""], + String::from("Invalid username or password") + ); } } From 8b15728bb2c9b686506affb0442697660625ee67 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Jan 2019 20:41:40 +0100 Subject: [PATCH 0787/1020] blocking, jingle_ft: Split #[cfg] sections into their own tests. --- src/blocking.rs | 23 +++++++++-------- src/jingle_ft.rs | 65 +++++++++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/blocking.rs b/src/blocking.rs index b2fe70df28c9bf49cbd6448a79504baf8f08f0d5..e12e7e49ae6757a078a2432892b3388f2551c60a 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -155,17 +155,6 @@ mod tests { }, ]; - #[cfg(not(feature = "compat"))] - { - let request_elem = elem.clone(); - let error = BlocklistRequest::try_from(request_elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in blocklist element."); - } - let result_elem = elem.clone(); let result = BlocklistResult::try_from(result_elem).unwrap(); assert_eq!(result.items, two_items); @@ -221,4 +210,16 @@ mod tests { }; assert_eq!(message, "Unknown attribute in unblock element."); } + + #[cfg(not(feature = "compat"))] + #[test] + fn test_non_empty_blocklist_request() { + let elem: Element = "".parse().unwrap(); + let error = BlocklistRequest::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in blocklist element."); + } } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 359cd8b07c17f86094696d201573b5c9169bdc2f..d9a73b29b5fdd281c2091bb0f2bb5fa063003166 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -510,17 +510,6 @@ mod tests { }; assert_eq!(message, "Unknown child in received element."); - #[cfg(not(feature = "compat"))] - { - let elem: Element = "".parse().unwrap(); - let error = Received::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in received element."); - } - let elem: Element = "" .parse() @@ -541,6 +530,18 @@ mod tests { assert_eq!(message, "Unknown value for 'creator' attribute."); } + #[cfg(not(feature = "compat"))] + #[test] + fn test_invalid_received() { + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in received element."); + } + #[test] fn test_checksum() { let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); @@ -578,17 +579,6 @@ mod tests { }; assert_eq!(message, "This is not a file element."); - #[cfg(not(feature = "compat"))] - { - let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); - let error = Checksum::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in checksum element."); - } - let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); let error = Checksum::try_from(elem).unwrap_err(); let message = match error { @@ -606,6 +596,18 @@ mod tests { assert_eq!(message, "Unknown value for 'creator' attribute."); } + #[cfg(not(feature = "compat"))] + #[test] + fn test_invalid_checksum() { + let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); + let error = Checksum::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in checksum element."); + } + #[test] fn test_range() { let elem: Element = "" @@ -633,18 +635,19 @@ mod tests { assert_eq!(range2.offset, 2048); assert_eq!(range2.length, Some(1024)); assert_eq!(range2.hashes, hashes); + } + #[cfg(not(feature = "compat"))] + #[test] + fn test_invalid_range() { let elem: Element = "" .parse() .unwrap(); - #[cfg(not(feature = "compat"))] - { - let error = Range::try_from(elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in range element."); - } + let error = Range::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in range element."); } } From c2b7e193788ca42f1ce07ab4a1bb404a799597ea Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Jan 2019 22:00:46 +0100 Subject: [PATCH 0788/1020] Rename "compat" into "disable-validation", to insist on it breaking some guarantees. --- .gitlab-ci.yml | 8 ++++---- Cargo.toml | 4 ++-- src/attention.rs | 6 +++--- src/bind.rs | 2 +- src/blocking.rs | 4 ++-- src/caps.rs | 2 +- src/chatstates.rs | 4 ++-- src/jingle_ft.rs | 6 +++--- src/macros.rs | 6 +++--- src/message_correct.rs | 2 +- src/muc/muc.rs | 2 +- src/muc/user.rs | 10 +++++----- src/nick.rs | 6 +++--- src/ping.rs | 6 +++--- src/presence.rs | 4 ++-- src/pubsub/event.rs | 2 +- src/roster.rs | 2 +- src/sasl.rs | 2 +- 18 files changed, 39 insertions(+), 39 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ba1742248ffe71a3ce5d45053f47159030da4b50..3f233d21b15fa678e2dae3df182edba04d670be8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,12 +18,12 @@ rust-nightly: - cargo build --verbose --no-default-features --features=$FEATURES - cargo test --verbose --no-default-features --features=$FEATURES -"rust-latest with features=compat": +"rust-latest with features=disable-validation": extends: rust-latest variables: - FEATURES: "compat" + FEATURES: "disable-validation" -"rust-nightly with features=compat": +"rust-nightly with features=disable-validation": extends: rust-nightly variables: - FEATURES: "compat" + FEATURES: "disable-validation" diff --git a/Cargo.toml b/Cargo.toml index 65935bab1f028d897229ee24541225749156fd59..0e3b9a36dbde483ea64a3f00074fcfd2d1a29199 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,5 +28,5 @@ try_from = "0.3.2" [features] # Build xmpp-parsers to make components instead of clients. component = [] -# Compatibility mode -compat = [] +# Disable validation of unknown attributes. +disable-validation = [] diff --git a/src/attention.rs b/src/attention.rs index c786f3137fce92ed8eb178329fda4ee4481a1da5..bd4c7a15e431cc8f5548cd837b96f97abbc59968 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -18,7 +18,7 @@ impl MessagePayload for Attention {} #[cfg(test)] mod tests { use super::*; - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -34,7 +34,7 @@ mod tests { Attention::try_from(elem).unwrap(); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_child() { let elem: Element = "" @@ -48,7 +48,7 @@ mod tests { assert_eq!(message, "Unknown child in attention element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/bind.rs b/src/bind.rs index bd09d4f3b36aa326f51790cc3f0ad0f905f6a2a0..9d3a894f8950c2aee20cda960de6b17acc6a4bac 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -112,7 +112,7 @@ mod tests { assert_eq!(bind, Bind::None); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_resource() { let elem: Element = "resource" diff --git a/src/blocking.rs b/src/blocking.rs index e12e7e49ae6757a078a2432892b3388f2551c60a..53417f7f749ccebc425fceeee2da39d63b03f337 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -168,7 +168,7 @@ mod tests { assert_eq!(unblock.items, two_items); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid() { let elem: Element = "" @@ -211,7 +211,7 @@ mod tests { assert_eq!(message, "Unknown attribute in unblock element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_non_empty_blocklist_request() { let elem: Element = "".parse().unwrap(); diff --git a/src/caps.rs b/src/caps.rs index 3befdc9bc2541de8d5d401c362f4733124eecb03..74042be31a368be7a0d5b756a7393ec0ad77a09c 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -233,7 +233,7 @@ mod tests { ); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_child() { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=".parse().unwrap(); diff --git a/src/chatstates.rs b/src/chatstates.rs index 9a972aa79a4e97a820c62ac1f76275936240b214..4eb173f11b796d1ef554f7f766638de486efef6f 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -63,7 +63,7 @@ mod tests { assert_eq!(message, "This is not a chatstate element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_child() { let elem: Element = "" @@ -77,7 +77,7 @@ mod tests { assert_eq!(message, "Unknown child in chatstate element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index d9a73b29b5fdd281c2091bb0f2bb5fa063003166..32dd41f6aeb7233bc5a236796ea7bfa6fa80418f 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -530,7 +530,7 @@ mod tests { assert_eq!(message, "Unknown value for 'creator' attribute."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_received() { let elem: Element = "".parse().unwrap(); @@ -596,7 +596,7 @@ mod tests { assert_eq!(message, "Unknown value for 'creator' attribute."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_checksum() { let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); @@ -637,7 +637,7 @@ mod tests { assert_eq!(range2.hashes, hashes); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_range() { let elem: Element = "" diff --git a/src/macros.rs b/src/macros.rs index daccec5c676346e14468723f34b9151e044972a2..aa89a45cbe2b6c429ef7c6300c75cc8fcc49621c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -248,7 +248,7 @@ macro_rules! check_ns_only { macro_rules! check_no_children { ($elem:ident, $name:tt) => { - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] for _ in $elem.children() { return Err(crate::error::Error::ParseError(concat!( "Unknown child in ", @@ -261,7 +261,7 @@ macro_rules! check_no_children { macro_rules! check_no_attributes { ($elem:ident, $name:tt) => { - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] for _ in $elem.attrs() { return Err(crate::error::Error::ParseError(concat!( "Unknown attribute in ", @@ -274,7 +274,7 @@ macro_rules! check_no_attributes { macro_rules! check_no_unknown_attributes { ($elem:ident, $name:tt, [$($attr:tt),*]) => ( - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] for (_attr, _) in $elem.attrs() { $( if _attr == $attr { diff --git a/src/message_correct.rs b/src/message_correct.rs index e0d22d6257322d3a31bccbb32d20ed6cfb44ce0c..3bba10e7a187abf3cb1fcdb2e116a9b014fda2f8 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -45,7 +45,7 @@ mod tests { Replace::try_from(elem).unwrap(); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 40528383da094ad9438d492d6ec92f95cf291540..fc596fa9c95adfe4f759cefead11bbabca89974e 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -142,7 +142,7 @@ mod tests { assert_eq!(elem, elem2); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_muc_invalid_attribute() { let elem: Element = "" diff --git a/src/muc/user.rs b/src/muc/user.rs index a104e319f9104715a22655c4901631cdad49d1a9..56c111e8ef18b7cd8a6be65a3544bdff4bb17efb 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -303,7 +303,7 @@ mod tests { assert!(elem.compare_to(&elem2)); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = " @@ -344,7 +344,7 @@ mod tests { assert_eq!(message, "Required attribute 'code' missing."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_status_invalid_child() { let elem: Element = " @@ -515,7 +515,7 @@ mod tests { assert_eq!(reason.0, "Reason".to_owned()); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_reason_invalid_attribute() { let elem: Element = " @@ -531,7 +531,7 @@ mod tests { assert_eq!(message, "Unknown attribute in reason element.".to_owned()); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_reason_invalid() { let elem: Element = " @@ -549,7 +549,7 @@ mod tests { assert_eq!(message, "Unknown child in reason element.".to_owned()); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_item_invalid_attr() { let elem: Element = " diff --git a/src/nick.rs b/src/nick.rs index 67a45ff9c5342ea6c2216ec4a4f99865e0aae859..03f544aaa9455e9d9b8894972098676b9f898df2 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -14,7 +14,7 @@ generate_elem_id!( #[cfg(test)] mod tests { use super::*; - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -49,7 +49,7 @@ mod tests { assert_eq!(elem1, elem2); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid() { let elem: Element = "" @@ -63,7 +63,7 @@ mod tests { assert_eq!(message, "Unknown child in nick element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/ping.rs b/src/ping.rs index 6f48dd5592941854555995db680452f7a901ccaf..018531205c3d16f9b6b681251fe12091b1e091ab 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -20,7 +20,7 @@ impl IqGetPayload for Ping {} #[cfg(test)] mod tests { use super::*; - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] use crate::error::Error; use minidom::Element; use try_from::TryFrom; @@ -43,7 +43,7 @@ mod tests { assert_eq!(elem1, elem2); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid() { let elem: Element = "" @@ -57,7 +57,7 @@ mod tests { assert_eq!(message, "Unknown child in ping element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "".parse().unwrap(); diff --git a/src/presence.rs b/src/presence.rs index 20d63e879e48ac4af6f1a147b30010a785d215cd..7e3e8aa8f78a6afb68bfd7430467580f96a7e527 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -570,7 +570,7 @@ mod tests { assert!(payload.is("test", "invalid")); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_status_child() { #[cfg(not(feature = "component"))] @@ -590,7 +590,7 @@ mod tests { assert_eq!(message, "Unknown child in status element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { #[cfg(not(feature = "component"))] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 0cad1cc0379fb9b7ad357821ad8f5a899057734b..0cef2c397c453bf00b9bddb5e08d43158a2a9e60 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -420,7 +420,7 @@ mod tests { assert_eq!(message, "Unknown child in event element."); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { let elem: Element = "" diff --git a/src/roster.rs b/src/roster.rs index c0d869d1d0079196f04dfa651fbdce888178d721..c13b0f59ad1eed7cc791c8ac512a56b721056ebd 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -245,7 +245,7 @@ mod tests { assert_eq!(roster.items[0].subscription, Subscription::Remove); } - #[cfg(not(feature = "compat"))] + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid() { let elem: Element = "" diff --git a/src/sasl.rs b/src/sasl.rs index 6f922901b2475da7cb72ecdcb6c55e47ddb30f17..45a56653fdc571d7df1b48165155732d5ba03e52 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -289,7 +289,7 @@ mod tests { ); } - #[cfg(feature = "compat")] + #[cfg(feature = "disable-validation")] #[test] fn failure_with_non_prefixed_text_lang() { let elem: Element = " From 04c5bcac1d45fdf990f5ee18d84ecfcab4cc43c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Thu, 20 Dec 2018 16:11:16 +0000 Subject: [PATCH 0789/1020] ci: split build and test stages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b4e77f06d233074e4efced741958cc6a524498c..cf5215c014f3dea2a872e706ee033404b8f4a39c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,16 +1,27 @@ stages: - build + - test -rust-latest: +rust-latest-build: stage: build image: rust:latest script: - cargo build --verbose - - cargo test --verbose -rust-nightly: +rust-nightly-build: stage: build image: rustlang/rust:nightly script: - cargo build --verbose + +rust-latest-test: + stage: test + image: rust:latest + script: + - cargo test --verbose + +rust-nightly-test: + stage: test + image: rustlang/rust:nightly + script: - cargo test --verbose From 635e8633a85075660a7e00750b715ffccb33c2f6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 13 Jan 2019 11:56:40 +0100 Subject: [PATCH 0790/1020] sasl: Document the reason for the unprefixed @lang test. --- src/sasl.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sasl.rs b/src/sasl.rs index 45a56653fdc571d7df1b48165155732d5ba03e52..db9b17e6fd3a90f071898c15cad65054fdd0dde3 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -289,9 +289,11 @@ mod tests { ); } + /// Some servers apparently use a non-namespaced 'lang' attribute, which is invalid as not part + /// of the schema. This tests whether we can parse it when disabling validation. #[cfg(feature = "disable-validation")] #[test] - fn failure_with_non_prefixed_text_lang() { + fn invalid_failure_with_non_prefixed_text_lang() { let elem: Element = " Invalid username or password From 62539cbae314052c44b5f1a9426d92560e61e384 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Jan 2019 22:50:22 +0100 Subject: [PATCH 0791/1020] macros: Add a singleton attribute. --- src/macros.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/macros.rs b/src/macros.rs index aa89a45cbe2b6c429ef7c6300c75cc8fcc49621c..d0dfbc0603df5dfd2e67abff8e7a10ef32ef1b00 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -107,6 +107,38 @@ macro_rules! generate_attribute { } } ); + ($(#[$meta:meta])* $elem:ident, $name:tt, ($(#[$meta_symbol:meta])* $symbol:ident => $value:tt)) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq)] + pub enum $elem { + $(#[$meta_symbol])* + $symbol, + /// Value when absent. + None, + } + impl ::std::str::FromStr for $elem { + type Err = crate::error::Error; + fn from_str(s: &str) -> Result { + Ok(match s { + $value => $elem::$symbol, + _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + }) + } + } + impl ::minidom::IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + match self { + $elem::$symbol => Some(String::from($value)), + $elem::None => None + } + } + } + impl ::std::default::Default for $elem { + fn default() -> $elem { + $elem::None + } + } + ); ($(#[$meta:meta])* $elem:ident, $name:tt, bool) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] From b6796d54e6e4eca02d1ceb53da03aa1bd0c6a945 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Jan 2019 22:51:02 +0100 Subject: [PATCH 0792/1020] roster: Simplify the @ask parsing using a singleton attribute. --- src/roster.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/roster.rs b/src/roster.rs index 1e049837cb9e7782e1a0806b91149f2b81e69e90..e86297275f14671d1681c30e05dafdd6b9c5ea1a 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -38,10 +38,10 @@ generate_attribute!( generate_attribute!( /// The sub-state of subscription with a contact. - Ask, "ask", { - /// Pending sub-state of the 'none' subscription state - Subscribe => "subscribe", - } + Ask, "ask", ( + /// Pending sub-state of the 'none' subscription state. + Subscribe => "subscribe" + ) ); generate_element!( @@ -58,8 +58,8 @@ generate_element!( /// Subscription status of this contact. subscription: Subscription = "subscription" => default, - /// - ask: Option = "ask" => optional_empty + /// Indicates “Pending Out” sub-states for this contact. + ask: Ask = "ask" => default, ], children: [ @@ -174,7 +174,7 @@ mod tests { ); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); assert_eq!(roster.items[0].subscription, Subscription::Both); - assert_eq!(roster.items[0].ask, None); + assert_eq!(roster.items[0].ask, Ask::None); assert_eq!( roster.items[0].groups, vec!(Group::from_str("Friends").unwrap()) @@ -186,7 +186,7 @@ mod tests { ); assert_eq!(roster.items[3].name, Some(String::from("MyContact"))); assert_eq!(roster.items[3].subscription, Subscription::None); - assert_eq!(roster.items[3].ask, Some(Ask::Subscribe)); + assert_eq!(roster.items[3].ask, Ask::Subscribe); assert_eq!( roster.items[3].groups, vec!(Group::from_str("MyBuddies").unwrap()) From 2a7cf487a49bdd8865fe674e317c9c437d4531e2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 13 Jan 2019 12:06:37 +0100 Subject: [PATCH 0793/1020] roster: Also test for the size of the new Ask attribute. --- src/roster.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/roster.rs b/src/roster.rs index e86297275f14671d1681c30e05dafdd6b9c5ea1a..b40d2d5dd3741e0d77e1b8d8216ccf813fe6bc64 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -103,6 +103,7 @@ mod tests { fn test_size() { assert_size!(Group, 12); assert_size!(Subscription, 1); + assert_size!(Ask, 1); assert_size!(Item, 64); assert_size!(Roster, 24); } @@ -112,6 +113,7 @@ mod tests { fn test_size() { assert_size!(Group, 24); assert_size!(Subscription, 1); + assert_size!(Ask, 1); assert_size!(Item, 128); assert_size!(Roster, 48); } From 409a1dafa9c8165b533cc66d4a2b0d1bd5a6bb1f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 13 Jan 2019 12:39:51 +0100 Subject: [PATCH 0794/1020] Move Error, helpers and macros into a util module. --- src/attention.rs | 2 +- src/bind.rs | 2 +- src/blocking.rs | 2 +- src/bookmarks.rs | 2 +- src/caps.rs | 2 +- src/chatstates.rs | 2 +- src/component.rs | 2 +- src/data_forms.rs | 2 +- src/date.rs | 2 +- src/delay.rs | 4 +- src/disco.rs | 4 +- src/ecaps2.rs | 2 +- src/eme.rs | 2 +- src/forwarding.rs | 2 +- src/hashes.rs | 4 +- src/ibb.rs | 4 +- src/ibr.rs | 4 +- src/idle.rs | 2 +- src/iq.rs | 4 +- src/jingle.rs | 2 +- src/jingle_ft.rs | 2 +- src/jingle_ibb.rs | 2 +- src/jingle_message.rs | 2 +- src/jingle_s5b.rs | 4 +- src/lib.rs | 11 +---- src/mam.rs | 2 +- src/media_element.rs | 4 +- src/message.rs | 4 +- src/message_correct.rs | 2 +- src/muc/muc.rs | 4 +- src/muc/user.rs | 4 +- src/nick.rs | 2 +- src/ping.rs | 2 +- src/presence.rs | 4 +- src/pubsub/event.rs | 4 +- src/pubsub/pubsub.rs | 4 +- src/roster.rs | 4 +- src/rsm.rs | 4 +- src/sasl.rs | 4 +- src/stanza_error.rs | 2 +- src/stanza_id.rs | 2 +- src/{ => util}/compare_elements.rs | 0 src/{ => util}/error.rs | 0 src/{ => util}/helpers.rs | 2 +- src/{ => util}/macros.rs | 76 +++++++++++++++--------------- src/version.rs | 2 +- 46 files changed, 98 insertions(+), 107 deletions(-) rename src/{ => util}/compare_elements.rs (100%) rename src/{ => util}/error.rs (100%) rename src/{ => util}/helpers.rs (97%) rename src/{ => util}/macros.rs (87%) diff --git a/src/attention.rs b/src/attention.rs index bd4c7a15e431cc8f5548cd837b96f97abbc59968..1910c8c13bf75657512b3db9b07c6e549bed0f89 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -19,7 +19,7 @@ impl MessagePayload for Attention {} mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/bind.rs b/src/bind.rs index 9d3a894f8950c2aee20cda960de6b17acc6a4bac..8ded3e6ac10c8bb01e61ba9bb0d3c56b1f1d4d24 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; use jid::Jid; diff --git a/src/blocking.rs b/src/blocking.rs index 53417f7f749ccebc425fceeee2da39d63b03f337..2b021185995c8427f28bcf1c921a780a60f57076 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use jid::Jid; diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 6048b2f074023e7df09ef2103777d42fcdd6e0c9..12f95707fc8f7c7bfbcc61d938be68aae9aa7b2e 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -72,7 +72,7 @@ impl Storage { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use minidom::Element; use try_from::TryFrom; diff --git a/src/caps.rs b/src/caps.rs index 74042be31a368be7a0d5b756a7393ec0ad77a09c..73c79378ffa1a613c406977599e150303db59f35 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -6,7 +6,7 @@ use crate::data_forms::DataForm; use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}; -use crate::error::Error; +use crate::util::error::Error; use crate::hashes::{Algo, Hash}; use crate::ns; use crate::presence::PresencePayload; diff --git a/src/chatstates.rs b/src/chatstates.rs index 4eb173f11b796d1ef554f7f766638de486efef6f..2bf2d1b1fe0cbd91cc4803101bb941d03add554a 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -32,7 +32,7 @@ impl MessagePayload for ChatState {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use crate::ns; use minidom::Element; use try_from::TryFrom; diff --git a/src/component.rs b/src/component.rs index cb42fd7a319e9b2223e93b8ff6fb2b104ced3af4..722495b8a02b6aea322c16a990070586e299ea4b 100644 --- a/src/component.rs +++ b/src/component.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::helpers::PlainText; +use crate::util::helpers::PlainText; use digest::Digest; use sha1::Sha1; diff --git a/src/data_forms.rs b/src/data_forms.rs index 9d7608dfc3cceb0fa504a77375568f9cda421542..30dbd29aa478ac2c9dd918bb3de0e7c08f4196c7 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::media_element::MediaElement; use crate::ns; use minidom::Element; diff --git a/src/date.rs b/src/date.rs index a6a99e0743ac9f78753091555fb48026eba97004..1d3389b0651623ffc7ec802a64e9de6106032b1a 100644 --- a/src/date.rs +++ b/src/date.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use chrono::{DateTime as ChronoDateTime, FixedOffset}; use minidom::{ElementEmitter, IntoAttributeValue, IntoElements}; use std::str::FromStr; diff --git a/src/delay.rs b/src/delay.rs index 4defc94ace00bfb8f219af40d953e62ef3a12a8d..a02b44b1a4338929806b75529c2af166cb945191 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::date::DateTime; -use crate::helpers::PlainText; +use crate::util::helpers::PlainText; use crate::message::MessagePayload; use crate::presence::PresencePayload; use jid::Jid; @@ -32,7 +32,7 @@ impl PresencePayload for Delay {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::str::FromStr; use try_from::TryFrom; diff --git a/src/disco.rs b/src/disco.rs index 79d89b26d0dfe345bc691ab7e89df4e60b1aeb7c..d61cf361dc3b3258bc238aa11f391fe22dfb282c 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::data_forms::{DataForm, DataFormType}; -use crate::error::Error; +use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use jid::Jid; @@ -236,7 +236,7 @@ impl IqResultPayload for DiscoItemsResult {} #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 2fc011b806408af9f4ef5300ebc67f6ab338970b..846fa6da0d75e1720e4bdff06a9a3e33cd3ffad6 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -156,7 +156,7 @@ pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery { #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/eme.rs b/src/eme.rs index 4b8ffbe6bcb2a6ee0e990b9a8b73ad9094e05e2a..cd24e43d03b7bf95799d75b86b086d3b280d8ab1 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -24,7 +24,7 @@ impl MessagePayload for ExplicitMessageEncryption {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/forwarding.rs b/src/forwarding.rs index e36ebf8eed09979f79e984e5a2d0ffe937e01f6b..5dfa4aea0413a754808f3ddbbf4f1601ac85c76e 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -26,7 +26,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/hashes.rs b/src/hashes.rs index a7773ee1cda05bc2b509270586cb361cbd59cd38..041b81fc4795415b230bda7d2393a40a11e16cab 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; -use crate::helpers::Base64; +use crate::util::error::Error; +use crate::util::helpers::Base64; use base64; use minidom::IntoAttributeValue; use std::str::FromStr; diff --git a/src/ibb.rs b/src/ibb.rs index f2aff83efe950cbd8475382dc054464d09393330..960761187becc66ba7d9e1f7dd2ec02922d635ba 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::helpers::Base64; +use crate::util::helpers::Base64; use crate::iq::IqSetPayload; generate_id!( @@ -72,7 +72,7 @@ impl IqSetPayload for Close {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; use try_from::TryFrom; diff --git a/src/ibr.rs b/src/ibr.rs index 39aa229d68dbfabcc353a2fdd7f9c64d1b587758..f19c47c131ba137644addac47d35d03ee884ee63 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::data_forms::DataForm; -use crate::error::Error; +use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use minidom::Element; @@ -118,7 +118,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/idle.rs b/src/idle.rs index df2d005d67b6a6361b3e02f51d961ee534f528a5..bbf3f15114546636f20c49de18edec939d15ce8e 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -21,7 +21,7 @@ impl PresencePayload for Idle {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; use std::str::FromStr; diff --git a/src/iq.rs b/src/iq.rs index d4ee8b4197fc5d696017fdabff473adc0e21798b..b155b97c928a7da5072e561d6d6d78888085adac 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use crate::stanza_error::StanzaError; use jid::Jid; @@ -218,7 +218,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use crate::disco::DiscoInfoQuery; use crate::stanza_error::{DefinedCondition, ErrorType}; diff --git a/src/jingle.rs b/src/jingle.rs index 2a3c09669e2eb1ec0a73901024ee99a2afd12a81..63c78a0f808ec9b678502ca1a19ddcc2e44ba2a8 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 32dd41f6aeb7233bc5a236796ea7bfa6fa80418f..fce18cdf47ff7d8eda6b0eb153b88364ab2249fb 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::date::DateTime; -use crate::error::Error; +use crate::util::error::Error; use crate::hashes::Hash; use crate::jingle::{ContentId, Creator}; use crate::ns; diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 1addc1ee4f9c5d41a87494cab0f3af5e9df17ced..08370e1f7f9dd328abb9dd3c2194547c20422111 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -24,7 +24,7 @@ attributes: [ #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; use try_from::TryFrom; diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 02c1e9924fb3e3449fce3c132ca26e7ee509aa7d..1a520223bcfadfa680e3526435d2c7db0b40724d 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::jingle::SessionId; use crate::ns; use minidom::Element; diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index e85390caa2b89b1b62a3cd2dcbbd226feac00db0..58c4a27dd36a705bb5a6bd159459a1700655116f 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use jid::Jid; use minidom::Element; @@ -275,7 +275,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/lib.rs b/src/lib.rs index 36eb788250d34834804e416b93a55a1c839e45fc..ed16e05ecf552375532a5de139310a621ace2f39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,20 +26,11 @@ pub use minidom::Element; -/// Error type returned by every parser on failure. -pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; -/// Various helpers. -mod helpers; -/// Helper macros to parse and serialise more easily. #[macro_use] -mod macros; - -#[cfg(test)] -/// Namespace-aware comparison for tests -mod compare_elements; +mod util; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod bind; diff --git a/src/mam.rs b/src/mam.rs index 8124be9feb760ef3132e9bf78b5d4bc81f45571f..453b4f606deeafbf321d58867e27564969f7e94f 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::data_forms::DataForm; -use crate::error::Error; +use crate::util::error::Error; use crate::forwarding::Forwarded; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::message::MessagePayload; diff --git a/src/media_element.rs b/src/media_element.rs index f3a623870c06c53da518519365e23857d804e378..639b71cea3401fcf764ffba7a54e36c760328900 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::helpers::TrimmedPlainText; +use crate::util::helpers::TrimmedPlainText; generate_element!( /// Represents an URI used in a media element. @@ -46,7 +46,7 @@ generate_element!( mod tests { use super::*; use crate::data_forms::DataForm; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; use try_from::TryFrom; diff --git a/src/message.rs b/src/message.rs index 159bcd2d8fe76492cb2a92236e24226ddf837239..fe32300e5d353a33aefda76822c6fbd66f436dfe 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use jid::Jid; use minidom::Element; @@ -254,7 +254,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[cfg(target_pointer_width = "32")] diff --git a/src/message_correct.rs b/src/message_correct.rs index 3bba10e7a187abf3cb1fcdb2e116a9b014fda2f8..05663c8daa34cb40755378242a03e74b1c37d586 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -21,7 +21,7 @@ impl MessagePayload for Replace {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/muc/muc.rs b/src/muc/muc.rs index fc596fa9c95adfe4f759cefead11bbabca89974e..9d29cc963c924481d79855c4e7791aebfa4e63b3 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -102,8 +102,8 @@ impl Muc { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; - use crate::error::Error; + use crate::util::compare_elements::NamespaceAwareCompare; + use crate::util::error::Error; use minidom::Element; use std::str::FromStr; use try_from::TryFrom; diff --git a/src/muc/user.rs b/src/muc/user.rs index 56c111e8ef18b7cd8a6be65a3544bdff4bb17efb..46a893337ea7b8525c35c67850dcc31382a45487 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use jid::Jid; use minidom::Element; @@ -238,7 +238,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use std::error::Error as StdError; #[test] diff --git a/src/nick.rs b/src/nick.rs index 03f544aaa9455e9d9b8894972098676b9f898df2..1fbeb1026752abc44799299b2b271f1fa3f450f2 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -15,7 +15,7 @@ generate_elem_id!( mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/ping.rs b/src/ping.rs index 018531205c3d16f9b6b681251fe12091b1e091ab..62bd0b1a2e6c215f644864d43c277833ca84787a 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -21,7 +21,7 @@ impl IqGetPayload for Ping {} mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use try_from::TryFrom; diff --git a/src/presence.rs b/src/presence.rs index 7e3e8aa8f78a6afb68bfd7430467580f96a7e527..4a464f9c3ced4dfb767f71f38404ef203479fb09 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -5,7 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use jid::Jid; use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; @@ -354,7 +354,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 0cef2c397c453bf00b9bddb5e08d43158a2a9e60..4e8b9311b167292fb9e11a6b1c00ba22223bac1b 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -6,7 +6,7 @@ use crate::data_forms::DataForm; use crate::date::DateTime; -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; use jid::Jid; @@ -298,7 +298,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[test] diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index e406d867a0eb126ea0c4c901e2c48ef53fc883b3..637b050aea1d04716d867789effcec21d862f9ea 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::data_forms::DataForm; -use crate::error::Error; +use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; @@ -540,7 +540,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; #[test] fn create() { diff --git a/src/roster.rs b/src/roster.rs index b40d2d5dd3741e0d77e1b8d8216ccf813fe6bc64..29086aec2ae7777b00ffebebcf072a4e2587c458 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -92,8 +92,8 @@ impl IqResultPayload for Roster {} #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; - use crate::error::Error; + use crate::util::compare_elements::NamespaceAwareCompare; + use crate::util::error::Error; use minidom::Element; use std::str::FromStr; use try_from::TryFrom; diff --git a/src/rsm.rs b/src/rsm.rs index 0cd048ec854d5c4bb11c16906093356c0498aaff..1dd5f47f71ccfd4c0d93971379a1385b1b6a6aa6 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::ns; use minidom::Element; use try_from::TryFrom; @@ -180,7 +180,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/sasl.rs b/src/sasl.rs index db9b17e6fd3a90f071898c15cad65054fdd0dde3..fecdbfa6b6ca7017593af6d3df9bd7839daa1e9d 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; -use crate::helpers::Base64; +use crate::util::error::Error; +use crate::util::helpers::Base64; use crate::ns; use minidom::Element; use std::collections::BTreeMap; diff --git a/src/stanza_error.rs b/src/stanza_error.rs index c5e62026848b668e5d269d0fdeb8c4f1e3a7eeef..7a4d421aad5505ce9f02a93c2169a4180ba6bd1b 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 2bac2de3e1d7be592ebc053bf1a84d30edb6187a..c9cefad724a1c8583bfdc908a56d777068ab915b 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -37,7 +37,7 @@ impl MessagePayload for OriginId {} #[cfg(test)] mod tests { use super::*; - use crate::error::Error; + use crate::util::error::Error; use minidom::Element; use std::str::FromStr; use try_from::TryFrom; diff --git a/src/compare_elements.rs b/src/util/compare_elements.rs similarity index 100% rename from src/compare_elements.rs rename to src/util/compare_elements.rs diff --git a/src/error.rs b/src/util/error.rs similarity index 100% rename from src/error.rs rename to src/util/error.rs diff --git a/src/helpers.rs b/src/util/helpers.rs similarity index 97% rename from src/helpers.rs rename to src/util/helpers.rs index e4d2324475a6eb3d13bb27eab7db787c4638d49e..fad0b671cc4670153339cfc31e2a3e503c375938 100644 --- a/src/helpers.rs +++ b/src/util/helpers.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::error::Error; +use crate::util::error::Error; use base64; /// Codec for plain text content. diff --git a/src/macros.rs b/src/util/macros.rs similarity index 87% rename from src/macros.rs rename to src/util/macros.rs index d0dfbc0603df5dfd2e67abff8e7a10ef32ef1b00..fd3a0ada1febe68c177c15e1a4e57dc7cf4968db 100644 --- a/src/macros.rs +++ b/src/util/macros.rs @@ -25,7 +25,7 @@ macro_rules! get_attr { match $elem.attr($attr) { Some($value) => $func, None => { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "Required attribute '", $attr, "' missing." @@ -58,11 +58,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -84,11 +84,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { Ok(match s { $($b => $elem::$a),+, - _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -117,11 +117,11 @@ macro_rules! generate_attribute { None, } impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result { Ok(match s { $value => $elem::$symbol, - _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -149,12 +149,12 @@ macro_rules! generate_attribute { False, } impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result { Ok(match s { "true" | "1" => $elem::True, "false" | "0" => $elem::False, - _ => return Err(crate::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), }) } } @@ -188,14 +188,14 @@ macro_rules! generate_element_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); Ok(match elem.name() { $($enum_name => $elem::$enum,)+ - _ => return Err(crate::error::Error::ParseError(concat!("This is not a ", $name, " element."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("This is not a ", $name, " element."))), }) } } @@ -227,14 +227,14 @@ macro_rules! generate_attribute_enum { ),+ } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); Ok(match get_attr!(elem, $attr, required) { $($enum_name => $elem::$enum,)+ - _ => return Err(crate::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), + _ => return Err(crate::util::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), }) } } @@ -257,7 +257,7 @@ macro_rules! check_self { }; ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => { if !$elem.is($name, crate::ns::$ns) { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "This is not a ", $pretty_name, " element." @@ -269,7 +269,7 @@ macro_rules! check_self { macro_rules! check_ns_only { ($elem:ident, $name:tt, $ns:ident) => { if !$elem.has_ns(crate::ns::$ns) { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "This is not a ", $name, " element." @@ -282,7 +282,7 @@ macro_rules! check_no_children { ($elem:ident, $name:tt) => { #[cfg(not(feature = "disable-validation"))] for _ in $elem.children() { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "Unknown child in ", $name, " element." @@ -295,7 +295,7 @@ macro_rules! check_no_attributes { ($elem:ident, $name:tt) => { #[cfg(not(feature = "disable-validation"))] for _ in $elem.attrs() { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "Unknown attribute in ", $name, " element." @@ -313,7 +313,7 @@ macro_rules! check_no_unknown_attributes { continue; } )* - return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); + return Err(crate::util::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); } ); } @@ -325,9 +325,9 @@ macro_rules! generate_empty_element { pub struct $elem; impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::error::Error; + type Err = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -351,8 +351,8 @@ macro_rules! generate_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } @@ -371,15 +371,15 @@ macro_rules! generate_elem_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = crate::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { + type Err = crate::util::error::Error; + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -440,7 +440,7 @@ macro_rules! do_parse_elem { }; ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { if $temp.is_some() { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "Element ", $parent_name, " must not have more than one ", @@ -452,7 +452,7 @@ macro_rules! do_parse_elem { }; ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { if $temp.is_some() { - return Err(crate::error::Error::ParseError(concat!( + return Err(crate::util::error::Error::ParseError(concat!( "Element ", $parent_name, " must not have more than one ", @@ -472,7 +472,7 @@ macro_rules! finish_parse_elem { $temp }; ($temp:ident: Required = $name:tt, $parent_name:tt) => { - $temp.ok_or(crate::error::Error::ParseError(concat!( + $temp.ok_or(crate::util::error::Error::ParseError(concat!( "Missing child ", $name, " in ", @@ -540,9 +540,9 @@ macro_rules! generate_element { } impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::error::Error; + type Err = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::error::Error> { + fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); $( @@ -555,7 +555,7 @@ macro_rules! generate_element { continue; } )* - return Err(crate::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); + return Err(crate::util::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); } Ok($elem { $( diff --git a/src/version.rs b/src/version.rs index c9e08c32199060179e7be0ab3b3229c3ce9a51d0..5aa4d2fbb2acbfe5e3d3d2e8207e57d43958a195 100644 --- a/src/version.rs +++ b/src/version.rs @@ -41,7 +41,7 @@ impl IqResultPayload for VersionResult {} #[cfg(test)] mod tests { use super::*; - use crate::compare_elements::NamespaceAwareCompare; + use crate::util::compare_elements::NamespaceAwareCompare; use minidom::Element; use try_from::TryFrom; From 15b9e65a5d84b9b1b9dc6e6cf9b7b350b270773f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 13 Jan 2019 14:45:13 +0100 Subject: [PATCH 0795/1020] util: Add forgotten mod.rs file. --- src/util/mod.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/util/mod.rs diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..2a595f98dcf136ad651002cee79194cd64c48733 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,19 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// Error type returned by every parser on failure. +pub mod error; + +/// Various helpers. +pub(crate) mod helpers; + +/// Helper macros to parse and serialise more easily. +#[macro_use] +mod macros; + +#[cfg(test)] +/// Namespace-aware comparison for tests +pub(crate) mod compare_elements; From 1225bf7027f624ad7cc0624ebe44943bc410e781 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Jan 2019 21:05:19 +0100 Subject: [PATCH 0796/1020] prefix support DRY --- src/xmpp_codec.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 199627ba4ffedbd4983dda7451ae6e06e95c4747..69ab5fc23c82b43370d0408a0bb86ce2c0632f11 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -14,6 +14,7 @@ use std::io; use std::iter::FromIterator; use std::rc::Rc; use std::str::from_utf8; +use std::borrow::Cow; use tokio_codec::{Decoder, Encoder}; use xml5ever::interface::Attribute; use xml5ever::tokenizer::{Tag, TagKind, Token, TokenSink, XmlTokenizer}; @@ -104,11 +105,12 @@ impl ParserSink { "xmlns" => (), _ if is_prefix_xmlns(attr) => (), _ => { - if let Some(ref prefix) = attr.name.prefix { - el_builder = el_builder.attr(format!("{}:{}", prefix, attr.name.local), attr.value.as_ref()); + let attr_name = if let Some(ref prefix) = attr.name.prefix { + Cow::Owned(format!("{}:{}", prefix, attr.name.local)) } else { - el_builder = el_builder.attr(attr.name.local.as_ref(), attr.value.as_ref()); - } + Cow::Borrowed(attr.name.local.as_ref()) + }; + el_builder = el_builder.attr(attr_name, attr.value.as_ref()); } } } From 49c0f6e7a6cabc4c08571aaf895209fef73663cf Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 14 Jan 2019 00:02:02 +0100 Subject: [PATCH 0797/1020] client: add new_with_jid() to reuse parsed Jid Should fix Gitlab issue #2 --- src/client/mod.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 37670131b72b44e078b99c79b01e1c7809dbf904..146d51a40fdd57a23775dcf8b4775f2334605eab 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -45,12 +45,19 @@ impl Client { /// and yield events. pub fn new(jid: &str, password: &str) -> Result { let jid = Jid::from_str(jid)?; + let client = Self::new_with_jid(jid, password); + Ok(client) + } + + /// Start a new client given that the JID is already parsed. + pub fn new_with_jid(jid: Jid, password: &str) -> Self { let password = password.to_owned(); let connect = Self::make_connect(jid.clone(), password.clone()); - Ok(Client { + let client = Client { jid, state: ClientState::Connecting(Box::new(connect)), - }) + }; + client } fn make_connect(jid: Jid, password: String) -> impl Future { From 5eb211c9fc7bd202bbbe3369b5003247865c923e Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 14 Jan 2019 02:49:50 +0100 Subject: [PATCH 0798/1020] tokio-xmpp 0.2.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ee3b80a49deb900dc4a568777042806a21962afe..e43d5d28c380ea9d4163a3b6b2fa7875834d44a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-xmpp" -version = "0.2.1" +version = "0.2.2" authors = ["Astro ", "Emmanuel Gil Peyrot ", "pep ", "O01eg "] description = "Asynchronous XMPP for Rust with tokio" license = "MPL-2.0" From 107e66f987ca988be6a169186f733553d8b4b9d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:17:33 +0100 Subject: [PATCH 0799/1020] ChangeLog: Add imminent version 0.12.0. --- ChangeLog | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ChangeLog b/ChangeLog index ffe9b95f53389a7bd5aaaa98599a07f9c57dde39..91d25b2945e1ffe85e28e9a085c98eb26bbf806e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +Version 0.12.0: +2018-09-20 Emmanuel Gil Peyrot + * Breaking changes: + - Update dependencies. + - Switch to git, upstream is now available at + https://gitlab.com/xmpp-rs/xmpp-parsers + - Switch to Edition 2018, this removes support for rustc + versions older than 1.31. + - Implement support for XEP-0030 2.5rc3, relaxing the ordering + of children in disco#info. + * Improvements: + - Test for struct size, to keep them known and avoid bloat. + - Add various constructors to make the API easier to use. + - Add forgotten 'ask' attribute on roster item (thanks O01eg!). + - Use cargo-fmt on the codebase, to lower the barrier of entry. + - Add a disable-validation feature, disabling many checks + xmpp-parsers is doing. This should be used for software + which want to let invalid XMPP pass through instead of being + rejected as invalid (thanks Astro-!). + Version 0.11.1: 2018-09-20 Emmanuel Gil Peyrot * Improvements: From 5efc64490ca34642c080d7ffc07f6407f703fa3b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:22:07 +0100 Subject: [PATCH 0800/1020] lib: Reexport Jid from the jid crate. --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index ed16e05ecf552375532a5de139310a621ace2f39..1bb6263ad59c7ae20c9c803e50ba2099f054c87b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,7 @@ #![deny(missing_docs)] pub use minidom::Element; +pub use jid::Jid; /// XML namespace definitions used through XMPP. pub mod ns; From 018a30309011578a24a964f783c180bd3390cd86 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:23:44 +0100 Subject: [PATCH 0801/1020] ChangeLog: Document why Jid is getting reexported. --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 91d25b2945e1ffe85e28e9a085c98eb26bbf806e..017b2e15386b7dc66df02facef05ce48edf9f3bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,8 @@ Version 0.12.0: * Improvements: - Test for struct size, to keep them known and avoid bloat. - Add various constructors to make the API easier to use. + - Reexport Jid from the jid crate, to avoid any weird issue on + using different incompatible versions of the same crate. - Add forgotten 'ask' attribute on roster item (thanks O01eg!). - Use cargo-fmt on the codebase, to lower the barrier of entry. - Add a disable-validation feature, disabling many checks From 1f260cfe8610ba5079fe185db93b94a72fae6f10 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:27:54 +0100 Subject: [PATCH 0802/1020] =?UTF-8?q?Prepare=20for=20release=C2=A00.5.3.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 733468e2183487d5616e5a0fd3ce4069f41da527..358538efda87382fa2a0276cf4877137872a7a5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Version 0.5.3, released 2019-01-16: + * Updates + - Link Mauve bumped the minidom dependency version. + - Use Edition 2018, putting the baseline rustc version to 1.31. + - Run cargo-fmt on the code, to lower the barrier of entry. + Version 0.5.2, released 2018-07-31: * Updates - Astro bumped the minidom dependency version. diff --git a/Cargo.toml b/Cargo.toml index d309aef6d50618c86f423581201e7404609af509..b29a3c3c9e52f6d6a07b81c1fda28800b2e257d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.5.2" +version = "0.5.3" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From 66b87257ead0fe3ea4136b03cab80e7e31a01945 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:32:55 +0100 Subject: [PATCH 0803/1020] Cargo.toml: Bump minidom and jid. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0e3b9a36dbde483ea64a3f00074fcfd2d1a29199..843dde156a603585b2d60caff4e62b9983a3faed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ license = "MPL-2.0" edition = "2018" [dependencies] -minidom = "0.9.1" -jid = { version = "0.5.2", features = ["minidom"] } +minidom = "0.10.0" +jid = { version = "0.5.3", features = ["minidom"] } base64 = "0.10" digest = "0.8" sha-1 = "0.8" From 9946c5dc5989aac481e3b9fe59e819911e1c3fcb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:45:19 +0100 Subject: [PATCH 0804/1020] Cargo.toml: Update the homepage and repository. --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 843dde156a603585b2d60caff4e62b9983a3faed..374d8973b5594431df5ae6358b00eb111057f9ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,9 @@ authors = [ "Maxime “pep” Buquet ", ] description = "Collection of parsers and serialisers for XMPP extensions" -homepage = "https://hg.linkmauve.fr/xmpp-parsers" -repository = "https://hg.linkmauve.fr/xmpp-parsers" -keywords = ["xmpp", "xml"] +homepage = "https://gitlab.com/xmpp-rs/xmpp-parsers" +repository = "https://gitlab.com/xmpp-rs/xmpp-parsers" +keywords = ["xmpp", "jabber", "xml"] categories = ["parsing", "network-programming"] license = "MPL-2.0" edition = "2018" From e3d6605659562921a2332f110e98cae8dec92005 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 13:48:01 +0100 Subject: [PATCH 0805/1020] Cargo.toml: Release version 0.12.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 374d8973b5594431df5ae6358b00eb111057f9ce..05cb4f83b84f14ae4ba1fd748607d1bfe7d81bf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.11.1" +version = "0.12.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From bfd90be0c9a06b1ad42f8a98e1a18d68879f0d17 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 14:32:51 +0100 Subject: [PATCH 0806/1020] lib: Reexport JidParseError from the jid crate. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1bb6263ad59c7ae20c9c803e50ba2099f054c87b..7a291f7ee3927a63e596afd0ef4e86c9c6ea0970 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ #![deny(missing_docs)] pub use minidom::Element; -pub use jid::Jid; +pub use jid::{Jid, JidParseError}; /// XML namespace definitions used through XMPP. pub mod ns; From 0b38be9ec9ec11d341a015571c65b7cb406e7613 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 14:33:08 +0100 Subject: [PATCH 0807/1020] ChangeLog: Fix release date of 0.12.0. --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 017b2e15386b7dc66df02facef05ce48edf9f3bd..fb3c8311d753bcf056ebd0dcd87cc9ea8688e240 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ Version 0.12.0: -2018-09-20 Emmanuel Gil Peyrot +2019-01-16 Emmanuel Gil Peyrot * Breaking changes: - Update dependencies. - Switch to git, upstream is now available at From 5982f8180509baad972d3f611fe548620bb82dbf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 14:33:19 +0100 Subject: [PATCH 0808/1020] ChangeLog: Add imminent 0.12.1 release. --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index fb3c8311d753bcf056ebd0dcd87cc9ea8688e240..2aff6b309f7dbdd20ea37ff79cd8a0c84a502092 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version 0.12.1: +2019-01-16 Emmanuel Gil Peyrot + * Improvements: + - Reexport missing JidParseError from the jid crate. + Version 0.12.0: 2019-01-16 Emmanuel Gil Peyrot * Breaking changes: From 6660ca4c869191f92173c180588e77d20785f171 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 14:33:37 +0100 Subject: [PATCH 0809/1020] Cargo.toml: Release version 0.12.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 05cb4f83b84f14ae4ba1fd748607d1bfe7d81bf2..c3a4afe36592cafd7438538947e7fe450c77e0a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.12.0" +version = "0.12.1" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From b59ca1cbcbce4d7afed7b4c5a260d2812aaf4472 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 15:22:51 +0100 Subject: [PATCH 0810/1020] lib: Reexport TryFrom and util::error::Error. --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 7a291f7ee3927a63e596afd0ef4e86c9c6ea0970..aea3e737a0aa977d3e5f05ca816f712070a0f68a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,8 @@ pub use minidom::Element; pub use jid::{Jid, JidParseError}; +pub use try_from::TryFrom; +pub use crate::util::error::Error; /// XML namespace definitions used through XMPP. pub mod ns; From f28a27185b9a861024397c07ea2c1f4068c523f8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 15:23:00 +0100 Subject: [PATCH 0811/1020] ChangeLog: Add imminent 0.12.2 release. --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 2aff6b309f7dbdd20ea37ff79cd8a0c84a502092..1c022a14b2a797e0628c581e90c616db9a8f496e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version 0.12.2: +2019-01-16 Emmanuel Gil Peyrot + * Improvements: + - Reexport missing util::error::Error and try_from::TryFrom. + Version 0.12.1: 2019-01-16 Emmanuel Gil Peyrot * Improvements: From 71603979127b5bf03b694af265ed305d9dfc2920 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 15:23:07 +0100 Subject: [PATCH 0812/1020] Cargo.toml: Release version 0.12.2. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c3a4afe36592cafd7438538947e7fe450c77e0a5..cf9ae10b937b6f662050aec715dd00e3749bdeb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.12.1" +version = "0.12.2" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 62e9dcc02293e0d77fc94b4f4cc079979d58daca Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 16 Jan 2019 14:35:38 +0100 Subject: [PATCH 0813/1020] Bump xmpp-parsers to 0.12.2. This also removes minidom, jid and try_from as direct dependencies, since they are now reexported by xmpp-parsers. --- Cargo.toml | 7 ++----- examples/echo_bot.rs | 4 +--- examples/echo_component.rs | 4 +--- src/client/auth.rs | 2 +- src/client/bind.rs | 2 +- src/client/mod.rs | 3 +-- src/component/mod.rs | 3 +-- src/error.rs | 2 +- src/event.rs | 2 +- src/starttls.rs | 3 +-- src/stream_start.rs | 3 +-- src/xmpp_codec.rs | 2 +- src/xmpp_stream.rs | 3 +-- 13 files changed, 14 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e43d5d28c380ea9d4163a3b6b2fa7875834d44a7..c397ed40be7620798b5c561757d37cd9dce2bf37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,15 +18,12 @@ tokio-io = "0.1" tokio-codec = "0.1" bytes = "0.4" xml5ever = "0.12" -minidom = "0.9" native-tls = "0.2" tokio-tls = "0.2" sasl = "0.4" -jid = { version = "0.5", features = ["minidom"] } trust-dns-resolver = "0.9.1" trust-dns-proto = "0.4.0" -xmpp-parsers = "0.11" +xmpp-parsers = "0.12.2" idna = "0.1" -try_from = "0.2" -quick-xml = "0.12" +quick-xml = "0.13" derive-error = "0.0.4" diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 80ffc97fcc142e4e035d3488abb05379a1c53008..24b101079d76d0800a4d46f73262d50eea99a594 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,11 +1,9 @@ use futures::{future, Sink, Stream}; -use jid::Jid; -use minidom::Element; use std::env::args; use std::process::exit; use tokio::runtime::current_thread::Runtime; use tokio_xmpp::Client; -use try_from::TryFrom; +use xmpp_parsers::{Jid, Element, TryFrom}; use xmpp_parsers::message::{Body, Message, MessageType}; use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType}; diff --git a/examples/echo_component.rs b/examples/echo_component.rs index 348b06a4d2d812303943b996e514a966b945f610..618032e324a422621c168df9e9a986f5f6fa282e 100644 --- a/examples/echo_component.rs +++ b/examples/echo_component.rs @@ -1,12 +1,10 @@ use futures::{future, Sink, Stream}; -use jid::Jid; -use minidom::Element; use std::env::args; use std::process::exit; use std::str::FromStr; use tokio::runtime::current_thread::Runtime; use tokio_xmpp::Component; -use try_from::TryFrom; +use xmpp_parsers::{Jid, Element, TryFrom}; use xmpp_parsers::message::{Body, Message, MessageType}; use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType}; diff --git a/src/client/auth.rs b/src/client/auth.rs index 588b1a73e14c757c356244f8884a6efe1680bc3c..d961f7a0988b1b26ef11b99038e8bebab312d499 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -6,7 +6,7 @@ use sasl::client::Mechanism; use sasl::common::scram::{Sha1, Sha256}; use sasl::common::Credentials; use tokio_io::{AsyncRead, AsyncWrite}; -use try_from::TryFrom; +use xmpp_parsers::TryFrom; use xmpp_parsers::sasl::{Auth, Challenge, Failure, Mechanism as XMPPMechanism, Response, Success}; use crate::xmpp_codec::Packet; diff --git a/src/client/bind.rs b/src/client/bind.rs index 3758a563708083650694d4419f14bd186ba3d15d..083650b9b94196fac4a797534f0083dc51222129 100644 --- a/src/client/bind.rs +++ b/src/client/bind.rs @@ -1,7 +1,7 @@ use futures::{sink, Async, Future, Poll, Stream}; use std::mem::replace; use tokio_io::{AsyncRead, AsyncWrite}; -use try_from::TryFrom; +use xmpp_parsers::TryFrom; use xmpp_parsers::bind::Bind; use xmpp_parsers::iq::{Iq, IqType}; diff --git a/src/client/mod.rs b/src/client/mod.rs index 146d51a40fdd57a23775dcf8b4775f2334605eab..acc60441223ba248a620b696cb8a9a8c98acb326 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,7 +1,6 @@ use futures::{done, Async, AsyncSink, Future, Poll, Sink, StartSend, Stream}; use idna; -use jid::{Jid, JidParseError}; -use minidom::Element; +use xmpp_parsers::{Jid, JidParseError, Element}; use sasl::common::{ChannelBinding, Credentials}; use std::mem::replace; use std::str::FromStr; diff --git a/src/component/mod.rs b/src/component/mod.rs index aac66ab4e5e64e40eac3a5233b9cf7cc4831bf0e..cfc1ee5e2452504a6a39daa06fa26b3e0a61db5f 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -2,8 +2,7 @@ //! XMPP server under a JID consisting of just a domain name. They are //! allowed to use any user and resource identifiers in their stanzas. use futures::{done, Async, AsyncSink, Future, Poll, Sink, StartSend, Stream}; -use jid::{Jid, JidParseError}; -use minidom::Element; +use xmpp_parsers::{Jid, JidParseError, Element}; use std::mem::replace; use std::str::FromStr; use tokio::net::TcpStream; diff --git a/src/error.rs b/src/error.rs index e7fad549fbda79e50f73884b7c7b2cee3ca4b397..9697f2681b860b1416cc16da6e60cd29e436a5f3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,7 +7,7 @@ use std::str::Utf8Error; use trust_dns_proto::error::ProtoError; use trust_dns_resolver::error::ResolveError; -use xmpp_parsers::error::Error as ParsersError; +use xmpp_parsers::Error as ParsersError; use xmpp_parsers::sasl::DefinedCondition as SaslDefinedCondition; /// Top-level error type diff --git a/src/event.rs b/src/event.rs index 7a225ba352d9f3015aaa9d78b8b7b1d3b0beff31..94dd36c2b7f298642a6e33671d1fbf2f138dd7f2 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,4 +1,4 @@ -use minidom::Element; +use xmpp_parsers::Element; /// High-level event on the Stream implemented by Client and Component #[derive(Debug)] diff --git a/src/starttls.rs b/src/starttls.rs index e48d1824a1220f98b70b015098cc9c93ec9a9b0a..00966ac1d491ba3d87d0b1b3bf0011d086e6a2ec 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -1,8 +1,7 @@ use futures::sink; use futures::stream::Stream; use futures::{Async, Future, Poll, Sink}; -use jid::Jid; -use minidom::Element; +use xmpp_parsers::{Jid, Element}; use native_tls::TlsConnector as NativeTlsConnector; use std::mem::replace; use tokio_io::{AsyncRead, AsyncWrite}; diff --git a/src/stream_start.rs b/src/stream_start.rs index 0a5945b7bda5e6d8dae908f23d20c59b3b5bb4e6..bf2d39e8b8968dc49474288be94a8c900f51beed 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -1,6 +1,5 @@ use futures::{sink, Async, Future, Poll, Sink, Stream}; -use jid::Jid; -use minidom::Element; +use xmpp_parsers::{Jid, Element}; use std::mem::replace; use tokio_codec::Framed; use tokio_io::{AsyncRead, AsyncWrite}; diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 69ab5fc23c82b43370d0408a0bb86ce2c0632f11..54ddcb51a5908b8716c339e7b18c78d11bfe2747 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -2,7 +2,7 @@ use crate::{ParseError, ParserError}; use bytes::{BufMut, BytesMut}; -use minidom::Element; +use xmpp_parsers::Element; use quick_xml::Writer as EventWriter; use std; use std::cell::RefCell; diff --git a/src/xmpp_stream.rs b/src/xmpp_stream.rs index c67cc1efdd6ea8b53e84c784b2e88b4829fb6772..129dd1d4fe8825b06409cf52fac2f145f6678969 100644 --- a/src/xmpp_stream.rs +++ b/src/xmpp_stream.rs @@ -2,8 +2,7 @@ use futures::sink::Send; use futures::{Poll, Sink, StartSend, Stream}; -use jid::Jid; -use minidom::Element; +use xmpp_parsers::{Jid, Element}; use tokio_codec::Framed; use tokio_io::{AsyncRead, AsyncWrite}; From 59f433fd218f5b3d1fd4d7b790f0b3754abc4217 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 17 Jan 2019 01:24:26 +0100 Subject: [PATCH 0814/1020] happy_eyeballs: migrate to newer trust-dns dependencies --- Cargo.toml | 4 ++-- src/error.rs | 10 +++++++- src/happy_eyeballs.rs | 56 ++++++++++++++++++------------------------- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c397ed40be7620798b5c561757d37cd9dce2bf37..954a6052809a3233418dcad48bdc8b48155b8159 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,8 @@ xml5ever = "0.12" native-tls = "0.2" tokio-tls = "0.2" sasl = "0.4" -trust-dns-resolver = "0.9.1" -trust-dns-proto = "0.4.0" +trust-dns-resolver = "0.10" +trust-dns-proto = "0.6" xmpp-parsers = "0.12.2" idna = "0.1" quick-xml = "0.13" diff --git a/src/error.rs b/src/error.rs index 9697f2681b860b1416cc16da6e60cd29e436a5f3..8ffa1b7a83c3de5204e9546544cf1df6699fad14 100644 --- a/src/error.rs +++ b/src/error.rs @@ -107,7 +107,7 @@ pub enum AuthError { } /// Error establishing connection -#[derive(Debug, Error)] +#[derive(Debug)] pub enum ConnecterError { /// All attempts failed, no error available AllFailed, @@ -116,3 +116,11 @@ pub enum ConnecterError { /// DNS resolution error Resolve(ResolveError), } + +impl std::error::Error for ConnecterError {} + +impl std::fmt::Display for ConnecterError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(fmt, "{:?}", self) + } +} diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 19dbe4939d4e7e51d84fa8843646ddd10cef5eac..3fa6169ca8d29681964c788ac7b6954eb8f3cb8a 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -8,17 +8,16 @@ use std::mem; use std::net::SocketAddr; use tokio::net::tcp::ConnectFuture; use tokio::net::TcpStream; -use trust_dns_resolver::config::LookupIpStrategy; +use trust_dns_resolver::{AsyncResolver, Name, IntoName, Background, BackgroundLookup}; use trust_dns_resolver::lookup::SrvLookupFuture; use trust_dns_resolver::lookup_ip::LookupIpFuture; -use trust_dns_resolver::system_conf; -use trust_dns_resolver::{error::ResolveError, IntoName, Name, ResolverFuture}; + enum State { - AwaitResolver(Box + Send>), - ResolveSrv(ResolverFuture, SrvLookupFuture), - ResolveTarget(ResolverFuture, LookupIpFuture, u16), - Connecting(Option, Vec>), + Start(AsyncResolver), + ResolveSrv(AsyncResolver, BackgroundLookup), + ResolveTarget(AsyncResolver, Background, u16), + Connecting(Option, Vec>), Invalid, } @@ -31,11 +30,10 @@ pub struct Connecter { error: Option, } -fn resolver_future( -) -> Result + Send>, IoError> { - let (conf, mut opts) = system_conf::read_system_conf()?; - opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6; - Ok(ResolverFuture::new(conf, opts)) +fn resolver() -> Result { + let (resolver, resolver_background) = AsyncResolver::from_system_conf()?; + tokio::runtime::current_thread::spawn(resolver_background); + Ok(resolver) } impl Connecter { @@ -57,7 +55,7 @@ impl Connecter { }); } - let state = State::AwaitResolver(resolver_future()?); + let state = State::Start(resolver()?); let srv_domain = match srv { Some(srv) => Some( format!("{}.{}.", srv, domain) @@ -85,29 +83,21 @@ impl Future for Connecter { fn poll(&mut self) -> Poll { let state = mem::replace(&mut self.state, State::Invalid); match state { - State::AwaitResolver(mut resolver_future) => { - match resolver_future.poll().map_err(ConnecterError::Resolve)? { - Async::NotReady => { - self.state = State::AwaitResolver(resolver_future); - Ok(Async::NotReady) + State::Start(resolver) => { + match &self.srv_domain { + &Some(ref srv_domain) => { + let srv_lookup = resolver.lookup_srv(srv_domain); + self.state = State::ResolveSrv(resolver, srv_lookup); } - Async::Ready(resolver) => { - match &self.srv_domain { - &Some(ref srv_domain) => { - let srv_lookup = resolver.lookup_srv(srv_domain); - self.state = State::ResolveSrv(resolver, srv_lookup); - } - None => { - self.targets = [(self.domain.clone(), self.fallback_port)] - .into_iter() - .cloned() - .collect(); - self.state = State::Connecting(Some(resolver), vec![]); - } - } - self.poll() + None => { + self.targets = [(self.domain.clone(), self.fallback_port)] + .into_iter() + .cloned() + .collect(); + self.state = State::Connecting(Some(resolver), vec![]); } } + self.poll() } State::ResolveSrv(resolver, mut srv_lookup) => { match srv_lookup.poll() { From e59d048cd04e6ba87c4668d689e430e28bb820c5 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 17 Jan 2019 01:29:48 +0100 Subject: [PATCH 0815/1020] happy_eyeballs: eliminate State::Start --- src/happy_eyeballs.rs | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index 3fa6169ca8d29681964c788ac7b6954eb8f3cb8a..a47ddd986262d70ecd4b1955446ee132fa8c6999 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -14,7 +14,6 @@ use trust_dns_resolver::lookup_ip::LookupIpFuture; enum State { - Start(AsyncResolver), ResolveSrv(AsyncResolver, BackgroundLookup), ResolveTarget(AsyncResolver, Background, u16), Connecting(Option, Vec>), @@ -55,7 +54,6 @@ impl Connecter { }); } - let state = State::Start(resolver()?); let srv_domain = match srv { Some(srv) => Some( format!("{}.{}.", srv, domain) @@ -65,14 +63,32 @@ impl Connecter { None => None, }; - Ok(Connecter { + let mut self_ = Connecter { fallback_port, srv_domain, domain: domain.into_name().map_err(ConnecterError::Dns)?, - state, + state: State::Invalid, targets: VecDeque::new(), error: None, - }) + }; + + let resolver = resolver()?; + // Initialize state + match &self_.srv_domain { + &Some(ref srv_domain) => { + let srv_lookup = resolver.lookup_srv(srv_domain); + self_.state = State::ResolveSrv(resolver, srv_lookup); + } + None => { + self_.targets = [(self_.domain.clone(), self_.fallback_port)] + .into_iter() + .cloned() + .collect(); + self_.state = State::Connecting(Some(resolver), vec![]); + } + } + + Ok(self_) } } @@ -83,22 +99,6 @@ impl Future for Connecter { fn poll(&mut self) -> Poll { let state = mem::replace(&mut self.state, State::Invalid); match state { - State::Start(resolver) => { - match &self.srv_domain { - &Some(ref srv_domain) => { - let srv_lookup = resolver.lookup_srv(srv_domain); - self.state = State::ResolveSrv(resolver, srv_lookup); - } - None => { - self.targets = [(self.domain.clone(), self.fallback_port)] - .into_iter() - .cloned() - .collect(); - self.state = State::Connecting(Some(resolver), vec![]); - } - } - self.poll() - } State::ResolveSrv(resolver, mut srv_lookup) => { match srv_lookup.poll() { Ok(Async::NotReady) => { From def0092b20a2aeef46cf8e9973efca8345da98b9 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 17 Jan 2019 01:38:24 +0100 Subject: [PATCH 0816/1020] happy_eyeballs: set LookupIpStrategy::Ipv4AndIpv6 this is what happy_eyeballs are all about --- src/happy_eyeballs.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index a47ddd986262d70ecd4b1955446ee132fa8c6999..fc4e0280501f1444f2f8b43439d68612352d5036 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -9,6 +9,7 @@ use std::net::SocketAddr; use tokio::net::tcp::ConnectFuture; use tokio::net::TcpStream; use trust_dns_resolver::{AsyncResolver, Name, IntoName, Background, BackgroundLookup}; +use trust_dns_resolver::config::LookupIpStrategy; use trust_dns_resolver::lookup::SrvLookupFuture; use trust_dns_resolver::lookup_ip::LookupIpFuture; @@ -30,7 +31,9 @@ pub struct Connecter { } fn resolver() -> Result { - let (resolver, resolver_background) = AsyncResolver::from_system_conf()?; + let (config, mut opts) = trust_dns_resolver::system_conf::read_system_conf()?; + opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6; + let (resolver, resolver_background) = AsyncResolver::new(config, opts); tokio::runtime::current_thread::spawn(resolver_background); Ok(resolver) } From 9921a59400c13f52f363cb972d929a61689d19f2 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 17 Jan 2019 01:41:42 +0100 Subject: [PATCH 0817/1020] add Cargo.lock --- .gitignore | 1 - Cargo.lock | 1709 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1709 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index a9d37c560c6ab8d4afbf47eda643e8c42e857716..eb5a316cbd195d26e3f768c7dd8e1b47299e17f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ target -Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..88d2a7cb6b842c4606c5786b35f8772be98850ef --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1709 @@ +[[package]] +name = "MacTypes-sys" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayvec" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base64" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base64" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-buffer" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-tools" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bytes" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "case" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "chrono" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-channel" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "derive-error" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "case 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding_rs" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "error-chain" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "generic-array" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hostname" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ipconfig" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "widestring 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "jid" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "minidom 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazycell" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.46" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "linked-hash-map" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lock_api" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lru-cache" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "markup5ever" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memoffset" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "minidom" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-xml 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-uds" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-integer" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "opaque-debug" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl" +version = "0.10.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl-sys" +version = "0.9.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "owning_ref" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "phf" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_codegen" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quick-error" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quick-xml" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_rs 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quote" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "remove_dir_all" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "resolv-conf" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "safemem" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "sasl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "schannel" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "security-framework" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "MacTypes-sys 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha-1" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha3" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "socket2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "string_cache" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "string_cache_codegen" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "string_cache_shared" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.15.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempfile" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tendril" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-codec" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-current-thread" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-fs" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-timer" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tls" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-udp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-uds" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-xmpp" +version = "0.2.2" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "derive-error 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-xml 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sasl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-resolver 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "xml5ever 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "xmpp-parsers 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "trust-dns-proto" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "ipconfig 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "try_from" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf-8" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vcpkg" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "widestring" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winreg" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winutil" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "xml5ever" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "markup5ever 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "xmpp-parsers" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jid 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "minidom 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha3 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum MacTypes-sys 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7dbbe033994ae2198a18517c7132d952a29fb1db44249a1234779da7c50f4698" +"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" +"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" +"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" +"checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2" +"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91721a6330935673395a0607df4d49a9cb90ae12d259f1b3e0a3f6e1d486872e" +"checksum block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49665c62e0e700857531fa5d3763e91b539ff1abeebd56808d378b495870d60d" +"checksum block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc4358306e344bf9775d0197fd00d2603e5afb0771bb353538630f022068ea3" +"checksum byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "980479e6fde23246dfb54d47580d66b4e99202e7579c5eaa9fe10ecb5ebd2182" +"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" +"checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" +"checksum case 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e88b166b48e29667f5443df64df3c61dc07dc2b1a0b0d231800e07f09a33ecc1" +"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" +"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" +"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" +"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" +"checksum crossbeam-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "137bc235f622ffaa0428e3854e24acb53291fc0b3ff6fb2cb75a8be6fb02f06b" +"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" +"checksum crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f10a4f8f409aaac4b16a5474fb233624238fcdeefb9ba50d5ea059aab63ba31c" +"checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" +"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +"checksum derive-error 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ec098440b29ea3b1ece3e641bac424c19cf996779b623c9e0f2171495425c2c8" +"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" +"checksum encoding_rs 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)" = "a69d152eaa438a291636c1971b0a370212165ca8a75759eb66818c5ce9b538f7" +"checksum error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6930e04918388a9a2e41d518c25cf679ccafe26733fb4127dbf21993f2575d46" +"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" +"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" +"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" +"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" +"checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum ipconfig 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "08f7eadeaf4b52700de180d147c4805f199854600b36faa963d91114827b2ffc" +"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" +"checksum jid 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "24e8a3f2ab860aa08074136e3144a2425e678d8823206e5adcc6145dc136503a" +"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" +"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" +"checksum libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)" = "023a4cd09b2ff695f9734c1934145a315594b7986398496841c7031a5a1bbdbd" +"checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939" +"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d06ff7ff06f729ce5f4e227876cb88d10bc59cd4ae1e09fbb2bde15c850dc21" +"checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +"checksum markup5ever 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "897636f9850c3eef4905a5540683ed53dc9393860f0846cab2c2ddf9939862ff" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +"checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9" +"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum minidom 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "275024eea6c6ff4ace22f2750843831183785288eec1cff91a4e6b8898cf94f9" +"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" +"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +"checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4" +"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" +"checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238" +"checksum opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51ecbcb821e1bd256d456fe858aaa7f380b63863eab2eb86eee1bd9f33dd6682" +"checksum openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ec7bd7ca4cce6dbdc77e7c1230682740d307d1218a87fb0349a571272be749f9" +"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +"checksum openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)" = "1bb974e77de925ef426b6bc82fce15fd45bdcbeb5728bffcfc7cdeeb7ce1c2d6" +"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" +"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" +"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +"checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" +"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quick-xml 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98d8d2d671bd29c6122a98b45ce3106391e89ba378f731274de677f1eff06e5f" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" +"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" +"checksum rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3906503e80ac6cbcacb2c2973fa8e473f24d7e2747c8c92bb230c2441cad96b5" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" +"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46fbd5550acf75b0c2730f5dd1873751daf9beb8f11b44027778fae50d7feca" +"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2" +"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" +"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" +"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" +"checksum sasl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4dbda5436dcd059da44fbf84b87a7164c5e3cad354efd0630c1f2be3c51c8859" +"checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "697d3f3c23a618272ead9e1fb259c1411102b31c6af8b93f1d64cca9c3b0e8e0" +"checksum security-framework-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "40d95f3d7da09612affe897f320d78264f0d2320f3e8eea27d12bd1bd94445e2" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "0e732ed5a5592c17d961555e3b552985baf98d50ce418b7b655f31f6ba7eb1b7" +"checksum serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d6115a3ca25c224e409185325afc16a0d5aaaabc15c42b09587d6f1ba39a5b" +"checksum serde_json 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb1277d4d0563e4593e0b8b5d23d744d277b55d2bc0bf1c38d0d8a6589d38aa" +"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" +"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" +"checksum sha3 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34a5e54083ce2b934bf059fdf38e7330a154177e029ab6c4e18638f2f624053a" +"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +"checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db" +"checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" +"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" +"checksum string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da" +"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" +"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)" = "734ecc29cd36e8123850d9bf21dfd62ef8300aaa8f879aabaa899721808be37c" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" +"checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2" +"checksum tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum tokio 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4790d0be6f4ba6ae4f48190efa2ed7780c9e3567796abdb285003cf39840d9c5" +"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" +"checksum tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "331c8acc267855ec06eb0c94618dcbbfea45bed2d20b77252940095273fb58f6" +"checksum tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30c6dbf2d1ad1de300b393910e8a3aa272b724a400b6531da03eed99e329fbf0" +"checksum tokio-fs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9cbbc8a3698b7ab652340f46633364f9eaa928ddaaee79d8b8f356dd79a09d" +"checksum tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b53aeb9d3f5ccf2ebb29e19788f96987fa1355f8fe45ea193928eaaaf3ae820f" +"checksum tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "afbcdb0f0d2a1e4c440af82d7bbf0bf91a8a8c0575bcd20c05d15be7e9d3a02f" +"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" +"checksum tokio-threadpool 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "17465013014410310f9f61fa10bf4724803c149ea1d51efece131c38efca93aa" +"checksum tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4f37f0111d76cc5da132fe9bc0590b9b9cfd079bc7e75ac3846278430a299ff8" +"checksum tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" +"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" +"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" +"checksum trust-dns-proto 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30dde452f5d142d5e316a3b32386da95280c98b7e266639f8f3bc6fdf507d279" +"checksum trust-dns-resolver 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "de630f95a192f793436ffae5137e88253cc4142a97d9a8e73c8d804fa85ddf0a" +"checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" +"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +"checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" +"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" +"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum widestring 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7157704c2e12e3d2189c507b7482c52820a16dfa4465ba91add92f266667cadb" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" +"checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum xml5ever 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cd7ebf0203c620906230ce22caa5df0b603c32b6fef72a275a48f6a2ae64b9" +"checksum xmpp-parsers 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "58b4400e1ae0d246044db5fa7f2e693fdfe9cc6e8eaa72ef2a68c5dc1d3c96de" From 438dbaafecbf595df1b152af51e090c0c84fe577 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 17 Jan 2019 01:41:59 +0100 Subject: [PATCH 0818/1020] tokio-xmpp 0.2.3 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 954a6052809a3233418dcad48bdc8b48155b8159..afda4d2765c1a2d43e456f863bc26f71405d4c55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-xmpp" -version = "0.2.2" +version = "0.2.3" authors = ["Astro ", "Emmanuel Gil Peyrot ", "pep ", "O01eg "] description = "Asynchronous XMPP for Rust with tokio" license = "MPL-2.0" From 82015de9646f20fe0c0076966aede427fb55b3f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 25 Jan 2019 02:49:56 +0100 Subject: [PATCH 0819/1020] hashes: Add an hex-encoded SHA-1 attribute helper. --- src/hashes.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/hashes.rs b/src/hashes.rs index 041b81fc4795415b230bda7d2393a40a11e16cab..d6a1f37baf3609a24a5ff68a84b837c1b0e4ea4d 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -8,6 +8,8 @@ use crate::util::error::Error; use crate::util::helpers::Base64; use base64; use minidom::IntoAttributeValue; +use std::num::ParseIntError; +use std::ops::{Deref, DerefMut}; use std::str::FromStr; /// List of the algorithms we support, or Unknown. @@ -121,6 +123,47 @@ impl Hash { } } +/// Helper for parsing and serialising a SHA-1 attribute. +#[derive(Debug, Clone, PartialEq)] +pub struct Sha1HexAttribute(Hash); + +impl FromStr for Sha1HexAttribute { + type Err = ParseIntError; + + fn from_str(hex: &str) -> Result { + let mut bytes = vec![]; + for i in 0..hex.len() / 2 { + let byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16); + bytes.push(byte?); + } + Ok(Sha1HexAttribute(Hash::new(Algo::Sha_1, bytes))) + } +} + +impl IntoAttributeValue for Sha1HexAttribute { + fn into_attribute_value(self) -> Option { + let mut bytes = vec![]; + for byte in self.0.hash { + bytes.push(format!("{:02x}", byte)); + } + Some(bytes.join("")) + } +} + +impl DerefMut for Sha1HexAttribute { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Deref for Sha1HexAttribute { + type Target = Hash; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[cfg(test)] mod tests { use super::*; From 35fccafc0909618f020b86fc3188d6a77d03f0d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 25 Jan 2019 02:57:37 +0100 Subject: [PATCH 0820/1020] avatar: Add a new XEP-0084 parser. --- src/avatar.rs | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 5 +++ 3 files changed, 129 insertions(+) create mode 100644 src/avatar.rs diff --git a/src/avatar.rs b/src/avatar.rs new file mode 100644 index 0000000000000000000000000000000000000000..07f0bde134b34f431d17bf36f29ce59a0181f211 --- /dev/null +++ b/src/avatar.rs @@ -0,0 +1,121 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::hashes::Sha1HexAttribute; +use crate::util::helpers::Base64; + +generate_element!( + /// Communicates information about an avatar. + Metadata, "metadata", AVATAR_METADATA, + children: [ + /// List of information elements describing this avatar. + infos: Vec = ("info", AVATAR_METADATA) => Info + ] +); + +generate_element!( + /// Communicates avatar metadata. + Info, "info", AVATAR_METADATA, + attributes: [ + /// The size of the image data in bytes. + bytes: u16 = "bytes" => required, + + /// The width of the image in pixels. + width: Option = "width" => optional, + + /// The height of the image in pixels. + height: Option = "height" => optional, + + /// The SHA-1 hash of the image data for the specified content-type. + id: Sha1HexAttribute = "id" => required, + + /// The IANA-registered content type of the image data. + type_: String = "type" => required, + + /// The http: or https: URL at which the image data file is hosted. + url: Option = "url" => optional, + ] +); + +generate_element!( + /// The actual avatar data. + Data, "data", AVATAR_DATA, + text: ( + /// Vector of bytes representing the avatar’s image. + data: Base64> + ) +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::hashes::Algo; + use crate::util::error::Error; + use minidom::Element; + use try_from::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Metadata, 12); + assert_size!(Info, 60); + assert_size!(Data, 12); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Metadata, 24); + assert_size!(Info, 112); + assert_size!(Data, 24); + } + + #[test] + fn test_simple() { + let elem: Element = " + + " + .parse() + .unwrap(); + let metadata = Metadata::try_from(elem).unwrap(); + assert_eq!(metadata.infos.len(), 1); + let info = &metadata.infos[0]; + assert_eq!(info.bytes, 12345); + assert_eq!(info.width, Some(64)); + assert_eq!(info.height, Some(64)); + assert_eq!(info.id.algo, Algo::Sha_1); + assert_eq!(info.type_, "image/png"); + assert_eq!(info.url, None); + assert_eq!( + info.id.hash, + [ + 17, 31, 75, 60, 80, 215, 176, 223, 114, 157, 41, 155, 198, 248, 233, 239, 144, 102, + 151, 31 + ] + ); + + let elem: Element = "AAAA" + .parse() + .unwrap(); + let data = Data::try_from(elem).unwrap(); + assert_eq!(data.data, b"\0\0\0"); + } + + #[test] + fn test_invalid() { + let elem: Element = "" + .parse() + .unwrap(); + let error = Data::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown attribute in data element.") + } +} diff --git a/src/lib.rs b/src/lib.rs index aea3e737a0aa977d3e5f05ca816f712070a0f68a..765c9a15cc14a641c05000eac73915ea4f28b949 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,9 @@ pub mod ibr; /// XEP-0082: XMPP Date and Time Profiles pub mod date; +/// XEP-0084: User Avatar +pub mod avatar; + /// XEP-0085: Chat State Notifications pub mod chatstates; diff --git a/src/ns.rs b/src/ns.rs index 658428f9cdcd5d681bc81ff6621f374796bc445c..7aa4768cfa4a85bfc061d59368cf0640c7494e49 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -56,6 +56,11 @@ pub const PUBSUB_OWNER: &str = "http://jabber.org/protocol/pubsub#owner"; /// XEP-0077: In-Band Registration pub const REGISTER: &str = "jabber:iq:register"; +/// XEP-0084: User Avatar +pub const AVATAR_DATA: &str = "urn:xmpp:avatar:data"; +/// XEP-0084: User Avatar +pub const AVATAR_METADATA: &str = "urn:xmpp:avatar:metadata"; + /// XEP-0085: Chat State Notifications pub const CHATSTATES: &str = "http://jabber.org/protocol/chatstates"; From b6c7a06edd5b62b7ff5ea89f1a150e1a3f3a036b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 25 Jan 2019 03:45:48 +0100 Subject: [PATCH 0821/1020] avatar: Fix build with --features=disable-validation. --- src/avatar.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/avatar.rs b/src/avatar.rs index 07f0bde134b34f431d17bf36f29ce59a0181f211..f8507bf0e0af1d1810e16c82cf4cbe78d16535ac 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -106,9 +106,10 @@ mod tests { assert_eq!(data.data, b"\0\0\0"); } + #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid() { - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); let error = Data::try_from(elem).unwrap_err(); From c75eafa553c4b20abe628e869a21241f7bd67309 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 26 Jan 2019 19:28:57 +0100 Subject: [PATCH 0822/1020] rm all annoying debug output --- src/component/auth.rs | 4 ++-- src/starttls.rs | 4 ++-- src/xmpp_codec.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/component/auth.rs b/src/component/auth.rs index 92ae0903cd8ab4aa93a0658cd8a7486f521878f8..44104f4c19052fe1db703563fab894cf32c2d7ca 100644 --- a/src/component/auth.rs +++ b/src/component/auth.rs @@ -73,8 +73,8 @@ impl Future for ComponentAuth { { Err(AuthError::ComponentFail.into()) } - Ok(Async::Ready(event)) => { - println!("ComponentAuth ignore {:?}", event); + Ok(Async::Ready(_event)) => { + // println!("ComponentAuth ignore {:?}", _event); Ok(Async::NotReady) } Ok(_) => { diff --git a/src/starttls.rs b/src/starttls.rs index 00966ac1d491ba3d87d0b1b3bf0011d086e6a2ec..41bc6001da06f74b49403108c45616d787a1ae7f 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -75,8 +75,8 @@ impl Future for StartTlsClient { retry = true; (new_state, Ok(Async::NotReady)) } - Ok(Async::Ready(value)) => { - println!("StartTlsClient ignore {:?}", value); + Ok(Async::Ready(_value)) => { + // println!("StartTlsClient ignore {:?}", _value); ( StartTlsClientState::AwaitProceed(xmpp_stream), Ok(Async::NotReady), diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 54ddcb51a5908b8716c339e7b18c78d11bfe2747..d8e5310a41aa99adff965b0c60655bf763ca0b58 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -289,7 +289,7 @@ impl Encoder for XMPPCodec { } write!(buf, ">\n").unwrap(); - print!(">> {}", buf); + // print!(">> {}", buf); write!(dst, "{}", buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)) } Packet::Stanza(stanza) => { From 3586e5134d4b87bc31ed9426045131f1a8b0af66 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 26 Jan 2019 19:39:05 +0100 Subject: [PATCH 0823/1020] client: reindent --- src/client/mod.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index acc60441223ba248a620b696cb8a9a8c98acb326..b4793f9c15809df273014be4cd0786e6409f9a58 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -184,16 +184,21 @@ impl Sink for Client { fn start_send(&mut self, item: Self::SinkItem) -> StartSend { match self.state { - ClientState::Connected(ref mut stream) => match stream.start_send(Packet::Stanza(item)) - { - Ok(AsyncSink::NotReady(Packet::Stanza(stanza))) => Ok(AsyncSink::NotReady(stanza)), - Ok(AsyncSink::NotReady(_)) => { - panic!("Client.start_send with stanza but got something else back") + ClientState::Connected(ref mut stream) => { + match stream.start_send(Packet::Stanza(item)) { + Ok(AsyncSink::NotReady(Packet::Stanza(stanza))) => + Ok(AsyncSink::NotReady(stanza)), + Ok(AsyncSink::NotReady(_)) => { + panic!("Client.start_send with stanza but got something else back") + } + Ok(AsyncSink::Ready) => + Ok(AsyncSink::Ready), + Err(e) => + Err(e)?, } - Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready), - Err(e) => Err(e)?, - }, - _ => Ok(AsyncSink::NotReady(item)), + } + _ => + Ok(AsyncSink::NotReady(item)), } } From 380bd2fc0286112abb434e9f6909f4bc53dc9b9a Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 26 Jan 2019 20:46:51 +0100 Subject: [PATCH 0824/1020] client: implement close() to close inner stream --- src/client/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/client/mod.rs b/src/client/mod.rs index b4793f9c15809df273014be4cd0786e6409f9a58..a33dade949bd60c4341e6d770ec495f7533b157a 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -208,4 +208,19 @@ impl Sink for Client { _ => Ok(Async::Ready(())), } } + + /// This closes the inner TCP stream. + /// + /// To synchronize your shutdown with the server side, you should + /// first send `Packet::StreamEnd` and wait it to be sent back + /// before closing the connection. + fn close(&mut self) -> Poll<(), Self::SinkError> { + match self.state { + ClientState::Connected(ref mut stream) => + stream.close() + .map_err(|e| e.into()), + _ => + Ok(Async::Ready(())), + } + } } From 599e3be32e6bd59976e8d74a73f9aea975993624 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 26 Jan 2019 21:07:15 +0100 Subject: [PATCH 0825/1020] xmpp_codec, client: handle StreamEnd --- src/client/mod.rs | 20 +++++++++++++++++--- src/error.rs | 2 ++ src/xmpp_codec.rs | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index a33dade949bd60c4341e6d770ec495f7533b157a..9033b83bcb624f779ee5f2b7fc5ba974ff90450d 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -164,10 +164,24 @@ impl Stream for Client { Ok(Async::Ready(Some(Event::Disconnected))) } Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { + // Receive stanza self.state = ClientState::Connected(stream); Ok(Async::Ready(Some(Event::Stanza(stanza)))) } - Ok(Async::NotReady) | Ok(Async::Ready(_)) => { + Ok(Async::Ready(Some(Packet::Text(_)))) => { + // Ignore text between stanzas + Ok(Async::NotReady) + } + Ok(Async::Ready(Some(Packet::StreamStart(_)))) => { + // + Err(ProtocolError::InvalidStreamStart.into()) + } + Ok(Async::Ready(Some(Packet::StreamEnd))) => { + // End of stream: + Ok(Async::Ready(None)) + } + Ok(Async::NotReady) => { + // Try again later self.state = ClientState::Connected(stream); Ok(Async::NotReady) } @@ -212,8 +226,8 @@ impl Sink for Client { /// This closes the inner TCP stream. /// /// To synchronize your shutdown with the server side, you should - /// first send `Packet::StreamEnd` and wait it to be sent back - /// before closing the connection. + /// first send `Packet::StreamEnd` and wait for the end of the + /// incoming stream before closing the connection. fn close(&mut self) -> Poll<(), Self::SinkError> { match self.state { ClientState::Connected(ref mut stream) => diff --git a/src/error.rs b/src/error.rs index 8ffa1b7a83c3de5204e9546544cf1df6699fad14..375c5178c6c4ff908c5a8bde3af12227b025ef6f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -88,6 +88,8 @@ pub enum ProtocolError { NoStreamId, /// Encountered an unexpected XML token InvalidToken, + /// Unexpected (shouldn't occur) + InvalidStreamStart, } /// Authentication error diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index d8e5310a41aa99adff965b0c60655bf763ca0b58..82eda4a614d63d20076bc6cf5a17ff0bf4cbff45 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -378,6 +378,25 @@ mod tests { }); } + #[test] + fn test_stream_end() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + b.clear(); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamEnd)) => true, + _ => false, + }); + } + #[test] fn test_truncated_stanza() { let mut c = XMPPCodec::new(); From 6379f91e02fc0813e8478a1115bb9050d2a23cb6 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 26 Jan 2019 23:58:12 +0100 Subject: [PATCH 0826/1020] client: add more state to make close() send `` --- src/client/mod.rs | 48 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 9033b83bcb624f779ee5f2b7fc5ba974ff90450d..025b4395f173fbfe52507d6af4021631584197fe 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -35,6 +35,8 @@ enum ClientState { Disconnected, Connecting(Box>), Connected(XMPPStream), + ClosingSendEnd(futures::sink::Send), + ClosingClose(XMPPStream), } impl Client { @@ -188,6 +190,14 @@ impl Stream for Client { Err(e) => Err(e)?, } } + ClientState::ClosingSendEnd(_) => { + self.state = state; + Ok(Async::NotReady) + } + ClientState::ClosingClose(_) => { + self.state = state; + Ok(Async::NotReady) + } } } } @@ -219,20 +229,40 @@ impl Sink for Client { fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { match self.state { ClientState::Connected(ref mut stream) => stream.poll_complete().map_err(|e| e.into()), + ClientState::ClosingSendEnd(ref mut send) => { + match send.poll()? { + Async::NotReady => + Ok(Async::NotReady), + Async::Ready(stream) => { + self.state = ClientState::ClosingClose(stream); + self.poll_complete() + } + } + } + ClientState::ClosingClose(ref mut stream) => { + match stream.close()? { + Async::NotReady => + Ok(Async::NotReady), + Async::Ready(()) => { + self.state = ClientState::Disconnected; + Ok(Async::Ready(())) + } + } + } _ => Ok(Async::Ready(())), } } - /// This closes the inner TCP stream. - /// - /// To synchronize your shutdown with the server side, you should - /// first send `Packet::StreamEnd` and wait for the end of the - /// incoming stream before closing the connection. + /// Send ` and later close the inner TCP stream. fn close(&mut self) -> Poll<(), Self::SinkError> { - match self.state { - ClientState::Connected(ref mut stream) => - stream.close() - .map_err(|e| e.into()), + let state = replace(&mut self.state, ClientState::Disconnected); + + match state { + ClientState::Connected(stream) => { + let send = stream.send(Packet::StreamEnd); + self.state = ClientState::ClosingSendEnd(send); + self.poll_complete() + } _ => Ok(Async::Ready(())), } From 234450b9d1615cfad6b4a6778d3f6f0501ce25ef Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 26 Jan 2019 23:59:06 +0100 Subject: [PATCH 0827/1020] echo_bot: illustrate close() --- examples/echo_bot.rs | 46 +++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 24b101079d76d0800a4d46f73262d50eea99a594..0b69b9342e824c8a96492429e051618d0fb88c2c 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,4 +1,4 @@ -use futures::{future, Sink, Stream}; +use futures::{future, Future, Sink, Stream}; use std::env::args; use std::process::exit; use tokio::runtime::current_thread::Runtime; @@ -23,36 +23,50 @@ fn main() { // Make the two interfaces for sending and receiving independent // of each other so we can move one into a closure. - let (mut sink, stream) = client.split(); - // Wrap sink in Option so that we can take() it for the send(self) - // to consume and return it back when ready. - let mut send = move |stanza| { - sink.start_send(stanza).expect("start_send"); - }; + let (sink, stream) = client.split(); + let mut sink_state = Some(sink); // Main loop, processes events - let done = stream.for_each(|event| { + let done = stream.for_each(move |event| { + let mut sink_future = None; + if event.is_online() { println!("Online!"); let presence = make_presence(); - send(presence); + let sink = sink_state.take().unwrap(); + sink_future = Some(Box::new(sink.send(presence))); } else if let Some(message) = event .into_stanza() .and_then(|stanza| Message::try_from(stanza).ok()) { - // This is a message we'll echo match (message.from, message.bodies.get("")) { - (Some(from), Some(body)) => { + (Some(ref from), Some(ref body)) if body.0 == "die" => { + println!("Secret die command triggered by {}", from); + let sink = sink_state.as_mut().unwrap(); + sink.close().expect("close"); + } + (Some(ref from), Some(ref body)) => { if message.type_ != MessageType::Error { - let reply = make_reply(from, &body.0); - send(reply); + // This is a message we'll echo + let reply = make_reply(from.clone(), &body.0); + let sink = sink_state.take().unwrap(); + sink_future = Some(Box::new(sink.send(reply))); } } - _ => (), + _ => {} } - } + }; - Box::new(future::ok(())) + sink_future + .map(|future| { + let wait_send: Box> = + Box::new(future + .map(|sink| { + sink_state = Some(sink); + })); + wait_send + }) + .unwrap_or_else(|| Box::new(future::ok(()))) }); // Start polling `done` From 1921f6819e5342c0502a737e9080fae22be4cc92 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 17:16:23 +0100 Subject: [PATCH 0828/1020] util.helpers: Add a whitespace-aware base64 codec. --- src/util/helpers.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/util/helpers.rs b/src/util/helpers.rs index fad0b671cc4670153339cfc31e2a3e503c375938..2aa37013ea5a7607ca057dd72846721f27e812e6 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -39,7 +39,7 @@ impl TrimmedPlainText { } } -/// Codec wrapping base64 encode/decode +/// Codec wrapping base64 encode/decode. pub struct Base64; impl Base64 { @@ -51,3 +51,17 @@ impl Base64 { Some(base64::encode(b)) } } + +/// Codec wrapping base64 encode/decode, while ignoring whitespace characters. +pub struct WhitespaceAwareBase64; + +impl WhitespaceAwareBase64 { + pub fn decode(s: &str) -> Result, Error> { + let s: String = s.chars().into_iter().filter(|ch| *ch != ' ' && *ch != '\n' && *ch != '\t').collect(); + Ok(base64::decode(&s)?) + } + + pub fn encode(b: &Vec) -> Option { + Some(base64::encode(b)) + } +} From 376fa9f92e4fd6036dcabba4889f8f1eef203da4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 17:17:12 +0100 Subject: [PATCH 0829/1020] avatar: Fix parsing of avatar data containing whitespace in the base64. --- src/avatar.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/avatar.rs b/src/avatar.rs index f8507bf0e0af1d1810e16c82cf4cbe78d16535ac..0f2e096244b362c548a4f0046daaa8a23554c771 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::hashes::Sha1HexAttribute; -use crate::util::helpers::Base64; +use crate::util::helpers::WhitespaceAwareBase64; generate_element!( /// Communicates information about an avatar. @@ -45,7 +45,7 @@ generate_element!( Data, "data", AVATAR_DATA, text: ( /// Vector of bytes representing the avatar’s image. - data: Base64> + data: WhitespaceAwareBase64> ) ); From ede2b08039112d414d8719a9d060969292565ea7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 17:18:58 +0100 Subject: [PATCH 0830/1020] disco: Add a helper constructor for Feature. --- src/disco.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/disco.rs b/src/disco.rs index d61cf361dc3b3258bc238aa11f391fe22dfb282c..ee673b8f92f0bfa314f4b4abfdf903725c7893f6 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -34,6 +34,15 @@ attributes: [ var: String = "var" => required, ]); +impl Feature { + /// Create a new `` with the according `@var`. + pub fn new(var: &str) -> Feature { + Feature { + var: String::from(var) + } + } +} + /// Structure representing an `` element. #[derive(Debug, Clone)] pub struct Identity { From b936ce595f2169fff4e792b21571c4db9147c815 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 17:20:34 +0100 Subject: [PATCH 0831/1020] caps: Add a helper constructor for Caps. --- src/caps.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/caps.rs b/src/caps.rs index 73c79378ffa1a613c406977599e150303db59f35..419e72ccce184ed963e4ca271d10e0e412367e71 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -71,6 +71,17 @@ impl From for Element { } } +impl Caps { + /// Create a Caps element from its node and hash. + pub fn new>(node: N, hash: Hash) -> Caps { + Caps { + ext: None, + node: node.into(), + hash, + } + } +} + fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(b'<'); From d60feffc22de32d0828e1a5b85e95d4873f94e19 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 17:40:46 +0100 Subject: [PATCH 0832/1020] pubsub: Add a PubSubPayload trait. --- src/avatar.rs | 5 +++++ src/pubsub/event.rs | 13 ++++++++++++- src/pubsub/mod.rs | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/avatar.rs b/src/avatar.rs index 0f2e096244b362c548a4f0046daaa8a23554c771..48893e4c55ce34762ac735548e9fe087221d281e 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::hashes::Sha1HexAttribute; +use crate::pubsub::PubSubPayload; use crate::util::helpers::WhitespaceAwareBase64; generate_element!( @@ -16,6 +17,8 @@ generate_element!( ] ); +impl PubSubPayload for Metadata {} + generate_element!( /// Communicates avatar metadata. Info, "info", AVATAR_METADATA, @@ -49,6 +52,8 @@ generate_element!( ) ); +impl PubSubPayload for Data {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 4e8b9311b167292fb9e11a6b1c00ba22223bac1b..5e53acb870e0a8ad80267a5943925f74229bb5f4 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -8,7 +8,7 @@ use crate::data_forms::DataForm; use crate::date::DateTime; use crate::util::error::Error; use crate::ns; -use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; +use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, PubSubPayload}; use jid::Jid; use minidom::Element; use try_from::TryFrom; @@ -58,6 +58,17 @@ impl From for Element { } } +impl Item { + /// Create a new item event, accepting only payloads implementing `PubSubPayload`. + pub fn new(id: Option, publisher: Option, payload: Option

) -> Item { + Item { + id, + publisher, + payload: payload.map(|payload| payload.into()), + } + } +} + /// Represents an event happening to a PubSub node. #[derive(Debug, Clone)] pub enum PubSubEvent { diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index a8af5c5ad067e8cd976ae279e02a291eef3ade62..dd39ea1d1179b065741a858145d93ed9d95e7417 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -45,3 +45,6 @@ generate_attribute!( Unconfigured => "unconfigured", }, Default = None ); + +/// This trait should be implemented on any element which can be included as a PubSub payload. +pub trait PubSubPayload: crate::TryFrom + Into {} From d811c10ed350453826dc6167b4d38245c0795625 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 27 Jan 2019 18:57:25 +0100 Subject: [PATCH 0833/1020] pubsub: Make Item common to both pubsub and pubsub#event namespaces. --- src/pubsub/event.rs | 58 +++----------------------------------------- src/pubsub/mod.rs | 26 ++++++++++++++++++++ src/pubsub/pubsub.rs | 42 +++----------------------------- src/util/macros.rs | 51 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 92 deletions(-) diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 5e53acb870e0a8ad80267a5943925f74229bb5f4..e0f2b598d06b1b3fb840fe99bb434f553516fbaf 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -8,66 +8,16 @@ use crate::data_forms::DataForm; use crate::date::DateTime; use crate::util::error::Error; use crate::ns; -use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, PubSubPayload}; +use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; use minidom::Element; use try_from::TryFrom; -/// One PubSub item from a node. +/// Event wrapper for a PubSub ``. #[derive(Debug, Clone)] -pub struct Item { - /// The identifier for this item, unique per node. - pub id: Option, +pub struct Item(pub PubSubItem); - /// The JID of the entity who published this item. - pub publisher: Option, - - /// The actual content of this item. - pub payload: Option, -} - -impl TryFrom for Item { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "item", PUBSUB_EVENT); - check_no_unknown_attributes!(elem, "item", ["id", "publisher"]); - let mut payloads = elem.children().cloned().collect::>(); - let payload = payloads.pop(); - if !payloads.is_empty() { - return Err(Error::ParseError( - "More than a single payload in item element.", - )); - } - Ok(Item { - payload, - id: get_attr!(elem, "id", optional), - publisher: get_attr!(elem, "publisher", optional), - }) - } -} - -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::PUBSUB_EVENT) - .attr("id", item.id) - .attr("publisher", item.publisher) - .append(item.payload) - .build() - } -} - -impl Item { - /// Create a new item event, accepting only payloads implementing `PubSubPayload`. - pub fn new(id: Option, publisher: Option, payload: Option

) -> Item { - Item { - id, - publisher, - payload: payload.map(|payload| payload.into()), - } - } -} +impl_pubsub_item!(Item, PUBSUB_EVENT); /// Represents an event happening to a PubSub node. #[derive(Debug, Clone)] diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index dd39ea1d1179b065741a858145d93ed9d95e7417..81e4e359f14cd54d83f8aa72848a0f474e1d1d8e 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -13,6 +13,8 @@ pub mod pubsub; pub use self::event::PubSubEvent; pub use self::pubsub::PubSub; +use crate::{Jid, Element}; + generate_id!( /// The name of a PubSub node, used to identify it on a JID. NodeName @@ -46,5 +48,29 @@ generate_attribute!( }, Default = None ); +/// An item from a PubSub node. +#[derive(Debug, Clone)] +pub struct Item { + /// The identifier for this item, unique per node. + pub id: Option, + + /// The JID of the entity who published this item. + pub publisher: Option, + + /// The payload of this item, in an arbitrary namespace. + pub payload: Option, +} + +impl Item { + /// Create a new item, accepting only payloads implementing `PubSubPayload`. + pub fn new(id: Option, publisher: Option, payload: Option

", style, children_to_html(children)) + } + Tag::Span { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + } + Tag::Strong { children } => format!("{}", children.into_iter().map(|child| child.to_html()).collect::>().join("")), + Tag::Ul { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + } + Tag::Unknown(children) => children_to_html(children), + } + } +} + +impl TryFrom for Tag { + type Error = Error; + + fn try_from(elem: Element) -> Result { + let mut children = vec![]; + for child in elem.nodes() { + match child { + Node::Element(child) => children.push(Child::Tag(Tag::try_from(child.clone())?)), + Node::Text(text) => children.push(Child::Text(text.clone())), + Node::Comment(_) => unimplemented!() // XXX: remove! + } + } + + Ok(match elem.name() { + "a" => Tag::A { href: elem.attr("href").map(|href| href.to_string()), style: parse_css(elem.attr("style")), type_: elem.attr("type").map(|type_| type_.to_string()), children }, + "blockquote" => Tag::Blockquote { style: parse_css(elem.attr("style")), children }, + "body" => Tag::Body { style: parse_css(elem.attr("style")), xml_lang: elem.attr("xml:lang").map(|xml_lang| xml_lang.to_string()), children }, + "br" => Tag::Br, + "cite" => Tag::Cite { style: parse_css(elem.attr("style")), children }, + "em" => Tag::Em { children }, + "img" => Tag::Img { src: elem.attr("src").map(|src| src.to_string()), alt: elem.attr("alt").map(|alt| alt.to_string()) }, + "li" => Tag::Li { style: parse_css(elem.attr("style")), children }, + "ol" => Tag::Ol { style: parse_css(elem.attr("style")), children }, + "p" => Tag::P { style: parse_css(elem.attr("style")), children }, + "span" => Tag::Span { style: parse_css(elem.attr("style")), children }, + "strong" => Tag::Strong { children }, + "ul" => Tag::Ul { style: parse_css(elem.attr("style")), children }, + _ => Tag::Unknown(children), + }) + } +} + +impl From for Element { + fn from(tag: Tag) -> Element { + let (name, attrs, children) = match tag { + Tag::A { href, style, type_, children } => ("a", { + let mut attrs = vec![]; + if let Some(href) = href { + attrs.push(("href", href)); + } + if let Some(style) = get_style_string(style) { + attrs.push(("style", style)); + } + if let Some(type_) = type_ { + attrs.push(("type", type_)); + } + attrs + }, children), + Tag::Blockquote { style, children } => ("blockquote", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Body { style, xml_lang, children } => ("body", { + let mut attrs = vec![]; + if let Some(style) = get_style_string(style) { + attrs.push(("style", style)); + } + if let Some(xml_lang) = xml_lang { + attrs.push(("xml:lang", xml_lang)); + } + attrs + }, children), + Tag::Br => ("br", vec![], vec![]), + Tag::Cite { style, children } => ("cite", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Em { children } => ("em", vec![], children), + Tag::Img { src, alt } => { + let mut attrs = vec![]; + if let Some(src) = src { + attrs.push(("src", src)); + } + if let Some(alt) = alt { + attrs.push(("alt", alt)); + } + ("img", attrs, vec![]) + }, + Tag::Li { style, children } => ("li", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Ol { style, children } => ("ol", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::P { style, children } => ("p", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Span { style, children } => ("span", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Strong { children } => ("strong", vec![], children), + Tag::Ul { style, children } => ("ul", match get_style_string(style) { + Some(style) => vec![("style", style)], + None => vec![], + }, children), + Tag::Unknown(children) => return Element::builder("unknown").ns(ns::XHTML).append(children_to_nodes(children)).build(), + }; + let mut builder = Element::builder(name) + .ns(ns::XHTML) + .append(children_to_nodes(children)); + for (key, value) in attrs { + builder = builder.attr(key, value); + } + builder.build() + } +} + +fn children_to_nodes(children: Vec) -> Vec { + children.into_iter().map(|child| match child { + Child::Tag(tag) => Node::Element(Element::from(tag)), + Child::Text(text) => Node::Text(text), + }).collect::>() +} + +fn children_to_html(children: Vec) -> String { + children.into_iter().map(|child| child.to_html()).collect::>().concat() +} + +fn write_attr(attr: Option, name: &str) -> String { + match attr { + Some(attr) => format!(" {}='{}'", name, attr), + None => String::new(), + } +} + +fn parse_css(style: Option<&str>) -> Css { + let mut properties = vec![]; + if let Some(style) = style { + // TODO: make that parser a bit more resilient to things. + for part in style.split(";") { + let mut part = part.splitn(2, ":").map(|a| a.to_string()).collect::>(); + let key = part.pop().unwrap(); + let value = part.pop().unwrap(); + properties.push(Property { key, value }); + } + } + properties +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(target_pointer_width = "32")] + #[test] + #[ignore] + fn test_size() { + assert_size!(XhtmlIm, 0); + assert_size!(Child, 0); + assert_size!(Tag, 0); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(XhtmlIm, 56); + assert_size!(Child, 112); + assert_size!(Tag, 104); + } + + #[test] + fn test_empty() { + let elem: Element = "" + .parse() + .unwrap(); + let xhtml = XhtmlIm::try_from(elem).unwrap(); + assert_eq!(xhtml.bodies.len(), 0); + + let elem: Element = "" + .parse() + .unwrap(); + let xhtml = XhtmlIm::try_from(elem).unwrap(); + assert_eq!(xhtml.bodies.len(), 1); + + let elem: Element = "" + .parse() + .unwrap(); + let xhtml = XhtmlIm::try_from(elem).unwrap(); + assert_eq!(xhtml.bodies.len(), 2); + } + + #[test] + fn invalid_two_same_langs() { + let elem: Element = "" + .parse() + .unwrap(); + let error = XhtmlIm::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Two identical language bodies found in XHTML-IM."); + } + + #[test] + fn test_tag() { + let elem: Element = "" + .parse() + .unwrap(); + let body = Tag::try_from(elem).unwrap(); + match body { + Tag::Body { style: _, xml_lang: _, children } => assert_eq!(children.len(), 0), + _ => panic!(), + } + + let elem: Element = "

Hello world!

" + .parse() + .unwrap(); + let body = Tag::try_from(elem).unwrap(); + let mut children = match body { + Tag::Body { style, xml_lang, children } => { + assert_eq!(style.len(), 0); + assert_eq!(xml_lang, None); + assert_eq!(children.len(), 1); + children + }, + _ => panic!(), + }; + let p = match children.pop() { + Some(Child::Tag(tag)) => tag, + _ => panic!(), + }; + let mut children = match p { + Tag::P { style, children } => { + assert_eq!(style.len(), 0); + assert_eq!(children.len(), 1); + children + }, + _ => panic!(), + }; + let text = match children.pop() { + Some(Child::Text(text)) => text, + _ => panic!(), + }; + assert_eq!(text, "Hello world!"); + } + + #[test] + fn test_unknown_element() { + let elem: Element = "Hello world!" + .parse() + .unwrap(); + let xhtml_im = XhtmlIm::try_from(elem).unwrap(); + let html = xhtml_im.to_html(); + assert_eq!(html, "Hello world!"); + } + + #[test] + fn test_generate_html() { + let elem: Element = "

Hello world!

" + .parse() + .unwrap(); + let xhtml_im = XhtmlIm::try_from(elem).unwrap(); + let html = xhtml_im.to_html(); + assert_eq!(html, "

Hello world!

"); + + let elem: Element = "

Hello world!

" + .parse() + .unwrap(); + let xhtml_im = XhtmlIm::try_from(elem).unwrap(); + let html = xhtml_im.to_html(); + assert_eq!(html, "

Hello world!

"); + } +} From 2f45d586b5b2783699090f7a1700a7d1f072d3d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Aug 2019 20:02:06 +0200 Subject: [PATCH 0937/1020] xhtml: Add a tree generation example. --- src/xhtml.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/xhtml.rs b/src/xhtml.rs index c4fc0c05685448160a05fe6bc98f882676099492..43ea9e611fe87f602039729dfe8b661d814bcdab 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -469,4 +469,19 @@ mod tests { let html = xhtml_im.to_html(); assert_eq!(html, "

Hello world!

"); } + + #[test] + fn generate_tree() { + let world = "world".to_string(); + + Body { style: vec![], xml_lang: Some("en".to_string()), children: vec![ + Child::Tag(Tag::P { style: vec![], children: vec![ + Child::Text("Hello ".to_string()), + Child::Tag(Tag::Strong { children: vec![ + Child::Text(world), + ] }), + Child::Text("!".to_string()), + ] }), + ] }; + } } From 63d0265284e75859747878f63a09231cc479e9e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Aug 2019 20:02:33 +0200 Subject: [PATCH 0938/1020] xhtml: Move Body out of Tag, because it is the only top-level element. --- src/xhtml.rs | 135 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 55 deletions(-) diff --git a/src/xhtml.rs b/src/xhtml.rs index 43ea9e611fe87f602039729dfe8b661d814bcdab..766109361e070980e1acc83f81dcf443a1a6cf53 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -18,7 +18,7 @@ type Lang = String; #[derive(Debug, Clone)] pub struct XhtmlIm { /// Map of language to body element. - bodies: HashMap, + bodies: HashMap, } impl XhtmlIm { @@ -27,22 +27,41 @@ impl XhtmlIm { let mut html = Vec::new(); // TODO: use the best language instead. for (lang, body) in self.bodies { - if let Tag::Body { style: _, xml_lang, children } = body { - if lang.is_empty() { - assert!(xml_lang.is_none()); - } else { - assert_eq!(Some(lang), xml_lang); - } - for tag in children { - html.push(tag.to_html()); - } - break; + if lang.is_empty() { + assert!(body.xml_lang.is_none()); } else { - unreachable!(); + assert_eq!(Some(lang), body.xml_lang); } + for tag in body.children { + html.push(tag.to_html()); + } + break; } html.concat() } + + /// Removes all unknown elements. + pub fn flatten(self) -> XhtmlIm { + let mut bodies = HashMap::new(); + for (lang, body) in self.bodies { + let children = body.children.into_iter().fold(vec![], |mut acc, child| { + match child { + Child::Tag(Tag::Unknown(children)) => acc.extend(children), + any => acc.push(any), + } + acc + }); + let body = Body { + style: body.style, + xml_lang: body.xml_lang, + children, + }; + bodies.insert(lang, body); + } + XhtmlIm { + bodies, + } + } } impl MessagePayload for XhtmlIm {} @@ -62,7 +81,7 @@ impl TryFrom for XhtmlIm { Some(lang) => lang, None => "", }.to_string(); - let body = Tag::try_from(child)?; + let body = Body::try_from(child)?; match bodies.insert(lang, body) { None => (), Some(_) => return Err(Error::ParseError("Two identical language bodies found in XHTML-IM.")) @@ -81,16 +100,12 @@ impl From for Element { Element::builder("html") .ns(ns::XHTML_IM) .append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { - if let Tag::Body { style, xml_lang, children } = body { - assert_eq!(Some(lang), xml_lang.as_ref()); - Element::builder("body") - .ns(ns::XHTML_IM) - .attr("style", get_style_string(style.clone())) - .attr("xml:lang", xml_lang.clone()) - .append(children_to_nodes(children.clone())) - } else { - unreachable!(); - } + assert_eq!(Some(lang), body.xml_lang.as_ref()); + Element::builder("body") + .ns(ns::XHTML_IM) + .attr("style", get_style_string(body.style.clone())) + .attr("xml:lang", body.xml_lang.clone()) + .append(children_to_nodes(body.children.clone())) }).collect::>()) .build() } @@ -130,11 +145,45 @@ fn get_style_string(style: Css) -> Option { Some(result.join("; ")) } +#[derive(Debug, Clone)] +struct Body { + style: Css, + xml_lang: Option, + children: Vec, +} + +impl TryFrom for Body { + type Error = Error; + + fn try_from(elem: Element) -> Result { + let mut children = vec![]; + for child in elem.nodes() { + match child { + Node::Element(child) => children.push(Child::Tag(Tag::try_from(child.clone())?)), + Node::Text(text) => children.push(Child::Text(text.clone())), + Node::Comment(_) => unimplemented!() // XXX: remove! + } + } + + Ok(Body { style: parse_css(elem.attr("style")), xml_lang: elem.attr("xml:lang").map(|xml_lang| xml_lang.to_string()), children }) + } +} + +impl From for Element { + fn from(body: Body) -> Element { + Element::builder("body") + .ns(ns::XHTML) + .attr("style", get_style_string(body.style)) + .attr("xml:lang", body.xml_lang) + .append(children_to_nodes(body.children)) + .build() + } +} + #[derive(Debug, Clone)] enum Tag { A { href: Option, style: Css, type_: Option, children: Vec }, Blockquote { style: Css, children: Vec }, - Body { style: Css, xml_lang: Option, children: Vec }, Br, Cite { style: Css, children: Vec }, Em { children: Vec }, @@ -161,10 +210,6 @@ impl Tag { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) }, - Tag::Body { style, xml_lang: _, children } => { - let style = write_attr(get_style_string(style), "style"); - format!("{}", style, children_to_html(children)) - }, Tag::Br => String::from("
"), Tag::Cite { style, children } => { let style = write_attr(get_style_string(style), "style"); @@ -218,7 +263,6 @@ impl TryFrom for Tag { Ok(match elem.name() { "a" => Tag::A { href: elem.attr("href").map(|href| href.to_string()), style: parse_css(elem.attr("style")), type_: elem.attr("type").map(|type_| type_.to_string()), children }, "blockquote" => Tag::Blockquote { style: parse_css(elem.attr("style")), children }, - "body" => Tag::Body { style: parse_css(elem.attr("style")), xml_lang: elem.attr("xml:lang").map(|xml_lang| xml_lang.to_string()), children }, "br" => Tag::Br, "cite" => Tag::Cite { style: parse_css(elem.attr("style")), children }, "em" => Tag::Em { children }, @@ -254,16 +298,6 @@ impl From for Element { Some(style) => vec![("style", style)], None => vec![], }, children), - Tag::Body { style, xml_lang, children } => ("body", { - let mut attrs = vec![]; - if let Some(style) = get_style_string(style) { - attrs.push(("style", style)); - } - if let Some(xml_lang) = xml_lang { - attrs.push(("xml:lang", xml_lang)); - } - attrs - }, children), Tag::Br => ("br", vec![], vec![]), Tag::Cite { style, children } => ("cite", match get_style_string(style) { Some(style) => vec![("style", style)], @@ -405,26 +439,17 @@ mod tests { let elem: Element = "" .parse() .unwrap(); - let body = Tag::try_from(elem).unwrap(); - match body { - Tag::Body { style: _, xml_lang: _, children } => assert_eq!(children.len(), 0), - _ => panic!(), - } + let body = Body::try_from(elem).unwrap(); + assert_eq!(body.children.len(), 0); let elem: Element = "

Hello world!

" .parse() .unwrap(); - let body = Tag::try_from(elem).unwrap(); - let mut children = match body { - Tag::Body { style, xml_lang, children } => { - assert_eq!(style.len(), 0); - assert_eq!(xml_lang, None); - assert_eq!(children.len(), 1); - children - }, - _ => panic!(), - }; - let p = match children.pop() { + let mut body = Body::try_from(elem).unwrap(); + assert_eq!(body.style.len(), 0); + assert_eq!(body.xml_lang, None); + assert_eq!(body.children.len(), 1); + let p = match body.children.pop() { Some(Child::Tag(tag)) => tag, _ => panic!(), }; From 56986a68e48dffbb875bb040a009939636d978d5 Mon Sep 17 00:00:00 2001 From: Randy von der Weide Date: Sat, 31 Aug 2019 13:17:51 +0000 Subject: [PATCH 0939/1020] Impl Display for Jid --- src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index c55c806731bc00b7de88369d535d77134b059c20..abe475ef95f28909903642c47926679eb1a1628c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,6 +92,12 @@ impl From for Jid { } } +impl fmt::Display for Jid { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt.write_str(String::from(self.clone()).as_ref()) + } +} + impl Jid { /// The node part of the Jabber ID, if it exists, else None. pub fn node(self) -> Option { @@ -696,6 +702,14 @@ mod tests { assert_eq!(FullJid::from_str("a@b"), Err(JidParseError::NoResource)); } + #[test] + fn display_jids() { + assert_eq!(format!("{}", FullJid::new("a", "b", "c")), String::from("a@b/c")); + assert_eq!(format!("{}", BareJid::new("a", "b")), String::from("a@b")); + assert_eq!(format!("{}", Jid::Full(FullJid::new("a", "b", "c"))), String::from("a@b/c")); + assert_eq!(format!("{}", Jid::Bare(BareJid::new("a", "b"))), String::from("a@b")); + } + #[cfg(feature = "minidom")] #[test] fn minidom() { From f528a45568af1a476026191d51143f58091ca377 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 4 Sep 2019 18:14:39 +0200 Subject: [PATCH 0940/1020] xhtml: Automatically flatten on parsing. --- src/xhtml.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/xhtml.rs b/src/xhtml.rs index 766109361e070980e1acc83f81dcf443a1a6cf53..8b9635e6a121974b7363ecbe0554a152c163dac9 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -41,7 +41,7 @@ impl XhtmlIm { } /// Removes all unknown elements. - pub fn flatten(self) -> XhtmlIm { + fn flatten(self) -> XhtmlIm { let mut bodies = HashMap::new(); for (lang, body) in self.bodies { let children = body.children.into_iter().fold(vec![], |mut acc, child| { @@ -91,7 +91,7 @@ impl TryFrom for XhtmlIm { } } - Ok(XhtmlIm { bodies }) + Ok(XhtmlIm { bodies }.flatten()) } } @@ -237,7 +237,7 @@ impl Tag { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) } - Tag::Strong { children } => format!("{}", children.into_iter().map(|child| child.to_html()).collect::>().join("")), + Tag::Strong { children } => format!("{}", children_to_html(children)), Tag::Ul { style, children } => { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) From 24e862e35290e88e3b25591fe431c36610e1af47 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 11:51:05 +0200 Subject: [PATCH 0941/1020] xhtml: Fix namespace on Body serialisation. --- src/xhtml.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/xhtml.rs b/src/xhtml.rs index 8b9635e6a121974b7363ecbe0554a152c163dac9..224e0bb54447a75dbccf50e9684e8d16f9432ce1 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -52,9 +52,8 @@ impl XhtmlIm { acc }); let body = Body { - style: body.style, - xml_lang: body.xml_lang, children, + ..body }; bodies.insert(lang, body); } @@ -100,12 +99,12 @@ impl From for Element { Element::builder("html") .ns(ns::XHTML_IM) .append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { - assert_eq!(Some(lang), body.xml_lang.as_ref()); - Element::builder("body") - .ns(ns::XHTML_IM) - .attr("style", get_style_string(body.style.clone())) - .attr("xml:lang", body.xml_lang.clone()) - .append(children_to_nodes(body.children.clone())) + if lang.is_empty() { + assert!(body.xml_lang.is_none()); + } else { + assert_eq!(Some(lang), body.xml_lang.as_ref()); + } + Element::from(body.clone()) }).collect::>()) .build() } @@ -473,9 +472,13 @@ mod tests { let elem: Element = "Hello world!" .parse() .unwrap(); - let xhtml_im = XhtmlIm::try_from(elem).unwrap(); - let html = xhtml_im.to_html(); + let parsed = XhtmlIm::try_from(elem).unwrap(); + let parsed2 = parsed.clone(); + let html = parsed.to_html(); assert_eq!(html, "Hello world!"); + + let elem = Element::from(parsed2); + assert_eq!(String::from(&elem), "Hello world!"); } #[test] From cde011aa5e6b32f6236b133439d2432a1baa9fef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 11:58:48 +0200 Subject: [PATCH 0942/1020] xhtml: Panic on unknown elements still present after parsing. --- src/xhtml.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xhtml.rs b/src/xhtml.rs index 224e0bb54447a75dbccf50e9684e8d16f9432ce1..1ec6a0478f08ec7cd2421ceb3d168f1671a6d371 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -241,7 +241,7 @@ impl Tag { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) } - Tag::Unknown(children) => children_to_html(children), + Tag::Unknown(children) => panic!("No unknown element should be present in XHTML-IM after parsing."), } } } @@ -334,7 +334,7 @@ impl From for Element { Some(style) => vec![("style", style)], None => vec![], }, children), - Tag::Unknown(children) => return Element::builder("unknown").ns(ns::XHTML).append(children_to_nodes(children)).build(), + Tag::Unknown(children) => panic!("No unknown element should be present in XHTML-IM after parsing."), }; let mut builder = Element::builder(name) .ns(ns::XHTML) From f25d4c79b7cb454db08916e510cf2c053519a9bb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 14:13:37 +0200 Subject: [PATCH 0943/1020] xhtml: Use _ for children of Unknown. --- src/xhtml.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xhtml.rs b/src/xhtml.rs index 1ec6a0478f08ec7cd2421ceb3d168f1671a6d371..9c43181bf06fc38e88568b101b6971a6ae466562 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -241,7 +241,7 @@ impl Tag { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) } - Tag::Unknown(children) => panic!("No unknown element should be present in XHTML-IM after parsing."), + Tag::Unknown(_) => panic!("No unknown element should be present in XHTML-IM after parsing."), } } } @@ -334,7 +334,7 @@ impl From for Element { Some(style) => vec![("style", style)], None => vec![], }, children), - Tag::Unknown(children) => panic!("No unknown element should be present in XHTML-IM after parsing."), + Tag::Unknown(_) => panic!("No unknown element should be present in XHTML-IM after parsing."), }; let mut builder = Element::builder(name) .ns(ns::XHTML) From c77221e43748aed5336de836fb4076c844b38fb3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 15:34:21 +0200 Subject: [PATCH 0944/1020] macros: Allow non-String in generate_elem_id!(). --- src/mood.rs | 4 ++++ src/muc/user.rs | 4 ++++ src/util/macros.rs | 13 ++++++++----- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/mood.rs b/src/mood.rs index 8d27c4f0629db50ff50675003532f7923535e4be..f4b529c592ba6e7719e261138c03a735c1d2ff04 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -302,7 +302,11 @@ mod tests { let elem: Element = "Yay!" .parse() .unwrap(); + let elem2 = elem.clone(); let text = Text::try_from(elem).unwrap(); assert_eq!(text.0, String::from("Yay!")); + + let elem3 = text.into(); + assert_eq!(elem2, elem3); } } diff --git a/src/muc/user.rs b/src/muc/user.rs index 44af3a9c734ff689a01f8f63e6846c8bd919b88a..d785d2356cd208adc2a4a22d0a363fb1a66f5128 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -511,8 +511,12 @@ mod tests { Reason" .parse() .unwrap(); + let elem2 = elem.clone(); let reason = Reason::try_from(elem).unwrap(); assert_eq!(reason.0, "Reason".to_owned()); + + let elem3 = reason.into(); + assert_eq!(elem2, elem3); } #[cfg(not(feature = "disable-validation"))] diff --git a/src/util/macros.rs b/src/util/macros.rs index 5b54cf65d57d97cf38778f18cc70bc616776b410..c4b5c4e0b85d75ac68624821b57bbe55ebd808ef 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -410,9 +410,7 @@ macro_rules! generate_id { macro_rules! generate_elem_id { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => ( - $(#[$meta])* - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $elem(pub String); + generate_elem_id!($(#[$meta])* $elem, $name, $ns, String); impl ::std::str::FromStr for $elem { type Err = crate::util::error::Error; fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { @@ -420,6 +418,11 @@ macro_rules! generate_elem_id { Ok($elem(String::from(s))) } } + ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $type:ty) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $elem(pub $type); impl ::std::convert::TryFrom<::minidom::Element> for $elem { type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { @@ -427,14 +430,14 @@ macro_rules! generate_elem_id { check_no_children!(elem, $name); check_no_attributes!(elem, $name); // TODO: add a way to parse that differently when needed. - Ok($elem(elem.text())) + Ok($elem(elem.text().parse()?)) } } impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { ::minidom::Element::builder($name) .ns(crate::ns::$ns) - .append(elem.0) + .append(elem.0.to_string()) .build() } } From 77920e5f466662e3a7cea03baf4f6aa4e0a68937 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 15:37:34 +0200 Subject: [PATCH 0945/1020] New User Tune parser (XEP-0118). --- ChangeLog | 1 + doap.xml | 8 ++ src/lib.rs | 3 + src/ns.rs | 3 + src/tune.rs | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+) create mode 100644 src/tune.rs diff --git a/ChangeLog b/ChangeLog index eec3bbb8cb21ca2b08b42a528f354d846e8f2aa6..5ecea28ef465af043d041b95750d673ead917e6a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - XHTML-IM (XEP-0071) + - User Tune (XEP-0118) - Bits of Binary (XEP-0231) - Message Carbons (XEP-0280) * Breaking changes: diff --git a/doap.xml b/doap.xml index a3165dc0b94df64facbfe2696f0579e0efc62bab..561f49dd133eef7655c5c2fbdbce8fda19175db2 100644 --- a/doap.xml +++ b/doap.xml @@ -183,6 +183,14 @@ 0.4.0 + + + + complete + 1.2 + NEXT + + diff --git a/src/lib.rs b/src/lib.rs index af2fc129566d64a4dc6ca7121fd95c0e56bbc6da..d989812348b2a9dd141974da0b672eec97e19ead 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,6 +102,9 @@ pub mod component; /// XEP-0115: Entity Capabilities pub mod caps; +/// XEP-0118: User Tune +pub mod tune; + /// XEP-0157: Contact Addresses for XMPP Services pub mod server_info; diff --git a/src/ns.rs b/src/ns.rs index 5c59ec03cccbf36169f5bf355ca27e10ea377528..2c932c3ced7c9e8e844aa7a362f1541f2f021008 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -84,6 +84,9 @@ pub const COMPONENT: &str = "jabber:component:accept"; /// XEP-0115: Entity Capabilities pub const CAPS: &str = "http://jabber.org/protocol/caps"; +/// XEP-0118: User Tune +pub const TUNE: &str = "http://jabber.org/protocol/tune"; + /// XEP-0157: Contact Addresses for XMPP Services pub const SERVER_INFO: &str = "http://jabber.org/network/serverinfo"; diff --git a/src/tune.rs b/src/tune.rs new file mode 100644 index 0000000000000000000000000000000000000000..7a0b9f3a74d6dc5b097ff0cf790273831a1a7192 --- /dev/null +++ b/src/tune.rs @@ -0,0 +1,214 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::util::error::Error; +use crate::pubsub::PubSubPayload; +use crate::ns; +use minidom::Element; +use std::convert::TryFrom; + +generate_elem_id!( + /// The artist or performer of the song or piece. + Artist, "artist", TUNE +); + +generate_elem_id!( + /// The duration of the song or piece in seconds. + Length, "length", TUNE, + u16 +); + +generate_elem_id!( + /// The user's rating of the song or piece, from 1 (lowest) to 10 (highest). + Rating, "rating", TUNE, + u8 +); + +generate_elem_id!( + /// The collection (e.g., album) or other source (e.g., a band website that hosts streams or + /// audio files). + Source, "source", TUNE +); + +generate_elem_id!( + /// The title of the song or piece. + Title, "title", TUNE +); + +generate_elem_id!( + /// A unique identifier for the tune; e.g., the track number within a collection or the + /// specific URI for the object (e.g., a stream or audio file). + Track, "track", TUNE +); + +generate_elem_id!( + /// A URI or URL pointing to information about the song, collection, or artist. + Uri, "uri", TUNE +); + +/// Container for formatted text. +#[derive(Debug, Clone)] +pub struct Tune { + /// The artist or performer of the song or piece. + artist: Option, + + /// The duration of the song or piece in seconds. + length: Option, + + /// The user's rating of the song or piece, from 1 (lowest) to 10 (highest). + rating: Option, + + /// The collection (e.g., album) or other source (e.g., a band website that hosts streams or + /// audio files). + source: Option, + + /// The title of the song or piece. + title: Option, + + /// A unique identifier for the tune; e.g., the track number within a collection or the + /// specific URI for the object (e.g., a stream or audio file). + track: Option<Track>, + + /// A URI or URL pointing to information about the song, collection, or artist. + uri: Option<Uri>, +} + +impl PubSubPayload for Tune {} + +impl Tune { + fn new() -> Tune { + Tune { + artist: None, + length: None, + rating: None, + source: None, + title: None, + track: None, + uri: None, + } + } +} + +impl TryFrom<Element> for Tune { + type Error = Error; + + fn try_from(elem: Element) -> Result<Tune, Error> { + check_self!(elem, "tune", TUNE); + check_no_attributes!(elem, "tune"); + + let mut tune = Tune::new(); + for child in elem.children() { + if child.is("artist", ns::TUNE) { + if tune.artist.is_some() { + return Err(Error::ParseError("Tune can’t have more than one artist.")); + } + tune.artist = Some(Artist::try_from(child.clone())?); + } else if child.is("length", ns::TUNE) { + if tune.length.is_some() { + return Err(Error::ParseError("Tune can’t have more than one length.")); + } + tune.length = Some(Length::try_from(child.clone())?); + } else if child.is("rating", ns::TUNE) { + if tune.rating.is_some() { + return Err(Error::ParseError("Tune can’t have more than one rating.")); + } + tune.rating = Some(Rating::try_from(child.clone())?); + } else if child.is("source", ns::TUNE) { + if tune.source.is_some() { + return Err(Error::ParseError("Tune can’t have more than one source.")); + } + tune.source = Some(Source::try_from(child.clone())?); + } else if child.is("title", ns::TUNE) { + if tune.title.is_some() { + return Err(Error::ParseError("Tune can’t have more than one title.")); + } + tune.title = Some(Title::try_from(child.clone())?); + } else if child.is("track", ns::TUNE) { + if tune.track.is_some() { + return Err(Error::ParseError("Tune can’t have more than one track.")); + } + tune.track = Some(Track::try_from(child.clone())?); + } else if child.is("uri", ns::TUNE) { + if tune.uri.is_some() { + return Err(Error::ParseError("Tune can’t have more than one uri.")); + } + tune.uri = Some(Uri::try_from(child.clone())?); + } else { + return Err(Error::ParseError("Unknown element in User Tune.")); + } + } + + Ok(tune) + } +} + +impl From<Tune> for Element { + fn from(tune: Tune) -> Element { + Element::builder("tune") + .ns(ns::TUNE) + .append(tune.artist) + .append(tune.length) + .append(tune.rating) + .append(tune.source) + .append(tune.title) + .append(tune.track) + .append(tune.uri) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[cfg(target_pointer_width = "32")] + #[test] + #[ignore] + fn test_size() { + assert_size!(Tune, 0); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Tune, 128); + } + + #[test] + fn empty() { + let elem: Element = "<tune xmlns='http://jabber.org/protocol/tune'/>" + .parse() + .unwrap(); + let elem2 = elem.clone(); + let tune = Tune::try_from(elem).unwrap(); + assert!(tune.artist.is_none()); + assert!(tune.length.is_none()); + assert!(tune.rating.is_none()); + assert!(tune.source.is_none()); + assert!(tune.title.is_none()); + assert!(tune.track.is_none()); + assert!(tune.uri.is_none()); + + let elem3 = tune.into(); + assert_eq!(elem2, elem3); + } + + #[test] + fn full() { + let elem: Element = "<tune xmlns='http://jabber.org/protocol/tune'><artist>Yes</artist><length>686</length><rating>8</rating><source>Yessongs</source><title>Heart of the Sunrise3http://www.yesworld.com/lyrics/Fragile.html#9" + .parse() + .unwrap(); + let tune = Tune::try_from(elem).unwrap(); + assert_eq!(tune.artist, Some(Artist::from_str("Yes").unwrap())); + assert_eq!(tune.length, Some(Length(686))); + assert_eq!(tune.rating, Some(Rating(8))); + assert_eq!(tune.source, Some(Source::from_str("Yessongs").unwrap())); + assert_eq!(tune.title, Some(Title::from_str("Heart of the Sunrise").unwrap())); + assert_eq!(tune.track, Some(Track::from_str("3").unwrap())); + assert_eq!(tune.uri, Some(Uri::from_str("http://www.yesworld.com/lyrics/Fragile.html#9").unwrap())); + } +} From 0ef4ba7e55f87ee5f6a439130bd7757f88a9ac4b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 17:58:45 +0200 Subject: [PATCH 0946/1020] tune: Add size checks for all elements. --- src/tune.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tune.rs b/src/tune.rs index 7a0b9f3a74d6dc5b097ff0cf790273831a1a7192..1bbb436a618f6c8c9c45fb5393f363fd47863bac 100644 --- a/src/tune.rs +++ b/src/tune.rs @@ -169,13 +169,27 @@ mod tests { #[test] #[ignore] fn test_size() { - assert_size!(Tune, 0); + assert_size!(Tune, 64); + assert_size!(Artist, 12); + assert_size!(Length, 2); + assert_size!(Rating, 1); + assert_size!(Source, 12); + assert_size!(Title, 12); + assert_size!(Track, 12); + assert_size!(Uri, 12); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Tune, 128); + assert_size!(Artist, 24); + assert_size!(Length, 2); + assert_size!(Rating, 1); + assert_size!(Source, 24); + assert_size!(Title, 24); + assert_size!(Track, 24); + assert_size!(Uri, 24); } #[test] From cba7a31ea0ec64c03acb8fca20df08c8f0e4c9fb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 18:42:22 +0200 Subject: [PATCH 0947/1020] Remove failure. --- Cargo.toml | 2 -- src/lib.rs | 20 ++++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 77cbae3d3acaac8e06265579d45926aa8a3ed513..162cb9a5bc633340dd41f35e3a0c322dd5d91320 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,4 @@ edition = "2018" gitlab = { repository = "xmpp-rs/jid-rs" } [dependencies] -failure = "0.1.1" -failure_derive = "0.1.1" minidom = { version = "0.11", optional = true } diff --git a/src/lib.rs b/src/lib.rs index c55c806731bc00b7de88369d535d77134b059c20..948576c35e7ac1e02547a0aaf984bf5848f59309 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,34 +14,38 @@ //! //! For usage, check the documentation on the `Jid` struct. -#[macro_use] -extern crate failure_derive; - use std::convert::Into; use std::fmt; use std::str::FromStr; /// An error that signifies that a `Jid` cannot be parsed from a string. -#[derive(Debug, Clone, PartialEq, Eq, Fail)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum JidParseError { /// Happens when there is no domain, that is either the string is empty, /// starts with a /, or contains the @/ sequence. - #[fail(display = "no domain found in this JID")] NoDomain, /// Happens when there is no resource, that is string contains no /. - #[fail(display = "no resource found in this full JID")] NoResource, /// Happens when the node is empty, that is the string starts with a @. - #[fail(display = "nodepart empty despite the presence of a @")] EmptyNode, /// Happens when the resource is empty, that is the string ends with a /. - #[fail(display = "resource empty despite the presence of a /")] EmptyResource, } +impl fmt::Display for JidParseError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", match self { + JidParseError::NoDomain => "no domain found in this JID", + JidParseError::NoResource => "no resource found in this full JID", + JidParseError::EmptyNode => "nodepart empty despite the presence of a @", + JidParseError::EmptyResource => "resource empty despite the presence of a /", + }) + } +} + /// An enum representing a Jabber ID. It can be either a `FullJid` or a `BareJid`. #[derive(Debug, Clone, PartialEq)] pub enum Jid { From b4035d122738e107d7f95c230ec3260026867e79 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 20:06:17 +0200 Subject: [PATCH 0948/1020] Remove failure. --- Cargo.toml | 2 -- src/error.rs | 30 +++++++++++++++++++----------- src/lib.rs | 2 -- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9ed80d59cf63cd19fe50694ffefe1e93f6d99e2f..4e1632aa3cba811fcfbe9bfcf15587f94516b9cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,6 @@ gitlab = { repository = "lumi/minidom-rs" } [dependencies] quick-xml = "0.15" -failure = "0.1.1" -failure_derive = "0.1.1" [features] default = ["comments"] diff --git a/src/error.rs b/src/error.rs index 773f214fa573a5434213c176ce6d03ee38b9a3b4..fc96a08bd1b69638cf3a5c0580a80e02d7b48bac 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,38 +3,46 @@ use std::convert::From; /// Our main error type. -#[derive(Debug, Fail)] +#[derive(Debug)] pub enum Error { /// An error from quick_xml. - #[fail(display = "XML error: {}", _0)] - XmlError(#[cause] ::quick_xml::Error), + XmlError(::quick_xml::Error), /// An UTF-8 conversion error. - #[fail(display = "UTF-8 error: {}", _0)] - Utf8Error(#[cause] ::std::str::Utf8Error), + Utf8Error(::std::str::Utf8Error), /// An I/O error, from std::io. - #[fail(display = "IO error: {}", _0)] - IoError(#[cause] ::std::io::Error), + IoError(::std::io::Error), /// An error which is returned when the end of the document was reached prematurely. - #[fail(display = "the end of the document has been reached prematurely")] EndOfDocument, /// An error which is returned when an element is closed when it shouldn't be - #[fail(display = "the XML is invalid, an element was wrongly closed")] InvalidElementClosed, /// An error which is returned when an elemet's name contains more than one colon - #[fail(display = "the XML element is invalid")] InvalidElement, /// An error which is returned when a comment is to be parsed by minidom #[cfg(not(comments))] - #[fail(display = "a comment has been found even though comments are disabled by feature")] CommentsDisabled, } +impl std::fmt::Display for Error { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Error::XmlError(e) => write!(fmt, "XML error: {}", e), + Error::Utf8Error(e) => write!(fmt, "UTF-8 error: {}", e), + Error::IoError(e) => write!(fmt, "IO error: {}", e), + Error::EndOfDocument => write!(fmt, "the end of the document has been reached prematurely"), + Error::InvalidElementClosed => write!(fmt, "the XML is invalid, an element was wrongly closed"), + Error::InvalidElement => write!(fmt, "the XML element is invalid"), + #[cfg(not(comments))] + Error::CommentsDisabled => write!(fmt, "a comment has been found even though comments are disabled by feature"), + } + } +} + impl From<::quick_xml::Error> for Error { fn from(err: ::quick_xml::Error) -> Error { Error::XmlError(err) diff --git a/src/lib.rs b/src/lib.rs index 5ea7e7063617163753b02f6550d957037be0fa13..cf17cdac5cdcb07c112de154701fc382abeecc07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,8 +65,6 @@ //! ``` extern crate quick_xml; -extern crate failure; -#[macro_use] extern crate failure_derive; pub mod error; pub mod element; From 17f902b503c30c398db9612e98dc059d8e7e1846 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 20:25:04 +0200 Subject: [PATCH 0949/1020] Update to quick-xml 0.16. --- Cargo.toml | 2 +- src/element.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e1632aa3cba811fcfbe9bfcf15587f94516b9cd..76b4b4dccbcc9f8ddfa3e5bd57e6c21beada66ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ license = "MIT" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -quick-xml = "0.15" +quick-xml = "0.16" [features] default = ["comments"] diff --git a/src/element.rs b/src/element.rs index cc9f8e11d4409cdc3d05033687d59c2c15de937f..68d445e27641b4ae73a28f3b3725ad7772eff03e 100644 --- a/src/element.rs +++ b/src/element.rs @@ -357,7 +357,7 @@ impl Element { } }, Event::CData(s) => { - let text = reader.decode(&s).into_owned(); + let text = reader.decode(&s)?.to_owned(); if text != "" { let current_elem = stack.last_mut().unwrap(); current_elem.append_text_node(text); @@ -370,7 +370,7 @@ impl Element { Event::Comment(_) => return Err(Error::CommentsDisabled), #[cfg(feature = "comments")] Event::Comment(s) => { - let comment = reader.decode(&s).into_owned(); + let comment = reader.decode(&s)?.to_owned(); if comment != "" { let current_elem = stack.last_mut().unwrap(); current_elem.append_comment_node(comment); From ed28ba1a7d63c1422443f3dca32c848869a14501 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 5 Sep 2019 22:25:28 +0200 Subject: [PATCH 0950/1020] Remove the derive-error dependency. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It depends on a way too old syn crate, let’s drop it. --- Cargo.toml | 1 - src/error.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++++---- src/lib.rs | 3 -- 3 files changed, 105 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a62b4ba1d696ff9e8e4047315844274c9af2b1e4..c22979f6e5531a579f3418d747487a535cbad39e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,4 +26,3 @@ trust-dns-proto = "0.7" xmpp-parsers = "0.13" idna = "0.1" quick-xml = "0.13" -derive-error = "0.0.4" diff --git a/src/error.rs b/src/error.rs index d8b2063d473b44039e3d271472d5f27cb855ec6f..68a1412e9f4d2b87c91c392514e2ec5f7b1217c6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,7 +11,7 @@ use xmpp_parsers::Error as ParsersError; use xmpp_parsers::sasl::DefinedCondition as SaslDefinedCondition; /// Top-level error type -#[derive(Debug, Error)] +#[derive(Debug)] pub enum Error { /// I/O error Io(IoError), @@ -32,8 +32,53 @@ pub enum Error { InvalidState, } +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Io(e) => write!(fmt, "IO error: {}", e), + Error::Connection(e) => write!(fmt, "connection error: {}", e), + Error::Idna => write!(fmt, "IDNA error"), + Error::Protocol(e) => write!(fmt, "protocol error: {}", e), + Error::Auth(e) => write!(fmt, "authentication error: {}", e), + Error::Tls(e) => write!(fmt, "TLS error: {}", e), + Error::Disconnected => write!(fmt, "disconnected"), + Error::InvalidState => write!(fmt, "invalid state"), + } + } +} + +impl From for Error { + fn from(e: IoError) -> Self { + Error::Io(e) + } +} + +impl From for Error { + fn from(e: ConnecterError) -> Self { + Error::Connection(e) + } +} + +impl From for Error { + fn from(e: ProtocolError) -> Self { + Error::Protocol(e) + } +} + +impl From for Error { + fn from(e: AuthError) -> Self { + Error::Auth(e) + } +} + +impl From for Error { + fn from(e: TlsError) -> Self { + Error::Tls(e) + } +} + /// Causes for stream parsing errors -#[derive(Debug, Error)] +#[derive(Debug)] pub enum ParserError { /// Encoding error Utf8(Utf8Error), @@ -42,7 +87,24 @@ pub enum ParserError { /// Illegal `` ShortTag, /// Required by `impl Decoder` - IO(IoError), + Io(IoError), +} + +impl fmt::Display for ParserError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + ParserError::Utf8(e) => write!(fmt, "UTF-8 error: {}", e), + ParserError::Parse(e) => write!(fmt, "parse error: {}", e), + ParserError::ShortTag => write!(fmt, "short tag"), + ParserError::Io(e) => write!(fmt, "IO error: {}", e), + } + } +} + +impl From for ParserError { + fn from(e: IoError) -> Self { + ParserError::Io(e) + } } impl From for Error { @@ -71,12 +133,11 @@ impl fmt::Display for ParseError { } /// XMPP protocol-level error -#[derive(Debug, Error)] +#[derive(Debug)] pub enum ProtocolError { /// XML parser error Parser(ParserError), /// Error with expected stanza schema - #[error(non_std)] Parsers(ParsersError), /// No TLS available NoTls, @@ -92,22 +153,57 @@ pub enum ProtocolError { InvalidStreamStart, } +impl fmt::Display for ProtocolError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + ProtocolError::Parser(e) => write!(fmt, "XML parser error: {}", e), + ProtocolError::Parsers(e) => write!(fmt, "error with expected stanza schema: {}", e), + ProtocolError::NoTls => write!(fmt, "no TLS available"), + ProtocolError::InvalidBindResponse => write!(fmt, "invalid response to resource binding"), + ProtocolError::NoStreamNamespace => write!(fmt, "no xmlns attribute in "), + ProtocolError::NoStreamId => write!(fmt, "no id attribute in "), + ProtocolError::InvalidToken => write!(fmt, "encountered an unexpected XML token"), + ProtocolError::InvalidStreamStart => write!(fmt, "unexpected "), + } + } +} + +impl From for ProtocolError { + fn from(e: ParserError) -> Self { + ProtocolError::Parser(e) + } +} + +impl From for ProtocolError { + fn from(e: ParsersError) -> Self { + ProtocolError::Parsers(e) + } +} + /// Authentication error -#[derive(Debug, Error)] +#[derive(Debug)] pub enum AuthError { /// No matching SASL mechanism available NoMechanism, /// Local SASL implementation error - #[error(no_from, non_std, msg_embedded)] Sasl(String), /// Failure from server - #[error(non_std)] Fail(SaslDefinedCondition), /// Component authentication failure - #[error(no_from)] ComponentFail, } +impl fmt::Display for AuthError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + AuthError::NoMechanism => write!(fmt, "no matching SASL mechanism available"), + AuthError::Sasl(s) => write!(fmt, "local SASL implementation error: {}", s), + AuthError::Fail(c) => write!(fmt, "failure from the server: {:?}", c), + AuthError::ComponentFail => write!(fmt, "component authentication failure"), + } + } +} + /// Error establishing connection #[derive(Debug)] pub enum ConnecterError { diff --git a/src/lib.rs b/src/lib.rs index 07ae878437455e1694c95b9cf41c8582ba73014f..2bd823eef822a644becfa69b81c3d3b45782317f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,6 @@ //! XMPP implementation with asynchronous I/O using Tokio. -#[macro_use] -extern crate derive_error; - mod starttls; mod stream_start; pub mod xmpp_codec; From a1ae45add88e424710073711935a3605cafe9ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Thu, 25 Jul 2019 00:20:38 +0200 Subject: [PATCH 0951/1020] Update minidom dependency to 0.11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 4 +- src/bind.rs | 4 +- src/blocking.rs | 4 +- src/data_forms.rs | 23 +++++----- src/date.rs | 8 ++-- src/disco.rs | 8 ++-- src/ibr.rs | 15 +++---- src/jingle.rs | 10 ++--- src/jingle_ft.rs | 58 +++++++++++++++---------- src/jingle_s5b.rs | 23 ++++++---- src/mam.rs | 29 ++++++++----- src/message.rs | 8 ++-- src/presence.rs | 36 ++++++++------- src/pubsub/event.rs | 11 +++-- src/pubsub/pubsub.rs | 8 ++-- src/rsm.rs | 16 +++---- src/sasl.rs | 3 +- src/stanza_error.rs | 9 ++-- src/tune.rs | 14 +++--- src/util/helpers.rs | 4 +- src/util/macros.rs | 101 ++++++++++++++++++++++++++++++++----------- src/xhtml.rs | 8 ++-- 22 files changed, 236 insertions(+), 168 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fba43cb47e76b689fe177309fbca26f4e52d0241..6690ccf66ba81a972eb767e3cb620e3e0d61707f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ license = "MPL-2.0" edition = "2018" [dependencies] -minidom = "0.10.0" -jid = { version = "0.6.0", features = ["minidom"] } +minidom = "0.11.0" +jid = { version = "0.7.0", features = ["minidom"] } base64 = "0.10" digest = "0.8" sha-1 = "0.8" diff --git a/src/bind.rs b/src/bind.rs index 357a7326b4448352494b2923d7456294ce1d8395..17e1e1240ac5ac9895bbd08250f6ae92a4c85e69 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -63,13 +63,13 @@ impl From for Element { fn from(bind: BindQuery) -> Element { Element::builder("bind") .ns(ns::BIND) - .append(match bind.resource { + .append_all((match bind.resource { None => vec![], Some(resource) => vec![Element::builder("resource") .ns(ns::BIND) .append(resource) .build()], - }) + }).into_iter()) .build() } } diff --git a/src/blocking.rs b/src/blocking.rs index f218b21d4985dd4894d56d1c3e1e9dcc34c16902..f9e6a821bc3769891a80da0404e1d6bf5ef86ceb 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -51,12 +51,12 @@ macro_rules! generate_blocking_element { fn from(elem: $elem) -> Element { Element::builder($name) .ns(ns::BLOCKING) - .append(elem.items.into_iter().map(|jid| { + .append_all(elem.items.into_iter().map(|jid| { Element::builder("item") .ns(ns::BLOCKING) .attr("jid", jid) .build() - }).collect::>()) + })) .build() } } diff --git a/src/data_forms.rs b/src/data_forms.rs index 323e91ed091b7411dc1a2eb82e40b28fc4357853..f252bb714f5b82efb9ee1417c426d5f3a8b63771 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::media_element::MediaElement; use crate::ns; -use minidom::Element; +use minidom::{Element, Node}; use std::convert::TryFrom; generate_element!( @@ -152,13 +152,13 @@ impl From for Element { .attr("var", field.var) .attr("type", field.type_) .attr("label", field.label) - .append(if field.required { + .append_all((if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None - }) - .append(field.options) - .append( + }).into_iter()) + .append_all(field.options.iter().cloned().map(Element::from).map(Node::Element).into_iter()) + .append_all( field .values .into_iter() @@ -168,9 +168,8 @@ impl From for Element { .append(value) .build() }) - .collect::>(), ) - .append(field.media) + .append_all(field.media.iter().cloned().map(Element::from).map(Node::Element)) .build() } } @@ -275,21 +274,21 @@ impl From for Element { Element::builder("x") .ns(ns::DATA_FORMS) .attr("type", form.type_) - .append( + .append_all( form.title .map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title)), ) - .append(form.instructions.map(|text| { + .append_all(form.instructions.map(|text| { Element::builder("instructions") .ns(ns::DATA_FORMS) .append(text) })) - .append(if let Some(form_type) = form.form_type { + .append_all((if let Some(form_type) = form.form_type { vec![Element::builder("field").ns(ns::DATA_FORMS).attr("var", "FORM_TYPE").attr("type", "hidden").append(Element::builder("value").ns(ns::DATA_FORMS).append(form_type).build()).build()] } else { vec![] - }) - .append(form.fields) + }).into_iter()) + .append_all(form.fields.iter().cloned().map(Element::from).map(Node::Element)) .build() } } diff --git a/src/date.rs b/src/date.rs index e5fd67e89dc1de926760f39aa1a107d2440ca011..bfcf4e166de8cf1097a5d4f6010c87ae48536b7e 100644 --- a/src/date.rs +++ b/src/date.rs @@ -6,7 +6,7 @@ use crate::util::error::Error; use chrono::{DateTime as ChronoDateTime, FixedOffset}; -use minidom::{ElementEmitter, IntoAttributeValue, IntoElements}; +use minidom::{IntoAttributeValue, Node}; use std::str::FromStr; /// Implements the DateTime profile of XEP-0082, which represents a @@ -46,9 +46,9 @@ impl IntoAttributeValue for DateTime { } } -impl IntoElements for DateTime { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(self.0.to_rfc3339()) +impl Into for DateTime { + fn into(self) -> Node { + Node::Text(self.0.to_rfc3339()) } } diff --git a/src/disco.rs b/src/disco.rs index abf0e238f152afcdab91ca8eb270d9cc32e4f7aa..3c2cde13835203e97c070c52d00c67ed8e43a87b 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use jid::Jid; -use minidom::Element; +use minidom::{Element, Node}; use std::convert::TryFrom; generate_element!( @@ -178,9 +178,9 @@ impl From for Element { Element::builder("query") .ns(ns::DISCO_INFO) .attr("node", disco.node) - .append(disco.identities) - .append(disco.features) - .append(disco.extensions) + .append_all(disco.identities.into_iter()) + .append_all(disco.features.into_iter()) + .append_all(disco.extensions.iter().cloned().map(Element::from).map(Node::Element)) .build() } } diff --git a/src/ibr.rs b/src/ibr.rs index 8ddca59dc4ebe34b3c1868f2d4d60f1dfbca9ce5..d539f2adce83fa4cdf8fe13ba650ba0be5f277af 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -8,7 +8,7 @@ use crate::data_forms::DataForm; use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use minidom::Element; +use minidom::{Element, Node}; use std::collections::HashMap; use std::convert::TryFrom; @@ -93,24 +93,23 @@ impl From for Element { fn from(query: Query) -> Element { Element::builder("query") .ns(ns::REGISTER) - .append(if query.registered { + .append_all((if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None - }) - .append( + }).into_iter()) + .append_all( query .fields .into_iter() .map(|(name, value)| Element::builder(name).ns(ns::REGISTER).append(value)) - .collect::>(), ) - .append(if query.remove { + .append_all((if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None - }) - .append(query.form) + }).into_iter()) + .append_all(query.form.map(Element::from).map(Node::Element).into_iter()) .build() } } diff --git a/src/jingle.rs b/src/jingle.rs index 98e5fcbb46db4620ed82b2efae8a6a4026ba5892..55a493ef407fa7785be7d94125cf2d4fcb55ea80 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; -use minidom::Element; +use minidom::{Element, Node}; use std::collections::BTreeMap; use std::str::FromStr; use std::convert::TryFrom; @@ -413,14 +413,14 @@ impl From for Element { Element::builder("reason") .ns(ns::JINGLE) .append(Element::from(reason.reason)) - .append( + .append_all( reason.texts.into_iter().map(|(lang, text)| { Element::builder("text") .ns(ns::JINGLE) .attr("xml:lang", lang) .append(text) .build() - }).collect::>()) + })) .build() } } @@ -542,8 +542,8 @@ impl From for Element { .attr("initiator", jingle.initiator) .attr("responder", jingle.responder) .attr("sid", jingle.sid) - .append(jingle.contents) - .append(jingle.reason) + .append_all(jingle.contents.into_iter()) + .append_all(jingle.reason.map(Element::from).map(Node::Element).into_iter()) .build() } } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index f0b7cf32461fe8e1982bc9a309f67f3fa3db5f74..b3da6f8b9661b027d46e5fa0002a5f28672f98db 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::hashes::Hash; use crate::jingle::{ContentId, Creator}; use crate::ns; -use minidom::Element; +use minidom::{Element, Node}; use std::collections::BTreeMap; use std::str::FromStr; use std::convert::TryFrom; @@ -196,43 +196,53 @@ impl From for Element { let mut root = Element::builder("file").ns(ns::JINGLE_FT); if let Some(date) = file.date { root = root.append( - Element::builder("date") - .ns(ns::JINGLE_FT) - .append(date) - .build(), + Node::Element( + Element::builder("date") + .ns(ns::JINGLE_FT) + .append(date) + .build() + ) ); } if let Some(media_type) = file.media_type { root = root.append( - Element::builder("media-type") - .ns(ns::JINGLE_FT) - .append(media_type) - .build(), + Node::Element( + Element::builder("media-type") + .ns(ns::JINGLE_FT) + .append(media_type) + .build() + ) ); } if let Some(name) = file.name { root = root.append( - Element::builder("name") - .ns(ns::JINGLE_FT) - .append(name) - .build(), + Node::Element( + Element::builder("name") + .ns(ns::JINGLE_FT) + .append(name) + .build() + ) ); } for (lang, desc) in file.descs.into_iter() { root = root.append( - Element::builder("desc") - .ns(ns::JINGLE_FT) - .attr("xml:lang", lang) - .append(desc.0) - .build(), + Node::Element( + Element::builder("desc") + .ns(ns::JINGLE_FT) + .attr("xml:lang", lang) + .append(desc.0) + .build() + ) ); } if let Some(size) = file.size { root = root.append( - Element::builder("size") - .ns(ns::JINGLE_FT) - .append(format!("{}", size)) - .build(), + Node::Element( + Element::builder("size") + .ns(ns::JINGLE_FT) + .append(format!("{}", size)) + .build() + ) ); } if let Some(range) = file.range { @@ -282,7 +292,7 @@ impl From for Element { fn from(description: Description) -> Element { Element::builder("description") .ns(ns::JINGLE_FT) - .append(description.file) + .append(Node::Element(description.file.into())) .build() } } @@ -334,7 +344,7 @@ impl From for Element { .ns(ns::JINGLE_FT) .attr("name", checksum.name) .attr("creator", checksum.creator) - .append(checksum.file) + .append(Node::Element(checksum.file.into())) .build() } } diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 969b83f30ed562ef354660cecea560ab88786a5b..0f246691c40c1c065c252ffe473113f3bd01b659 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -247,26 +247,31 @@ impl From for Element { .attr("sid", transport.sid) .attr("dstaddr", transport.dstaddr) .attr("mode", transport.mode) - .append(match transport.payload { + .append_all(match transport.payload { TransportPayload::Candidates(candidates) => candidates .into_iter() .map(Element::from) - .collect::>(), + .collect::>() + .into_iter(), TransportPayload::Activated(cid) => vec![Element::builder("activated") .ns(ns::JINGLE_S5B) .attr("cid", cid) - .build()], + .build()] + .into_iter(), TransportPayload::CandidateError => vec![Element::builder("candidate-error") .ns(ns::JINGLE_S5B) - .build()], + .build()] + .into_iter(), TransportPayload::CandidateUsed(cid) => vec![Element::builder("candidate-used") .ns(ns::JINGLE_S5B) .attr("cid", cid) - .build()], - TransportPayload::ProxyError => { - vec![Element::builder("proxy-error").ns(ns::JINGLE_S5B).build()] - } - TransportPayload::None => vec![], + .build()] + .into_iter(), + TransportPayload::ProxyError => vec![Element::builder("proxy-error") + .ns(ns::JINGLE_S5B) + .build()] + .into_iter(), + TransportPayload::None => vec![].into_iter(), }) .build() } diff --git a/src/mam.rs b/src/mam.rs index 6889f2edf85cd5d1139748b7cf2aa893c15cc570..edc0116c4269dcdf57655c761e306d814496e39e 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -13,7 +13,7 @@ use crate::ns; use crate::pubsub::NodeName; use crate::rsm::{SetQuery, SetResult}; use jid::Jid; -use minidom::Element; +use minidom::{Element, Node}; use std::convert::TryFrom; generate_id!( @@ -161,20 +161,29 @@ impl TryFrom for Prefs { } } -fn serialise_jid_list(name: &str, jids: Vec) -> Option { +fn serialise_jid_list(name: &str, jids: Vec) -> ::std::option::IntoIter { if jids.is_empty() { - None + None.into_iter() } else { Some( Element::builder(name) .ns(ns::MAM) - .append( + .append_all( jids.into_iter() - .map(|jid| Element::builder("jid").ns(ns::MAM).append(jid).build()) - .collect::>(), + .map(|jid| + Node::Element( + Element::builder("jid") + .ns(ns::MAM) + .append(Node::Text(String::from(jid))) + .build() + .into() + ) + ) + .into_iter(), ) - .build(), - ) + .build() + .into(), + ).into_iter() } } @@ -183,8 +192,8 @@ impl From for Element { Element::builder("prefs") .ns(ns::MAM) .attr("default", prefs.default_) - .append(serialise_jid_list("always", prefs.always)) - .append(serialise_jid_list("never", prefs.never)) + .append_all(serialise_jid_list("always", prefs.always)) + .append_all(serialise_jid_list("never", prefs.never)) .build() } } diff --git a/src/message.rs b/src/message.rs index f9bae2b16ee3351555b469534481f18f79324a14..4a78c6c0b7794e1cda6738af6739718031acd9e0 100644 --- a/src/message.rs +++ b/src/message.rs @@ -212,7 +212,7 @@ impl From for Element { .attr("to", message.to) .attr("id", message.id) .attr("type", message.type_) - .append( + .append_all( message .subjects .into_iter() @@ -227,9 +227,8 @@ impl From for Element { ); subject }) - .collect::>(), ) - .append( + .append_all( message .bodies .into_iter() @@ -244,9 +243,8 @@ impl From for Element { ); body }) - .collect::>(), ) - .append(message.payloads) + .append_all(message.payloads.into_iter()) .build() } } diff --git a/src/presence.rs b/src/presence.rs index ffb734c596be6d0b7575f85e2079b51693108cf2..4aad1622941265e2d93a7994e83aaf1f99c6da5c 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::ns; use jid::Jid; -use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; +use minidom::{Element, IntoAttributeValue, Node}; use std::collections::BTreeMap; use std::str::FromStr; use std::convert::TryFrom; @@ -48,18 +48,17 @@ impl FromStr for Show { } } -impl IntoElements for Show { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child( - Element::builder("show") - .append(match self { - Show::Away => Some("away"), - Show::Chat => Some("chat"), - Show::Dnd => Some("dnd"), - Show::Xa => Some("xa"), - }) - .build(), - ) +impl Into for Show { + fn into(self) -> Node { + Element::builder("show") + .append(match self { + Show::Away => "away", + Show::Chat => "chat", + Show::Dnd => "dnd", + Show::Xa => "xa", + }) + .build() + .into() } } @@ -310,8 +309,8 @@ impl From for Element { .attr("to", presence.to) .attr("id", presence.id) .attr("type", presence.type_) - .append(presence.show) - .append( + .append_all(presence.show.into_iter()) + .append_all( presence .statuses .into_iter() @@ -327,9 +326,8 @@ impl From for Element { .append(status) .build() }) - .collect::>(), ) - .append(if presence.priority == 0 { + .append_all((if presence.priority == 0 { None } else { Some( @@ -337,8 +335,8 @@ impl From for Element { .append(format!("{}", presence.priority)) .build() ) - }) - .append(presence.payloads) + }).into_iter()) + .append_all(presence.payloads.into_iter()) .build() } } diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 5d65eb783d9411505b26065f8aee07c25e476605..9327c50bc7d2d8c57adfdea09e68ed382e3aa1c6 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -10,7 +10,7 @@ use crate::util::error::Error; use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; -use minidom::Element; +use minidom::{Element, Node}; use std::convert::TryFrom; /// Event wrapper for a PubSub ``. @@ -198,12 +198,12 @@ impl From for Element { PubSubEvent::Configuration { node, form } => Element::builder("configuration") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append(form) + .append_all(form.map(Element::from).map(Node::Element).into_iter()) .build(), PubSubEvent::Delete { node, redirect } => Element::builder("purge") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append(redirect.map(|redirect| { + .append_all(redirect.map(|redirect| { Element::builder("redirect") .ns(ns::PUBSUB_EVENT) .attr("uri", redirect) @@ -213,12 +213,12 @@ impl From for Element { PubSubEvent::PublishedItems { node, items } => Element::builder("items") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append(items) + .append_all(items.into_iter()) .build(), PubSubEvent::RetractedItems { node, items } => Element::builder("items") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append( + .append_all( items .into_iter() .map(|id| { @@ -227,7 +227,6 @@ impl From for Element { .attr("id", id) .build() }) - .collect::>(), ) .build(), PubSubEvent::Purge { node } => Element::builder("purge") diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 0e166bd3ebcf86cd17e2327f864e5f53a0ba670f..c49d5fbfd19101e72a62305af9fffadac9557d29 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -219,11 +219,11 @@ impl From for Element { fn from(subscribe_options: SubscribeOptions) -> Element { Element::builder("subscribe-options") .ns(ns::PUBSUB) - .append(if subscribe_options.required { + .append_all((if subscribe_options.required { vec![Element::builder("required").ns(ns::PUBSUB).build()] } else { vec![] - }) + }).into_iter()) .build() } } @@ -473,7 +473,7 @@ impl From for Element { fn from(pubsub: PubSub) -> Element { Element::builder("pubsub") .ns(ns::PUBSUB) - .append(match pubsub { + .append_all((match pubsub { PubSub::Create { create, configure } => { let mut elems = vec![Element::from(create)]; if let Some(configure) = configure { @@ -498,7 +498,7 @@ impl From for Element { PubSub::Subscription(subscription) => vec![Element::from(subscription)], PubSub::Subscriptions(subscriptions) => vec![Element::from(subscriptions)], PubSub::Unsubscribe(unsubscribe) => vec![Element::from(unsubscribe)], - }) + }).into_iter()) .build() } } diff --git a/src/rsm.rs b/src/rsm.rs index a4f97365388da78a6e47e3ebc7555afd0a2623f2..691e5e9846d50095369a9f333fc1336d6edeb0ab 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -6,7 +6,7 @@ use crate::util::error::Error; use crate::ns; -use minidom::Element; +use minidom::{Element, Node}; use std::convert::TryFrom; /// Requests paging through a potentially big set of items (represented by an @@ -72,23 +72,23 @@ impl From for Element { fn from(set: SetQuery) -> Element { Element::builder("set") .ns(ns::RSM) - .append(set.max.map(|max| { + .append_all(set.max.map(|max| { Element::builder("max") .ns(ns::RSM) .append(format!("{}", max)) .build() })) - .append( + .append_all( set.after .map(|after| Element::builder("after").ns(ns::RSM).append(after).build()), ) - .append(set.before.map(|before| { + .append_all(set.before.map(|before| { Element::builder("before") .ns(ns::RSM) .append(before) .build() })) - .append(set.index.map(|index| { + .append_all(set.index.map(|index| { Element::builder("index") .ns(ns::RSM) .append(format!("{}", index)) @@ -162,12 +162,12 @@ impl From for Element { }); Element::builder("set") .ns(ns::RSM) - .append(first) - .append( + .append_all(first.map(Element::from).map(Node::Element).into_iter()) + .append_all( set.last .map(|last| Element::builder("last").ns(ns::RSM).append(last).build()), ) - .append(set.count.map(|count| { + .append_all(set.count.map(|count| { Element::builder("count") .ns(ns::RSM) .append(format!("{}", count)) diff --git a/src/sasl.rs b/src/sasl.rs index 3deed9e1aa6b0fface1dcd41319f8eafc96641c0..038e9e4f5a26e8cc159a65fee2c941ae14c3b292 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -203,7 +203,7 @@ impl From for Element { Element::builder("failure") .ns(ns::SASL) .append(failure.defined_condition) - .append( + .append_all( failure .texts .into_iter() @@ -214,7 +214,6 @@ impl From for Element { .append(text) .build() }) - .collect::>(), ) .build() } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index db9230ebbddee84e8035c673291404c3365fa886..e871a12b79b459b2d0ffb925b9c0e4ece1c7acce 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,7 +9,7 @@ use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; use jid::Jid; -use minidom::Element; +use minidom::{Element, Node}; use std::collections::BTreeMap; use std::convert::TryFrom; @@ -294,15 +294,16 @@ impl From for Element { .attr("type", err.type_) .attr("by", err.by) .append(err.defined_condition) - .append( + .append_all( err.texts.into_iter().map(|(lang, text)| { Element::builder("text") .ns(ns::XMPP_STANZAS) .attr("xml:lang", lang) .append(text) .build() - }).collect::>()) - .append(err.other) + }) + ) + .append_all(err.other.map(Element::from).map(Node::Element).into_iter()) .build() } } diff --git a/src/tune.rs b/src/tune.rs index 1bbb436a618f6c8c9c45fb5393f363fd47863bac..df9b6679865114fffb7c652eb93d28666c3f78b2 100644 --- a/src/tune.rs +++ b/src/tune.rs @@ -149,13 +149,13 @@ impl From for Element { fn from(tune: Tune) -> Element { Element::builder("tune") .ns(ns::TUNE) - .append(tune.artist) - .append(tune.length) - .append(tune.rating) - .append(tune.source) - .append(tune.title) - .append(tune.track) - .append(tune.uri) + .append_all(tune.artist) + .append_all(tune.length) + .append_all(tune.rating) + .append_all(tune.source) + .append_all(tune.title) + .append_all(tune.track) + .append_all(tune.uri) .build() } } diff --git a/src/util/helpers.rs b/src/util/helpers.rs index b7bdc8443cbaf4cbf1f5d3f3f6b055bd7ef16550..b51b18a9c8ee6259b450fb5169eeff90c2badf33 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -34,8 +34,8 @@ impl TrimmedPlainText { }) } - pub fn encode(string: &str) -> String { - string.to_owned() + pub fn encode(string: &str) -> Option { + Some(string.to_owned()) } } diff --git a/src/util/macros.rs b/src/util/macros.rs index c4b5c4e0b85d75ac68624821b57bbe55ebd808ef..6843369d12dfc49b9809108ce72ba3e6c3bc75b3 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -253,6 +253,11 @@ macro_rules! generate_element_enum { .build() } } + impl From<$elem> for ::minidom::Node { + fn from(elem: $elem) -> ::minidom::Node { + ::minidom::Node::Element(elem.into()) + } + } ); } @@ -291,6 +296,11 @@ macro_rules! generate_attribute_enum { .build() } } + impl From<$elem> for ::minidom::Node { + fn from(elem: $elem) -> ::minidom::Node { + ::minidom::Node::Element(elem.into()) + } + } ); } @@ -385,6 +395,12 @@ macro_rules! generate_empty_element { .build() } } + + impl From<$elem> for ::minidom::Node { + fn from(elem: $elem) -> ::minidom::Node { + ::minidom::Node::Element(elem.into()) + } + } ); } @@ -441,6 +457,12 @@ macro_rules! generate_elem_id { .build() } } + + impl From<$elem> for ::minidom::Node { + fn from(elem: $elem) -> ::minidom::Node { + ::minidom::Node::Element(elem.into()) + } + } ); } @@ -547,22 +569,38 @@ macro_rules! finish_parse_elem { } macro_rules! generate_serialiser { - ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => { - ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - .append($parent.$elem) - .build() - }; - ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => { - $parent.$elem.map(|elem| { + ($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => { + $builder.append(::minidom::Node::Element( ::minidom::Element::builder($name) .ns(crate::ns::$ns) - .append(elem) + .append(::minidom::Node::Text($parent.$elem)) .build() - }) + ) + ) }; - ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => { - $parent.$elem + ($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => { + $builder.append_all($parent.$elem.map(|elem| { + ::minidom::Element::builder($name) + .ns(crate::ns::$ns) + .append(::minidom::Node::Text(elem)) + .build() + }).into_iter() + ) + }; + ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => { + $builder.append_all($parent.$elem.map(|elem| { + ::minidom::Element::builder($name) + .ns(crate::ns::$ns) + .append(::minidom::Node::Element(::minidom::Element::from(elem))) + .build() + }).into_iter() + ) + }; + ($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => { + $builder.append_all($parent.$elem.into_iter()) + }; + ($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => { + $builder.append(::minidom::Node::Element(::minidom::Element::from($parent.$elem))) }; } @@ -637,18 +675,25 @@ macro_rules! generate_element { impl From<$elem> for ::minidom::Element { fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) - .ns(crate::ns::$ns) - $( - .attr($attr_name, elem.$attr) - )* - $( - .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns))) - )* - $( - .append($codec::encode(&elem.$text_ident)) - )* - .build() + let mut builder = ::minidom::Element::builder($name) + .ns(crate::ns::$ns); + $( + builder = builder.attr($attr_name, elem.$attr); + )* + $( + builder = generate_serialiser!(builder, elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns)); + )* + $( + builder = builder.append_all($codec::encode(&elem.$text_ident).map(::minidom::Node::Text).into_iter()); + )* + + builder.build() + } + } + + impl From<$elem> for ::minidom::Node { + fn from(elem: $elem) -> ::minidom::Node { + ::minidom::Node::Element(elem.into()) } } ); @@ -691,11 +736,17 @@ macro_rules! impl_pubsub_item { .ns(ns::$ns) .attr("id", item.0.id) .attr("publisher", item.0.publisher) - .append(item.0.payload) + .append_all(item.0.payload.map(::minidom::Node::Element).into_iter()) .build() } } + impl From<$item> for ::minidom::Node { + fn from(item: $item) -> ::minidom::Node { + ::minidom::Node::Element(item.into()) + } + } + impl ::std::ops::Deref for $item { type Target = crate::pubsub::Item; diff --git a/src/xhtml.rs b/src/xhtml.rs index 9c43181bf06fc38e88568b101b6971a6ae466562..0216e7c9e72fe518af5b60e53b19c34dca278157 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -98,14 +98,14 @@ impl From for Element { fn from(wrapper: XhtmlIm) -> Element { Element::builder("html") .ns(ns::XHTML_IM) - .append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { + .append_all(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { if lang.is_empty() { assert!(body.xml_lang.is_none()); } else { assert_eq!(Some(lang), body.xml_lang.as_ref()); } Element::from(body.clone()) - }).collect::>()) + })) .build() } } @@ -174,7 +174,7 @@ impl From for Element { .ns(ns::XHTML) .attr("style", get_style_string(body.style)) .attr("xml:lang", body.xml_lang) - .append(children_to_nodes(body.children)) + .append_all(children_to_nodes(body.children)) .build() } } @@ -338,7 +338,7 @@ impl From for Element { }; let mut builder = Element::builder(name) .ns(ns::XHTML) - .append(children_to_nodes(children)); + .append_all(children_to_nodes(children)); for (key, value) in attrs { builder = builder.attr(key, value); } From 0db94e554d551477b6ed4f61ed9dcc8d2dea43d0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 11:33:32 +0200 Subject: [PATCH 0952/1020] Prepare for 0.11.1 release. --- CHANGELOG.md | 5 +++-- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dc9482e07e49389541adfa7b1f551533ab5e086..72468faa528a78d3b5b33411a34ef48cf50e838a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ -Version XXX, released YYY: +Version 0.11.1, released 2019-09-06: * Changes - * Update to quick-xml 0.15 + * Update to quick-xml 0.16 * Add a default "comments" feature to transform comments into errors when unset. + * Remove the mostly-unused failure dependency, to dramatically reduce compilation times. Version 0.11.0, released 2019-06-14: * Breaking * Get rid of IntoElements, replace with `Into` and ` IntoIterator>` diff --git a/Cargo.toml b/Cargo.toml index 76b4b4dccbcc9f8ddfa3e5bd57e6c21beada66ec..204d729e3fd245b169dbd19a1650a0b8713d34b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.11.0" +version = "0.11.1" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From b244a21e1afa965c7430f35e7bb13d6b5b40d96f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 11:39:03 +0200 Subject: [PATCH 0953/1020] Prepare for 0.7.1 release. --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5a731d25b0013e60d0e25784c4020d346f69443..d15bd671bd2d8cbd9a13120d5f3660b844a76f83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +Version 0.7.1, released 2019-09-06: + * Updates + - Remove failure dependency, to keep compilation times in check + - Impl Display for Jid + Version 0.7.0, released 2019-07-26: * Breaking - Update minidom dependency to 0.11 diff --git a/Cargo.toml b/Cargo.toml index 162cb9a5bc633340dd41f35e3a0c322dd5d91320..f2f0ad10df4861275431793a8009e1c5817ade3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.7.0" +version = "0.7.1" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From 6f1fc7b009ef22083790d1c786dbd633d8f41ab4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 11:45:04 +0200 Subject: [PATCH 0954/1020] bind: Easily convert BindResponse into FullJid or Jid. --- src/bind.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/bind.rs b/src/bind.rs index 17e1e1240ac5ac9895bbd08250f6ae92a4c85e69..22beccd19b8e2c54a5e9e3b73cb7fb4e9e570bda 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; -use jid::FullJid; +use jid::{FullJid, Jid}; use minidom::Element; use std::str::FromStr; use std::convert::TryFrom; @@ -85,6 +85,18 @@ pub struct BindResponse { impl IqResultPayload for BindResponse {} +impl From for FullJid { + fn from(bind: BindResponse) -> FullJid { + bind.jid + } +} + +impl From for Jid { + fn from(bind: BindResponse) -> Jid { + Jid::Full(bind.jid) + } +} + impl TryFrom for BindResponse { type Error = Error; From 188de32dacd263746de32ed65eceb9c8d0d75101 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 11:45:46 +0200 Subject: [PATCH 0955/1020] Prepare for the 0.15.0 release. --- Cargo.toml | 2 +- ChangeLog | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6690ccf66ba81a972eb767e3cb620e3e0d61707f..91f5fccc380bd1158005d22307f52d812fd9ca07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.14.0" +version = "0.15.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", diff --git a/ChangeLog b/ChangeLog index 5ecea28ef465af043d041b95750d673ead917e6a..05db5edadd5153f0d5d2abf4558786ec9fee03bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ -Version NEXT: -DATE Emmanuel Gil Peyrot +Version 0.15.0: +2019-09-06 Emmanuel Gil Peyrot * New parsers/serialisers: - XHTML-IM (XEP-0071) - User Tune (XEP-0118) @@ -11,6 +11,7 @@ DATE Emmanuel Gil Peyrot - Bind has been split into BindQuery and BindResponse. * Improvements: - New DOAP file for a machine-readable description of the features. + - Add various parser and formatter helpers on Hash. Version 0.14.0: 2019-07-13 Emmanuel Gil Peyrot , Maxime “pep” Buquet From a5011c59adc00150dd86553e710c468b98ac5ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 30 Mar 2019 18:47:07 +0100 Subject: [PATCH 0956/1020] Add structs for OpenPGP for XMPP (XEP-0373). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- ChangeLog | 5 +++ src/lib.rs | 3 ++ src/ns.rs | 5 +++ src/openpgp.rs | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+) create mode 100644 src/openpgp.rs diff --git a/ChangeLog b/ChangeLog index 05db5edadd5153f0d5d2abf4558786ec9fee03bd..ed5aae07e7aadf06c1a45153c68a9cf158fdd112 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version NEXT: +DATE Emmanuel Gil Peyrot + * New parsers/serialisers: + - OpenPGP for XMPP (XEP-0373) + Version 0.15.0: 2019-09-06 Emmanuel Gil Peyrot * New parsers/serialisers: diff --git a/src/lib.rs b/src/lib.rs index d989812348b2a9dd141974da0b672eec97e19ead..ea09f2f0ea5e50e06f2a7059d98dbe7abb7263e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -183,6 +183,9 @@ pub mod jingle_message; /// XEP-0359: Unique and Stable Stanza IDs pub mod stanza_id; +/// XEP-0373: OpenPGP for XMPP +pub mod openpgp; + /// XEP-0380: Explicit Message Encryption pub mod eme; diff --git a/src/ns.rs b/src/ns.rs index 2c932c3ced7c9e8e844aa7a362f1541f2f021008..e5242b25667a70312c2e5b4f405c4efb6796a18a 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -188,6 +188,11 @@ pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; /// XEP-0359: Unique and Stable Stanza IDs pub const SID: &str = "urn:xmpp:sid:0"; +/// XEP-0373: OpenPGP for XMPP +pub const OX: &str = "urn:xmpp:openpgp:0"; +/// XEP-0373: OpenPGP for XMPP +pub const OX_PUBKEYS: &str = "urn:xmpp:openpgp:0:public-keys"; + /// XEP-0380: Explicit Message Encryption pub const EME: &str = "urn:xmpp:eme:0"; diff --git a/src/openpgp.rs b/src/openpgp.rs new file mode 100644 index 0000000000000000000000000000000000000000..86f4829a77fa8af1fae6448a3aa27b3635418e6b --- /dev/null +++ b/src/openpgp.rs @@ -0,0 +1,104 @@ +// Copyright (c) 2019 Maxime “pep” Buquet +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::date::DateTime; +use crate::util::helpers::Base64; +use crate::pubsub::PubSubPayload; + +// TODO: Merge this container with the PubKey struct +generate_element!( + /// Data contained in the PubKey element + PubKeyData, "data", OX, + text: ( + /// Base64 data + data: Base64> + ) +); + +generate_element!( + /// Pubkey element to be used in PubSub publish payloads. + PubKey, "pubkey", OX, + attributes: [ + /// Last updated date + date: Option = "date" + ], + children: [ + /// Public key as base64 data + data: Required = ("data", OX) => PubKeyData + ] +); + +impl PubSubPayload for PubKey {} + +generate_element!( + /// Public key metadata + PubKeyMeta, "pubkey-metadata", OX, + attributes: [ + /// OpenPGP v4 fingerprint + v4fingerprint: Required = "v4-fingerprint", + /// Time the key was published or updated + date: Required = "date", + ] +); + +generate_element!( + /// List of public key metadata + PubKeysMeta, "public-key-list", OX, + children: [ + /// Public keys + pubkeys: Vec = ("pubkey-metadata", OX) => PubKeyMeta + ] +); + +impl PubSubPayload for PubKeysMeta {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ns; + use std::str::FromStr; + use crate::pubsub::{NodeName, Item, pubsub::{Item as PubSubItem, Publish}}; + + #[test] + fn pubsub_publish_pubkey_data() { + let pubkey = PubKey { + date: None, + data: PubKeyData { + data: (&"Foo").as_bytes().to_vec(), + } + }; + println!("Foo1: {:?}", pubkey); + + let pubsub = Publish { + node: NodeName(format!("{}:{}", ns::OX_PUBKEYS, "some-fingerprint")), + items: vec![ + PubSubItem(Item::new(None, None, Some(pubkey))), + ], + }; + println!("Foo2: {:?}", pubsub); + } + + #[test] + fn pubsub_publish_pubkey_meta() { + let pubkeymeta = PubKeysMeta { + pubkeys: vec![ + PubKeyMeta { + v4fingerprint: "some-fingerprint".to_owned(), + date: DateTime::from_str("2019-03-30T18:30:25Z").unwrap(), + }, + ], + }; + println!("Foo1: {:?}", pubkeymeta); + + let pubsub = Publish { + node: NodeName("foo".to_owned()), + items: vec![ + PubSubItem(Item::new(None, None, Some(pubkeymeta))), + ], + }; + println!("Foo2: {:?}", pubsub); + } +} From ecd9502f58430e220327a6ec138b6e730ef3eca6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 13:35:40 +0200 Subject: [PATCH 0957/1020] doap: Update for the latest release. --- doap.xml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/doap.xml b/doap.xml index 561f49dd133eef7655c5c2fbdbce8fda19175db2..b8099544b4c1e24be4d768e91b6718133d7fd96c 100644 --- a/doap.xml +++ b/doap.xml @@ -116,7 +116,7 @@ complete 1.5.4 - NEXT + 0.15.0 @@ -188,7 +188,7 @@ complete 1.2 - NEXT + 0.15.0 @@ -300,7 +300,7 @@ complete 1.0 - NEXT + 0.15.0 @@ -341,7 +341,7 @@ complete 0.13.0 - NEXT + 0.15.0 @@ -425,6 +425,13 @@ + + + 0.15.0 + 2019-09-06 + + + 0.14.0 @@ -432,7 +439,6 @@ - 0.13.1 From d3157c77f00bfdb92416de735ed1168dbcf46be1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 13:37:27 +0200 Subject: [PATCH 0958/1020] doap: Add XEP-0373 there. --- doap.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doap.xml b/doap.xml index b8099544b4c1e24be4d768e91b6718133d7fd96c..b05c4c42b3e6f2d14940f2e386d0037c014538c2 100644 --- a/doap.xml +++ b/doap.xml @@ -408,6 +408,14 @@ 0.1.0 + + + + partial + 0.4.0 + NEXT + + From cdf80c897993a1ce0e1f959a01c09a01aae21df6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 15:00:14 +0200 Subject: [PATCH 0959/1020] presence: Simplify constructors. --- ChangeLog | 2 ++ src/presence.rs | 40 ++++++++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index ed5aae07e7aadf06c1a45153c68a9cf158fdd112..372ebddef25d66a6bec783f258542a143e58123f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,8 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - OpenPGP for XMPP (XEP-0373) + * Breaking changes: + - Presence constructors now take Into and assume Some. Version 0.15.0: 2019-09-06 Emmanuel Gil Peyrot diff --git a/src/presence.rs b/src/presence.rs index 4aad1622941265e2d93a7994e83aaf1f99c6da5c..14ae559c492d23a5b24f3fe4b155cf42b6491d31 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -194,21 +194,21 @@ impl Presence { /// Set the emitter of this presence, this should only be useful for /// servers and components, as clients can only send presences from their /// own resource (which is implicit). - pub fn with_from(mut self, from: Option) -> Presence { - self.from = from; + pub fn with_from>(mut self, from: J) -> Presence { + self.from = Some(from.into()); self } /// Set the recipient of this presence, this is only useful for directed /// presences. - pub fn with_to(mut self, to: Option) -> Presence { - self.to = to; + pub fn with_to>(mut self, to: J) -> Presence { + self.to = Some(to.into()); self } /// Set the identifier for this presence. - pub fn with_id(mut self, id: Option) -> Presence { - self.id = id; + pub fn with_id(mut self, id: String) -> Presence { + self.id = Some(id); self } @@ -345,6 +345,7 @@ impl From for Element { mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; + use jid::{BareJid, FullJid}; #[cfg(target_pointer_width = "32")] #[test] @@ -634,4 +635,31 @@ mod tests { assert!(priority.is("priority", ns::DEFAULT_NS)); assert_eq!(priority.text(), "42"); } + + #[test] + fn presence_with_to() { + let presence = Presence::new(Type::None); + let elem: Element = presence.into(); + assert_eq!(elem.attr("to"), None); + + let presence = Presence::new(Type::None) + .with_to(Jid::Bare(BareJid::domain("localhost"))); + let elem: Element = presence.into(); + assert_eq!(elem.attr("to"), Some("localhost")); + + let presence = Presence::new(Type::None) + .with_to(BareJid::domain("localhost")); + let elem: Element = presence.into(); + assert_eq!(elem.attr("to"), Some("localhost")); + + let presence = Presence::new(Type::None) + .with_to(Jid::Full(FullJid::new("test", "localhost", "coucou"))); + let elem: Element = presence.into(); + assert_eq!(elem.attr("to"), Some("test@localhost/coucou")); + + let presence = Presence::new(Type::None) + .with_to(FullJid::new("test", "localhost", "coucou")); + let elem: Element = presence.into(); + assert_eq!(elem.attr("to"), Some("test@localhost/coucou")); + } } From ff77f6141fa008b1890528a08d9c3209e162869d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 6 Sep 2019 16:03:58 +0200 Subject: [PATCH 0960/1020] Cleanup a1ae45add88e424710073711935a3605cafe9ef9 a bit. --- src/bind.rs | 11 +++--- src/blocking.rs | 1 - src/data_forms.rs | 29 +++++++++------- src/disco.rs | 4 +-- src/ibr.rs | 12 +++---- src/jingle.rs | 10 +++--- src/jingle_ft.rs | 80 ++++++++++++-------------------------------- src/jingle_s5b.rs | 17 ++++------ src/mam.rs | 14 ++------ src/presence.rs | 12 +++---- src/pubsub/event.rs | 22 ++++-------- src/pubsub/pubsub.rs | 12 +++---- src/rsm.rs | 13 +++---- src/sasl.rs | 1 - src/stanza_error.rs | 5 ++- src/time.rs | 6 ++-- src/util/macros.rs | 12 +++---- src/xhtml.rs | 10 +++--- 18 files changed, 97 insertions(+), 174 deletions(-) diff --git a/src/bind.rs b/src/bind.rs index 22beccd19b8e2c54a5e9e3b73cb7fb4e9e570bda..16fb487bc7ef00a931d3bcd16a736589c19ec50c 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -63,13 +63,10 @@ impl From for Element { fn from(bind: BindQuery) -> Element { Element::builder("bind") .ns(ns::BIND) - .append_all((match bind.resource { - None => vec![], - Some(resource) => vec![Element::builder("resource") + .append_all(bind.resource.map(|resource| + Element::builder("resource") .ns(ns::BIND) - .append(resource) - .build()], - }).into_iter()) + .append(resource))) .build() } } @@ -129,7 +126,7 @@ impl From for Element { fn from(bind: BindResponse) -> Element { Element::builder("bind") .ns(ns::BIND) - .append(Element::builder("jid").ns(ns::BIND).append(bind.jid).build()) + .append(Element::builder("jid").ns(ns::BIND).append(bind.jid)) .build() } } diff --git a/src/blocking.rs b/src/blocking.rs index f9e6a821bc3769891a80da0404e1d6bf5ef86ceb..741d43405412864166729d53f17e0d210ba5cfe6 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -55,7 +55,6 @@ macro_rules! generate_blocking_element { Element::builder("item") .ns(ns::BLOCKING) .attr("jid", jid) - .build() })) .build() } diff --git a/src/data_forms.rs b/src/data_forms.rs index f252bb714f5b82efb9ee1417c426d5f3a8b63771..80c9131b32da9798eba8ce64d57505ab9500b5db 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::media_element::MediaElement; use crate::ns; -use minidom::{Element, Node}; +use minidom::Element; use std::convert::TryFrom; generate_element!( @@ -152,12 +152,12 @@ impl From for Element { .attr("var", field.var) .attr("type", field.type_) .attr("label", field.label) - .append_all((if field.required { - Some(Element::builder("required").ns(ns::DATA_FORMS).build()) + .append_all(if field.required { + Some(Element::builder("required").ns(ns::DATA_FORMS)) } else { None - }).into_iter()) - .append_all(field.options.iter().cloned().map(Element::from).map(Node::Element).into_iter()) + }) + .append_all(field.options.iter().cloned().map(Element::from)) .append_all( field .values @@ -166,10 +166,9 @@ impl From for Element { Element::builder("value") .ns(ns::DATA_FORMS) .append(value) - .build() }) ) - .append_all(field.media.iter().cloned().map(Element::from).map(Node::Element)) + .append_all(field.media.iter().cloned().map(Element::from)) .build() } } @@ -283,12 +282,16 @@ impl From for Element { .ns(ns::DATA_FORMS) .append(text) })) - .append_all((if let Some(form_type) = form.form_type { - vec![Element::builder("field").ns(ns::DATA_FORMS).attr("var", "FORM_TYPE").attr("type", "hidden").append(Element::builder("value").ns(ns::DATA_FORMS).append(form_type).build()).build()] - } else { - vec![] - }).into_iter()) - .append_all(form.fields.iter().cloned().map(Element::from).map(Node::Element)) + .append_all(form.form_type.map(|form_type| { + Element::builder("field") + .ns(ns::DATA_FORMS) + .attr("var", "FORM_TYPE") + .attr("type", "hidden") + .append(Element::builder("value") + .ns(ns::DATA_FORMS) + .append(form_type)) + })) + .append_all(form.fields.iter().cloned().map(Element::from)) .build() } } diff --git a/src/disco.rs b/src/disco.rs index 3c2cde13835203e97c070c52d00c67ed8e43a87b..857afaf8c758de96eac503d50249a572475375f5 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use jid::Jid; -use minidom::{Element, Node}; +use minidom::Element; use std::convert::TryFrom; generate_element!( @@ -180,7 +180,7 @@ impl From for Element { .attr("node", disco.node) .append_all(disco.identities.into_iter()) .append_all(disco.features.into_iter()) - .append_all(disco.extensions.iter().cloned().map(Element::from).map(Node::Element)) + .append_all(disco.extensions.iter().cloned().map(Element::from)) .build() } } diff --git a/src/ibr.rs b/src/ibr.rs index d539f2adce83fa4cdf8fe13ba650ba0be5f277af..8ced963f259e0c27375efbd4875b7e38d23b354a 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -8,7 +8,7 @@ use crate::data_forms::DataForm; use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use minidom::{Element, Node}; +use minidom::Element; use std::collections::HashMap; use std::convert::TryFrom; @@ -93,23 +93,23 @@ impl From for Element { fn from(query: Query) -> Element { Element::builder("query") .ns(ns::REGISTER) - .append_all((if query.registered { + .append_all(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None - }).into_iter()) + }) .append_all( query .fields .into_iter() .map(|(name, value)| Element::builder(name).ns(ns::REGISTER).append(value)) ) - .append_all((if query.remove { + .append_all(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None - }).into_iter()) - .append_all(query.form.map(Element::from).map(Node::Element).into_iter()) + }) + .append_all(query.form.map(Element::from)) .build() } } diff --git a/src/jingle.rs b/src/jingle.rs index 55a493ef407fa7785be7d94125cf2d4fcb55ea80..4a8e5ac357b577e88861aeb5db7535db0a3b67fe 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; -use minidom::{Element, Node}; +use minidom::Element; use std::collections::BTreeMap; use std::str::FromStr; use std::convert::TryFrom; @@ -351,7 +351,8 @@ impl From for Element { Reason::UnsupportedApplications => "unsupported-applications", Reason::UnsupportedTransports => "unsupported-transports", }) - .build() + .ns(ns::JINGLE) + .build() } } @@ -419,7 +420,6 @@ impl From for Element { .ns(ns::JINGLE) .attr("xml:lang", lang) .append(text) - .build() })) .build() } @@ -542,8 +542,8 @@ impl From for Element { .attr("initiator", jingle.initiator) .attr("responder", jingle.responder) .attr("sid", jingle.sid) - .append_all(jingle.contents.into_iter()) - .append_all(jingle.reason.map(Element::from).map(Node::Element).into_iter()) + .append_all(jingle.contents) + .append_all(jingle.reason.map(Element::from)) .build() } } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index b3da6f8b9661b027d46e5fa0002a5f28672f98db..9902fed773142f24231dc0f076ae4d9fde4aa821 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -193,65 +193,27 @@ impl TryFrom for File { impl From for Element { fn from(file: File) -> Element { - let mut root = Element::builder("file").ns(ns::JINGLE_FT); - if let Some(date) = file.date { - root = root.append( - Node::Element( - Element::builder("date") - .ns(ns::JINGLE_FT) - .append(date) - .build() - ) - ); - } - if let Some(media_type) = file.media_type { - root = root.append( - Node::Element( - Element::builder("media-type") - .ns(ns::JINGLE_FT) - .append(media_type) - .build() - ) - ); - } - if let Some(name) = file.name { - root = root.append( - Node::Element( - Element::builder("name") - .ns(ns::JINGLE_FT) - .append(name) - .build() - ) - ); - } - for (lang, desc) in file.descs.into_iter() { - root = root.append( - Node::Element( - Element::builder("desc") - .ns(ns::JINGLE_FT) - .attr("xml:lang", lang) - .append(desc.0) - .build() - ) - ); - } - if let Some(size) = file.size { - root = root.append( - Node::Element( - Element::builder("size") - .ns(ns::JINGLE_FT) - .append(format!("{}", size)) - .build() - ) - ); - } - if let Some(range) = file.range { - root = root.append(range); - } - for hash in file.hashes { - root = root.append(hash); - } - root.build() + Element::builder("file") + .ns(ns::JINGLE_FT) + .append_all(file.date.map(|date| + Element::builder("date") + .append(date))) + .append_all(file.media_type.map(|media_type| + Element::builder("media-type") + .append(media_type))) + .append_all(file.name.map(|name| + Element::builder("name") + .append(name))) + .append_all(file.descs.into_iter().map(|(lang, desc)| + Element::builder("desc") + .attr("xml:lang", lang) + .append(desc.0))) + .append_all(file.size.map(|size| + Element::builder("size") + .append(format!("{}", size)))) + .append_all(file.range) + .append_all(file.hashes) + .build() } } diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 0f246691c40c1c065c252ffe473113f3bd01b659..17c5d996fb3bb7070cf8f3bf5d442d310883deb4 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -251,27 +251,22 @@ impl From for Element { TransportPayload::Candidates(candidates) => candidates .into_iter() .map(Element::from) - .collect::>() - .into_iter(), + .collect::>(), TransportPayload::Activated(cid) => vec![Element::builder("activated") .ns(ns::JINGLE_S5B) .attr("cid", cid) - .build()] - .into_iter(), + .build()], TransportPayload::CandidateError => vec![Element::builder("candidate-error") .ns(ns::JINGLE_S5B) - .build()] - .into_iter(), + .build()], TransportPayload::CandidateUsed(cid) => vec![Element::builder("candidate-used") .ns(ns::JINGLE_S5B) .attr("cid", cid) - .build()] - .into_iter(), + .build()], TransportPayload::ProxyError => vec![Element::builder("proxy-error") .ns(ns::JINGLE_S5B) - .build()] - .into_iter(), - TransportPayload::None => vec![].into_iter(), + .build()], + TransportPayload::None => vec![], }) .build() } diff --git a/src/mam.rs b/src/mam.rs index edc0116c4269dcdf57655c761e306d814496e39e..28362bf917c7081d49c28757434af018a485c9b6 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -171,17 +171,9 @@ fn serialise_jid_list(name: &str, jids: Vec) -> ::std::option::IntoIter for Element { }, ) .append(status) - .build() }) ) - .append_all((if presence.priority == 0 { + .append_all(if presence.priority == 0 { None } else { - Some( - Element::builder("priority") - .append(format!("{}", presence.priority)) - .build() - ) - }).into_iter()) + Some(Element::builder("priority") + .append(format!("{}", presence.priority))) + }) .append_all(presence.payloads.into_iter()) .build() } diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 9327c50bc7d2d8c57adfdea09e68ed382e3aa1c6..30c118de688830d9b8924de15c7a97a42c7274b0 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -10,7 +10,7 @@ use crate::util::error::Error; use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; -use minidom::{Element, Node}; +use minidom::Element; use std::convert::TryFrom; /// Event wrapper for a PubSub ``. @@ -198,8 +198,7 @@ impl From for Element { PubSubEvent::Configuration { node, form } => Element::builder("configuration") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append_all(form.map(Element::from).map(Node::Element).into_iter()) - .build(), + .append_all(form.map(Element::from)), PubSubEvent::Delete { node, redirect } => Element::builder("purge") .ns(ns::PUBSUB_EVENT) .attr("node", node) @@ -207,14 +206,11 @@ impl From for Element { Element::builder("redirect") .ns(ns::PUBSUB_EVENT) .attr("uri", redirect) - .build() - })) - .build(), + })), PubSubEvent::PublishedItems { node, items } => Element::builder("items") .ns(ns::PUBSUB_EVENT) .attr("node", node) - .append_all(items.into_iter()) - .build(), + .append_all(items.into_iter()), PubSubEvent::RetractedItems { node, items } => Element::builder("items") .ns(ns::PUBSUB_EVENT) .attr("node", node) @@ -225,14 +221,11 @@ impl From for Element { Element::builder("retract") .ns(ns::PUBSUB_EVENT) .attr("id", id) - .build() }) - ) - .build(), + ), PubSubEvent::Purge { node } => Element::builder("purge") .ns(ns::PUBSUB_EVENT) - .attr("node", node) - .build(), + .attr("node", node), PubSubEvent::Subscription { node, expiry, @@ -245,8 +238,7 @@ impl From for Element { .attr("expiry", expiry) .attr("jid", jid) .attr("subid", subid) - .attr("subscription", subscription) - .build(), + .attr("subscription", subscription), }; Element::builder("event") .ns(ns::PUBSUB_EVENT) diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index c49d5fbfd19101e72a62305af9fffadac9557d29..727ef2b882d5c86de46ec215e82127a317b029fc 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -219,11 +219,11 @@ impl From for Element { fn from(subscribe_options: SubscribeOptions) -> Element { Element::builder("subscribe-options") .ns(ns::PUBSUB) - .append_all((if subscribe_options.required { - vec![Element::builder("required").ns(ns::PUBSUB).build()] + .append_all(if subscribe_options.required { + Some(Element::builder("required").ns(ns::PUBSUB)) } else { - vec![] - }).into_iter()) + None + }) .build() } } @@ -473,7 +473,7 @@ impl From for Element { fn from(pubsub: PubSub) -> Element { Element::builder("pubsub") .ns(ns::PUBSUB) - .append_all((match pubsub { + .append_all(match pubsub { PubSub::Create { create, configure } => { let mut elems = vec![Element::from(create)]; if let Some(configure) = configure { @@ -498,7 +498,7 @@ impl From for Element { PubSub::Subscription(subscription) => vec![Element::from(subscription)], PubSub::Subscriptions(subscriptions) => vec![Element::from(subscriptions)], PubSub::Unsubscribe(unsubscribe) => vec![Element::from(unsubscribe)], - }).into_iter()) + }) .build() } } diff --git a/src/rsm.rs b/src/rsm.rs index 691e5e9846d50095369a9f333fc1336d6edeb0ab..8299c9b67132b27d50e43d6df1222d5cc20facf3 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -6,7 +6,7 @@ use crate::util::error::Error; use crate::ns; -use minidom::{Element, Node}; +use minidom::Element; use std::convert::TryFrom; /// Requests paging through a potentially big set of items (represented by an @@ -76,23 +76,20 @@ impl From for Element { Element::builder("max") .ns(ns::RSM) .append(format!("{}", max)) - .build() })) .append_all( set.after - .map(|after| Element::builder("after").ns(ns::RSM).append(after).build()), + .map(|after| Element::builder("after").ns(ns::RSM).append(after)) ) .append_all(set.before.map(|before| { Element::builder("before") .ns(ns::RSM) .append(before) - .build() })) .append_all(set.index.map(|index| { Element::builder("index") .ns(ns::RSM) .append(format!("{}", index)) - .build() })) .build() } @@ -158,20 +155,18 @@ impl From for Element { .ns(ns::RSM) .attr("index", set.first_index) .append(first) - .build() }); Element::builder("set") .ns(ns::RSM) - .append_all(first.map(Element::from).map(Node::Element).into_iter()) + .append_all(first) .append_all( set.last - .map(|last| Element::builder("last").ns(ns::RSM).append(last).build()), + .map(|last| Element::builder("last").ns(ns::RSM).append(last)), ) .append_all(set.count.map(|count| { Element::builder("count") .ns(ns::RSM) .append(format!("{}", count)) - .build() })) .build() } diff --git a/src/sasl.rs b/src/sasl.rs index 038e9e4f5a26e8cc159a65fee2c941ae14c3b292..e8f3aa6e041727102c112b3ea28ddd9f0130837b 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -212,7 +212,6 @@ impl From for Element { .ns(ns::SASL) .attr("xml:lang", lang) .append(text) - .build() }) ) .build() diff --git a/src/stanza_error.rs b/src/stanza_error.rs index e871a12b79b459b2d0ffb925b9c0e4ece1c7acce..1f4dbbe16de934e405af539040c7231763341bb5 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,7 +9,7 @@ use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; use jid::Jid; -use minidom::{Element, Node}; +use minidom::Element; use std::collections::BTreeMap; use std::convert::TryFrom; @@ -300,10 +300,9 @@ impl From for Element { .ns(ns::XMPP_STANZAS) .attr("xml:lang", lang) .append(text) - .build() }) ) - .append_all(err.other.map(Element::from).map(Node::Element).into_iter()) + .append_all(err.other) .build() } } diff --git a/src/time.rs b/src/time.rs index cbedb9883ed1fae397356c9536cbb58c40c7298b..601b3a4cd0032a323c90664f9c554328f24e8746 100644 --- a/src/time.rs +++ b/src/time.rs @@ -78,11 +78,9 @@ impl From for Element { Element::builder("time") .ns(ns::TIME) .append(Element::builder("tzo") - .append(format!("{}", time.0.timezone())) - .build()) + .append(format!("{}", time.0.timezone()))) .append(Element::builder("utc") - .append(time.0.with_timezone(FixedOffset::east(0)).format("%FT%TZ")) - .build()) + .append(time.0.with_timezone(FixedOffset::east(0)).format("%FT%TZ"))) .build() } } diff --git a/src/util/macros.rs b/src/util/macros.rs index 6843369d12dfc49b9809108ce72ba3e6c3bc75b3..f5fda080cdd1fdd0c3d52f8fd3e83c66881e5474 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -570,12 +570,10 @@ macro_rules! finish_parse_elem { macro_rules! generate_serialiser { ($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => { - $builder.append(::minidom::Node::Element( + $builder.append( ::minidom::Element::builder($name) .ns(crate::ns::$ns) .append(::minidom::Node::Text($parent.$elem)) - .build() - ) ) }; ($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => { @@ -583,8 +581,7 @@ macro_rules! generate_serialiser { ::minidom::Element::builder($name) .ns(crate::ns::$ns) .append(::minidom::Node::Text(elem)) - .build() - }).into_iter() + }) ) }; ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => { @@ -592,8 +589,7 @@ macro_rules! generate_serialiser { ::minidom::Element::builder($name) .ns(crate::ns::$ns) .append(::minidom::Node::Element(::minidom::Element::from(elem))) - .build() - }).into_iter() + }) ) }; ($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => { @@ -736,7 +732,7 @@ macro_rules! impl_pubsub_item { .ns(ns::$ns) .attr("id", item.0.id) .attr("publisher", item.0.publisher) - .append_all(item.0.payload.map(::minidom::Node::Element).into_iter()) + .append_all(item.0.payload) .build() } } diff --git a/src/xhtml.rs b/src/xhtml.rs index 0216e7c9e72fe518af5b60e53b19c34dca278157..080fb4a2e4e0d604f4efed8342fe1a034d11df45 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -98,13 +98,13 @@ impl From for Element { fn from(wrapper: XhtmlIm) -> Element { Element::builder("html") .ns(ns::XHTML_IM) - .append_all(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { + .append_all(wrapper.bodies.into_iter().map(|(lang, body)| { if lang.is_empty() { assert!(body.xml_lang.is_none()); } else { - assert_eq!(Some(lang), body.xml_lang.as_ref()); + assert_eq!(Some(lang), body.xml_lang); } - Element::from(body.clone()) + Element::from(body) })) .build() } @@ -346,11 +346,11 @@ impl From for Element { } } -fn children_to_nodes(children: Vec) -> Vec { +fn children_to_nodes(children: Vec) -> impl IntoIterator { children.into_iter().map(|child| match child { Child::Tag(tag) => Node::Element(Element::from(tag)), Child::Text(text) => Node::Text(text), - }).collect::>() + }) } fn children_to_html(children: Vec) -> String { From 11a5c49470d1e3d8875a7ccefc0cbd34855eafe7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:02:40 +0200 Subject: [PATCH 0961/1020] Implement std::error::Error for Error. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was removed in 0.11.1 with the removal of failure, but is used by people so let’s reintroduce it. The cause of an XmlError is pending on this PR from quick-xml: https://github.com/tafia/quick-xml/pull/170 Fixes #15. Fixes #18. --- src/error.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/error.rs b/src/error.rs index fc96a08bd1b69638cf3a5c0580a80e02d7b48bac..579cee405aca8c68af1aa1f97dd0ff02329774df 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,7 @@ //! Provides an error type for this crate. use std::convert::From; +use std::error::Error as StdError; /// Our main error type. #[derive(Debug)] @@ -28,6 +29,23 @@ pub enum Error { CommentsDisabled, } +impl StdError for Error { + fn cause(&self) -> Option<&dyn StdError> { + match self { + // TODO: return Some(e) for this case after the merge of + // https://github.com/tafia/quick-xml/pull/170 + Error::XmlError(_e) => None, + Error::Utf8Error(e) => Some(e), + Error::IoError(e) => Some(e), + Error::EndOfDocument => None, + Error::InvalidElementClosed => None, + Error::InvalidElement => None, + #[cfg(not(comments))] + Error::CommentsDisabled => None, + } + } +} + impl std::fmt::Display for Error { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { match self { From 1638288644bbb6cb3cc9d5858827b4196271670b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:08:53 +0200 Subject: [PATCH 0962/1020] Reimplement std::error::Error for Error. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was removed with the removal of failure, but like in minidom (#18) it was probably used by people, so let’s reintroduce it. --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 1bf038166e5d1fc6b175962799062203c74ca4a5..900eed5f89fdce90468b759ce9b3bc1fd88c62ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ //! For usage, check the documentation on the `Jid` struct. use std::convert::Into; +use std::error::Error as StdError; use std::fmt; use std::str::FromStr; @@ -35,6 +36,8 @@ pub enum JidParseError { EmptyResource, } +impl StdError for JidParseError {} + impl fmt::Display for JidParseError { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", match self { From 79804e2b01d4cf3861bd6ba7504e71c4fbe4f467 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:15:32 +0200 Subject: [PATCH 0963/1020] error: Use better error messages. --- src/util/error.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/util/error.rs b/src/util/error.rs index f2378a19b4d99314abc43445a86335a960d10085..408c649bf4c46101dc054ab593cd32de7e46d1b0 100644 --- a/src/util/error.rs +++ b/src/util/error.rs @@ -48,14 +48,14 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::ParseError(s) => write!(fmt, "{}", s), - Error::Base64Error(ref e) => write!(fmt, "{}", e), - Error::ParseIntError(ref e) => write!(fmt, "{}", e), - Error::ParseStringError(ref e) => write!(fmt, "{}", e), - Error::ParseAddrError(ref e) => write!(fmt, "{}", e), - Error::JidParseError(_) => write!(fmt, "JID parse error"), - Error::ChronoParseError(ref e) => write!(fmt, "{}", e), + match self { + Error::ParseError(s) => write!(fmt, "parse error: {}", s), + Error::Base64Error(e) => write!(fmt, "base64 error: {}", e), + Error::ParseIntError(e) => write!(fmt, "integer parsing error: {}", e), + Error::ParseStringError(e) => write!(fmt, "string parsing error: {}", e), + Error::ParseAddrError(e) => write!(fmt, "IP address parsing error: {}", e), + Error::JidParseError(e) => write!(fmt, "JID parsing error: {}", e), + Error::ChronoParseError(e) => write!(fmt, "time parsing error: {}", e), } } } From f83e9fd9281d4c6613c4a453b194f6f30696b6da Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:15:44 +0200 Subject: [PATCH 0964/1020] error: implement std::error::Error. --- src/util/error.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/util/error.rs b/src/util/error.rs index 408c649bf4c46101dc054ab593cd32de7e46d1b0..b103711a5a0930f0d32746fe6478875bac6a401e 100644 --- a/src/util/error.rs +++ b/src/util/error.rs @@ -8,6 +8,7 @@ use base64; use chrono; use jid; use std::convert::From; +use std::error::Error as StdError; use std::fmt; use std::net; use std::num; @@ -46,6 +47,20 @@ pub enum Error { ChronoParseError(chrono::ParseError), } +impl StdError for Error { + fn cause(&self) -> Option<&dyn StdError> { + match self { + Error::ParseError(_) => None, + Error::Base64Error(e) => Some(e), + Error::ParseIntError(e) => Some(e), + Error::ParseStringError(e) => Some(e), + Error::ParseAddrError(e) => Some(e), + Error::JidParseError(e) => Some(e), + Error::ChronoParseError(e) => Some(e), + } + } +} + impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { From 0328ec446a7a67eb9e20559863d9352adc2cc015 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:18:25 +0200 Subject: [PATCH 0965/1020] error: Remove unused imports. --- src/util/error.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/util/error.rs b/src/util/error.rs index b103711a5a0930f0d32746fe6478875bac6a401e..33b2a699a2dea2cf8cbde53cd6a66ec5fa388a83 100644 --- a/src/util/error.rs +++ b/src/util/error.rs @@ -4,15 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use base64; -use chrono; -use jid; -use std::convert::From; use std::error::Error as StdError; use std::fmt; -use std::net; -use std::num; -use std::string; /// Contains one of the potential errors triggered while parsing an /// [Element](../struct.Element.html) into a specialised struct. @@ -29,14 +22,14 @@ pub enum Error { Base64Error(base64::DecodeError), /// Generated when text which should be an integer fails to parse. - ParseIntError(num::ParseIntError), + ParseIntError(std::num::ParseIntError), /// Generated when text which should be a string fails to parse. - ParseStringError(string::ParseError), + ParseStringError(std::string::ParseError), /// Generated when text which should be an IP address (IPv4 or IPv6) fails /// to parse. - ParseAddrError(net::AddrParseError), + ParseAddrError(std::net::AddrParseError), /// Generated when text which should be a [JID](../../jid/struct.Jid.html) /// fails to parse. @@ -81,20 +74,20 @@ impl From for Error { } } -impl From for Error { - fn from(err: num::ParseIntError) -> Error { +impl From for Error { + fn from(err: std::num::ParseIntError) -> Error { Error::ParseIntError(err) } } -impl From for Error { - fn from(err: string::ParseError) -> Error { +impl From for Error { + fn from(err: std::string::ParseError) -> Error { Error::ParseStringError(err) } } -impl From for Error { - fn from(err: net::AddrParseError) -> Error { +impl From for Error { + fn from(err: std::net::AddrParseError) -> Error { Error::ParseAddrError(err) } } From b307652421cc5bf344f303ed41152786240349bc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:21:42 +0200 Subject: [PATCH 0966/1020] Remove unused base64 imports. --- src/caps.rs | 2 -- src/ecaps2.rs | 1 - src/hashes.rs | 1 - src/jingle_ft.rs | 1 - src/util/helpers.rs | 1 - 5 files changed, 6 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index e5e998b1df7561085baa580473f576030cd1c0af..32f8f5735edd94fad2c3cb6e217bce745f264c50 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -10,7 +10,6 @@ use crate::util::error::Error; use crate::hashes::{Algo, Hash}; use crate::ns; use crate::presence::PresencePayload; -use base64; use blake2::VarBlake2b; use digest::{Digest, Input, VariableOutput}; use minidom::Element; @@ -218,7 +217,6 @@ pub fn query_caps(caps: Caps) -> DiscoInfoQuery { mod tests { use super::*; use crate::caps; - use base64; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/ecaps2.rs b/src/ecaps2.rs index d408b28de48f1ccc74c09ad074e0322b35b278b0..8722d065050bca5c65d33291c20db39da6c4c962 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -9,7 +9,6 @@ use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}; use crate::hashes::{Algo, Hash}; use crate::ns; use crate::presence::PresencePayload; -use base64; use blake2::VarBlake2b; use digest::{Digest, Input, VariableOutput}; use sha2::{Sha256, Sha512}; diff --git a/src/hashes.rs b/src/hashes.rs index e3f73ca83c51dfbf6187b90d78b4911915826e30..9987407a694fb736b4f783606b43e4a1fe6d2586 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -6,7 +6,6 @@ use crate::util::error::Error; use crate::util::helpers::Base64; -use base64; use minidom::IntoAttributeValue; use std::num::ParseIntError; use std::ops::{Deref, DerefMut}; diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 9902fed773142f24231dc0f076ae4d9fde4aa821..f3fc9a33fdad6ac7d5e221df9e7d2f31586e6434 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -327,7 +327,6 @@ generate_element!( mod tests { use super::*; use crate::hashes::Algo; - use base64; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/util/helpers.rs b/src/util/helpers.rs index b51b18a9c8ee6259b450fb5169eeff90c2badf33..a455a139f0eb26bd9e13f01f9e1a0a47f6cad8c1 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::util::error::Error; -use base64; /// Codec for plain text content. pub struct PlainText; From d58321ebba16651bb693538d6936d9d34476cea8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:32:03 +0200 Subject: [PATCH 0967/1020] Add a new CSI parser (XEP-0352). --- ChangeLog | 1 + src/csi.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ 4 files changed, 66 insertions(+) create mode 100644 src/csi.rs diff --git a/ChangeLog b/ChangeLog index 372ebddef25d66a6bec783f258542a143e58123f..825c7a4065d7861e6e96a61b6f7168c3122a8dce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: + - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) * Breaking changes: - Presence constructors now take Into and assume Some. diff --git a/src/csi.rs b/src/csi.rs new file mode 100644 index 0000000000000000000000000000000000000000..0465f94b2014f3d98e050a069052414e3d8647ff --- /dev/null +++ b/src/csi.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_empty_element!( + /// Stream:feature sent by the server to advertise it supports CSI. + Feature, "csi", CSI +); + +generate_empty_element!( + /// Client indicates it is inactive. + Inactive, "inactive", CSI +); + +generate_empty_element!( + /// Client indicates it is active again. + Active, "active", CSI +); + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + use crate::ns; + + #[test] + fn test_size() { + assert_size!(Feature, 0); + assert_size!(Inactive, 0); + assert_size!(Active, 0); + } + + #[test] + fn parsing() { + let elem: Element = "".parse().unwrap(); + Feature::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Inactive::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Active::try_from(elem).unwrap(); + } + + #[test] + fn serialising() { + let elem: Element = Feature.into(); + assert!(elem.is("csi", ns::CSI)); + + let elem: Element = Inactive.into(); + assert!(elem.is("inactive", ns::CSI)); + + let elem: Element = Active.into(); + assert!(elem.is("active", ns::CSI)); + } +} diff --git a/src/lib.rs b/src/lib.rs index ea09f2f0ea5e50e06f2a7059d98dbe7abb7263e3..da1af26c4cd99effc751de78b9bd465739a7619d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,6 +177,9 @@ pub mod idle; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub mod jingle_dtls_srtp; +/// XEP-0352: Client State Indication +pub mod csi; + /// XEP-0353: Jingle Message Initiation pub mod jingle_message; diff --git a/src/ns.rs b/src/ns.rs index e5242b25667a70312c2e5b4f405c4efb6796a18a..f5941258a8c7459370c215595a75c9567b236ae2 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -182,6 +182,9 @@ pub const IDLE: &str = "urn:xmpp:idle:1"; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0"; +/// XEP-0352: Client State Indication +pub const CSI: &str = "urn:xmpp:csi:0"; + /// XEP-0353: Jingle Message Initiation pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; From 05ab0cdc3844c6db261e2abf7ab91ade2e2cb2fa Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:32:03 +0200 Subject: [PATCH 0968/1020] Add a new CSI parser (XEP-0352). --- ChangeLog | 1 + doap.xml | 8 ++++++++ src/csi.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ 5 files changed, 74 insertions(+) create mode 100644 src/csi.rs diff --git a/ChangeLog b/ChangeLog index 372ebddef25d66a6bec783f258542a143e58123f..825c7a4065d7861e6e96a61b6f7168c3122a8dce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: + - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) * Breaking changes: - Presence constructors now take Into and assume Some. diff --git a/doap.xml b/doap.xml index b05c4c42b3e6f2d14940f2e386d0037c014538c2..713ca9f5e0cc815fab411f695a544d2a67c389b8 100644 --- a/doap.xml +++ b/doap.xml @@ -392,6 +392,14 @@ 0.13.0 + + + + complete + 0.3.0 + NEXT + + diff --git a/src/csi.rs b/src/csi.rs new file mode 100644 index 0000000000000000000000000000000000000000..0465f94b2014f3d98e050a069052414e3d8647ff --- /dev/null +++ b/src/csi.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_empty_element!( + /// Stream:feature sent by the server to advertise it supports CSI. + Feature, "csi", CSI +); + +generate_empty_element!( + /// Client indicates it is inactive. + Inactive, "inactive", CSI +); + +generate_empty_element!( + /// Client indicates it is active again. + Active, "active", CSI +); + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + use crate::ns; + + #[test] + fn test_size() { + assert_size!(Feature, 0); + assert_size!(Inactive, 0); + assert_size!(Active, 0); + } + + #[test] + fn parsing() { + let elem: Element = "".parse().unwrap(); + Feature::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Inactive::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Active::try_from(elem).unwrap(); + } + + #[test] + fn serialising() { + let elem: Element = Feature.into(); + assert!(elem.is("csi", ns::CSI)); + + let elem: Element = Inactive.into(); + assert!(elem.is("inactive", ns::CSI)); + + let elem: Element = Active.into(); + assert!(elem.is("active", ns::CSI)); + } +} diff --git a/src/lib.rs b/src/lib.rs index ea09f2f0ea5e50e06f2a7059d98dbe7abb7263e3..da1af26c4cd99effc751de78b9bd465739a7619d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,6 +177,9 @@ pub mod idle; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub mod jingle_dtls_srtp; +/// XEP-0352: Client State Indication +pub mod csi; + /// XEP-0353: Jingle Message Initiation pub mod jingle_message; diff --git a/src/ns.rs b/src/ns.rs index e5242b25667a70312c2e5b4f405c4efb6796a18a..f5941258a8c7459370c215595a75c9567b236ae2 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -182,6 +182,9 @@ pub const IDLE: &str = "urn:xmpp:idle:1"; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0"; +/// XEP-0352: Client State Indication +pub const CSI: &str = "urn:xmpp:csi:0"; + /// XEP-0353: Jingle Message Initiation pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; From 943292a74942ec7c496d7431278ec098983d557b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 17:30:52 +0200 Subject: [PATCH 0969/1020] Implement occupant-id (XEP-0421). --- src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/occupant_id.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 src/occupant_id.rs diff --git a/src/lib.rs b/src/lib.rs index da1af26c4cd99effc751de78b9bd465739a7619d..06048c8e30634d8c8b4051047592105832d59e45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -194,3 +194,6 @@ pub mod eme; /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; + +/// XEP-0421: Anonymous unique occupant identifiers for MUCs +pub mod occupant_id; diff --git a/src/ns.rs b/src/ns.rs index f5941258a8c7459370c215595a75c9567b236ae2..ef785082e8a4809b24a9db5f130952d26be448b5 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -204,6 +204,9 @@ pub const ECAPS2: &str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2_OPTIMIZE: &str = "urn:xmpp:caps:optimize"; +/// XEP-0421: Anonymous unique occupant identifiers for MUCs +pub const OID: &str = "urn:xmpp:occupant-id:0"; + /// Alias for the main namespace of the stream, that is "jabber:client" when /// the component feature isn’t enabled. #[cfg(not(feature = "component"))] diff --git a/src/occupant_id.rs b/src/occupant_id.rs new file mode 100644 index 0000000000000000000000000000000000000000..730652942d9073973f3e7d149796e5155a6b5a7d --- /dev/null +++ b/src/occupant_id.rs @@ -0,0 +1,89 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::message::MessagePayload; +use crate::presence::PresencePayload; + +generate_element!( + /// Unique identifier given to a MUC participant. + /// + /// It allows clients to identify a MUC participant across reconnects and + /// renames. It thus prevents impersonification of anonymous users. + OccupantId, "occupant-id", OID, + + attributes: [ + /// The id associated to the sending user by the MUC service. + id: Required = "id", + ] +); + +impl MessagePayload for OccupantId {} +impl PresencePayload for OccupantId {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::util::error::Error; + use minidom::Element; + use std::convert::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(OccupantId, 12); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(OccupantId, 24); + } + + #[test] + fn test_simple() { + let elem: Element = "" + .parse() + .unwrap(); + let origin_id = OccupantId::try_from(elem).unwrap(); + assert_eq!(origin_id.id, "coucou"); + } + + #[test] + fn test_invalid_child() { + let elem: Element = "" + .parse() + .unwrap(); + let error = OccupantId::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in occupant-id element."); + } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = OccupantId::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'id' missing."); + } + + #[test] + fn test_serialise() { + let elem: Element = "" + .parse() + .unwrap(); + let occupant_id = OccupantId { + id: String::from("coucou"), + }; + let elem2 = occupant_id.into(); + assert_eq!(elem, elem2); + } +} From d9a6aeea9981e0e2e013ef392130b1d9648f790e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Sep 2019 16:43:05 +0200 Subject: [PATCH 0970/1020] doap, ChangeLog: Add support for occupand-id (XEP-0421). --- ChangeLog | 1 + doap.xml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/ChangeLog b/ChangeLog index 825c7a4065d7861e6e96a61b6f7168c3122a8dce..275600e1314ce35b739d26fae0b97aa842b372d4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ DATE Emmanuel Gil Peyrot * New parsers/serialisers: - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) + - Anonymous unique occupant identifiers for MUCs (XEP-0421) * Breaking changes: - Presence constructors now take Into and assume Some. diff --git a/doap.xml b/doap.xml index 713ca9f5e0cc815fab411f695a544d2a67c389b8..6b8bf5a135796468aea85186800636e92458d68b 100644 --- a/doap.xml +++ b/doap.xml @@ -440,6 +440,14 @@ 0.1.0 + + + + complete + 0.1.0 + NEXT + + From 2e97f4de2e44c64524bbe9990553627e7a47f805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 8 Sep 2019 15:05:57 +0200 Subject: [PATCH 0971/1020] Fixes #11: Trim whitespace before feeding parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/xmpp_codec.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 0002087361b4d9341e630b15afc772b90642f813..654cc21ae71ea823a73fb6fa2244f5779de6a92e 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -229,7 +229,8 @@ impl Decoder for XMPPCodec { }; let buf1 = buf1.as_ref().as_ref(); match from_utf8(buf1) { - Ok(s) => { + Ok(mut s) => { + s = s.trim(); if !s.is_empty() { // println!("<< {}", s); let mut buffer_queue = BufferQueue::new(); @@ -508,4 +509,24 @@ mod tests { &("".to_owned() + &text + "").as_bytes() ); } + + #[test] + fn test_lone_whitespace() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + + b.clear(); + b.put(r" "); + let r = c.decode(&mut b); + assert!(match r { + Ok(None) => true, + _ => false, + }); + } } From 46522ceb198b68ce392b1be8b531fb3f38e4fc27 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 8 Sep 2019 15:53:55 +0200 Subject: [PATCH 0972/1020] Add a new client certificate management parser (XEP-0257). --- ChangeLog | 1 + doap.xml | 8 ++ src/cert_management.rs | 191 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 3 + src/util/macros.rs | 24 ++++++ 6 files changed, 230 insertions(+) create mode 100644 src/cert_management.rs diff --git a/ChangeLog b/ChangeLog index 275600e1314ce35b739d26fae0b97aa842b372d4..f1f253dca3490fdb4ae7b171f6a52af7d028e4d1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: + - Client Certificate Management for SASL EXTERNAL (XEP-0257) - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) - Anonymous unique occupant identifiers for MUCs (XEP-0421) diff --git a/doap.xml b/doap.xml index 6b8bf5a135796468aea85186800636e92458d68b..b9fc9965d9b3c03739fade79adc7e193a4633ac1 100644 --- a/doap.xml +++ b/doap.xml @@ -311,6 +311,14 @@ 0.1.0 + + + + complete + 0.3 + NEXT + + diff --git a/src/cert_management.rs b/src/cert_management.rs new file mode 100644 index 0000000000000000000000000000000000000000..a68909eb8b06d9c2f070b556a4737659479edd51 --- /dev/null +++ b/src/cert_management.rs @@ -0,0 +1,191 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::iq::{IqSetPayload, IqGetPayload, IqResultPayload}; +use crate::util::helpers::Base64; + +generate_elem_id!( + /// The name of a certificate. + Name, "name", SASL_CERT +); + +generate_element!( + /// An X.509 certificate. + Cert, "x509cert", SASL_CERT, + text: ( + /// The BER X.509 data. + data: Base64> + ) +); + +generate_element!( + /// For the client to upload an X.509 certificate. + Append, "append", SASL_CERT, + children: [ + /// The name of this certificate. + name: Required = ("name", SASL_CERT) => Name, + + /// The X.509 certificate to set. + cert: Required = ("x509cert", SASL_CERT) => Cert, + + /// This client is forbidden from managing certificates. + no_cert_management: Present<_> = ("no-cert-management", SASL_CERT) => bool + ] +); + +impl IqSetPayload for Append {} + +generate_empty_element!( + /// Client requests the current list of X.509 certificates. + ListCertsQuery, "items", SASL_CERT +); + +impl IqGetPayload for ListCertsQuery {} + +generate_elem_id!( + /// One resource currently using a certificate. + Resource, "resource", SASL_CERT +); + +generate_element!( + /// A list of resources currently using this certificate. + Users, "users", SASL_CERT, + children: [ + /// Resources currently using this certificate. + resources: Vec = ("resource", SASL_CERT) => Resource + ] +); + +generate_element!( + /// An X.509 certificate being set for this user. + Item, "item", SASL_CERT, + children: [ + /// The name of this certificate. + name: Required = ("name", SASL_CERT) => Name, + + /// The X.509 certificate to set. + cert: Required = ("x509cert", SASL_CERT) => Cert, + + /// This client is forbidden from managing certificates. + no_cert_management: Present<_> = ("no-cert-management", SASL_CERT) => bool, + + /// List of resources currently using this certificate. + users: Option = ("users", SASL_CERT) => Users + ] +); + +generate_element!( + /// Server answers with the current list of X.509 certificates. + ListCertsResponse, "items", SASL_CERT, + children: [ + /// List of certificates. + items: Vec = ("item", SASL_CERT) => Item + ] +); + +impl IqResultPayload for ListCertsResponse {} + +generate_element!( + /// Client disables an X.509 certificate. + Disable, "disable", SASL_CERT, + children: [ + /// Name of the certificate to disable. + name: Required = ("name", SASL_CERT) => Name + ] +); + +impl IqSetPayload for Disable {} + +generate_element!( + /// Client revokes an X.509 certificate. + Revoke, "revoke", SASL_CERT, + children: [ + /// Name of the certificate to revoke. + name: Required = ("name", SASL_CERT) => Name + ] +); + +impl IqSetPayload for Revoke {} + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + use std::str::FromStr; + use crate::ns; + + #[test] + fn test_size() { + assert_size!(Append, 56); + } + + #[test] + fn simple() { + let elem: Element = "Mobile ClientAAAA".parse().unwrap(); + let append = Append::try_from(elem).unwrap(); + assert_eq!(append.name.0, "Mobile Client"); + assert_eq!(append.cert.data, b"\0\0\0"); + + let elem: Element = "Mobile Client".parse().unwrap(); + let disable = Disable::try_from(elem).unwrap(); + assert_eq!(disable.name.0, "Mobile Client"); + + let elem: Element = "Mobile Client".parse().unwrap(); + let revoke = Revoke::try_from(elem).unwrap(); + assert_eq!(revoke.name.0, "Mobile Client"); + } + + #[test] + fn list() { + let elem: Element = r#" + + + Mobile Client + AAAA + + Phone + + + + Laptop + BBBB + + "#.parse().unwrap(); + let mut list = ListCertsResponse::try_from(elem).unwrap(); + assert_eq!(list.items.len(), 2); + + let item = list.items.pop().unwrap(); + assert_eq!(item.name.0, "Laptop"); + assert_eq!(item.cert.data, [4, 16, 65]); + assert!(item.users.is_none()); + + let item = list.items.pop().unwrap(); + assert_eq!(item.name.0, "Mobile Client"); + assert_eq!(item.cert.data, b"\0\0\0"); + assert_eq!(item.users.unwrap().resources.len(), 1); + } + + #[test] + fn test_serialise() { + let append = Append { + name: Name::from_str("Mobile Client").unwrap(), + cert: Cert { data: b"\0\0\0".to_vec() }, + no_cert_management: false, + }; + let elem: Element = append.into(); + assert!(elem.is("append", ns::SASL_CERT)); + + let disable = Disable { + name: Name::from_str("Mobile Client").unwrap(), + }; + let elem: Element = disable.into(); + assert!(elem.is("disable", ns::SASL_CERT)); + let elem = elem.children().cloned().collect::>().pop().unwrap(); + assert!(elem.is("name", ns::SASL_CERT)); + assert_eq!(elem.text(), "Mobile Client"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 06048c8e30634d8c8b4051047592105832d59e45..9f6053cc6fce8b24d146c93e99c9f1144fdbd84c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,6 +150,9 @@ pub mod bob; /// XEP-0234: Jingle File Transfer pub mod jingle_ft; +/// XEP-0257: Client Certificate Management for SASL EXTERNAL +pub mod cert_management; + /// XEP-0260: Jingle SOCKS5 Bytestreams Transport Method pub mod jingle_s5b; diff --git a/src/ns.rs b/src/ns.rs index ef785082e8a4809b24a9db5f130952d26be448b5..328ed4820484e2dcc758e37d51f049ada5b8fa4f 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -140,6 +140,9 @@ pub const JINGLE_FT: &str = "urn:xmpp:jingle:apps:file-transfer:5"; /// XEP-0234: Jingle File Transfer pub const JINGLE_FT_ERROR: &str = "urn:xmpp:jingle:apps:file-transfer:errors:0"; +/// XEP-0257: Client Certificate Management for SASL EXTERNAL +pub const SASL_CERT: &str = "urn:xmpp:saslcert:1"; + /// XEP-0260: Jingle SOCKS5 Bytestreams Transport Method pub const JINGLE_S5B: &str = "urn:xmpp:jingle:transports:s5b:1"; diff --git a/src/util/macros.rs b/src/util/macros.rs index f5fda080cdd1fdd0c3d52f8fd3e83c66881e5474..fd7f58cc649c2ff3ef5352ff5e6a3a047edb395d 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -494,6 +494,9 @@ macro_rules! start_decl { (Required, $type:ty) => ( $type ); + (Present, $type:ty) => ( + bool + ); } macro_rules! start_parse_elem { @@ -506,6 +509,9 @@ macro_rules! start_parse_elem { ($temp:ident: Required) => { let mut $temp = None; }; + ($temp:ident: Present) => { + let mut $temp = false; + }; } macro_rules! do_parse { @@ -548,6 +554,18 @@ macro_rules! do_parse_elem { } $temp = Some(do_parse!($elem, $constructor)); }; + ($temp:ident: Present = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { + if $temp { + return Err(crate::util::error::Error::ParseError(concat!( + "Element ", + $parent_name, + " must not have more than one ", + $name, + " child." + ))); + } + $temp = true; + }; } macro_rules! finish_parse_elem { @@ -566,6 +584,9 @@ macro_rules! finish_parse_elem { " element." )))? }; + ($temp:ident: Present = $name:tt, $parent_name:tt) => { + $temp + }; } macro_rules! generate_serialiser { @@ -595,6 +616,9 @@ macro_rules! generate_serialiser { ($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => { $builder.append_all($parent.$elem.into_iter()) }; + ($builder:ident, $parent:ident, $elem:ident, Present, $constructor:ident, ($name:tt, $ns:ident)) => { + $builder.append(::minidom::Node::Element(::minidom::Element::builder($name).ns(crate::ns::$ns).build())) + }; ($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => { $builder.append(::minidom::Node::Element(::minidom::Element::from($parent.$elem))) }; From 9941e9c34f8eae76292fa43799aa3da41e821eef Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 8 Sep 2019 16:09:49 +0200 Subject: [PATCH 0973/1020] Add a new JID Prep parser (XEP-0328). --- ChangeLog | 1 + doap.xml | 8 +++++++ src/jid_prep.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/ns.rs | 3 +++ src/util/helpers.rs | 15 ++++++++++++ 6 files changed, 86 insertions(+) create mode 100644 src/jid_prep.rs diff --git a/ChangeLog b/ChangeLog index f1f253dca3490fdb4ae7b171f6a52af7d028e4d1..43367a608978f220b79ddfb5d19b4a00e973522e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - Client Certificate Management for SASL EXTERNAL (XEP-0257) + - JID Prep (XEP-0328) - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) - Anonymous unique occupant identifiers for MUCs (XEP-0421) diff --git a/doap.xml b/doap.xml index b9fc9965d9b3c03739fade79adc7e193a4633ac1..f591bfff5e86ea6bc965e187cf6967b64a0e7e96 100644 --- a/doap.xml +++ b/doap.xml @@ -400,6 +400,14 @@ 0.13.0 + + + + complete + 0.1 + NEXT + + diff --git a/src/jid_prep.rs b/src/jid_prep.rs new file mode 100644 index 0000000000000000000000000000000000000000..9ecff61ad7dd09f5dde4b4d831eb5f4354c96b9d --- /dev/null +++ b/src/jid_prep.rs @@ -0,0 +1,56 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::iq::{IqGetPayload, IqResultPayload}; +use crate::util::helpers::{PlainText, JidCodec}; +use jid::Jid; + +generate_element!( + /// TODO + JidPrepQuery, "jid", JID_PREP, + text: ( + /// TODO + data: PlainText> + ) +); + +impl IqGetPayload for JidPrepQuery {} + +generate_element!( + /// TODO + JidPrepResponse, "jid", JID_PREP, + text: ( + /// TODO + jid: JidCodec + ) +); + +impl IqResultPayload for JidPrepResponse {} + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + use std::str::FromStr; + + #[test] + fn test_size() { + assert_size!(JidPrepQuery, 24); + assert_size!(JidPrepResponse, 80); + } + + #[test] + fn simple() { + let elem: Element = "ROMeo@montague.lit/orchard".parse().unwrap(); + let query = JidPrepQuery::try_from(elem).unwrap(); + assert_eq!(query.data.unwrap(), "ROMeo@montague.lit/orchard"); + + let elem: Element = "romeo@montague.lit/orchard".parse().unwrap(); + let response = JidPrepResponse::try_from(elem).unwrap(); + assert_eq!(response.jid, Jid::from_str("romeo@montague.lit/orchard").unwrap()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9f6053cc6fce8b24d146c93e99c9f1144fdbd84c..b69e56ad4408e19f4542926a5725394ab6a3a867 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,6 +180,9 @@ pub mod idle; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub mod jingle_dtls_srtp; +/// XEP-0328: JID Prep +pub mod jid_prep; + /// XEP-0352: Client State Indication pub mod csi; diff --git a/src/ns.rs b/src/ns.rs index 328ed4820484e2dcc758e37d51f049ada5b8fa4f..70d68cff156aff8aebddb24e91b02c2559caf018 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -185,6 +185,9 @@ pub const IDLE: &str = "urn:xmpp:idle:1"; /// XEP-0320: Use of DTLS-SRTP in Jingle Sessions pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0"; +/// XEP-0328: JID Prep +pub const JID_PREP: &str = "urn:xmpp:jidprep:0"; + /// XEP-0352: Client State Indication pub const CSI: &str = "urn:xmpp:csi:0"; diff --git a/src/util/helpers.rs b/src/util/helpers.rs index a455a139f0eb26bd9e13f01f9e1a0a47f6cad8c1..a78fa7f6db822e2091ba8182a1420244165fdfe9 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -5,6 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::util::error::Error; +use jid::Jid; +use std::str::FromStr; /// Codec for plain text content. pub struct PlainText; @@ -89,3 +91,16 @@ impl ColonSeparatedHex { Some(bytes.join(":")) } } + +/// Codec for a JID. +pub struct JidCodec; + +impl JidCodec { + pub fn decode(s: &str) -> Result { + Ok(Jid::from_str(s)?) + } + + pub fn encode(jid: &Jid) -> Option { + Some(jid.to_string()) + } +} From a9a68cb1d749051558ef6978e3804d32e1bf3a6c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 8 Sep 2019 16:22:12 +0200 Subject: [PATCH 0974/1020] jid_prep: Add constructor, documentation, and switch from Option to String. --- src/jid_prep.rs | 23 ++++++++++++++++------- src/util/helpers.rs | 13 +++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/jid_prep.rs b/src/jid_prep.rs index 9ecff61ad7dd09f5dde4b4d831eb5f4354c96b9d..d151f6c04bec1d5af2e6106c3c812e8239536644 100644 --- a/src/jid_prep.rs +++ b/src/jid_prep.rs @@ -5,25 +5,34 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::iq::{IqGetPayload, IqResultPayload}; -use crate::util::helpers::{PlainText, JidCodec}; +use crate::util::helpers::{Text, JidCodec}; use jid::Jid; generate_element!( - /// TODO + /// Request from a client to stringprep/PRECIS a string into a JID. JidPrepQuery, "jid", JID_PREP, text: ( - /// TODO - data: PlainText> + /// The potential JID. + data: Text ) ); impl IqGetPayload for JidPrepQuery {} +impl JidPrepQuery { + /// Create a new JID Prep query. + pub fn new>(jid: J) -> JidPrepQuery { + JidPrepQuery { + data: jid.into(), + } + } +} + generate_element!( - /// TODO + /// Response from the server with the stringprep’d/PRECIS’d JID. JidPrepResponse, "jid", JID_PREP, text: ( - /// TODO + /// The JID. jid: JidCodec ) ); @@ -47,7 +56,7 @@ mod tests { fn simple() { let elem: Element = "ROMeo@montague.lit/orchard".parse().unwrap(); let query = JidPrepQuery::try_from(elem).unwrap(); - assert_eq!(query.data.unwrap(), "ROMeo@montague.lit/orchard"); + assert_eq!(query.data, "ROMeo@montague.lit/orchard"); let elem: Element = "romeo@montague.lit/orchard".parse().unwrap(); let response = JidPrepResponse::try_from(elem).unwrap(); diff --git a/src/util/helpers.rs b/src/util/helpers.rs index a78fa7f6db822e2091ba8182a1420244165fdfe9..7bf3feb1f2b59e453bd4dd58694aa9083e1d6f3e 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -8,6 +8,19 @@ use crate::util::error::Error; use jid::Jid; use std::str::FromStr; +/// Codec for text content. +pub struct Text; + +impl Text { + pub fn decode(s: &str) -> Result { + Ok(s.to_owned()) + } + + pub fn encode(string: &str) -> Option { + Some(string.to_owned()) + } +} + /// Codec for plain text content. pub struct PlainText; From c7038ace1a1a77e209b48fd955be0c79c8cd01a8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 8 Sep 2019 18:24:45 +0200 Subject: [PATCH 0975/1020] jingle, pubsub: Fix spelling thanks to codespell! --- src/jingle.rs | 2 +- src/pubsub/pubsub.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 4a8e5ac357b577e88861aeb5db7535db0a3b67fe..7e27ebbc6efe2ff73d628fdbcc4aedb5819db276 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -445,7 +445,7 @@ pub struct Jingle { /// Unique session identifier between two entities. pub sid: SessionId, - /// A list of contents to be negociated in this session. + /// A list of contents to be negotiated in this session. pub contents: Vec, /// An optional reason. diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 727ef2b882d5c86de46ec215e82127a317b029fc..875c1989538364ba805e8fac39ffcb8fe525f13f 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -46,7 +46,7 @@ generate_attribute!( /// You are a publisher on this node, you can publish and retract items to it. Publisher => "publisher", - /// You can publish and retract items on this node, but not subscribe or retrive items. + /// You can publish and retract items on this node, but not subscribe or retrieve items. PublishOnly => "publish-only", } ); @@ -85,7 +85,7 @@ generate_element!( /// Request for a default node configuration. Default, "default", PUBSUB, attributes: [ - /// The node targetted by this request, otherwise the entire service. + /// The node targeted by this request, otherwise the entire service. node: Option = "node", // TODO: do we really want to support collection nodes? From 3b37646d0a60ff2bc99139fa68802ac89b211f72 Mon Sep 17 00:00:00 2001 From: SonnyX Date: Sun, 8 Sep 2019 21:28:44 +0200 Subject: [PATCH 0976/1020] Update dependencies --- Cargo.lock | 1164 +++++++++++++++++----------------- Cargo.toml | 20 +- examples/contact_addr.rs | 2 +- examples/download_avatars.rs | 13 +- examples/echo_bot.rs | 7 +- examples/echo_component.rs | 7 +- src/client/auth.rs | 2 +- src/client/bind.rs | 21 +- src/client/mod.rs | 4 +- src/starttls.rs | 2 +- src/stream_start.rs | 2 +- src/xmpp_codec.rs | 2 +- 12 files changed, 617 insertions(+), 629 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 306ce86be222bbce12d47add98d776a0e0422ce8..ac0af39db11a1f3ebf49e483cba9db20c546e3d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,16 +1,8 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "MacTypes-sys" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "arrayvec" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -18,29 +10,27 @@ dependencies = [ [[package]] name = "autocfg" -version = "0.1.2" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.13" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -48,39 +38,39 @@ name = "base64" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bitflags" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "blake2" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "block-buffer" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "block-padding" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -93,40 +83,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bytes" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "case" -version = "0.1.0" +name = "c2-chacha" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "cc" -version = "1.0.28" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "chrono" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -135,79 +130,60 @@ name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation" -version = "0.5.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation-sys" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] -name = "crossbeam-channel" -version = "0.3.8" +name = "crossbeam-deque" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "crossbeam-deque" -version = "0.6.3" +name = "crossbeam-epoch" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "crossbeam-epoch" -version = "0.7.1" +name = "crossbeam-queue" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -215,42 +191,26 @@ name = "crypto-mac" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "derive-error" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "case 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "digest" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "encoding_rs" -version = "0.8.15" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "error-chain" -version = "0.8.1" +name = "enum-as-inner" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -258,7 +218,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -267,10 +227,10 @@ name = "failure_derive" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -278,6 +238,11 @@ name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "foreign-types" version = "0.3.2" @@ -293,7 +258,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "fuchsia-cprng" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -301,7 +266,7 @@ name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -316,29 +281,39 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures" -version = "0.1.25" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "generic-array" -version = "0.12.0" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "hmac" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -346,13 +321,13 @@ name = "hostname" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "idna" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -365,35 +340,32 @@ name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ipconfig" -version = "0.1.9" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "widestring 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itoa" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jid" -version = "0.5.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "minidom 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -412,22 +384,17 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lazycell" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.48" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "linked-hash-map" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -441,18 +408,18 @@ dependencies = [ [[package]] name = "log" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lru-cache" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -462,14 +429,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "markup5ever" -version = "0.7.5" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -482,40 +450,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memchr" -version = "2.1.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "memoffset" -version = "0.2.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "minidom" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-xml 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-xml 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "mio" -version = "0.6.16" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -528,8 +492,8 @@ version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -545,19 +509,19 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -565,18 +529,15 @@ name = "net2" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "new_debug_unreachable" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "nodrop" @@ -585,41 +546,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num-integer" -version = "0.1.39" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "num_cpus" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "opaque-debug" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.10.16" +version = "0.10.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -629,13 +594,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.40" +version = "0.9.49" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -660,11 +626,11 @@ name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -672,13 +638,13 @@ name = "pbkdf2" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "percent-encoding" -version = "1.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -717,7 +683,12 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.14" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ppv-lite86" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -727,12 +698,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "0.4.26" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro2" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quick-error" version = "1.2.2" @@ -740,38 +719,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quick-xml" -version = "0.13.2" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "encoding_rs 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" -version = "0.6.11" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rand" -version = "0.5.6" +name = "quote" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -779,17 +746,29 @@ name = "rand" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -797,22 +776,39 @@ name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_core" -version = "0.4.0" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_core" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "rand_hc" @@ -822,6 +818,14 @@ dependencies = [ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_isaac" version = "0.1.1" @@ -832,34 +836,34 @@ dependencies = [ [[package]] name = "rand_jitter" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_os" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_pcg" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -880,15 +884,15 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.51" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "remove_dir_all" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -902,7 +906,7 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.13" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -915,7 +919,7 @@ dependencies = [ [[package]] name = "ryu" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -924,20 +928,20 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "schannel" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -945,25 +949,28 @@ name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "security-framework" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "security-framework-sys" -version = "0.2.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -981,27 +988,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.85" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.85" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1009,10 +1016,10 @@ name = "sha-1" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1020,22 +1027,22 @@ name = "sha2" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sha3" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1050,21 +1057,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "0.6.8" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "socket2" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1077,11 +1081,11 @@ name = "string_cache" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1093,8 +1097,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1110,54 +1114,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.11.11" +version = "0.15.44" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "0.15.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "synom" -version = "0.11.3" +name = "syn" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "synstructure" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tempfile" -version = "3.0.5" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1175,31 +1171,31 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio" -version = "0.1.15" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1208,72 +1204,74 @@ name = "tokio-codec" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-current-thread" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-executor" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-fs" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-io" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-reactor" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-sync" -version = "0.1.0" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1281,40 +1279,39 @@ name = "tokio-tcp" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-threadpool" -version = "0.1.11" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-timer" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1322,23 +1319,23 @@ name = "tokio-tls" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-udp" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1346,91 +1343,85 @@ name = "tokio-uds" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-xmpp" version = "1.0.0" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "derive-error 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-xml 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-xml 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", "sasl 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-resolver 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "xml5ever 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "xmpp-parsers 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-resolver 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xml5ever 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xmpp-parsers 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trust-dns-proto" -version = "0.6.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trust-dns-resolver" -version = "0.10.3" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "ipconfig 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "try_from" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "typenum" -version = "1.10.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1446,35 +1437,27 @@ name = "unicode-normalization" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "unreachable" -version = "1.0.0" +name = "unicode-xid" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "url" -version = "1.7.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1484,17 +1467,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "vcpkg" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "void" -version = "1.0.2" +name = "wasi" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "widestring" -version = "0.2.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1504,7 +1487,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1528,10 +1511,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winreg" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1539,7 +1522,7 @@ name = "winutil" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1553,208 +1536,207 @@ dependencies = [ [[package]] name = "xml5ever" -version = "0.12.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "markup5ever 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "markup5ever 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "xmpp-parsers" -version = "0.13.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jid 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "minidom 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] -"checksum MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf9f0d0b1cc33a4d2aee14fb4b2eac03462ef4db29c8ac4057327d8a71ad86f" -"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" -"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" -"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" -"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" +"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" +"checksum backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2" +"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91721a6330935673395a0607df4d49a9cb90ae12d259f1b3e0a3f6e1d486872e" -"checksum block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "509de513cca6d92b6aacf9c61acfe7eaa160837323a81068d690cc1f8e5740da" -"checksum block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d75255892aeb580d3c566f213a2b6fdc1c66667839f45719ee1d30ebf2aea591" +"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" +"checksum blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" -"checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" -"checksum case 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e88b166b48e29667f5443df64df3c61dc07dc2b1a0b0d231800e07f09a33ecc1" -"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" -"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" -"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" +"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" +"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" +"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" -"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" -"checksum crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad4c7ea749d9fb09e23c5cb17e3b70650860553a0e2744e38446b1803bf7db94" -"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" -"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" -"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" -"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" +"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" +"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" +"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" +"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum derive-error 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ec098440b29ea3b1ece3e641bac424c19cf996779b623c9e0f2171495425c2c8" -"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" -"checksum encoding_rs 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)" = "fd251508d65030820f3a4317af2248180db337fdb25d89967956242580277813" -"checksum error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6930e04918388a9a2e41d518c25cf679ccafe26733fb4127dbf21993f2575d46" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d58266c97445680766be408285e798d3401c6d4c378ec5552e78737e681e37d" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -"checksum fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f7f8eb465745ea9b02e2704612a9946a59fa40572086c6fd49d6ddcf30bf31" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" -"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" -"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" -"checksum hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f127a908633569f208325f86f71255d3363c79721d7f9fe31cd5569908819771" +"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" +"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" -"checksum ipconfig 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "08f7eadeaf4b52700de180d147c4805f199854600b36faa963d91114827b2ffc" -"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" -"checksum jid 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "24e8a3f2ab860aa08074136e3144a2425e678d8823206e5adcc6145dc136503a" +"checksum ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f" +"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum jid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e3676672e1f13e4963e6af2c1d17114e7f9f96f96477c278fccdd192c618c67" "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" -"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" -"checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d06ff7ff06f729ce5f4e227876cb88d10bc59cd4ae1e09fbb2bde15c850dc21" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" "checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -"checksum markup5ever 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "897636f9850c3eef4905a5540683ed53dc9393860f0846cab2c2ddf9939862ff" +"checksum markup5ever 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "65381d9d47506b8592b97c4efd936afcf673b09b059f2bef39c7211ee78b9d03" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e1dd4eaac298c32ce07eb6ed9242eda7d82955b9170b7d6db59b2e02cc63fcb8" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum minidom 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "275024eea6c6ff4ace22f2750843831183785288eec1cff91a4e6b8898cf94f9" -"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" +"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" +"checksum minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "df1f29c97815c4328d6b533d2b8976e80cbed02d97063970aa4a80c100ecf29a" +"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" +"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4" +"checksum new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" -"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" -"checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238" -"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" -"checksum openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ec7bd7ca4cce6dbdc77e7c1230682740d307d1218a87fb0349a571272be749f9" +"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" +"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)" = "1bb974e77de925ef426b6bc82fce15fd45bdcbeb5728bffcfc7cdeeb7ce1c2d6" +"checksum openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f4fad9e54bd23bd4cbbe48fdc08a1b8091707ac869ef8508edea2fec77dcc884" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" "checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" +"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" +"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -"checksum proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "38fddd23d98b2144d197c0eca5705632d4fe2667d14a6be5df8934f8d74f1978" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quick-xml 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98d8d2d671bd29c6122a98b45ce3106391e89ba378f731274de677f1eff06e5f" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" -"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +"checksum quick-xml 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd45021132c1cb5540995e93fcc2cf5a874ef84f9639168fb6819caa023d4be" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" +"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "29fe7b8bc348249f3b1bbb9ab8baa6fa3419196ecfbf213408bca1b2494710de" -"checksum rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b7c690732391ae0abafced5015ffb53656abfaec61b342290e5eb56b286a679d" -"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" +"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" -"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" -"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" +"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" "checksum sasl 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e457758c85b736bbad56dc099406cd2a9c19554cf81880dba7a51d092929e600" -"checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56" +"checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum security-framework 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfab8dda0e7a327c696d893df9ffa19cadc4bd195797997f5223cf5831beaf05" -"checksum security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3d6696852716b589dff9e886ff83778bb635150168e83afa8ac6b8a78cb82abc" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2" +"checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)" = "534b8b91a95e0f71bca3ed5824752d558da048d4248c91af873b63bd60519752" -"checksum serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)" = "a915306b0f1ac5607797697148c223bedeaa36bcc2e28a01441cd638cc6567b4" -"checksum serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "4b90a9fbe1211e57d3e1c15670f1cb00802988fb23a1a4aad7a2b63544f1920e" +"checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" +"checksum serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "11e410fde43e157d789fc290d26bc940778ad0fdd47836426fbac36573710dbb" +"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" "checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"checksum sha3 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34a5e54083ce2b934bf059fdf38e7330a154177e029ab6c4e18638f2f624053a" +"checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "88aea073965ab29f6edb5493faf96ad662fb18aa9eeb186a3b7057951605ed15" -"checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" +"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" +"checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" "checksum string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da" "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" -"checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2" +"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" +"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "e0500b88064f08bebddd0c0bed39e19f5c567a5f30975bee52b0c0d3e2eeb38c" +"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" "checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "331c8acc267855ec06eb0c94618dcbbfea45bed2d20b77252940095273fb58f6" -"checksum tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30c6dbf2d1ad1de300b393910e8a3aa272b724a400b6531da03eed99e329fbf0" -"checksum tokio-fs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9cbbc8a3698b7ab652340f46633364f9eaa928ddaaee79d8b8f356dd79a09d" -"checksum tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b53aeb9d3f5ccf2ebb29e19788f96987fa1355f8fe45ea193928eaaaf3ae820f" -"checksum tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "afbcdb0f0d2a1e4c440af82d7bbf0bf91a8a8c0575bcd20c05d15be7e9d3a02f" -"checksum tokio-sync 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d65a58e2215c13179e6eeb2cf00511e0aee455cad40a9bfaef15a2fd8aab1c7" +"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" +"checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac" +"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" +"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" +"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" +"checksum tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2162248ff317e2bc713b261f242b69dbb838b85248ed20bb21df56d60ea4cae7" "checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c3fd86cb15547d02daa2b21aadaf4e37dee3368df38a526178a5afa3c034d2fb" -"checksum tokio-timer 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "21c04a314a1f69f73c0227beba6250e06cdc1e9a62e7eff912bf54a59b6d1b94" +"checksum tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "90ca01319dea1e376a001e8dc192d42ebde6dd532532a5bad988ac37db365b19" +"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" "checksum tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" -"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" +"checksum tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f02298505547f73e60f568359ef0d016d5acd6e830ab9bc7c4a5b3403440121b" "checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" -"checksum trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09144f0992b0870fa8d2972cc069cbf1e3c0fda64d1f3d45c4d68d0e0b52ad4e" -"checksum trust-dns-resolver 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8a9f877f7a1ad821ab350505e1f1b146a4960402991787191d6d8cab2ce2de2c" -"checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum trust-dns-proto 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05457ece29839d056d8cb66ec080209d34492b3d2e7e00641b486977be973db9" +"checksum trust-dns-resolver 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb1b3a41ee784f8da051cd342c6f42a3a75ee45818164acad867eac8f2f85332" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" "checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" -"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum widestring 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7157704c2e12e3d2189c507b7482c52820a16dfa4465ba91add92f266667cadb" +"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" +"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum xml5ever 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cd7ebf0203c620906230ce22caa5df0b603c32b6fef72a275a48f6a2ae64b9" -"checksum xmpp-parsers 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c467bb13e01718be793cb7a1c3d38d0e9ba45898db306aa43e70657a8aa3c2f2" +"checksum xml5ever 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b93ca59bfbbc3c0f807a61faea54e6a4c753f82857d3730e9afb5523b6149c7" +"checksum xmpp-parsers 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57991f57c0011b66caeed8ed4380489b42c39c41364e31e4bbe011649cb79002" diff --git a/Cargo.toml b/Cargo.toml index c22979f6e5531a579f3418d747487a535cbad39e..c6b5c26ed4cfbb038a92b2e01bb616ba39bb1f1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,17 +12,17 @@ keywords = ["xmpp", "tokio"] edition = "2018" [dependencies] +bytes = "0.4" futures = "0.1" +idna = "0.2" +native-tls = "0.2" +sasl = "0.4" tokio = "0.1" -tokio-io = "0.1" tokio-codec = "0.1" -bytes = "0.4" -xml5ever = "0.14" -native-tls = "0.2" +trust-dns-resolver = "0.12" +trust-dns-proto = "0.8" +tokio-io = "0.1" tokio-tls = "0.2" -sasl = "0.4" -trust-dns-resolver = "0.11" -trust-dns-proto = "0.7" -xmpp-parsers = "0.13" -idna = "0.1" -quick-xml = "0.13" +quick-xml = "0.16" +xml5ever = "0.15" +xmpp-parsers = "0.15" diff --git a/examples/contact_addr.rs b/examples/contact_addr.rs index 0d74f8ee16f329e8e1c091a1180d31be0d6ff2b3..f60601a8cdacad09d2b0a5577e89e8d6591b0b4b 100644 --- a/examples/contact_addr.rs +++ b/examples/contact_addr.rs @@ -1,4 +1,5 @@ use futures::{future, Sink, Stream}; +use std::convert::TryFrom; use std::env::args; use std::process::exit; use tokio::runtime::current_thread::Runtime; @@ -6,7 +7,6 @@ use tokio_xmpp::{Client, xmpp_codec::Packet}; use xmpp_parsers::{ Element, Jid, - TryFrom, ns, iq::{ Iq, diff --git a/examples/download_avatars.rs b/examples/download_avatars.rs index 06ff8c343e330cfa7c2220ff150d2731683ba048..837dbec4dbe3415b60c22d26d4669e167496697d 100644 --- a/examples/download_avatars.rs +++ b/examples/download_avatars.rs @@ -1,4 +1,5 @@ use futures::{future, Future, Sink, Stream}; +use std::convert::TryFrom; use std::env::args; use std::fs::{create_dir_all, File}; use std::io::{self, Write}; @@ -21,7 +22,7 @@ use xmpp_parsers::{ NodeName, }, stanza_error::{StanzaError, ErrorType, DefinedCondition}, - Jid, TryFrom, + Jid, }; fn main() { @@ -116,7 +117,7 @@ fn main() { let from = message.from.clone().unwrap(); if let Some(body) = message.get_best_body(vec!["en"]) { if body.1 .0 == "die" { - println!("Secret die command triggered by {}", from); + println!("Secret die command triggered by {}", from.clone()); wait_for_stream_end = true; tx.start_send(Packet::StreamEnd).unwrap(); } @@ -133,7 +134,7 @@ fn main() { let _metadata = AvatarMetadata::try_from(payload).unwrap(); println!( "{} has published an avatar, downloading...", - from + from.clone() ); let iq = download_avatar(&from); tx.start_send(Packet::Stanza(iq.into())).unwrap(); @@ -218,11 +219,11 @@ fn handle_iq_result(pubsub: PubSub, from: &Jid) { } fn save_avatar(from: &Jid, id: String, data: &[u8]) -> io::Result<()> { - let directory = format!("data/{}", from); - let filename = format!("data/{}/{}", from, id); + let directory = format!("data/{}", from.clone()); + let filename = format!("data/{}/{}", from.clone(), id); println!( "Saving avatar from {} to {}.", - from, filename + from.clone(), filename ); create_dir_all(directory)?; let mut file = File::create(filename)?; diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index cda70536a2eab561d00ec4d4bfc89437669f73d6..6de2ce72d9132b2347d28ddbe56d985d768b0deb 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -1,9 +1,10 @@ use futures::{future, Future, Sink, Stream}; +use std::convert::TryFrom; use std::env::args; use std::process::exit; use tokio::runtime::current_thread::Runtime; use tokio_xmpp::{Client, Packet}; -use xmpp_parsers::{Jid, Element, TryFrom}; +use xmpp_parsers::{Jid, Element}; use xmpp_parsers::message::{Body, Message, MessageType}; use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType}; @@ -56,7 +57,7 @@ fn main() { { match (message.from, message.bodies.get("")) { (Some(ref from), Some(ref body)) if body.0 == "die" => { - println!("Secret die command triggered by {}", from); + println!("Secret die command triggered by {}", from.clone()); wait_for_stream_end = true; tx.start_send(Packet::StreamEnd).unwrap(); } @@ -87,7 +88,7 @@ fn main() { // Construct a fn make_presence() -> Element { let mut presence = Presence::new(PresenceType::None); - presence.show = PresenceShow::Chat; + presence.show = Some(PresenceShow::Chat); presence .statuses .insert(String::from("en"), String::from("Echoing messages.")); diff --git a/examples/echo_component.rs b/examples/echo_component.rs index 618032e324a422621c168df9e9a986f5f6fa282e..6a8bc33c8fbd09ca93e0bac20516de5eda72975c 100644 --- a/examples/echo_component.rs +++ b/examples/echo_component.rs @@ -1,10 +1,11 @@ use futures::{future, Sink, Stream}; +use std::convert::TryFrom; use std::env::args; use std::process::exit; use std::str::FromStr; use tokio::runtime::current_thread::Runtime; use tokio_xmpp::Component; -use xmpp_parsers::{Jid, Element, TryFrom}; +use xmpp_parsers::{Jid, Element}; use xmpp_parsers::message::{Body, Message, MessageType}; use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType}; @@ -31,7 +32,7 @@ fn main() { // Make the two interfaces for sending and receiving independent // of each other so we can move one into a closure. - println!("Got it: {}", component.jid); + println!("Got it: {}", component.jid.clone()); let (mut sink, stream) = component.split(); // Wrap sink in Option so that we can take() it for the send(self) // to consume and return it back when ready. @@ -83,7 +84,7 @@ fn make_presence(from: Jid, to: Jid) -> Element { let mut presence = Presence::new(PresenceType::None); presence.from = Some(from); presence.to = Some(to); - presence.show = PresenceShow::Chat; + presence.show = Some(PresenceShow::Chat); presence .statuses .insert(String::from("en"), String::from("Echoing messages.")); diff --git a/src/client/auth.rs b/src/client/auth.rs index 81210dc7539c85ebfada329cc7b8accec7f404e4..096a0bfbca3070dea65c31942c55fc5d8dce2e99 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -1,12 +1,12 @@ use std::str::FromStr; use std::collections::HashSet; +use std::convert::TryFrom; use futures::{Future, Poll, Stream, future::{ok, err, IntoFuture}}; use sasl::client::mechanisms::{Anonymous, Plain, Scram}; use sasl::client::Mechanism; use sasl::common::scram::{Sha1, Sha256}; use sasl::common::Credentials; use tokio_io::{AsyncRead, AsyncWrite}; -use xmpp_parsers::TryFrom; use xmpp_parsers::sasl::{Auth, Challenge, Failure, Mechanism as XMPPMechanism, Response, Success}; use crate::xmpp_codec::Packet; diff --git a/src/client/bind.rs b/src/client/bind.rs index 322458e524bdfbb793398fc103051eee3d0b5f9f..f7d1828740dff5bfde15e22c01a4cba3ecfbdd6d 100644 --- a/src/client/bind.rs +++ b/src/client/bind.rs @@ -1,8 +1,9 @@ use futures::{sink, Async, Future, Poll, Stream}; +use std::convert::TryFrom; use std::mem::replace; use tokio_io::{AsyncRead, AsyncWrite}; -use xmpp_parsers::TryFrom; -use xmpp_parsers::bind::Bind; +use xmpp_parsers::Jid; +use xmpp_parsers::bind::{BindQuery, BindResponse}; use xmpp_parsers::iq::{Iq, IqType}; use crate::xmpp_codec::Packet; @@ -32,8 +33,13 @@ impl ClientBind { ClientBind::Unsupported(stream) } Some(_) => { - let resource = stream.jid.resource.clone(); - let iq = Iq::from_set(BIND_REQ_ID, Bind::new(resource)); + let resource; + if let Jid::Full(jid) = stream.jid.clone() { + resource = Some(jid.resource); + } else { + resource = None; + } + let iq = Iq::from_set(BIND_REQ_ID, BindQuery::new(resource)); let send = stream.send_stanza(iq); ClientBind::WaitSend(send) } @@ -68,11 +74,8 @@ impl Future for ClientBind { match iq.payload { IqType::Result(payload) => { payload - .and_then(|payload| Bind::try_from(payload).ok()) - .map(|bind| match bind { - Bind::Jid(jid) => stream.jid = jid, - _ => {} - }); + .and_then(|payload| BindResponse::try_from(payload).ok()) + .map(|bind| stream.jid = bind.into()); Ok(Async::Ready(stream)) } _ => Err(ProtocolError::InvalidBindResponse)?, diff --git a/src/client/mod.rs b/src/client/mod.rs index 37085788976a041c07b8339b991ddea0c0f849c7..4f31dad037645c8066cf5a92e3b3e2cf45beb6d3 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -60,11 +60,11 @@ impl Client { } fn make_connect(jid: Jid, password: String) -> impl Future { - let username = jid.node.as_ref().unwrap().to_owned(); + let username = jid.clone().node().unwrap(); let jid1 = jid.clone(); let jid2 = jid.clone(); let password = password; - done(idna::domain_to_ascii(&jid.domain)) + done(idna::domain_to_ascii(&jid.domain())) .map_err(|_| Error::Idna) .and_then(|domain| { done(Connecter::from_lookup( diff --git a/src/starttls.rs b/src/starttls.rs index 41bc6001da06f74b49403108c45616d787a1ae7f..e23402792b96ac6f858c8ead10df9a154414d966 100644 --- a/src/starttls.rs +++ b/src/starttls.rs @@ -70,7 +70,7 @@ impl Future for StartTlsClient { let stream = xmpp_stream.stream.into_inner(); let connect = TlsConnector::from(NativeTlsConnector::builder().build().unwrap()) - .connect(&self.jid.domain, stream); + .connect(&self.jid.clone().domain(), stream); let new_state = StartTlsClientState::StartingTls(connect); retry = true; (new_state, Ok(Async::NotReady)) diff --git a/src/stream_start.rs b/src/stream_start.rs index bf2d39e8b8968dc49474288be94a8c900f51beed..f82c3901e0aaf8f4d58adf2fd75af38621800b95 100644 --- a/src/stream_start.rs +++ b/src/stream_start.rs @@ -26,7 +26,7 @@ enum StreamStartState { impl StreamStart { pub fn from_stream(stream: Framed, jid: Jid, ns: String) -> Self { let attrs = [ - ("to".to_owned(), jid.domain.clone()), + ("to".to_owned(), jid.clone().domain()), ("version".to_owned(), "1.0".to_owned()), ("xmlns".to_owned(), ns.clone()), ("xmlns:stream".to_owned(), NS_XMPP_STREAM.to_owned()), diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 654cc21ae71ea823a73fb6fa2244f5779de6a92e..588d947d5c09898da7e6bbba36f3e6ff618fc218 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -501,7 +501,7 @@ mod tests { text = text + "A"; } let stanza = Element::builder("message") - .append(Element::builder("body").append(&text).build()) + .append(Element::builder("body").append(text.as_ref()).build()) .build(); let framed = framed.send(Packet::Stanza(stanza)).wait().expect("send"); assert_eq!( From 0138a6295761cb69bf9cee3ee81ae963db371111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 8 Sep 2019 22:09:25 +0200 Subject: [PATCH 0977/1020] Add more helpers on Jid to convert to Bare/FullJid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/lib.rs | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 900eed5f89fdce90468b759ce9b3bc1fd88c62ce..700edaad39ccdd747ef85c63ab76a78800dcdede 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ //! //! For usage, check the documentation on the `Jid` struct. -use std::convert::Into; +use std::convert::{Into, TryFrom}; use std::error::Error as StdError; use std::fmt; use std::str::FromStr; @@ -121,6 +121,26 @@ impl Jid { } } +impl From for BareJid { + fn from(jid: Jid) -> BareJid { + match jid { + Jid::Full(full) => full.into(), + Jid::Bare(bare) => bare, + } + } +} + +impl TryFrom for FullJid { + type Error = JidParseError; + + fn try_from(jid: Jid) -> Result { + match jid { + Jid::Full(full) => Ok(full), + Jid::Bare(_) => Err(JidParseError::NoResource), + } + } +} + /// A struct representing a full Jabber ID. /// /// A full Jabber ID is composed of 3 components, of which one is optional: @@ -684,6 +704,29 @@ mod tests { ); } + #[test] + fn jid_to_full_bare() { + let full = FullJid::new("a", "b.c", "d"); + let bare = BareJid::new("a", "b.c"); + + assert_eq!( + FullJid::try_from(Jid::Full(full.clone())), + Ok(full.clone()), + ); + assert_eq!( + FullJid::try_from(Jid::Bare(bare.clone())), + Err(JidParseError::NoResource), + ); + assert_eq!( + BareJid::from(Jid::Full(full.clone())), + bare.clone(), + ); + assert_eq!( + BareJid::from(Jid::Bare(bare.clone())), + bare, + ); + } + #[test] fn serialise() { assert_eq!( From fd27582ac74544434d32949fd3aa261350b403e1 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 9 Sep 2019 14:57:08 +0200 Subject: [PATCH 0978/1020] examples: remove a few .clone() calls --- examples/download_avatars.rs | 23 ++++++++++++----------- examples/echo_bot.rs | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/examples/download_avatars.rs b/examples/download_avatars.rs index 837dbec4dbe3415b60c22d26d4669e167496697d..ec27b6e5a9ad97285735a4409179aea22d50d2a6 100644 --- a/examples/download_avatars.rs +++ b/examples/download_avatars.rs @@ -117,7 +117,7 @@ fn main() { let from = message.from.clone().unwrap(); if let Some(body) = message.get_best_body(vec!["en"]) { if body.1 .0 == "die" { - println!("Secret die command triggered by {}", from.clone()); + println!("Secret die command triggered by {}", from); wait_for_stream_end = true; tx.start_send(Packet::StreamEnd).unwrap(); } @@ -127,7 +127,7 @@ fn main() { let event = PubSubEvent::try_from(child).unwrap(); if let PubSubEvent::PublishedItems { node, items } = event { if node.0 == ns::AVATAR_METADATA { - for item in items { + for item in items.into_iter() { let payload = item.payload.clone().unwrap(); if payload.is("metadata", ns::AVATAR_METADATA) { // TODO: do something with these metadata. @@ -136,7 +136,7 @@ fn main() { "{} has published an avatar, downloading...", from.clone() ); - let iq = download_avatar(&from); + let iq = download_avatar(from.clone()); tx.start_send(Packet::Stanza(iq.into())).unwrap(); } } @@ -193,25 +193,26 @@ fn make_presence(caps: Caps) -> Presence { presence } -fn download_avatar(from: &Jid) -> Iq { +fn download_avatar(from: Jid) -> Iq { Iq::from_get("coucou", PubSub::Items(Items { max_items: None, node: NodeName(String::from(ns::AVATAR_DATA)), subid: None, items: Vec::new(), })) - .with_to(from.clone()) + .with_to(from) } fn handle_iq_result(pubsub: PubSub, from: &Jid) { if let PubSub::Items(items) = pubsub { if items.node.0 == ns::AVATAR_DATA { for item in items.items { - if let Some(id) = item.id.clone() { - if let Some(payload) = &item.payload { - let data = AvatarData::try_from(payload.clone()).unwrap(); + match (item.id.clone(), item.payload.clone()) { + (Some(id), Some(payload)) => { + let data = AvatarData::try_from(payload).unwrap(); save_avatar(from, id.0, &data.data).unwrap(); } + _ => {} } } } @@ -219,11 +220,11 @@ fn handle_iq_result(pubsub: PubSub, from: &Jid) { } fn save_avatar(from: &Jid, id: String, data: &[u8]) -> io::Result<()> { - let directory = format!("data/{}", from.clone()); - let filename = format!("data/{}/{}", from.clone(), id); + let directory = format!("data/{}", from); + let filename = format!("data/{}/{}", from, id); println!( "Saving avatar from {} to {}.", - from.clone(), filename + from, filename ); create_dir_all(directory)?; let mut file = File::create(filename)?; diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 6de2ce72d9132b2347d28ddbe56d985d768b0deb..0d08dc966ee56a4be4a8f01e45efc17afedad06c 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -57,7 +57,7 @@ fn main() { { match (message.from, message.bodies.get("")) { (Some(ref from), Some(ref body)) if body.0 == "die" => { - println!("Secret die command triggered by {}", from.clone()); + println!("Secret die command triggered by {}", from); wait_for_stream_end = true; tx.start_send(Packet::StreamEnd).unwrap(); } From a572bf4dea0e61012aa013318f0e560819d066fa Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 9 Sep 2019 15:29:50 +0200 Subject: [PATCH 0979/1020] tokio-xmpp 1.0.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c6b5c26ed4cfbb038a92b2e01bb616ba39bb1f1f..b2cb5183bad08ac2bf40424976235d0c53407fbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-xmpp" -version = "1.0.0" +version = "1.0.1" authors = ["Astro ", "Emmanuel Gil Peyrot ", "pep ", "O01eg "] description = "Asynchronous XMPP for Rust with tokio" license = "MPL-2.0" From 59e0e75d695e11ffc6985c79302037d015efba57 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 13 Sep 2019 00:42:18 +0200 Subject: [PATCH 0980/1020] Prepare for 0.7.2 release. --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d15bd671bd2d8cbd9a13120d5f3660b844a76f83..c1a83e53e7937cb77ae6d7a7a149f75b4a157c1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +Version 0.7.2, released 2019-09-13: + * Updates + - Impl Error for JidParseError again, it got removed due to the failure removal but is still wanted. + Version 0.7.1, released 2019-09-06: * Updates - Remove failure dependency, to keep compilation times in check diff --git a/Cargo.toml b/Cargo.toml index f2f0ad10df4861275431793a8009e1c5817ade3b..fad2217dba7600edfb561c6c9ebcb45d3cc2d3ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.7.1" +version = "0.7.2" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From 5aee776f667af655d05076273cb21bb8d3214129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 13 Sep 2019 03:07:02 +0200 Subject: [PATCH 0981/1020] Update to edition 2018 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- CHANGELOG.md | 3 +++ Cargo.toml | 1 + src/element.rs | 11 +++++------ src/node.rs | 7 +++---- src/tests.rs | 10 ++++------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72468faa528a78d3b5b33411a34ef48cf50e838a..f9d098f876e6b9b3c1cfd58658fe83135d370a77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +Version XXX, released YYY: + * Changes + * Update edition to 2018 Version 0.11.1, released 2019-09-06: * Changes * Update to quick-xml 0.16 diff --git a/Cargo.toml b/Cargo.toml index 204d729e3fd245b169dbd19a1650a0b8713d34b9..292b2e01c2eb7f6177bba6114ebc4c5237477cf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ documentation = "https://docs.rs/minidom" readme = "README.md" keywords = ["xml"] license = "MIT" +edition = "2018" [badges] gitlab = { repository = "lumi/minidom-rs" } diff --git a/src/element.rs b/src/element.rs index 68d445e27641b4ae73a28f3b3725ad7772eff03e..08ccc709ecad30354909a673d5eefd9321c349a8 100644 --- a/src/element.rs +++ b/src/element.rs @@ -1,5 +1,10 @@ //! Provides an `Element` type, which represents DOM nodes, and a builder to create them with. +use crate::convert::IntoAttributeValue; +use crate::error::{Error, Result}; +use crate::namespace_set::NamespaceSet; +use crate::node::Node; + use std::io:: Write; use std::collections::{btree_map, BTreeMap}; @@ -7,8 +12,6 @@ use std::str; use std::rc::Rc; use std::borrow::Cow; -use error::{Error, Result}; - use quick_xml::Reader as EventReader; use quick_xml::Writer as EventWriter; use quick_xml::events::{Event, BytesStart, BytesEnd, BytesDecl}; @@ -19,10 +22,6 @@ use std::str::FromStr; use std::slice; -use convert::IntoAttributeValue; -use namespace_set::NamespaceSet; -use node::Node; - /// helper function to escape a `&[u8]` and replace all /// xml special characters (<, >, &, ', ") with their corresponding /// xml escaped value. diff --git a/src/node.rs b/src/node.rs index 56069d09f80f7a0960111a91914371389a00f717..e0d9a9ebbcf5dd3c2d68a879664491b042c07568 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,14 +1,13 @@ //! Provides the `Node` struct, which represents a node in the DOM. +use crate::element::{Element, ElementBuilder}; +use crate::error::Result; + use std::io::Write; use quick_xml::Writer as EventWriter; use quick_xml::events::{Event, BytesText}; -use error::Result; - -use element::{Element, ElementBuilder}; - /// A node in an element tree. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Node { diff --git a/src/tests.rs b/src/tests.rs index 1ff37e6ccfb816ea41f89d4f67cfd08da8b641ca..046d8e5c817ac79febb208709c4f96b7671f8297 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,9 +1,7 @@ -use std::iter::Iterator; +use crate::element::Element; use quick_xml::Reader; -use element::Element; - const TEST_STRING: &'static str = r#"meownya"#; fn build_test_tree() -> Element { @@ -234,12 +232,12 @@ fn write_comments() { #[test] fn xml_error() { match "".parse::() { - Err(::error::Error::XmlError(_)) => (), + Err(crate::error::Error::XmlError(_)) => (), err => panic!("No or wrong error: {:?}", err) } match "() { - Err(::error::Error::XmlError(_)) => (), + Err(crate::error::Error::XmlError(_)) => (), err => panic!("No or wrong error: {:?}", err) } } @@ -247,7 +245,7 @@ fn xml_error() { #[test] fn invalid_element_error() { match "".parse::() { - Err(::error::Error::InvalidElement) => (), + Err(crate::error::Error::InvalidElement) => (), err => panic!("No or wrong error: {:?}", err) } } From 7a0ba4f39d247dff4e2a496a68d910d2f957f9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 13 Sep 2019 03:10:06 +0200 Subject: [PATCH 0982/1020] CI: Import .gitlab-ci.yml from xmpp-rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml | 56 +++++++++++++++++++++++++++++++++++++++++++------- CHANGELOG.md | 3 ++- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c02e04ce55fee669db09a49bfa08f4aea2d5cfbc..bc899d9fd2c40d66af66f5c346e972f69850a6d8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,10 +1,52 @@ -image: "pitkley/rust:nightly" +stages: + - build + - test -before_script: -- apt-get update -yqq -- apt-get install -yqq --no-install-recommends build-essential +variables: + FEATURES: "" + RUST_BACKTRACE: "full" -test:cargo: +.stable: + image: rust:latest + cache: + key: stable + paths: + - target/ + +.nightly: + image: rustlang/rust:nightly + cache: + key: nightly + paths: + - target/ + +.build: + stage: build script: - - rustc --version && cargo --version - - cargo test --verbose --jobs 1 --release + - cargo build --verbose --no-default-features --features=$FEATURES + +.test: + stage: test + script: + - cargo test --verbose --no-default-features --features=$FEATURES + +rust-latest-build: + extends: + - .build + - .stable + +rust-nightly-build: + extends: + - .build + - .nightly + + +rust-latest-test: + extends: + - .test + - .stable + +rust-nightly-test: + extends: + - .test + - .nightly diff --git a/CHANGELOG.md b/CHANGELOG.md index f9d098f876e6b9b3c1cfd58658fe83135d370a77..275c0e69884a0b3638462e6353d571fe7390aad0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,12 @@ Version XXX, released YYY: * Changes * Update edition to 2018 + * Fixes + * Update old CI configuration with newer Rust images Version 0.11.1, released 2019-09-06: * Changes * Update to quick-xml 0.16 * Add a default "comments" feature to transform comments into errors when unset. - * Remove the mostly-unused failure dependency, to dramatically reduce compilation times. Version 0.11.0, released 2019-06-14: * Breaking * Get rid of IntoElements, replace with `Into` and ` IntoIterator>` From 6ce9496fc301376b89aa244f96d8514cb40b9d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 13 Sep 2019 03:14:00 +0200 Subject: [PATCH 0983/1020] CI: Add jobs for 'comments' feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bc899d9fd2c40d66af66f5c346e972f69850a6d8..efd57f1e923b2926fbf8cf39bbd5ef0727d7a1e9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,3 +50,13 @@ rust-nightly-test: extends: - .test - .nightly + +rust-latest-build with features=comments: + extends: rust-latest-build + variables: + FEATURES: "comments" + +rust-latest-test with features=comments: + extends: rust-latest-test + variables: + FEATURES: "comments" From d5f6c181af91e05027c24a3e2a8d1466cb044ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 13 Sep 2019 03:26:06 +0200 Subject: [PATCH 0984/1020] CI: Refactor: split jobs, add tests, and caching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml | 64 +++++++++++++++++++++++++++++++++++++++++++------- CHANGELOG.md | 4 ++++ 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5a1213e308553ae38cf2d4ca5f295d4699792d5e..6996a5520f6bae6c976cf77023259e37e247f9c8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,16 +1,62 @@ stages: - build + - test -rust-latest: - stage: build +variables: + FEATURES: "" + RUST_BACKTRACE: "full" + +.stable: image: rust:latest - script: - - cargo build --verbose --features=minidom - - cargo test --lib --verbose --features=minidom + cache: + key: stable + paths: + - target/ -rust-nightly: - stage: build +.nightly: image: rustlang/rust:nightly + cache: + key: nightly + paths: + - target/ + +.build: + stage: build + script: + - cargo build --verbose --no-default-features --features=$FEATURES + +.test: + stage: test script: - - cargo build --verbose --features=minidom - - cargo test --lib --verbose --features=minidom + - cargo test --lib --verbose --no-default-features --features=$FEATURES + +rust-latest-build: + extends: + - .build + - .stable + +rust-nightly-build: + extends: + - .build + - .nightly + + +rust-latest-test: + extends: + - .test + - .stable + +rust-nightly-test: + extends: + - .test + - .nightly + +rust-latest-build with features=minidom: + extends: rust-latest-build + variables: + FEATURES: "minidom" + +rust-latest-test with features=minidom: + extends: rust-latest-test + variables: + FEATURES: "minidom" diff --git a/CHANGELOG.md b/CHANGELOG.md index c1a83e53e7937cb77ae6d7a7a149f75b4a157c1d..70108afddf06ed8d5d265b9b95aaeb6e2c235f94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +Version XXX, released YYY: + * Updates + - CI: Split jobs, add tests, and caching + Version 0.7.2, released 2019-09-13: * Updates - Impl Error for JidParseError again, it got removed due to the failure removal but is still wanted. From 8b54b7fd43083babdf188e731d6f6c579a3645f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 13 Sep 2019 03:33:08 +0200 Subject: [PATCH 0985/1020] CI: refactor, add caching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml | 54 +++++++++++++++++++++++++++++++++++--------------- ChangeLog | 2 ++ 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9f74e6dca4c9dc3dca4175f28c3d106d2e7cfc1a..cd0be07974f035ea057fe9f435605d282972c53a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,37 +4,59 @@ stages: variables: FEATURES: "" + RUST_BACKTRACE: "full" -rust-latest-build: - stage: build +.stable: image: rust:latest - script: - - cargo build --verbose --no-default-features --features=$FEATURES + cache: + key: stable + paths: + - target/ -rust-nightly-build: - stage: build +.nightly: image: rustlang/rust:nightly + cache: + key: nightly + paths: + - target/ + +.build: + stage: build script: - cargo build --verbose --no-default-features --features=$FEATURES -rust-latest-test: +.test: stage: test - image: rust:latest script: - cargo test --verbose --no-default-features --features=$FEATURES +rust-latest-build: + extends: + - .build + - .stable + +rust-nightly-build: + extends: + - .build + - .nightly + + +rust-latest-test: + extends: + - .test + - .stable + rust-nightly-test: - stage: test - image: rustlang/rust:nightly - script: - - cargo test --verbose --no-default-features --features=$FEATURES + extends: + - .test + - .nightly -"rust-latest-test with features=disable-validation": - extends: rust-latest-test +rust-latest-build with features=disable-validation: + extends: rust-latest-build variables: FEATURES: "disable-validation" -"rust-nightly-test with features=disable-validation": - extends: rust-nightly-test +rust-latest-test with features=disable-validation: + extends: rust-latest-test variables: FEATURES: "disable-validation" diff --git a/ChangeLog b/ChangeLog index 43367a608978f220b79ddfb5d19b4a00e973522e..381e8933ae03738206b530c64882c47642aeb9db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,8 @@ DATE Emmanuel Gil Peyrot - Anonymous unique occupant identifiers for MUCs (XEP-0421) * Breaking changes: - Presence constructors now take Into and assume Some. + * Improvements: + - CI: refactor, add caching Version 0.15.0: 2019-09-06 Emmanuel Gil Peyrot From eac385700c336d3e9190db5a5344afb679c1f87e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 17 Sep 2019 17:13:18 +0200 Subject: [PATCH 0986/1020] bob, jid_prep, cert_management: Add size tests for 32-bit. --- src/bob.rs | 4 ++-- src/cert_management.rs | 23 +++++++++++++++++++++++ src/jid_prep.rs | 8 ++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/bob.rs b/src/bob.rs index 704ed0c94ef7c692f1cea8478f353306c9affdd8..edb0c89103128ef94f5045c05c9ade37f78f9d74 100644 --- a/src/bob.rs +++ b/src/bob.rs @@ -95,8 +95,8 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(ContentId, 24); - assert_size!(Data, 24); + assert_size!(ContentId, 28); + assert_size!(Data, 60); } #[cfg(target_pointer_width = "64")] diff --git a/src/cert_management.rs b/src/cert_management.rs index a68909eb8b06d9c2f070b556a4737659479edd51..7ed5ca7a4f461bf90cdc9814a2667006cd428cd6 100644 --- a/src/cert_management.rs +++ b/src/cert_management.rs @@ -118,9 +118,32 @@ mod tests { use std::str::FromStr; use crate::ns; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Append, 28); + assert_size!(Disable, 12); + assert_size!(Revoke, 12); + assert_size!(ListCertsQuery, 0); + assert_size!(ListCertsResponse, 12); + assert_size!(Item, 40); + assert_size!(Resource, 12); + assert_size!(Users, 12); + assert_size!(Cert, 12); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Append, 56); + assert_size!(Disable, 24); + assert_size!(Revoke, 24); + assert_size!(ListCertsQuery, 0); + assert_size!(ListCertsResponse, 24); + assert_size!(Item, 80); + assert_size!(Resource, 24); + assert_size!(Users, 24); + assert_size!(Cert, 24); } #[test] diff --git a/src/jid_prep.rs b/src/jid_prep.rs index d151f6c04bec1d5af2e6106c3c812e8239536644..ba8c9d84e18a28a3ade067108b53765af97bc84a 100644 --- a/src/jid_prep.rs +++ b/src/jid_prep.rs @@ -46,6 +46,14 @@ mod tests { use std::convert::TryFrom; use std::str::FromStr; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(JidPrepQuery, 12); + assert_size!(JidPrepResponse, 40); + } + + #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(JidPrepQuery, 24); From 89d24de30234bffd175e6768f780f64b9a240a48 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 20 Sep 2019 01:41:30 +0200 Subject: [PATCH 0987/1020] receipts: Update to XEP-0184 version 1.4.0. --- doap.xml | 2 +- src/avatar.rs | 1 + src/receipts.rs | 19 ++++++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/doap.xml b/doap.xml index f591bfff5e86ea6bc965e187cf6967b64a0e7e96..3320bfa7c02bb7134498891960886ebf2fb2afc8 100644 --- a/doap.xml +++ b/doap.xml @@ -235,7 +235,7 @@ complete - 1.3.0 + 1.4.0 0.1.0 diff --git a/src/avatar.rs b/src/avatar.rs index e5c09d2a14957737c50a266ced9db2d1f25be35f..0940c33a41ce68a2d13c9e3b63a84ef9113b47d9 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -58,6 +58,7 @@ impl PubSubPayload for Data {} mod tests { use super::*; use crate::hashes::Algo; + #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; use minidom::Element; use std::convert::TryFrom; diff --git a/src/receipts.rs b/src/receipts.rs index 3b54d3cddb1663a8c4084f15493c29ae12667393..af40f641e605088bcefd14fbb938f7c5eba8ddd4 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -22,7 +22,7 @@ generate_element!( Received, "received", RECEIPTS, attributes: [ /// The 'id' attribute of the received message. - id: Option = "id", + id: Required = "id", ] ); @@ -34,6 +34,7 @@ mod tests { use crate::ns; use minidom::Element; use std::convert::TryFrom; + use crate::util::error::Error; #[cfg(target_pointer_width = "32")] #[test] @@ -54,15 +55,23 @@ mod tests { let elem: Element = "".parse().unwrap(); Request::try_from(elem).unwrap(); - let elem: Element = "".parse().unwrap(); - Received::try_from(elem).unwrap(); - let elem: Element = "" .parse() .unwrap(); Received::try_from(elem).unwrap(); } + #[test] + fn test_missing_id() { + let elem: Element = "".parse().unwrap(); + let error = Received::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'id' missing."); + } + #[test] fn test_serialise() { let receipt = Request; @@ -71,7 +80,7 @@ mod tests { assert_eq!(elem.attrs().count(), 0); let receipt = Received { - id: Some(String::from("coucou")), + id: String::from("coucou"), }; let elem: Element = receipt.into(); assert!(elem.is("received", ns::RECEIPTS)); From 552fef90d31bfb4c66fa8f2b17aac623926f00f1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 20 Sep 2019 01:46:44 +0200 Subject: [PATCH 0988/1020] DOAP: Mention that XEP-0068 is supported There is no module for this XEP, but it is only a best practice document about XEP-0004 so there is no need for one. --- doap.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doap.xml b/doap.xml index 3320bfa7c02bb7134498891960886ebf2fb2afc8..e75603dc198d6c2de3d54551d71bf184f28e7f77 100644 --- a/doap.xml +++ b/doap.xml @@ -111,6 +111,15 @@ 0.5.0 + + + + complete + 1.2 + 0.1.0 + there is no specific module for this, the feature is all in the XEP-0004 module + + From d1d98ff3d58109f8ade63abd226994e30c5ef736 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 25 Sep 2019 10:28:44 +0200 Subject: [PATCH 0989/1020] Use crate::Element instead of minidom::Element. This makes refactoring much easier. --- src/attention.rs | 2 +- src/avatar.rs | 2 +- src/bind.rs | 2 +- src/blocking.rs | 2 +- src/bob.rs | 2 +- src/bookmarks.rs | 2 +- src/caps.rs | 2 +- src/carbons.rs | 2 +- src/cert_management.rs | 2 +- src/chatstates.rs | 2 +- src/component.rs | 2 +- src/csi.rs | 2 +- src/data_forms.rs | 2 +- src/delay.rs | 2 +- src/disco.rs | 2 +- src/ecaps2.rs | 2 +- src/eme.rs | 2 +- src/forwarding.rs | 2 +- src/hashes.rs | 2 +- src/ibb.rs | 2 +- src/ibr.rs | 2 +- src/idle.rs | 2 +- src/iq.rs | 2 +- src/jid_prep.rs | 2 +- src/jingle.rs | 2 +- src/jingle_dtls_srtp.rs | 2 +- src/jingle_ibb.rs | 2 +- src/jingle_ice_udp.rs | 2 +- src/jingle_message.rs | 2 +- src/jingle_rtp.rs | 2 +- src/jingle_s5b.rs | 2 +- src/media_element.rs | 2 +- src/message.rs | 2 +- src/message_correct.rs | 2 +- src/mood.rs | 2 +- src/muc/muc.rs | 2 +- src/muc/user.rs | 2 +- src/nick.rs | 2 +- src/occupant_id.rs | 2 +- src/ping.rs | 2 +- src/pubsub/event.rs | 2 +- src/pubsub/pubsub.rs | 2 +- src/receipts.rs | 2 +- src/roster.rs | 2 +- src/rsm.rs | 2 +- src/sasl.rs | 4 +-- src/sm.rs | 2 +- src/stanza_error.rs | 2 +- src/stanza_id.rs | 2 +- src/stream.rs | 2 +- src/time.rs | 2 +- src/tune.rs | 2 +- src/util/compare_elements.rs | 2 +- src/util/macros.rs | 62 ++++++++++++++++++------------------ src/version.rs | 2 +- src/websocket.rs | 2 +- 56 files changed, 87 insertions(+), 87 deletions(-) diff --git a/src/attention.rs b/src/attention.rs index a2af3a9cd79a9be54cef033e5b7468da397c1ccc..83a871ccfba3a0dc3c31e4e71839b8e157512dde 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -20,7 +20,7 @@ mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[test] diff --git a/src/avatar.rs b/src/avatar.rs index 0940c33a41ce68a2d13c9e3b63a84ef9113b47d9..c475db82ac767caee63f9300a30717d3dfecef66 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -60,7 +60,7 @@ mod tests { use crate::hashes::Algo; #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/bind.rs b/src/bind.rs index 16fb487bc7ef00a931d3bcd16a736589c19ec50c..4da3564156abb8abd6025e0fa49fa9442508d11f 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; use jid::{FullJid, Jid}; -use minidom::Element; +use crate::Element; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/blocking.rs b/src/blocking.rs index 741d43405412864166729d53f17e0d210ba5cfe6..1ddfa5c9a6d7cddc6212577292308983aefbd009 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use jid::Jid; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; generate_empty_element!( diff --git a/src/bob.rs b/src/bob.rs index edb0c89103128ef94f5045c05c9ade37f78f9d74..c78f6c34ed274ecf8c06fe498aa14385cb862b92 100644 --- a/src/bob.rs +++ b/src/bob.rs @@ -88,7 +88,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use std::error::Error as StdError; diff --git a/src/bookmarks.rs b/src/bookmarks.rs index c00195a526bd6b8568d94fe241e5660916a8d854..900a3a911656feccdd6bce3c556ac9a892eea570 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -71,7 +71,7 @@ impl Storage { mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/caps.rs b/src/caps.rs index 32f8f5735edd94fad2c3cb6e217bce745f264c50..96ffa22adefe47fcf24891c7df2aec091ab14cba 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -12,7 +12,7 @@ use crate::ns; use crate::presence::PresencePayload; use blake2::VarBlake2b; use digest::{Digest, Input, VariableOutput}; -use minidom::Element; +use crate::Element; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; diff --git a/src/carbons.rs b/src/carbons.rs index b40c1e2e7563c8b1f02e4c3311079c7567cb619e..43047c2b27f511fa8b05e55e69714deeabb868f6 100644 --- a/src/carbons.rs +++ b/src/carbons.rs @@ -63,7 +63,7 @@ impl MessagePayload for Sent {} #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/cert_management.rs b/src/cert_management.rs index 7ed5ca7a4f461bf90cdc9814a2667006cd428cd6..817a01bb284fcdb81cff9a2e36a6e8ec333aa79d 100644 --- a/src/cert_management.rs +++ b/src/cert_management.rs @@ -113,7 +113,7 @@ impl IqSetPayload for Revoke {} #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use std::str::FromStr; use crate::ns; diff --git a/src/chatstates.rs b/src/chatstates.rs index d7ae1e37d8a6dbe94bdf41b76ae6351a4da26b1b..4d8f9a4dbb7edcf17cb0919a6ee785407eb00359 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -34,7 +34,7 @@ mod tests { use super::*; use crate::util::error::Error; use crate::ns; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[test] diff --git a/src/component.rs b/src/component.rs index a0588177fecc45c3c133b319ea4d2fb8238bdcef..b8120ffde74e18f3ae3120489090525d6adfe6f4 100644 --- a/src/component.rs +++ b/src/component.rs @@ -43,7 +43,7 @@ impl Handshake { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/csi.rs b/src/csi.rs index 0465f94b2014f3d98e050a069052414e3d8647ff..f3a9130d4afca79f24f772517dbbead2de9ef501 100644 --- a/src/csi.rs +++ b/src/csi.rs @@ -22,7 +22,7 @@ generate_empty_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use crate::ns; diff --git a/src/data_forms.rs b/src/data_forms.rs index 80c9131b32da9798eba8ce64d57505ab9500b5db..37f94dfa541e068694b8830b45eeddab99ddaff0 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::media_element::MediaElement; use crate::ns; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; generate_element!( diff --git a/src/delay.rs b/src/delay.rs index e987a4f0e013352c8221ce586f4c67fc49d74df9..e7c7449a73d3e4fafc3832439fbaf520770828ca 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -33,7 +33,7 @@ impl PresencePayload for Delay {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/disco.rs b/src/disco.rs index 857afaf8c758de96eac503d50249a572475375f5..040ce8c42e9b4d94f242d09092240b3c80f9ca67 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use jid::Jid; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; generate_element!( diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 8722d065050bca5c65d33291c20db39da6c4c962..cfcb340f34a1891465f015736174104d6c51beb8 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -174,7 +174,7 @@ pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery { mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/eme.rs b/src/eme.rs index 623a0cd1eb1378c6b24a9fb55e15eb72ee633167..1014318e36eceb79dd3ef0313969aa7d3102596a 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -25,7 +25,7 @@ impl MessagePayload for ExplicitMessageEncryption {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/forwarding.rs b/src/forwarding.rs index 5286323cfe1bcb21228b886c230acd69f8ca7436..6517d22f9f280d27fb4d339d99312a11efca36df 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -27,7 +27,7 @@ generate_element!( mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/hashes.rs b/src/hashes.rs index 9987407a694fb736b4f783606b43e4a1fe6d2586..ae7b61bc2585efc6199ed1b5dc48f8c2c936a498 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -191,7 +191,7 @@ impl Deref for Sha1HexAttribute { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/ibb.rs b/src/ibb.rs index 8a81b651826903733a271e3aab7c9953302ace4d..719fd8537e66390ba8722dc8c9edaafecad4c6ec 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -73,7 +73,7 @@ impl IqSetPayload for Close {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::error::Error as StdError; use std::convert::TryFrom; diff --git a/src/ibr.rs b/src/ibr.rs index 8ced963f259e0c27375efbd4875b7e38d23b354a..b1b15286653395d7b63bb619d342f91ecd3a2bd8 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -8,7 +8,7 @@ use crate::data_forms::DataForm; use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use minidom::Element; +use crate::Element; use std::collections::HashMap; use std::convert::TryFrom; diff --git a/src/idle.rs b/src/idle.rs index cba3698b4db829c20cc6afd7232cd7cef1d7e07b..47d1c18335c7627c7f20449b21eba42cd491927d 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -22,7 +22,7 @@ impl PresencePayload for Idle {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::error::Error as StdError; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/iq.rs b/src/iq.rs index f3aa7f5d17a8f2f477b4b8ed79bdbdc53d6d876b..644926163157e0e0dedea535fe9adbb5c7db5023 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::ns; use crate::stanza_error::StanzaError; use jid::Jid; -use minidom::Element; +use crate::Element; use minidom::IntoAttributeValue; use std::convert::TryFrom; diff --git a/src/jid_prep.rs b/src/jid_prep.rs index ba8c9d84e18a28a3ade067108b53765af97bc84a..5fbc0a0c0f3f7ab29bf42f978cd329ab248af273 100644 --- a/src/jid_prep.rs +++ b/src/jid_prep.rs @@ -42,7 +42,7 @@ impl IqResultPayload for JidPrepResponse {} #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use std::str::FromStr; diff --git a/src/jingle.rs b/src/jingle.rs index 7e27ebbc6efe2ff73d628fdbcc4aedb5819db276..4c568bfc55e683fb449cf6a65146829641b882d2 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; -use minidom::Element; +use crate::Element; use std::collections::BTreeMap; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/jingle_dtls_srtp.rs b/src/jingle_dtls_srtp.rs index e65c368170a229208e130dc0a9f4d8b4fb63449c..150388f6eac7f3128f079f376964c725b5d42911 100644 --- a/src/jingle_dtls_srtp.rs +++ b/src/jingle_dtls_srtp.rs @@ -49,7 +49,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index d1fe61f9d295271c6582a3285f3e178f32cccb70..f0fb90f0f8a12b43758d046b8f1cf7933fa8e99d 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -25,7 +25,7 @@ attributes: [ mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::error::Error as StdError; use std::convert::TryFrom; diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 323014380d89a17a26d30390ae96e337e58d075c..51a725ee705ed6b0598e7b6a862a4973bb669950 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -91,7 +91,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use crate::hashes::Algo; use crate::jingle_dtls_srtp::Setup; diff --git a/src/jingle_message.rs b/src/jingle_message.rs index bf0478cf4801aed8018eae1a115a7069ac3626a1..78bf1db22ca38112eae8b3a363ce9563e8515b9d 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::jingle::SessionId; use crate::ns; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; /// Defines a protocol for broadcasting Jingle requests to all of the clients diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 108010d00404ae87fdc3ccbf4086c146ed6660da..5e686ca965eab32e60edaa0391896cc4b515e88a 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -75,7 +75,7 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 17c5d996fb3bb7070cf8f3bf5d442d310883deb4..b6df19ed6d835abb8f7b5e4779366b9ea715d2c9 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::ns; use jid::Jid; -use minidom::Element; +use crate::Element; use std::net::IpAddr; use std::convert::TryFrom; diff --git a/src/media_element.rs b/src/media_element.rs index f632e48c13428c4e722e9871a62019f07d3f2b9b..099dadae5ab37387540ca22170afbec6d5f7eeaa 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -47,7 +47,7 @@ mod tests { use super::*; use crate::data_forms::DataForm; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::error::Error as StdError; use std::convert::TryFrom; diff --git a/src/message.rs b/src/message.rs index 4a78c6c0b7794e1cda6738af6739718031acd9e0..2c7eb29946abca98cfa5926ab459a109c78e9a78 100644 --- a/src/message.rs +++ b/src/message.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::ns; use jid::Jid; -use minidom::Element; +use crate::Element; use std::collections::BTreeMap; use std::convert::TryFrom; diff --git a/src/message_correct.rs b/src/message_correct.rs index 1055af9bd261bc0e3a9cb0b72c0a5996aab92fed..374900f41304c35e879e6d5980a9aa767d20fedf 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -22,7 +22,7 @@ impl MessagePayload for Replace {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/mood.rs b/src/mood.rs index f4b529c592ba6e7719e261138c03a735c1d2ff04..4270c5889642bbce1c2f2b631175615a38e14586 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -271,7 +271,7 @@ generate_elem_id!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/muc/muc.rs b/src/muc/muc.rs index a4d13f8a90a082740069319f6d1e92a041253c52..01688ec3623c4a073a8482fcd7592e520695c3fd 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -96,7 +96,7 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/muc/user.rs b/src/muc/user.rs index d785d2356cd208adc2a4a22d0a363fb1a66f5128..31157f7681d7b367f98336d24ec11344cb27e93d 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::ns; use jid::FullJid; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; generate_attribute_enum!( diff --git a/src/nick.rs b/src/nick.rs index 07f07f770125550a41cbd5e53674ff842f8586c1..20ae7a94c9cf03eb34751f4b87c2647af521fe2e 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -16,7 +16,7 @@ mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/occupant_id.rs b/src/occupant_id.rs index 730652942d9073973f3e7d149796e5155a6b5a7d..418a878bd5729fb44d0d8b266cb32a8083aee383 100644 --- a/src/occupant_id.rs +++ b/src/occupant_id.rs @@ -27,7 +27,7 @@ impl PresencePayload for OccupantId {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/ping.rs b/src/ping.rs index be8ac48c2be8f2ef4625bc683fd89428135dda7e..70663fa3a79d483da66f040b3b2419c4245272f6 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -22,7 +22,7 @@ mod tests { use super::*; #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[test] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 30c118de688830d9b8924de15c7a97a42c7274b0..2228a067dd1ea49af5b73598414e3d3d72a5f5f3 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -10,7 +10,7 @@ use crate::util::error::Error; use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; /// Event wrapper for a PubSub ``. diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 875c1989538364ba805e8fac39ffcb8fe525f13f..8faf9c433c2dcece35c7af4c80a5f02a63b8eb98 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -10,7 +10,7 @@ use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use crate::pubsub::{NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; // TODO: a better solution would be to split this into a query and a result elements, like for diff --git a/src/receipts.rs b/src/receipts.rs index af40f641e605088bcefd14fbb938f7c5eba8ddd4..e837a17fdcedf984266206c513561f5433dc4847 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -32,7 +32,7 @@ impl MessagePayload for Received {} mod tests { use super::*; use crate::ns; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; use crate::util::error::Error; diff --git a/src/roster.rs b/src/roster.rs index 6c871ba651d84cfaf9f22668143c68ed732e3219..730da52df65f0b2f3d2e0a0ae9f9c607e3f1caa0 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -94,7 +94,7 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/rsm.rs b/src/rsm.rs index 8299c9b67132b27d50e43d6df1222d5cc20facf3..169681d1e911f47aed85802c0cae31aaf92e12b0 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -6,7 +6,7 @@ use crate::util::error::Error; use crate::ns; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; /// Requests paging through a potentially big set of items (represented by an diff --git a/src/sasl.rs b/src/sasl.rs index e8f3aa6e041727102c112b3ea28ddd9f0130837b..e3f0900576adf0f29af36d679f68e398a16283bd 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::util::helpers::Base64; use crate::ns; -use minidom::Element; +use crate::Element; use std::collections::BTreeMap; use std::convert::TryFrom; @@ -221,7 +221,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/sm.rs b/src/sm.rs index b483514023031ca34bdf02fe07ddf1c75daacdfb..a78320f22c54b2a33d64f1be1646a67cd5e580d6 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -145,7 +145,7 @@ generate_empty_element!( #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 1f4dbbe16de934e405af539040c7231763341bb5..0bdc2887f1c36a55dc62a7dba7927ee64cf7cdb7 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -9,7 +9,7 @@ use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; use jid::Jid; -use minidom::Element; +use crate::Element; use std::collections::BTreeMap; use std::convert::TryFrom; diff --git a/src/stanza_id.rs b/src/stanza_id.rs index bbbfb48f2c5bfbfef5f0f4baba3946c3b1d522e0..1ba48f85da115c69d93a790b021b0eee6fd1e418 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -38,7 +38,7 @@ impl MessagePayload for OriginId {} mod tests { use super::*; use crate::util::error::Error; - use minidom::Element; + use crate::Element; use std::str::FromStr; use std::convert::TryFrom; diff --git a/src/stream.rs b/src/stream.rs index e6f5be009b0dfce49565f359c4b193f4dd43c589..c1b5e1ea6276aedb5523e79e48f345ce5c284ef7 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -73,7 +73,7 @@ impl Stream { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/time.rs b/src/time.rs index 601b3a4cd0032a323c90664f9c554328f24e8746..363cd210890eb6de2f0427ad02e4058c17d56fe5 100644 --- a/src/time.rs +++ b/src/time.rs @@ -9,7 +9,7 @@ use crate::date::DateTime; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use crate::util::error::Error; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; use std::str::FromStr; diff --git a/src/tune.rs b/src/tune.rs index df9b6679865114fffb7c652eb93d28666c3f78b2..ed35357d8bef5e9b4e7fdd7854e6de13bd516e1a 100644 --- a/src/tune.rs +++ b/src/tune.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::pubsub::PubSubPayload; use crate::ns; -use minidom::Element; +use crate::Element; use std::convert::TryFrom; generate_elem_id!( diff --git a/src/util/compare_elements.rs b/src/util/compare_elements.rs index df2e4157cc2930f7bc6625280e352407bbe532ae..7494be7bf2fd14a724bb0e683227b143840c8328 100644 --- a/src/util/compare_elements.rs +++ b/src/util/compare_elements.rs @@ -51,7 +51,7 @@ impl NamespaceAwareCompare for Element { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; #[test] fn simple() { diff --git a/src/util/macros.rs b/src/util/macros.rs index fd7f58cc649c2ff3ef5352ff5e6a3a047edb395d..107083a4b9ad7687d13af85de235324ea8b96416 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -230,9 +230,9 @@ macro_rules! generate_element_enum { $enum ),+ } - impl ::std::convert::TryFrom<::minidom::Element> for $elem { + impl ::std::convert::TryFrom for $elem { type Error = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { + fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -242,9 +242,9 @@ macro_rules! generate_element_enum { }) } } - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder( + impl From<$elem> for crate::Element { + fn from(elem: $elem) -> crate::Element { + crate::Element::builder( match elem { $($elem::$enum => $enum_name,)+ } @@ -274,9 +274,9 @@ macro_rules! generate_attribute_enum { $enum ),+ } - impl ::std::convert::TryFrom<::minidom::Element> for $elem { + impl ::std::convert::TryFrom for $elem { type Error = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { + fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); @@ -286,9 +286,9 @@ macro_rules! generate_attribute_enum { }) } } - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) + impl From<$elem> for crate::Element { + fn from(elem: $elem) -> crate::Element { + crate::Element::builder($name) .ns(crate::ns::$ns) .attr($attr, match elem { $($elem::$enum => $enum_name,)+ @@ -377,10 +377,10 @@ macro_rules! generate_empty_element { #[derive(Debug, Clone)] pub struct $elem; - impl ::std::convert::TryFrom<::minidom::Element> for $elem { + impl ::std::convert::TryFrom for $elem { type Error = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { + fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -388,9 +388,9 @@ macro_rules! generate_empty_element { } } - impl From<$elem> for ::minidom::Element { - fn from(_: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) + impl From<$elem> for crate::Element { + fn from(_: $elem) -> crate::Element { + crate::Element::builder($name) .ns(crate::ns::$ns) .build() } @@ -439,9 +439,9 @@ macro_rules! generate_elem_id { $(#[$meta])* #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub $type); - impl ::std::convert::TryFrom<::minidom::Element> for $elem { + impl ::std::convert::TryFrom for $elem { type Error = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { + fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -449,9 +449,9 @@ macro_rules! generate_elem_id { Ok($elem(elem.text().parse()?)) } } - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - ::minidom::Element::builder($name) + impl From<$elem> for crate::Element { + fn from(elem: $elem) -> crate::Element { + crate::Element::builder($name) .ns(crate::ns::$ns) .append(elem.0.to_string()) .build() @@ -592,14 +592,14 @@ macro_rules! finish_parse_elem { macro_rules! generate_serialiser { ($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => { $builder.append( - ::minidom::Element::builder($name) + crate::Element::builder($name) .ns(crate::ns::$ns) .append(::minidom::Node::Text($parent.$elem)) ) }; ($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => { $builder.append_all($parent.$elem.map(|elem| { - ::minidom::Element::builder($name) + crate::Element::builder($name) .ns(crate::ns::$ns) .append(::minidom::Node::Text(elem)) }) @@ -607,9 +607,9 @@ macro_rules! generate_serialiser { }; ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => { $builder.append_all($parent.$elem.map(|elem| { - ::minidom::Element::builder($name) + crate::Element::builder($name) .ns(crate::ns::$ns) - .append(::minidom::Node::Element(::minidom::Element::from(elem))) + .append(::minidom::Node::Element(crate::Element::from(elem))) }) ) }; @@ -617,10 +617,10 @@ macro_rules! generate_serialiser { $builder.append_all($parent.$elem.into_iter()) }; ($builder:ident, $parent:ident, $elem:ident, Present, $constructor:ident, ($name:tt, $ns:ident)) => { - $builder.append(::minidom::Node::Element(::minidom::Element::builder($name).ns(crate::ns::$ns).build())) + $builder.append(::minidom::Node::Element(crate::Element::builder($name).ns(crate::ns::$ns).build())) }; ($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => { - $builder.append(::minidom::Node::Element(::minidom::Element::from($parent.$elem))) + $builder.append(::minidom::Node::Element(crate::Element::from($parent.$elem))) }; } @@ -661,10 +661,10 @@ macro_rules! generate_element { )* } - impl ::std::convert::TryFrom<::minidom::Element> for $elem { + impl ::std::convert::TryFrom for $elem { type Error = crate::util::error::Error; - fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { + fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); $( @@ -693,9 +693,9 @@ macro_rules! generate_element { } } - impl From<$elem> for ::minidom::Element { - fn from(elem: $elem) -> ::minidom::Element { - let mut builder = ::minidom::Element::builder($name) + impl From<$elem> for crate::Element { + fn from(elem: $elem) -> crate::Element { + let mut builder = crate::Element::builder($name) .ns(crate::ns::$ns); $( builder = builder.attr($attr_name, elem.$attr); diff --git a/src/version.rs b/src/version.rs index afc617897adf033e0dfd46865165e04af4e551f2..028ce968a2bc70e7a5bf168366958d04109b0061 100644 --- a/src/version.rs +++ b/src/version.rs @@ -42,7 +42,7 @@ impl IqResultPayload for VersionResult {} mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] diff --git a/src/websocket.rs b/src/websocket.rs index 1092423a10cddb9612823af7865ff81d62774b97..fbc1fb2b7aa983210195e1f4d439b0efb2332020 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -72,7 +72,7 @@ impl Open { #[cfg(test)] mod tests { use super::*; - use minidom::Element; + use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] From 2eb521fa023383e46e1621e9e7d205f975b390e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 29 Sep 2019 01:47:21 +0200 Subject: [PATCH 0990/1020] Add a Bookmarks 2 (This Time it's Serious) parser. --- ChangeLog | 1 + doap.xml | 8 ++++ src/bookmarks2.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 5 ++ 5 files changed, 136 insertions(+) create mode 100644 src/bookmarks2.rs diff --git a/ChangeLog b/ChangeLog index 381e8933ae03738206b530c64882c47642aeb9db..182d559713bd5acc6bfdfbb43d7a8279c1ca78f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ DATE Emmanuel Gil Peyrot - JID Prep (XEP-0328) - Client State Indication (XEP-0352) - OpenPGP for XMPP (XEP-0373) + - Bookmarks 2 (This Time it's Serious) (XEP-0402) - Anonymous unique occupant identifiers for MUCs (XEP-0421) * Breaking changes: - Presence constructors now take Into and assume Some. diff --git a/doap.xml b/doap.xml index e75603dc198d6c2de3d54551d71bf184f28e7f77..40b28e3bd08a5a950638c2ea27e0e4daf5d74c18 100644 --- a/doap.xml +++ b/doap.xml @@ -465,6 +465,14 @@ 0.1.0 + + + + complete + 0.3.0 + NEXT + + diff --git a/src/bookmarks2.rs b/src/bookmarks2.rs new file mode 100644 index 0000000000000000000000000000000000000000..2a182f12a613dfcd9580e4490bf3a07df17959d3 --- /dev/null +++ b/src/bookmarks2.rs @@ -0,0 +1,119 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_attribute!( + /// Whether a conference bookmark should be joined automatically. + Autojoin, + "autojoin", + bool +); + +generate_element!( + /// A conference bookmark. + Conference, "conference", BOOKMARKS2, + attributes: [ + /// Whether a conference bookmark should be joined automatically. + autojoin: Default = "autojoin", + + /// A user-defined name for this conference. + name: Option = "name", + ], + children: [ + /// The nick the user will use to join this conference. + nick: Option = ("nick", BOOKMARKS2) => String, + + /// The password required to join this conference. + password: Option = ("password", BOOKMARKS2) => String + ] +); + +impl Conference { + /// Create a new conference. + pub fn new() -> Conference { + Conference { + autojoin: Autojoin::False, + name: None, + nick: None, + password: None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::util::compare_elements::NamespaceAwareCompare; + use crate::Element; + use std::convert::TryFrom; + use crate::pubsub::pubsub::Item as PubSubItem; + use crate::pubsub::event::PubSubEvent; + use crate::ns; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Conference, 40); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Conference, 80); + } + + #[test] + fn simple() { + let elem: Element = "".parse().unwrap(); + let elem1 = elem.clone(); + let conference = Conference::try_from(elem).unwrap(); + assert_eq!(conference.autojoin, Autojoin::False); + assert_eq!(conference.name, None); + assert_eq!(conference.nick, None); + assert_eq!(conference.password, None); + + let elem2 = Element::from(Conference::new()); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn complete() { + let elem: Element = "Coucousecret".parse().unwrap(); + let conference = Conference::try_from(elem).unwrap(); + assert_eq!(conference.autojoin, Autojoin::True); + assert_eq!(conference.name, Some(String::from("Test MUC"))); + assert_eq!(conference.clone().nick.unwrap(), "Coucou"); + assert_eq!(conference.clone().password.unwrap(), "secret"); + } + + #[test] + fn wrapped() { + let elem: Element = "Coucousecret".parse().unwrap(); + let item = PubSubItem::try_from(elem).unwrap(); + let payload = item.payload.clone().unwrap(); + let conference = Conference::try_from(payload).unwrap(); + assert_eq!(conference.autojoin, Autojoin::True); + assert_eq!(conference.name, Some(String::from("Test MUC"))); + assert_eq!(conference.clone().nick.unwrap(), "Coucou"); + assert_eq!(conference.clone().password.unwrap(), "secret"); + + let elem: Element = "Coucousecret".parse().unwrap(); + let mut items = match PubSubEvent::try_from(elem) { + Ok(PubSubEvent::PublishedItems { node, items }) => { + assert_eq!(&node.0, ns::BOOKMARKS2); + items + }, + _ => panic!(), + }; + assert_eq!(items.len(), 1); + let item = items.pop().unwrap(); + let payload = item.payload.clone().unwrap(); + let conference = Conference::try_from(payload).unwrap(); + assert_eq!(conference.autojoin, Autojoin::True); + assert_eq!(conference.name, Some(String::from("Test MUC"))); + assert_eq!(conference.clone().nick.unwrap(), "Coucou"); + assert_eq!(conference.clone().password.unwrap(), "secret"); + } +} diff --git a/src/lib.rs b/src/lib.rs index b69e56ad4408e19f4542926a5725394ab6a3a867..2f856e0a27d6d56e80d8ff5961742e741900a149 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -201,5 +201,8 @@ pub mod eme; /// XEP-0390: Entity Capabilities 2.0 pub mod ecaps2; +/// XEP-0402: Bookmarks 2 (This Time it's Serious) +pub mod bookmarks2; + /// XEP-0421: Anonymous unique occupant identifiers for MUCs pub mod occupant_id; diff --git a/src/ns.rs b/src/ns.rs index 70d68cff156aff8aebddb24e91b02c2559caf018..5aea7a65b56a7069f3a1a35b074a9a789e05ed3a 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -210,6 +210,11 @@ pub const ECAPS2: &str = "urn:xmpp:caps"; /// XEP-0390: Entity Capabilities 2.0 pub const ECAPS2_OPTIMIZE: &str = "urn:xmpp:caps:optimize"; +/// XEP-0402: Bookmarks 2 (This Time it's Serious) +pub const BOOKMARKS2: &str = "urn:xmpp:bookmarks:0"; +/// XEP-0402: Bookmarks 2 (This Time it's Serious) +pub const BOOKMARKS2_COMPAT: &str = "urn:xmpp:bookmarks:0#compat"; + /// XEP-0421: Anonymous unique occupant identifiers for MUCs pub const OID: &str = "urn:xmpp:occupant-id:0"; From fcdffba31cd1de76fdb6c66c0ec421f57fd711e7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:10:36 +0200 Subject: [PATCH 0991/1020] macros: Add support for wildcard namespaces. --- src/util/macros.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/util/macros.rs b/src/util/macros.rs index 107083a4b9ad7687d13af85de235324ea8b96416..d63591aa6964e9078f92448f9349c926cbfa2b25 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -605,6 +605,14 @@ macro_rules! generate_serialiser { }) ) }; + ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, *)) => { + $builder.append_all($parent.$elem.map(|elem| { + crate::Element::builder($name) + .ns(elem.get_ns()) + .append(::minidom::Node::Element(crate::Element::from(elem))) + }) + ) + }; ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => { $builder.append_all($parent.$elem.map(|elem| { crate::Element::builder($name) @@ -624,6 +632,15 @@ macro_rules! generate_serialiser { }; } +macro_rules! generate_child_test { + ($child:ident, $name:tt, *) => { + true + }; + ($child:ident, $name:tt, $ns:tt) => { + $child.is($name, crate::ns::$ns) + }; +} + macro_rules! generate_element { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+,]) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []); @@ -631,10 +648,10 @@ macro_rules! generate_element { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+]) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*]) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*]) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => ( @@ -643,7 +660,7 @@ macro_rules! generate_element { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>)); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { @@ -672,7 +689,7 @@ macro_rules! generate_element { )* for _child in elem.children() { $( - if _child.is($child_name, crate::ns::$child_ns) { + if generate_child_test!(_child, $child_name, $child_ns) { do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name); continue; } From 7f8cdc5bf0395353066059e3b56f7b3c6c5aebed Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:11:34 +0200 Subject: [PATCH 0992/1020] jingle_dtls_srtp: Add constructors from Hash and from strings. --- src/hashes.rs | 17 +++++++++++++++-- src/jingle_dtls_srtp.rs | 21 ++++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index ae7b61bc2585efc6199ed1b5dc48f8c2c936a498..56b61355507471a549f96caf3125910009b5d307 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -131,6 +131,19 @@ impl Hash { Ok(Hash::new(algo, bytes)) } + /// Like [new](#method.new) but takes hex-encoded data before decoding it. + pub fn from_colon_separated_hex(algo: Algo, hex: &str) -> Result { + let mut bytes = vec![]; + for i in 0..(1 + hex.len()) / 3 { + let byte = u8::from_str_radix(&hex[3 * i..3 * i + 2], 16)?; + if 3 * i + 2 < hex.len() { + assert_eq!(&hex[3 * i + 2..3 * i + 3], ":"); + } + bytes.push(byte); + } + Ok(Hash::new(algo, bytes)) + } + /// Formats this hash into base64. pub fn to_base64(&self) -> String { base64::encode(&self.hash[..]) @@ -146,7 +159,7 @@ impl Hash { } /// Formats this hash into colon-separated hexadecimal. - pub fn to_colon_hex(&self) -> String { + pub fn to_colon_separated_hex(&self) -> String { let mut bytes = vec![]; for byte in self.hash.iter() { bytes.push(format!("{:02x}", byte)); @@ -225,7 +238,7 @@ mod tests { let hash = Hash::try_from(elem).unwrap(); assert_eq!(hash.to_base64(), "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); assert_eq!(hash.to_hex(), "d976ab9b04e53710c0324bf29a5a17dd2e7e55bca536b26dfe5e50c8f6be6285"); - assert_eq!(hash.to_colon_hex(), "d9:76:ab:9b:04:e5:37:10:c0:32:4b:f2:9a:5a:17:dd:2e:7e:55:bc:a5:36:b2:6d:fe:5e:50:c8:f6:be:62:85"); + assert_eq!(hash.to_colon_separated_hex(), "d9:76:ab:9b:04:e5:37:10:c0:32:4b:f2:9a:5a:17:dd:2e:7e:55:bc:a5:36:b2:6d:fe:5e:50:c8:f6:be:62:85"); } #[test] diff --git a/src/jingle_dtls_srtp.rs b/src/jingle_dtls_srtp.rs index 150388f6eac7f3128f079f376964c725b5d42911..e6a3ee235730fa3c102e41064c6b07a87764da0f 100644 --- a/src/jingle_dtls_srtp.rs +++ b/src/jingle_dtls_srtp.rs @@ -5,7 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::util::helpers::ColonSeparatedHex; -use crate::hashes::Algo; +use crate::util::error::Error; +use crate::hashes::{Hash, Algo}; generate_attribute!( /// Indicates which of the end points should initiate the TCP connection establishment. @@ -46,6 +47,24 @@ generate_element!( ) ); +impl Fingerprint { + /// Create a new Fingerprint from a Setup and a Hash. + pub fn from_hash(setup: Setup, hash: Hash) -> Fingerprint { + Fingerprint { + hash: hash.algo, + setup, + value: hash.hash, + } + } + + /// Create a new Fingerprint from a Setup and parsing the hash. + pub fn from_colon_separated_hex(setup: Setup, algo: &str, hash: &str) -> Result { + let algo = algo.parse()?; + let hash = Hash::from_colon_separated_hex(algo, hash)?; + Ok(Fingerprint::from_hash(setup, hash)) + } +} + #[cfg(test)] mod tests { use super::*; From b91e5bdc4e7f3b0135273f653b346e95eb47d232 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:13:02 +0200 Subject: [PATCH 0993/1020] jingle_ice_udp: Add a constructor for Transport. --- src/jingle_ice_udp.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 51a725ee705ed6b0598e7b6a862a4973bb669950..be5dbc97e1af4c1a849c233f0f58481038b51045 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -26,6 +26,18 @@ generate_element!( ] ); +impl Transport { + /// Create a new ICE-UDP transport. + pub fn new() -> Transport { + Transport { + pwd: None, + ufrag: None, + candidates: Vec::new(), + fingerprint: None, + } + } +} + generate_attribute!( /// A Candidate Type as defined in ICE-CORE. Type, "type", { From 7665f7e5d8577441d078da9bc69989124d531bba Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:13:42 +0200 Subject: [PATCH 0994/1020] jingle: Wrap all supported and unknown transports in an enum. --- src/jingle.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 4c568bfc55e683fb449cf6a65146829641b882d2..fbccce0da23d9d96dfa304f91dca34a90dd8a2e1 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -6,6 +6,9 @@ use crate::util::error::Error; use crate::iq::IqSetPayload; +use crate::jingle_ice_udp::Transport as IceUdpTransport; +use crate::jingle_ibb::Transport as IbbTransport; +use crate::jingle_s5b::Transport as Socks5Transport; use crate::ns; use jid::Jid; use crate::Element; @@ -164,6 +167,78 @@ generate_id!( ContentId ); +/// Enum wrapping all of the various supported transports of a Content. +#[derive(Debug, Clone)] +pub enum Transport { + /// Jingle ICE-UDP Bytestreams (XEP-0176) transport. + IceUdp(IceUdpTransport), + + /// Jingle In-Band Bytestreams (XEP-0261) transport. + Ibb(IbbTransport), + + /// Jingle SOCKS5 Bytestreams (XEP-0260) transport. + Socks5(Socks5Transport), + + /// To be used for any transport that isn’t known at compile-time. + Unknown(Element), +} + +impl TryFrom for Transport { + type Error = Error; + + fn try_from(elem: Element) -> Result { + Ok(if elem.is("transport", ns::JINGLE_ICE_UDP) { + Transport::IceUdp(IceUdpTransport::try_from(elem)?) + } else if elem.is("transport", ns::JINGLE_IBB) { + Transport::Ibb(IbbTransport::try_from(elem)?) + } else if elem.is("transport", ns::JINGLE_S5B) { + Transport::Socks5(Socks5Transport::try_from(elem)?) + } else { + Transport::Unknown(elem) + }) + } +} + +impl From for Transport { + fn from(transport: IceUdpTransport) -> Transport { + Transport::IceUdp(transport) + } +} + +impl From for Transport { + fn from(transport: IbbTransport) -> Transport { + Transport::Ibb(transport) + } +} + +impl From for Transport { + fn from(transport: Socks5Transport) -> Transport { + Transport::Socks5(transport) + } +} + +impl From for Element { + fn from(transport: Transport) -> Element { + match transport { + Transport::IceUdp(transport) => transport.into(), + Transport::Ibb(transport) => transport.into(), + Transport::Socks5(transport) => transport.into(), + Transport::Unknown(elem) => elem, + } + } +} + +impl Transport { + fn get_ns(&self) -> String { + match self { + Transport::IceUdp(_) => String::from(ns::JINGLE_ICE_UDP), + Transport::Ibb(_) => String::from(ns::JINGLE_IBB), + Transport::Socks5(_) => String::from(ns::JINGLE_S5B), + Transport::Unknown(elem) => elem.ns().unwrap_or_else(|| String::new()), + } + } +} + generate_element!( /// Describes a session’s content, there can be multiple content in one /// session. @@ -186,7 +261,7 @@ generate_element!( description: Option = ("description", JINGLE) => Element, /// How to send it. - transport: Option = ("transport", JINGLE) => Element, + transport: Option = ("transport", *) => Transport, /// With which security. security: Option = ("security", JINGLE) => Element @@ -226,8 +301,8 @@ impl Content { } /// Set the transport of this content. - pub fn with_transport(mut self, transport: Element) -> Content { - self.transport = Some(transport); + pub fn with_transport>(mut self, transport: T) -> Content { + self.transport = Some(transport.into()); self } @@ -575,7 +650,7 @@ mod tests { assert_size!(Senders, 1); assert_size!(Disposition, 1); assert_size!(ContentId, 24); - assert_size!(Content, 344); + assert_size!(Content, 384); assert_size!(Reason, 1); assert_size!(ReasonElement, 32); assert_size!(SessionId, 24); @@ -626,18 +701,18 @@ mod tests { #[test] fn test_content() { - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].creator, Creator::Initiator); assert_eq!(jingle.contents[0].name, ContentId(String::from("coucou"))); assert_eq!(jingle.contents[0].senders, Senders::Both); assert_eq!(jingle.contents[0].disposition, Disposition::Session); - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].senders, Senders::Both); - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); assert_eq!(jingle.contents[0].disposition, Disposition::EarlySession); } From 5338cd659909aefa2e3b916abe152c88336258c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:14:00 +0200 Subject: [PATCH 0995/1020] pubsub: Add a constructor for items request. --- src/pubsub/pubsub.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 8faf9c433c2dcece35c7af4c80a5f02a63b8eb98..39b30c7427ab6abf379d3d880e78cd71826cd927 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -113,6 +113,18 @@ generate_element!( ] ); +impl Items { + /// Create a new items request. + pub fn new(node: &str) -> Items { + Items { + node: NodeName(String::from(node)), + max_items: None, + subid: None, + items: Vec::new(), + } + } +} + /// Response wrapper for a PubSub ``. #[derive(Debug, Clone)] pub struct Item(pub PubSubItem); From d654b1bd309d90d3aece15977e21666ab6235a52 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 17:16:36 +0200 Subject: [PATCH 0996/1020] jingle_rtp: Add constructors. --- src/jingle_rtp.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 5e686ca965eab32e60edaa0391896cc4b515e88a..7e5557b2f43e098f1b424fe229c2ba4253f35d4a 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -24,6 +24,17 @@ generate_element!( ] ); +impl Description { + /// Create a new RTP description. + pub fn new(media: String) -> Description { + Description { + media, + ssrc: None, + payload_types: Vec::new(), + } + } +} + generate_attribute!( /// The number of channels. Channels, "channels", u8, Default = 1 @@ -59,6 +70,21 @@ generate_element!( ] ); +impl PayloadType { + /// Create a new RTP payload-type. + pub fn new(id: u8, name: String) -> PayloadType { + PayloadType { + channels: Default::default(), + clockrate: None, + id, + maxptime: None, + name: Some(name), + ptime: None, + parameters: Vec::new(), + } + } +} + generate_element!( /// Parameter related to a payload. Parameter, "parameter", JINGLE_RTP, From cd32ea4c034d547023a595b6df66535ea35bf10c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 18:17:04 +0200 Subject: [PATCH 0997/1020] Add a parser for XEP-0339. --- doap.xml | 8 ++++ src/jingle_rtp.rs | 12 ++++- src/jingle_ssma.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ 5 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/jingle_ssma.rs diff --git a/doap.xml b/doap.xml index 40b28e3bd08a5a950638c2ea27e0e4daf5d74c18..f3f43b13988a25e4ac650294ec32f5ce98f6e0ba 100644 --- a/doap.xml +++ b/doap.xml @@ -417,6 +417,14 @@ NEXT + + + + complete + 0.3 + NEXT + + diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 7e5557b2f43e098f1b424fe229c2ba4253f35d4a..7f9e5a1aa18fdc88b7a4f403781143944b6776a4 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use crate::jingle_ssma::{Source, Group}; + generate_element!( /// Wrapper element describing an RTP session. Description, "description", JINGLE_RTP, @@ -18,7 +20,13 @@ generate_element!( ], children: [ /// List of encodings that can be used for this RTP stream. - payload_types: Vec = ("payload-type", JINGLE_RTP) => PayloadType + payload_types: Vec = ("payload-type", JINGLE_RTP) => PayloadType, + + /// List of ssrc-group. + ssrc_groups: Vec = ("ssrc-group", JINGLE_SSMA) => Group, + + /// List of ssrc. + ssrcs: Vec = ("ssrc", JINGLE_SSMA) => Source // TODO: Add support for and . ] @@ -31,6 +39,8 @@ impl Description { media, ssrc: None, payload_types: Vec::new(), + ssrc_groups: Vec::new(), + ssrcs: Vec::new(), } } } diff --git a/src/jingle_ssma.rs b/src/jingle_ssma.rs new file mode 100644 index 0000000000000000000000000000000000000000..4d966b86ff8dd0676150363ee352b0228ee7fede --- /dev/null +++ b/src/jingle_ssma.rs @@ -0,0 +1,114 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_element!( + /// Source element for the ssrc SDP attribute. + Source, "source", JINGLE_SSMA, + attributes: [ + /// Maps to the ssrc-id parameter. + id: Required = "ssrc", + ], + children: [ + /// List of attributes for this source. + parameters: Vec = ("parameter", JINGLE_SSMA) => Parameter + ] +); + +impl Source { + /// Create a new SSMA Source element. + pub fn new(id: String) -> Source { + Source { + id, + parameters: Vec::new(), + } + } +} + +generate_element!( + /// Parameter associated with a ssrc. + Parameter, "parameter", JINGLE_SSMA, + attributes: [ + /// The name of the parameter. + name: Required = "name", + + /// The optional value of the parameter. + value: Option = "value", + ] +); + +generate_element!( + /// Element grouping multiple ssrc. + Group, "ssrc-group", JINGLE_SSMA, + attributes: [ + /// The semantics of this group. + semantics: Required = "semantics", + ], + children: [ + /// The various ssrc concerned by this group. + sources: Vec = ("source", JINGLE_SSMA) => Source + ] +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::Element; + use std::convert::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Source, 24); + assert_size!(Parameter, 24); + assert_size!(Group, 24); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Source, 48); + assert_size!(Parameter, 48); + assert_size!(Group, 48); + } + + #[test] + fn parse_source() { + let elem: Element = " + + + +" + .parse() + .unwrap(); + let mut ssrc = Source::try_from(elem).unwrap(); + assert_eq!(ssrc.id, "1656081975"); + assert_eq!(ssrc.parameters.len(), 2); + let parameter = ssrc.parameters.pop().unwrap(); + assert_eq!(parameter.name, "msid"); + assert_eq!(parameter.value.unwrap(), "MLTJKIHilGn71fNQoszkQ4jlPTuS5vJyKVIv MLTJKIHilGn71fNQoszkQ4jlPTuS5vJyKVIva0"); + let parameter = ssrc.parameters.pop().unwrap(); + assert_eq!(parameter.name, "cname"); + assert_eq!(parameter.value.unwrap(), "Yv/wvbCdsDW2Prgd"); + } + + #[test] + fn parse_source_group() { + let elem: Element = " + + + +" + .parse() + .unwrap(); + let mut group = Group::try_from(elem).unwrap(); + assert_eq!(group.semantics, "FID"); + assert_eq!(group.sources.len(), 2); + let source = group.sources.pop().unwrap(); + assert_eq!(source.id, "386328120"); + let source = group.sources.pop().unwrap(); + assert_eq!(source.id, "2301230316"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 2f856e0a27d6d56e80d8ff5961742e741900a149..7e4f034e30dd0a55ee8bb8c53ff7e6b185b2be0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -183,6 +183,9 @@ pub mod jingle_dtls_srtp; /// XEP-0328: JID Prep pub mod jid_prep; +/// XEP-0339: Source-Specific Media Attributes in Jingle +pub mod jingle_ssma; + /// XEP-0352: Client State Indication pub mod csi; diff --git a/src/ns.rs b/src/ns.rs index 5aea7a65b56a7069f3a1a35b074a9a789e05ed3a..46090f09e77fdc567d201d932cb60031335e7e92 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -188,6 +188,9 @@ pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0"; /// XEP-0328: JID Prep pub const JID_PREP: &str = "urn:xmpp:jidprep:0"; +/// XEP-0339: Source-Specific Media Attributes in Jingle +pub const JINGLE_SSMA: &str = "urn:xmpp:jingle:apps:rtp:ssma:0"; + /// XEP-0352: Client State Indication pub const CSI: &str = "urn:xmpp:csi:0"; From aeb8bc95f4a376781763f5ac67738aa1fff23055 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 12 Oct 2019 19:10:50 +0200 Subject: [PATCH 0998/1020] Add a parser for XEP-0293. --- doap.xml | 9 +++++++++ src/jingle_rtcp_fb.rs | 46 +++++++++++++++++++++++++++++++++++++++++++ src/jingle_rtp.rs | 11 ++++++++--- src/lib.rs | 3 +++ src/ns.rs | 3 +++ 5 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/jingle_rtcp_fb.rs diff --git a/doap.xml b/doap.xml index f3f43b13988a25e4ac650294ec32f5ce98f6e0ba..86c9fdb525a4878f79779106a5d9acf99c5ed2f0 100644 --- a/doap.xml +++ b/doap.xml @@ -361,6 +361,15 @@ 0.15.0 + + + + partial + 1.0.1 + NEXT + Only supported in payload-type, and only for rtcp-fb. + + diff --git a/src/jingle_rtcp_fb.rs b/src/jingle_rtcp_fb.rs new file mode 100644 index 0000000000000000000000000000000000000000..da52b88794b23c581bc39334f4bf4a817504db79 --- /dev/null +++ b/src/jingle_rtcp_fb.rs @@ -0,0 +1,46 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +generate_element!( + /// Wrapper element for a rtcp-fb. + RtcpFb, "rtcp-fb", JINGLE_RTCP_FB, + attributes: [ + /// Type of this rtcp-fb. + type_: Required = "type", + + /// Subtype of this rtcp-fb, if relevant. + subtype: Option = "subtype", + ] +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::Element; + use std::convert::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(RtcpFb, 24); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(RtcpFb, 48); + } + + #[test] + fn parse_simple() { + let elem: Element = "" + .parse() + .unwrap(); + let rtcp_fb = RtcpFb::try_from(elem).unwrap(); + assert_eq!(rtcp_fb.type_, "nack"); + assert_eq!(rtcp_fb.subtype.unwrap(), "sli"); + } +} diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 7f9e5a1aa18fdc88b7a4f403781143944b6776a4..0e9dcd85430179d46f0e8511e7943b33c195bcd0 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::jingle_ssma::{Source, Group}; +use crate::jingle_rtcp_fb::RtcpFb; generate_element!( /// Wrapper element describing an RTP session. @@ -76,7 +77,10 @@ generate_element!( /// List of parameters specifying this payload-type. /// /// Their order MUST be ignored. - parameters: Vec = ("parameter", JINGLE_RTP) => Parameter + parameters: Vec = ("parameter", JINGLE_RTP) => Parameter, + + /// List of rtcp-fb parameters from XEP-0293. + rtcp_fbs: Vec = ("rtcp-fb", JINGLE_RTCP_FB) => RtcpFb ] ); @@ -91,6 +95,7 @@ impl PayloadType { name: Some(name), ptime: None, parameters: Vec::new(), + rtcp_fbs: Vec::new(), } } } @@ -126,9 +131,9 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Description, 72); + assert_size!(Description, 120); assert_size!(Channels, 1); - assert_size!(PayloadType, 80); + assert_size!(PayloadType, 104); assert_size!(Parameter, 48); } diff --git a/src/lib.rs b/src/lib.rs index 7e4f034e30dd0a55ee8bb8c53ff7e6b185b2be0a..4977a8bc9937aab5a9840bb20e52d42602a052e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -162,6 +162,9 @@ pub mod jingle_ibb; /// XEP-0280: Message Carbons pub mod carbons; +/// XEP-0293: Jingle RTP Feedback Negotiation +pub mod jingle_rtcp_fb; + /// XEP-0297: Stanza Forwarding pub mod forwarding; diff --git a/src/ns.rs b/src/ns.rs index 46090f09e77fdc567d201d932cb60031335e7e92..231d78ed4168eaae90989b0feb169289cd335660 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -155,6 +155,9 @@ pub const MICROBLOG: &str = "urn:xmpp:microblog:0"; /// XEP-0280: Message Carbons pub const CARBONS: &str = "urn:xmpp:carbons:2"; +/// XEP-0293: Jingle RTP Feedback Negotiation +pub const JINGLE_RTCP_FB: &str = "urn:xmpp:jingle:apps:rtp:rtcp-fb:0"; + /// XEP-0297: Stanza Forwarding pub const FORWARD: &str = "urn:xmpp:forward:0"; From 16458dedf112d49401f5752aec069e760bc97727 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 15 Oct 2019 21:18:24 +0200 Subject: [PATCH 0999/1020] client: replace jid field with bound_jid() resolves gitlab MR #8 --- src/client/mod.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 4f31dad037645c8066cf5a92e3b3e2cf45beb6d3..fab4571cf034e16b6113c1abfeceb8de141d493c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -22,8 +22,6 @@ use self::bind::ClientBind; /// XMPP client connection and state pub struct Client { - /// The client's current Jabber-Id - pub jid: Jid, state: ClientState, } @@ -51,9 +49,8 @@ impl Client { /// Start a new client given that the JID is already parsed. pub fn new_with_jid(jid: Jid, password: &str) -> Self { let password = password.to_owned(); - let connect = Self::make_connect(jid.clone(), password.clone()); + let connect = Self::make_connect(jid, password.clone()); let client = Client { - jid, state: ClientState::Connecting(Box::new(connect)), }; client @@ -125,6 +122,15 @@ impl Client { fn bind(stream: xmpp_stream::XMPPStream) -> ClientBind { ClientBind::new(stream) } + + /// Get the client's bound JID (the one reported by the XMPP + /// server). + pub fn bound_jid(&self) -> Option<&Jid> { + match self.state { + ClientState::Connected(ref stream) => Some(&stream.jid), + _ => None, + } + } } impl Stream for Client { From 9a5c95694b36c2b48426a3a994b1a0b5681dc504 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 15 Oct 2019 22:02:14 +0200 Subject: [PATCH 1000/1020] add jid to Event::Online breaks the API --- examples/echo_bot.rs | 5 ++++- src/client/mod.rs | 3 ++- src/component/mod.rs | 2 +- src/event.rs | 14 +++++++++++--- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 0d08dc966ee56a4be4a8f01e45efc17afedad06c..a4144bf4f695475fc174bdc735867fd9a19a41c0 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -47,7 +47,10 @@ fn main() { if wait_for_stream_end { /* Do nothing */ } else if event.is_online() { - println!("Online!"); + let jid = event.get_jid() + .map(|jid| format!("{}", jid)) + .unwrap_or("unknown".to_owned()); + println!("Online at {}", jid); let presence = make_presence(); tx.start_send(Packet::Stanza(presence)).unwrap(); diff --git a/src/client/mod.rs b/src/client/mod.rs index fab4571cf034e16b6113c1abfeceb8de141d493c..bcd07c954cac2f53b4571544fcefed19b6ce1c73 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -145,8 +145,9 @@ impl Stream for Client { ClientState::Disconnected => Ok(Async::Ready(None)), ClientState::Connecting(mut connect) => match connect.poll() { Ok(Async::Ready(stream)) => { + let jid = stream.jid.clone(); self.state = ClientState::Connected(stream); - Ok(Async::Ready(Some(Event::Online))) + Ok(Async::Ready(Some(Event::Online(jid)))) } Ok(Async::NotReady) => { self.state = ClientState::Connecting(connect); diff --git a/src/component/mod.rs b/src/component/mod.rs index 8738111780499591c021ffa9a9693d55d8b8c322..d822e8839ae24993521f430191a62ac6fcd876d2 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -90,7 +90,7 @@ impl Stream for Component { ComponentState::Connecting(mut connect) => match connect.poll() { Ok(Async::Ready(stream)) => { self.state = ComponentState::Connected(stream); - Ok(Async::Ready(Some(Event::Online))) + Ok(Async::Ready(Some(Event::Online(self.jid.clone())))) } Ok(Async::NotReady) => { self.state = ComponentState::Connecting(connect); diff --git a/src/event.rs b/src/event.rs index 94dd36c2b7f298642a6e33671d1fbf2f138dd7f2..bd3dc402a2c2ad2f6b9eee29414e034db14406a2 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,10 +1,10 @@ -use xmpp_parsers::Element; +use xmpp_parsers::{Element, Jid}; /// High-level event on the Stream implemented by Client and Component #[derive(Debug)] pub enum Event { /// Stream is connected and initialized - Online, + Online(Jid), /// Stream end Disconnected, /// Received stanza/nonza @@ -15,11 +15,19 @@ impl Event { /// `Online` event? pub fn is_online(&self) -> bool { match *self { - Event::Online => true, + Event::Online(_) => true, _ => false, } } + /// Get the server-assigned JID for the `Online` event + pub fn get_jid(&self) -> Option<&Jid> { + match *self { + Event::Online(ref jid) => Some(jid), + _ => None, + } + } + /// `Stanza` event? pub fn is_stanza(&self, name: &str) -> bool { match *self { From 147d07832edb7d7f82d71c39306dad0f0f6f7a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 22:35:46 +0200 Subject: [PATCH 1001/1020] Prepare for 0.8 release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- CHANGELOG.md | 4 +++- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70108afddf06ed8d5d265b9b95aaeb6e2c235f94..7d82b5a77f6979243980efca911330da4f1958a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ -Version XXX, released YYY: +Version 0.8, released 2019-10-15: * Updates - CI: Split jobs, add tests, and caching + * Breaking + - 0.7.1 was actually a breaking release Version 0.7.2, released 2019-09-13: * Updates diff --git a/Cargo.toml b/Cargo.toml index fad2217dba7600edfb561c6c9ebcb45d3cc2d3ee..4f4894d07d13c7bd39a35e1f58b69cc57160f0d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.7.2" +version = "0.8.0" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From 450d43a0eeaeec1f6bf397740a90830f85295a5d Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 15 Oct 2019 22:37:50 +0200 Subject: [PATCH 1002/1020] update deps --- Cargo.lock | 341 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 166 insertions(+), 177 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac0af39db11a1f3ebf49e483cba9db20c546e3d2..de1c70ae4c095007dbad31a9e699e2d1b6c07919 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,10 +2,10 @@ # It is not intended for manual editing. [[package]] name = "arrayvec" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -15,12 +15,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.37" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -30,7 +30,7 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -43,7 +43,7 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -92,7 +92,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -111,7 +111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -119,7 +119,7 @@ name = "chrono" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -130,7 +130,7 @@ name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -139,7 +139,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -161,8 +161,8 @@ name = "crossbeam-epoch" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -182,7 +182,7 @@ name = "crossbeam-utils" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -215,22 +215,22 @@ dependencies = [ [[package]] name = "failure" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure_derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -266,7 +266,7 @@ name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -302,8 +302,8 @@ name = "getrandom" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -321,7 +321,7 @@ name = "hostname" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -337,11 +337,10 @@ dependencies = [ [[package]] name = "iovec" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -362,7 +361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jid" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -389,7 +388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.62" +version = "0.2.64" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -399,11 +398,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lock_api" -version = "0.1.5" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -411,7 +409,7 @@ name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -435,11 +433,11 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", - "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_codegen 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -476,9 +474,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", @@ -491,8 +489,8 @@ name = "mio-uds" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -513,12 +511,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -529,8 +527,8 @@ name = "net2" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -541,7 +539,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "nodrop" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -566,7 +564,7 @@ name = "num_cpus" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -576,15 +574,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.10.24" +version = "0.10.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -594,40 +592,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.49" +version = "0.9.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "owning_ref" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parking_lot" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parking_lot_core" -version = "0.4.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -683,7 +676,7 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -706,7 +699,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -725,6 +718,14 @@ dependencies = [ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quick-xml" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.6.13" @@ -738,7 +739,7 @@ name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -747,7 +748,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -761,11 +762,11 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -839,7 +840,7 @@ name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -851,7 +852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -919,7 +920,7 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -937,18 +938,13 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "scopeguard" version = "1.0.0" @@ -961,7 +957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -988,27 +984,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1065,40 +1061,35 @@ name = "socket2" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "stable_deref_trait" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "string_cache" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_codegen 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "string_cache_codegen" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1127,20 +1118,20 @@ name = "syn" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "synstructure" -version = "0.10.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1148,9 +1139,9 @@ name = "tempfile" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1171,7 +1162,7 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1190,10 +1181,10 @@ dependencies = [ "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1234,7 +1225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1249,7 +1240,7 @@ dependencies = [ [[package]] name = "tokio-reactor" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1258,16 +1249,16 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-sync" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1281,24 +1272,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-threadpool" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1335,7 +1326,7 @@ dependencies = [ "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1345,25 +1336,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-xmpp" -version = "1.0.0" +version = "1.0.1" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-xml 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-xml 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "sasl 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1381,17 +1372,17 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1403,8 +1394,8 @@ name = "trust-dns-resolver" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1554,7 +1545,7 @@ dependencies = [ "blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1562,12 +1553,12 @@ dependencies = [ ] [metadata] -"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2" +"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" @@ -1576,7 +1567,7 @@ dependencies = [ "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" @@ -1588,8 +1579,8 @@ dependencies = [ "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d58266c97445680766be408285e798d3401c6d4c378ec5552e78737e681e37d" -"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" -"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" @@ -1604,16 +1595,16 @@ dependencies = [ "checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum jid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e3676672e1f13e4963e6af2c1d17114e7f9f96f96477c278fccdd192c618c67" +"checksum jid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "32b4cfc8edfd18c386be7b4e7307e59471aed5e21cd8b80e1aaf070b42de163d" "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +"checksum libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)" = "74dfca3d9957906e8d1e6a0b641dc9a59848e793f1da2165889fd4f62d10d79c" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" "checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" @@ -1628,34 +1619,34 @@ dependencies = [ "checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15" +"checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f4fad9e54bd23bd4cbbe48fdc08a1b8091707ac869ef8508edea2fec77dcc884" -"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" -"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)" = "ba24190c8f0805d3bd2ce028f439fe5af1d55882bbe6261bed1dbc93b50dd6b1" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" "checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" +"checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" +"checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quick-xml 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd45021132c1cb5540995e93fcc2cf5a874ef84f9639168fb6819caa023d4be" +"checksum quick-xml 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcdba8c8d71275493d966ef052a88726ac8590c15a09968b32158205c672ef" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" +"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" @@ -1674,18 +1665,17 @@ dependencies = [ "checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" +"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum sasl 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e457758c85b736bbad56dc099406cd2a9c19554cf81880dba7a51d092929e600" -"checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2" "checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" -"checksum serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "11e410fde43e157d789fc290d26bc940778ad0fdd47836426fbac36573710dbb" -"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" +"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" +"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" +"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" "checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" "checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" @@ -1693,14 +1683,13 @@ dependencies = [ "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" -"checksum string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da" +"checksum string_cache 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "96ccb3a75a3caf2d7f2eb9ada86ec1fbbd4c74ad2bd8dc00a96a0c2f93509ef0" +"checksum string_cache_codegen 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f45ed1b65bf9a4bf2f7b7dc59212d1926e9eaf00fa998988e420fd124467c6" "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" -"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" +"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" @@ -1710,10 +1699,10 @@ dependencies = [ "checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac" "checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" "checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" -"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" -"checksum tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2162248ff317e2bc713b261f242b69dbb838b85248ed20bb21df56d60ea4cae7" +"checksum tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c56391be9805bc80163151c0b9e5164ee64f4b0200962c346fea12773158f22d" +"checksum tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d06554cce1ae4a50f42fba8023918afa931413aded705b560e29600ccf7c6d76" "checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "90ca01319dea1e376a001e8dc192d42ebde6dd532532a5bad988ac37db365b19" +"checksum tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2bd2c6a3885302581f4401c82af70d792bb9df1700e7437b0aeb4ada94d5388c" "checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" "checksum tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" "checksum tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f02298505547f73e60f568359ef0d016d5acd6e830ab9bc7c4a5b3403440121b" diff --git a/Cargo.toml b/Cargo.toml index b2cb5183bad08ac2bf40424976235d0c53407fbe..7bee03f8fb0cb463381d1b677488da7aedc75860 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,6 @@ trust-dns-resolver = "0.12" trust-dns-proto = "0.8" tokio-io = "0.1" tokio-tls = "0.2" -quick-xml = "0.16" +quick-xml = "0.17" xml5ever = "0.15" xmpp-parsers = "0.15" From ed5e6608afad4876c7a6fa1656e1211708b8991e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 22:49:16 +0200 Subject: [PATCH 1003/1020] DOAP: Update foaf:mbox_sha1sum for pep. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- doap.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 91f5fccc380bd1158005d22307f52d812fd9ca07..203a3cd216fb8f243694f186457229e4b755c683 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" [dependencies] minidom = "0.11.0" -jid = { version = "0.7.0", features = ["minidom"] } +jid = { version = "0.7.2", features = ["minidom"] } base64 = "0.10" digest = "0.8" sha-1 = "0.8" diff --git a/doap.xml b/doap.xml index 86c9fdb525a4878f79779106a5d9acf99c5ed2f0..80dc172d0c088518f676187faf4db54fca1d7756 100644 --- a/doap.xml +++ b/doap.xml @@ -41,7 +41,7 @@ pep. - TODO + 99bcf9784288e323b0d2dea9c9ac7a2ede98395a From 567b22db72df6f24d7ed197ff30b2e468c9783ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 22:51:08 +0200 Subject: [PATCH 1004/1020] DOAP: Update chatroom URI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- doap.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doap.xml b/doap.xml index 80dc172d0c088518f676187faf4db54fca1d7756..101eddad9931175ac9e4c703f2c47c8fe8c07bc0 100644 --- a/doap.xml +++ b/doap.xml @@ -18,8 +18,8 @@ - - + + From 0b936b57c709b56d2a6cf065f2b6594a14f00ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 23:15:17 +0200 Subject: [PATCH 1005/1020] Prepare for release 0.16.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- ChangeLog | 7 +++++-- doap.xml | 16 ++++++++-------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 203a3cd216fb8f243694f186457229e4b755c683..2b48af295bd7567dd2207337f97bbc68c25b3678 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" [dependencies] minidom = "0.11.0" -jid = { version = "0.7.2", features = ["minidom"] } +jid = { version = "0.8", features = ["minidom"] } base64 = "0.10" digest = "0.8" sha-1 = "0.8" diff --git a/ChangeLog b/ChangeLog index 182d559713bd5acc6bfdfbb43d7a8279c1ca78f6..9f9ea7df6583936a9a9cf34315aa2f869fdd807a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ -Version NEXT: -DATE Emmanuel Gil Peyrot +Version 0.16.0: +2019-10-15 Emmanuel Gil Peyrot * New parsers/serialisers: - Client Certificate Management for SASL EXTERNAL (XEP-0257) - JID Prep (XEP-0328) @@ -7,10 +7,13 @@ DATE Emmanuel Gil Peyrot - OpenPGP for XMPP (XEP-0373) - Bookmarks 2 (This Time it's Serious) (XEP-0402) - Anonymous unique occupant identifiers for MUCs (XEP-0421) + - Source-Specific Media Attributes in Jingle (XEP-0339) + - Jingle RTP Feedback Negotiation (XEP-0293) * Breaking changes: - Presence constructors now take Into and assume Some. * Improvements: - CI: refactor, add caching + - Update jid-rs to 0.8 Version 0.15.0: 2019-09-06 Emmanuel Gil Peyrot diff --git a/doap.xml b/doap.xml index 101eddad9931175ac9e4c703f2c47c8fe8c07bc0..87e6fe952cf5f9b24e2d41a032844835673beb0c 100644 --- a/doap.xml +++ b/doap.xml @@ -325,7 +325,7 @@ complete 0.3 - NEXT + 0.16.0 @@ -366,7 +366,7 @@ partial 1.0.1 - NEXT + 0.16.0 Only supported in payload-type, and only for rtcp-fb. @@ -423,7 +423,7 @@ complete 0.1 - NEXT + 0.16.0 @@ -431,7 +431,7 @@ complete 0.3 - NEXT + 0.16.0 @@ -439,7 +439,7 @@ complete 0.3.0 - NEXT + 0.16.0 @@ -463,7 +463,7 @@ partial 0.4.0 - NEXT + 0.16.0 @@ -487,7 +487,7 @@ complete 0.3.0 - NEXT + 0.16.0 @@ -495,7 +495,7 @@ complete 0.1.0 - NEXT + 0.16.0 From dfd28eaf406ba88e39ac1ee71835288780ddf7c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 23:18:08 +0200 Subject: [PATCH 1006/1020] Actually bump version to 0.16.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2b48af295bd7567dd2207337f97bbc68c25b3678..e02950d45585dbada1aabbd99692d34d870b0db2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.15.0" +version = "0.16.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 1d5276ccbdc36e280af6af4da2f2148df0a22385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 23:50:35 +0200 Subject: [PATCH 1007/1020] Update quick-xml dep to 0.17 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 292b2e01c2eb7f6177bba6114ebc4c5237477cf4..e46db5a346cca27dacd00c280ea00690bdc7016a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ edition = "2018" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -quick-xml = "0.16" +quick-xml = "0.17" [features] default = ["comments"] From 1ee6609c97b9cca6a890b1ee6f5ea057dc2c1e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Tue, 15 Oct 2019 23:50:57 +0200 Subject: [PATCH 1008/1020] Re-export quick_xml dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index cf17cdac5cdcb07c112de154701fc382abeecc07..c7f43b23010d693ef3df8a92e2969cb8c608aa41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,7 +64,7 @@ //! minidom = "*" //! ``` -extern crate quick_xml; +pub use quick_xml; pub mod error; pub mod element; From 176166b60a610bac455899aff39a06f2e802dade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 16 Oct 2019 01:23:21 +0200 Subject: [PATCH 1009/1020] Ensure Jid is Hash-able MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 700edaad39ccdd747ef85c63ab76a78800dcdede..ec56a71ecd626d839c8b5efb72cdfbe1c022355c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,7 @@ impl fmt::Display for JidParseError { } /// An enum representing a Jabber ID. It can be either a `FullJid` or a `BareJid`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Jid { /// Bare Jid Bare(BareJid), @@ -624,6 +624,7 @@ mod tests { use super::*; use std::str::FromStr; + use std::collections::HashMap; #[test] fn can_parse_full_jids() { @@ -736,6 +737,11 @@ mod tests { assert_eq!(String::from(BareJid::new("a", "b")), String::from("a@b")); } + #[test] + fn hash() { + let _map: HashMap = HashMap::new(); + } + #[test] fn invalid_jids() { assert_eq!(BareJid::from_str(""), Err(JidParseError::NoDomain)); From 8c21efb2ac9402e5b79ddbe5150e81ad219c1e96 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 18 Oct 2019 13:05:14 +0200 Subject: [PATCH 1010/1020] jingle_rtp: Use the correct element name for ssrc. --- src/jingle_rtp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 0e9dcd85430179d46f0e8511e7943b33c195bcd0..4fa596a991a12db2bc049eeae18f18c239cc38a0 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -27,7 +27,7 @@ generate_element!( ssrc_groups: Vec = ("ssrc-group", JINGLE_SSMA) => Group, /// List of ssrc. - ssrcs: Vec = ("ssrc", JINGLE_SSMA) => Source + ssrcs: Vec = ("source", JINGLE_SSMA) => Source // TODO: Add support for and . ] From 7b536e5bfc4be06773c761c5efc1257cc36cb0c5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 18 Oct 2019 13:05:33 +0200 Subject: [PATCH 1011/1020] macros: Implement Display for attributes. --- src/util/macros.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/util/macros.rs b/src/util/macros.rs index d63591aa6964e9078f92448f9349c926cbfa2b25..d703e258325dbb0a03a67ee46a64f6712d29afe2 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -85,6 +85,13 @@ macro_rules! generate_attribute { }) } } + impl std::fmt::Display for $elem { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(fmt, "{}", match self { + $($elem::$a => $b),+ + }) + } + } impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { Some(String::from(match self { From 03e8ef75691adc5eae5fbc391a0f3fe1de5a444d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 18 Oct 2019 13:06:08 +0200 Subject: [PATCH 1012/1020] jingle: Expose Description the same way we expose Transport. --- src/jingle.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index fbccce0da23d9d96dfa304f91dca34a90dd8a2e1..82875686b8d6835eb4e0d2a894ac4eec03730429 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -6,6 +6,7 @@ use crate::util::error::Error; use crate::iq::IqSetPayload; +use crate::jingle_rtp::Description as RtpDescription; use crate::jingle_ice_udp::Transport as IceUdpTransport; use crate::jingle_ibb::Transport as IbbTransport; use crate::jingle_s5b::Transport as Socks5Transport; @@ -167,6 +168,52 @@ generate_id!( ContentId ); +/// Enum wrapping all of the various supported descriptions of a Content. +#[derive(Debug, Clone)] +pub enum Description { + /// Jingle RTP Sessions (XEP-0167) description. + Rtp(RtpDescription), + + /// To be used for any description that isn’t known at compile-time. + Unknown(Element), +} + +impl TryFrom for Description { + type Error = Error; + + fn try_from(elem: Element) -> Result { + Ok(if elem.is("description", ns::JINGLE_RTP) { + Description::Rtp(RtpDescription::try_from(elem)?) + } else { + Description::Unknown(elem) + }) + } +} + +impl From for Description { + fn from(desc: RtpDescription) -> Description { + Description::Rtp(desc) + } +} + +impl From for Element { + fn from(desc: Description) -> Element { + match desc { + Description::Rtp(desc) => desc.into(), + Description::Unknown(elem) => elem, + } + } +} + +impl Description { + fn get_ns(&self) -> String { + match self { + Description::Rtp(_) => String::from(ns::JINGLE_RTP), + Description::Unknown(elem) => elem.ns().unwrap_or_else(|| String::new()), + } + } +} + /// Enum wrapping all of the various supported transports of a Content. #[derive(Debug, Clone)] pub enum Transport { @@ -258,7 +305,7 @@ generate_element!( ], children: [ /// What to send. - description: Option = ("description", JINGLE) => Element, + description: Option = ("description", *) => Description, /// How to send it. transport: Option = ("transport", *) => Transport, @@ -295,8 +342,8 @@ impl Content { } /// Set the description of this content. - pub fn with_description(mut self, description: Element) -> Content { - self.description = Some(description); + pub fn with_description>(mut self, description: D) -> Content { + self.description = Some(description.into()); self } From 8a13d7f0106230ca0b4a6f82328a507d53d9f47a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 18 Oct 2019 13:06:36 +0200 Subject: [PATCH 1013/1020] jingle_ice_udp: Add methods to add a RTP candidate and set the DTLS-SRTP fingerprint. --- src/jingle_ice_udp.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index be5dbc97e1af4c1a849c233f0f58481038b51045..b7f9e777886a2e686355b88a96b1d15f3b6620e5 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -36,6 +36,18 @@ impl Transport { fingerprint: None, } } + + /// Add a candidate to this transport. + pub fn add_candidate(mut self, candidate: Candidate) -> Self { + self.candidates.push(candidate); + self + } + + /// Set the DTLS-SRTP fingerprint of this transport. + pub fn with_fingerprint(mut self, fingerprint: Fingerprint) -> Self { + self.fingerprint = Some(fingerprint); + self + } } generate_attribute!( From 2fec9083a4e84e17e6958b415d2adf869cff988e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 18 Oct 2019 13:44:25 +0200 Subject: [PATCH 1014/1020] Prepare for merge: Move all xmpp-rs files into xmpp-rs/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml => xmpp-rs/.gitlab-ci.yml | 0 Cargo.toml => xmpp-rs/Cargo.toml | 0 ChangeLog => xmpp-rs/ChangeLog | 0 LICENSE => xmpp-rs/LICENSE | 0 README.md => xmpp-rs/README.md | 0 {examples => xmpp-rs/examples}/hello_bot.rs | 0 {src => xmpp-rs/src}/lib.rs | 0 {src => xmpp-rs/src}/pubsub/avatar.rs | 0 {src => xmpp-rs/src}/pubsub/mod.rs | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename .gitlab-ci.yml => xmpp-rs/.gitlab-ci.yml (100%) rename Cargo.toml => xmpp-rs/Cargo.toml (100%) rename ChangeLog => xmpp-rs/ChangeLog (100%) rename LICENSE => xmpp-rs/LICENSE (100%) rename README.md => xmpp-rs/README.md (100%) rename {examples => xmpp-rs/examples}/hello_bot.rs (100%) rename {src => xmpp-rs/src}/lib.rs (100%) rename {src => xmpp-rs/src}/pubsub/avatar.rs (100%) rename {src => xmpp-rs/src}/pubsub/mod.rs (100%) diff --git a/.gitlab-ci.yml b/xmpp-rs/.gitlab-ci.yml similarity index 100% rename from .gitlab-ci.yml rename to xmpp-rs/.gitlab-ci.yml diff --git a/Cargo.toml b/xmpp-rs/Cargo.toml similarity index 100% rename from Cargo.toml rename to xmpp-rs/Cargo.toml diff --git a/ChangeLog b/xmpp-rs/ChangeLog similarity index 100% rename from ChangeLog rename to xmpp-rs/ChangeLog diff --git a/LICENSE b/xmpp-rs/LICENSE similarity index 100% rename from LICENSE rename to xmpp-rs/LICENSE diff --git a/README.md b/xmpp-rs/README.md similarity index 100% rename from README.md rename to xmpp-rs/README.md diff --git a/examples/hello_bot.rs b/xmpp-rs/examples/hello_bot.rs similarity index 100% rename from examples/hello_bot.rs rename to xmpp-rs/examples/hello_bot.rs diff --git a/src/lib.rs b/xmpp-rs/src/lib.rs similarity index 100% rename from src/lib.rs rename to xmpp-rs/src/lib.rs diff --git a/src/pubsub/avatar.rs b/xmpp-rs/src/pubsub/avatar.rs similarity index 100% rename from src/pubsub/avatar.rs rename to xmpp-rs/src/pubsub/avatar.rs diff --git a/src/pubsub/mod.rs b/xmpp-rs/src/pubsub/mod.rs similarity index 100% rename from src/pubsub/mod.rs rename to xmpp-rs/src/pubsub/mod.rs From dc76f296de77ff828c2acb9e188648513e73fb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 18 Oct 2019 13:45:51 +0200 Subject: [PATCH 1015/1020] Prepare for merge: Move all xmpp-parsers files into xmpp-parsers/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml => xmpp-parsers/.gitlab-ci.yml | 0 Cargo.toml => xmpp-parsers/Cargo.toml | 0 ChangeLog => xmpp-parsers/ChangeLog | 0 LICENSE => xmpp-parsers/LICENSE | 0 doap.xml => xmpp-parsers/doap.xml | 0 {examples => xmpp-parsers/examples}/generate-caps.rs | 0 {src => xmpp-parsers/src}/attention.rs | 0 {src => xmpp-parsers/src}/avatar.rs | 0 {src => xmpp-parsers/src}/bind.rs | 0 {src => xmpp-parsers/src}/blocking.rs | 0 {src => xmpp-parsers/src}/bob.rs | 0 {src => xmpp-parsers/src}/bookmarks.rs | 0 {src => xmpp-parsers/src}/bookmarks2.rs | 0 {src => xmpp-parsers/src}/caps.rs | 0 {src => xmpp-parsers/src}/carbons.rs | 0 {src => xmpp-parsers/src}/cert_management.rs | 0 {src => xmpp-parsers/src}/chatstates.rs | 0 {src => xmpp-parsers/src}/component.rs | 0 {src => xmpp-parsers/src}/csi.rs | 0 {src => xmpp-parsers/src}/data_forms.rs | 0 {src => xmpp-parsers/src}/date.rs | 0 {src => xmpp-parsers/src}/delay.rs | 0 {src => xmpp-parsers/src}/disco.rs | 0 {src => xmpp-parsers/src}/ecaps2.rs | 0 {src => xmpp-parsers/src}/eme.rs | 0 {src => xmpp-parsers/src}/forwarding.rs | 0 {src => xmpp-parsers/src}/hashes.rs | 0 {src => xmpp-parsers/src}/ibb.rs | 0 {src => xmpp-parsers/src}/ibr.rs | 0 {src => xmpp-parsers/src}/idle.rs | 0 {src => xmpp-parsers/src}/iq.rs | 0 {src => xmpp-parsers/src}/jid_prep.rs | 0 {src => xmpp-parsers/src}/jingle.rs | 0 {src => xmpp-parsers/src}/jingle_dtls_srtp.rs | 0 {src => xmpp-parsers/src}/jingle_ft.rs | 0 {src => xmpp-parsers/src}/jingle_ibb.rs | 0 {src => xmpp-parsers/src}/jingle_ice_udp.rs | 0 {src => xmpp-parsers/src}/jingle_message.rs | 0 {src => xmpp-parsers/src}/jingle_rtcp_fb.rs | 0 {src => xmpp-parsers/src}/jingle_rtp.rs | 0 {src => xmpp-parsers/src}/jingle_s5b.rs | 0 {src => xmpp-parsers/src}/jingle_ssma.rs | 0 {src => xmpp-parsers/src}/lib.rs | 0 {src => xmpp-parsers/src}/mam.rs | 0 {src => xmpp-parsers/src}/media_element.rs | 0 {src => xmpp-parsers/src}/message.rs | 0 {src => xmpp-parsers/src}/message_correct.rs | 0 {src => xmpp-parsers/src}/mood.rs | 0 {src => xmpp-parsers/src}/muc/mod.rs | 0 {src => xmpp-parsers/src}/muc/muc.rs | 0 {src => xmpp-parsers/src}/muc/user.rs | 0 {src => xmpp-parsers/src}/nick.rs | 0 {src => xmpp-parsers/src}/ns.rs | 0 {src => xmpp-parsers/src}/occupant_id.rs | 0 {src => xmpp-parsers/src}/openpgp.rs | 0 {src => xmpp-parsers/src}/ping.rs | 0 {src => xmpp-parsers/src}/presence.rs | 0 {src => xmpp-parsers/src}/pubsub/event.rs | 0 {src => xmpp-parsers/src}/pubsub/mod.rs | 0 {src => xmpp-parsers/src}/pubsub/pubsub.rs | 0 {src => xmpp-parsers/src}/receipts.rs | 0 {src => xmpp-parsers/src}/roster.rs | 0 {src => xmpp-parsers/src}/rsm.rs | 0 {src => xmpp-parsers/src}/sasl.rs | 0 {src => xmpp-parsers/src}/server_info.rs | 0 {src => xmpp-parsers/src}/sm.rs | 0 {src => xmpp-parsers/src}/stanza_error.rs | 0 {src => xmpp-parsers/src}/stanza_id.rs | 0 {src => xmpp-parsers/src}/stream.rs | 0 {src => xmpp-parsers/src}/time.rs | 0 {src => xmpp-parsers/src}/tune.rs | 0 {src => xmpp-parsers/src}/util/compare_elements.rs | 0 {src => xmpp-parsers/src}/util/error.rs | 0 {src => xmpp-parsers/src}/util/helpers.rs | 0 {src => xmpp-parsers/src}/util/macros.rs | 0 {src => xmpp-parsers/src}/util/mod.rs | 0 {src => xmpp-parsers/src}/version.rs | 0 {src => xmpp-parsers/src}/websocket.rs | 0 {src => xmpp-parsers/src}/xhtml.rs | 0 79 files changed, 0 insertions(+), 0 deletions(-) rename .gitlab-ci.yml => xmpp-parsers/.gitlab-ci.yml (100%) rename Cargo.toml => xmpp-parsers/Cargo.toml (100%) rename ChangeLog => xmpp-parsers/ChangeLog (100%) rename LICENSE => xmpp-parsers/LICENSE (100%) rename doap.xml => xmpp-parsers/doap.xml (100%) rename {examples => xmpp-parsers/examples}/generate-caps.rs (100%) rename {src => xmpp-parsers/src}/attention.rs (100%) rename {src => xmpp-parsers/src}/avatar.rs (100%) rename {src => xmpp-parsers/src}/bind.rs (100%) rename {src => xmpp-parsers/src}/blocking.rs (100%) rename {src => xmpp-parsers/src}/bob.rs (100%) rename {src => xmpp-parsers/src}/bookmarks.rs (100%) rename {src => xmpp-parsers/src}/bookmarks2.rs (100%) rename {src => xmpp-parsers/src}/caps.rs (100%) rename {src => xmpp-parsers/src}/carbons.rs (100%) rename {src => xmpp-parsers/src}/cert_management.rs (100%) rename {src => xmpp-parsers/src}/chatstates.rs (100%) rename {src => xmpp-parsers/src}/component.rs (100%) rename {src => xmpp-parsers/src}/csi.rs (100%) rename {src => xmpp-parsers/src}/data_forms.rs (100%) rename {src => xmpp-parsers/src}/date.rs (100%) rename {src => xmpp-parsers/src}/delay.rs (100%) rename {src => xmpp-parsers/src}/disco.rs (100%) rename {src => xmpp-parsers/src}/ecaps2.rs (100%) rename {src => xmpp-parsers/src}/eme.rs (100%) rename {src => xmpp-parsers/src}/forwarding.rs (100%) rename {src => xmpp-parsers/src}/hashes.rs (100%) rename {src => xmpp-parsers/src}/ibb.rs (100%) rename {src => xmpp-parsers/src}/ibr.rs (100%) rename {src => xmpp-parsers/src}/idle.rs (100%) rename {src => xmpp-parsers/src}/iq.rs (100%) rename {src => xmpp-parsers/src}/jid_prep.rs (100%) rename {src => xmpp-parsers/src}/jingle.rs (100%) rename {src => xmpp-parsers/src}/jingle_dtls_srtp.rs (100%) rename {src => xmpp-parsers/src}/jingle_ft.rs (100%) rename {src => xmpp-parsers/src}/jingle_ibb.rs (100%) rename {src => xmpp-parsers/src}/jingle_ice_udp.rs (100%) rename {src => xmpp-parsers/src}/jingle_message.rs (100%) rename {src => xmpp-parsers/src}/jingle_rtcp_fb.rs (100%) rename {src => xmpp-parsers/src}/jingle_rtp.rs (100%) rename {src => xmpp-parsers/src}/jingle_s5b.rs (100%) rename {src => xmpp-parsers/src}/jingle_ssma.rs (100%) rename {src => xmpp-parsers/src}/lib.rs (100%) rename {src => xmpp-parsers/src}/mam.rs (100%) rename {src => xmpp-parsers/src}/media_element.rs (100%) rename {src => xmpp-parsers/src}/message.rs (100%) rename {src => xmpp-parsers/src}/message_correct.rs (100%) rename {src => xmpp-parsers/src}/mood.rs (100%) rename {src => xmpp-parsers/src}/muc/mod.rs (100%) rename {src => xmpp-parsers/src}/muc/muc.rs (100%) rename {src => xmpp-parsers/src}/muc/user.rs (100%) rename {src => xmpp-parsers/src}/nick.rs (100%) rename {src => xmpp-parsers/src}/ns.rs (100%) rename {src => xmpp-parsers/src}/occupant_id.rs (100%) rename {src => xmpp-parsers/src}/openpgp.rs (100%) rename {src => xmpp-parsers/src}/ping.rs (100%) rename {src => xmpp-parsers/src}/presence.rs (100%) rename {src => xmpp-parsers/src}/pubsub/event.rs (100%) rename {src => xmpp-parsers/src}/pubsub/mod.rs (100%) rename {src => xmpp-parsers/src}/pubsub/pubsub.rs (100%) rename {src => xmpp-parsers/src}/receipts.rs (100%) rename {src => xmpp-parsers/src}/roster.rs (100%) rename {src => xmpp-parsers/src}/rsm.rs (100%) rename {src => xmpp-parsers/src}/sasl.rs (100%) rename {src => xmpp-parsers/src}/server_info.rs (100%) rename {src => xmpp-parsers/src}/sm.rs (100%) rename {src => xmpp-parsers/src}/stanza_error.rs (100%) rename {src => xmpp-parsers/src}/stanza_id.rs (100%) rename {src => xmpp-parsers/src}/stream.rs (100%) rename {src => xmpp-parsers/src}/time.rs (100%) rename {src => xmpp-parsers/src}/tune.rs (100%) rename {src => xmpp-parsers/src}/util/compare_elements.rs (100%) rename {src => xmpp-parsers/src}/util/error.rs (100%) rename {src => xmpp-parsers/src}/util/helpers.rs (100%) rename {src => xmpp-parsers/src}/util/macros.rs (100%) rename {src => xmpp-parsers/src}/util/mod.rs (100%) rename {src => xmpp-parsers/src}/version.rs (100%) rename {src => xmpp-parsers/src}/websocket.rs (100%) rename {src => xmpp-parsers/src}/xhtml.rs (100%) diff --git a/.gitlab-ci.yml b/xmpp-parsers/.gitlab-ci.yml similarity index 100% rename from .gitlab-ci.yml rename to xmpp-parsers/.gitlab-ci.yml diff --git a/Cargo.toml b/xmpp-parsers/Cargo.toml similarity index 100% rename from Cargo.toml rename to xmpp-parsers/Cargo.toml diff --git a/ChangeLog b/xmpp-parsers/ChangeLog similarity index 100% rename from ChangeLog rename to xmpp-parsers/ChangeLog diff --git a/LICENSE b/xmpp-parsers/LICENSE similarity index 100% rename from LICENSE rename to xmpp-parsers/LICENSE diff --git a/doap.xml b/xmpp-parsers/doap.xml similarity index 100% rename from doap.xml rename to xmpp-parsers/doap.xml diff --git a/examples/generate-caps.rs b/xmpp-parsers/examples/generate-caps.rs similarity index 100% rename from examples/generate-caps.rs rename to xmpp-parsers/examples/generate-caps.rs diff --git a/src/attention.rs b/xmpp-parsers/src/attention.rs similarity index 100% rename from src/attention.rs rename to xmpp-parsers/src/attention.rs diff --git a/src/avatar.rs b/xmpp-parsers/src/avatar.rs similarity index 100% rename from src/avatar.rs rename to xmpp-parsers/src/avatar.rs diff --git a/src/bind.rs b/xmpp-parsers/src/bind.rs similarity index 100% rename from src/bind.rs rename to xmpp-parsers/src/bind.rs diff --git a/src/blocking.rs b/xmpp-parsers/src/blocking.rs similarity index 100% rename from src/blocking.rs rename to xmpp-parsers/src/blocking.rs diff --git a/src/bob.rs b/xmpp-parsers/src/bob.rs similarity index 100% rename from src/bob.rs rename to xmpp-parsers/src/bob.rs diff --git a/src/bookmarks.rs b/xmpp-parsers/src/bookmarks.rs similarity index 100% rename from src/bookmarks.rs rename to xmpp-parsers/src/bookmarks.rs diff --git a/src/bookmarks2.rs b/xmpp-parsers/src/bookmarks2.rs similarity index 100% rename from src/bookmarks2.rs rename to xmpp-parsers/src/bookmarks2.rs diff --git a/src/caps.rs b/xmpp-parsers/src/caps.rs similarity index 100% rename from src/caps.rs rename to xmpp-parsers/src/caps.rs diff --git a/src/carbons.rs b/xmpp-parsers/src/carbons.rs similarity index 100% rename from src/carbons.rs rename to xmpp-parsers/src/carbons.rs diff --git a/src/cert_management.rs b/xmpp-parsers/src/cert_management.rs similarity index 100% rename from src/cert_management.rs rename to xmpp-parsers/src/cert_management.rs diff --git a/src/chatstates.rs b/xmpp-parsers/src/chatstates.rs similarity index 100% rename from src/chatstates.rs rename to xmpp-parsers/src/chatstates.rs diff --git a/src/component.rs b/xmpp-parsers/src/component.rs similarity index 100% rename from src/component.rs rename to xmpp-parsers/src/component.rs diff --git a/src/csi.rs b/xmpp-parsers/src/csi.rs similarity index 100% rename from src/csi.rs rename to xmpp-parsers/src/csi.rs diff --git a/src/data_forms.rs b/xmpp-parsers/src/data_forms.rs similarity index 100% rename from src/data_forms.rs rename to xmpp-parsers/src/data_forms.rs diff --git a/src/date.rs b/xmpp-parsers/src/date.rs similarity index 100% rename from src/date.rs rename to xmpp-parsers/src/date.rs diff --git a/src/delay.rs b/xmpp-parsers/src/delay.rs similarity index 100% rename from src/delay.rs rename to xmpp-parsers/src/delay.rs diff --git a/src/disco.rs b/xmpp-parsers/src/disco.rs similarity index 100% rename from src/disco.rs rename to xmpp-parsers/src/disco.rs diff --git a/src/ecaps2.rs b/xmpp-parsers/src/ecaps2.rs similarity index 100% rename from src/ecaps2.rs rename to xmpp-parsers/src/ecaps2.rs diff --git a/src/eme.rs b/xmpp-parsers/src/eme.rs similarity index 100% rename from src/eme.rs rename to xmpp-parsers/src/eme.rs diff --git a/src/forwarding.rs b/xmpp-parsers/src/forwarding.rs similarity index 100% rename from src/forwarding.rs rename to xmpp-parsers/src/forwarding.rs diff --git a/src/hashes.rs b/xmpp-parsers/src/hashes.rs similarity index 100% rename from src/hashes.rs rename to xmpp-parsers/src/hashes.rs diff --git a/src/ibb.rs b/xmpp-parsers/src/ibb.rs similarity index 100% rename from src/ibb.rs rename to xmpp-parsers/src/ibb.rs diff --git a/src/ibr.rs b/xmpp-parsers/src/ibr.rs similarity index 100% rename from src/ibr.rs rename to xmpp-parsers/src/ibr.rs diff --git a/src/idle.rs b/xmpp-parsers/src/idle.rs similarity index 100% rename from src/idle.rs rename to xmpp-parsers/src/idle.rs diff --git a/src/iq.rs b/xmpp-parsers/src/iq.rs similarity index 100% rename from src/iq.rs rename to xmpp-parsers/src/iq.rs diff --git a/src/jid_prep.rs b/xmpp-parsers/src/jid_prep.rs similarity index 100% rename from src/jid_prep.rs rename to xmpp-parsers/src/jid_prep.rs diff --git a/src/jingle.rs b/xmpp-parsers/src/jingle.rs similarity index 100% rename from src/jingle.rs rename to xmpp-parsers/src/jingle.rs diff --git a/src/jingle_dtls_srtp.rs b/xmpp-parsers/src/jingle_dtls_srtp.rs similarity index 100% rename from src/jingle_dtls_srtp.rs rename to xmpp-parsers/src/jingle_dtls_srtp.rs diff --git a/src/jingle_ft.rs b/xmpp-parsers/src/jingle_ft.rs similarity index 100% rename from src/jingle_ft.rs rename to xmpp-parsers/src/jingle_ft.rs diff --git a/src/jingle_ibb.rs b/xmpp-parsers/src/jingle_ibb.rs similarity index 100% rename from src/jingle_ibb.rs rename to xmpp-parsers/src/jingle_ibb.rs diff --git a/src/jingle_ice_udp.rs b/xmpp-parsers/src/jingle_ice_udp.rs similarity index 100% rename from src/jingle_ice_udp.rs rename to xmpp-parsers/src/jingle_ice_udp.rs diff --git a/src/jingle_message.rs b/xmpp-parsers/src/jingle_message.rs similarity index 100% rename from src/jingle_message.rs rename to xmpp-parsers/src/jingle_message.rs diff --git a/src/jingle_rtcp_fb.rs b/xmpp-parsers/src/jingle_rtcp_fb.rs similarity index 100% rename from src/jingle_rtcp_fb.rs rename to xmpp-parsers/src/jingle_rtcp_fb.rs diff --git a/src/jingle_rtp.rs b/xmpp-parsers/src/jingle_rtp.rs similarity index 100% rename from src/jingle_rtp.rs rename to xmpp-parsers/src/jingle_rtp.rs diff --git a/src/jingle_s5b.rs b/xmpp-parsers/src/jingle_s5b.rs similarity index 100% rename from src/jingle_s5b.rs rename to xmpp-parsers/src/jingle_s5b.rs diff --git a/src/jingle_ssma.rs b/xmpp-parsers/src/jingle_ssma.rs similarity index 100% rename from src/jingle_ssma.rs rename to xmpp-parsers/src/jingle_ssma.rs diff --git a/src/lib.rs b/xmpp-parsers/src/lib.rs similarity index 100% rename from src/lib.rs rename to xmpp-parsers/src/lib.rs diff --git a/src/mam.rs b/xmpp-parsers/src/mam.rs similarity index 100% rename from src/mam.rs rename to xmpp-parsers/src/mam.rs diff --git a/src/media_element.rs b/xmpp-parsers/src/media_element.rs similarity index 100% rename from src/media_element.rs rename to xmpp-parsers/src/media_element.rs diff --git a/src/message.rs b/xmpp-parsers/src/message.rs similarity index 100% rename from src/message.rs rename to xmpp-parsers/src/message.rs diff --git a/src/message_correct.rs b/xmpp-parsers/src/message_correct.rs similarity index 100% rename from src/message_correct.rs rename to xmpp-parsers/src/message_correct.rs diff --git a/src/mood.rs b/xmpp-parsers/src/mood.rs similarity index 100% rename from src/mood.rs rename to xmpp-parsers/src/mood.rs diff --git a/src/muc/mod.rs b/xmpp-parsers/src/muc/mod.rs similarity index 100% rename from src/muc/mod.rs rename to xmpp-parsers/src/muc/mod.rs diff --git a/src/muc/muc.rs b/xmpp-parsers/src/muc/muc.rs similarity index 100% rename from src/muc/muc.rs rename to xmpp-parsers/src/muc/muc.rs diff --git a/src/muc/user.rs b/xmpp-parsers/src/muc/user.rs similarity index 100% rename from src/muc/user.rs rename to xmpp-parsers/src/muc/user.rs diff --git a/src/nick.rs b/xmpp-parsers/src/nick.rs similarity index 100% rename from src/nick.rs rename to xmpp-parsers/src/nick.rs diff --git a/src/ns.rs b/xmpp-parsers/src/ns.rs similarity index 100% rename from src/ns.rs rename to xmpp-parsers/src/ns.rs diff --git a/src/occupant_id.rs b/xmpp-parsers/src/occupant_id.rs similarity index 100% rename from src/occupant_id.rs rename to xmpp-parsers/src/occupant_id.rs diff --git a/src/openpgp.rs b/xmpp-parsers/src/openpgp.rs similarity index 100% rename from src/openpgp.rs rename to xmpp-parsers/src/openpgp.rs diff --git a/src/ping.rs b/xmpp-parsers/src/ping.rs similarity index 100% rename from src/ping.rs rename to xmpp-parsers/src/ping.rs diff --git a/src/presence.rs b/xmpp-parsers/src/presence.rs similarity index 100% rename from src/presence.rs rename to xmpp-parsers/src/presence.rs diff --git a/src/pubsub/event.rs b/xmpp-parsers/src/pubsub/event.rs similarity index 100% rename from src/pubsub/event.rs rename to xmpp-parsers/src/pubsub/event.rs diff --git a/src/pubsub/mod.rs b/xmpp-parsers/src/pubsub/mod.rs similarity index 100% rename from src/pubsub/mod.rs rename to xmpp-parsers/src/pubsub/mod.rs diff --git a/src/pubsub/pubsub.rs b/xmpp-parsers/src/pubsub/pubsub.rs similarity index 100% rename from src/pubsub/pubsub.rs rename to xmpp-parsers/src/pubsub/pubsub.rs diff --git a/src/receipts.rs b/xmpp-parsers/src/receipts.rs similarity index 100% rename from src/receipts.rs rename to xmpp-parsers/src/receipts.rs diff --git a/src/roster.rs b/xmpp-parsers/src/roster.rs similarity index 100% rename from src/roster.rs rename to xmpp-parsers/src/roster.rs diff --git a/src/rsm.rs b/xmpp-parsers/src/rsm.rs similarity index 100% rename from src/rsm.rs rename to xmpp-parsers/src/rsm.rs diff --git a/src/sasl.rs b/xmpp-parsers/src/sasl.rs similarity index 100% rename from src/sasl.rs rename to xmpp-parsers/src/sasl.rs diff --git a/src/server_info.rs b/xmpp-parsers/src/server_info.rs similarity index 100% rename from src/server_info.rs rename to xmpp-parsers/src/server_info.rs diff --git a/src/sm.rs b/xmpp-parsers/src/sm.rs similarity index 100% rename from src/sm.rs rename to xmpp-parsers/src/sm.rs diff --git a/src/stanza_error.rs b/xmpp-parsers/src/stanza_error.rs similarity index 100% rename from src/stanza_error.rs rename to xmpp-parsers/src/stanza_error.rs diff --git a/src/stanza_id.rs b/xmpp-parsers/src/stanza_id.rs similarity index 100% rename from src/stanza_id.rs rename to xmpp-parsers/src/stanza_id.rs diff --git a/src/stream.rs b/xmpp-parsers/src/stream.rs similarity index 100% rename from src/stream.rs rename to xmpp-parsers/src/stream.rs diff --git a/src/time.rs b/xmpp-parsers/src/time.rs similarity index 100% rename from src/time.rs rename to xmpp-parsers/src/time.rs diff --git a/src/tune.rs b/xmpp-parsers/src/tune.rs similarity index 100% rename from src/tune.rs rename to xmpp-parsers/src/tune.rs diff --git a/src/util/compare_elements.rs b/xmpp-parsers/src/util/compare_elements.rs similarity index 100% rename from src/util/compare_elements.rs rename to xmpp-parsers/src/util/compare_elements.rs diff --git a/src/util/error.rs b/xmpp-parsers/src/util/error.rs similarity index 100% rename from src/util/error.rs rename to xmpp-parsers/src/util/error.rs diff --git a/src/util/helpers.rs b/xmpp-parsers/src/util/helpers.rs similarity index 100% rename from src/util/helpers.rs rename to xmpp-parsers/src/util/helpers.rs diff --git a/src/util/macros.rs b/xmpp-parsers/src/util/macros.rs similarity index 100% rename from src/util/macros.rs rename to xmpp-parsers/src/util/macros.rs diff --git a/src/util/mod.rs b/xmpp-parsers/src/util/mod.rs similarity index 100% rename from src/util/mod.rs rename to xmpp-parsers/src/util/mod.rs diff --git a/src/version.rs b/xmpp-parsers/src/version.rs similarity index 100% rename from src/version.rs rename to xmpp-parsers/src/version.rs diff --git a/src/websocket.rs b/xmpp-parsers/src/websocket.rs similarity index 100% rename from src/websocket.rs rename to xmpp-parsers/src/websocket.rs diff --git a/src/xhtml.rs b/xmpp-parsers/src/xhtml.rs similarity index 100% rename from src/xhtml.rs rename to xmpp-parsers/src/xhtml.rs From 34aa71036627ca32621d01cae162416787e6b6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 18 Oct 2019 14:16:01 +0200 Subject: [PATCH 1016/1020] Prepare for merge: Move all tokio-xmpp files into tokio-xmpp/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitignore => tokio-xmpp/.gitignore | 0 .gitlab-ci.yml => tokio-xmpp/.gitlab-ci.yml | 0 Cargo.lock => tokio-xmpp/Cargo.lock | 0 Cargo.toml => tokio-xmpp/Cargo.toml | 0 README.md => tokio-xmpp/README.md | 0 {examples => tokio-xmpp/examples}/contact_addr.rs | 0 {examples => tokio-xmpp/examples}/download_avatars.rs | 0 {examples => tokio-xmpp/examples}/echo_bot.rs | 0 {examples => tokio-xmpp/examples}/echo_component.rs | 0 logo.svg => tokio-xmpp/logo.svg | 0 {src => tokio-xmpp/src}/client/auth.rs | 0 {src => tokio-xmpp/src}/client/bind.rs | 0 {src => tokio-xmpp/src}/client/mod.rs | 0 {src => tokio-xmpp/src}/component/auth.rs | 0 {src => tokio-xmpp/src}/component/mod.rs | 0 {src => tokio-xmpp/src}/error.rs | 0 {src => tokio-xmpp/src}/event.rs | 0 {src => tokio-xmpp/src}/happy_eyeballs.rs | 0 {src => tokio-xmpp/src}/lib.rs | 0 {src => tokio-xmpp/src}/starttls.rs | 0 {src => tokio-xmpp/src}/stream_start.rs | 0 {src => tokio-xmpp/src}/xmpp_codec.rs | 0 {src => tokio-xmpp/src}/xmpp_stream.rs | 0 23 files changed, 0 insertions(+), 0 deletions(-) rename .gitignore => tokio-xmpp/.gitignore (100%) rename .gitlab-ci.yml => tokio-xmpp/.gitlab-ci.yml (100%) rename Cargo.lock => tokio-xmpp/Cargo.lock (100%) rename Cargo.toml => tokio-xmpp/Cargo.toml (100%) rename README.md => tokio-xmpp/README.md (100%) rename {examples => tokio-xmpp/examples}/contact_addr.rs (100%) rename {examples => tokio-xmpp/examples}/download_avatars.rs (100%) rename {examples => tokio-xmpp/examples}/echo_bot.rs (100%) rename {examples => tokio-xmpp/examples}/echo_component.rs (100%) rename logo.svg => tokio-xmpp/logo.svg (100%) rename {src => tokio-xmpp/src}/client/auth.rs (100%) rename {src => tokio-xmpp/src}/client/bind.rs (100%) rename {src => tokio-xmpp/src}/client/mod.rs (100%) rename {src => tokio-xmpp/src}/component/auth.rs (100%) rename {src => tokio-xmpp/src}/component/mod.rs (100%) rename {src => tokio-xmpp/src}/error.rs (100%) rename {src => tokio-xmpp/src}/event.rs (100%) rename {src => tokio-xmpp/src}/happy_eyeballs.rs (100%) rename {src => tokio-xmpp/src}/lib.rs (100%) rename {src => tokio-xmpp/src}/starttls.rs (100%) rename {src => tokio-xmpp/src}/stream_start.rs (100%) rename {src => tokio-xmpp/src}/xmpp_codec.rs (100%) rename {src => tokio-xmpp/src}/xmpp_stream.rs (100%) diff --git a/.gitignore b/tokio-xmpp/.gitignore similarity index 100% rename from .gitignore rename to tokio-xmpp/.gitignore diff --git a/.gitlab-ci.yml b/tokio-xmpp/.gitlab-ci.yml similarity index 100% rename from .gitlab-ci.yml rename to tokio-xmpp/.gitlab-ci.yml diff --git a/Cargo.lock b/tokio-xmpp/Cargo.lock similarity index 100% rename from Cargo.lock rename to tokio-xmpp/Cargo.lock diff --git a/Cargo.toml b/tokio-xmpp/Cargo.toml similarity index 100% rename from Cargo.toml rename to tokio-xmpp/Cargo.toml diff --git a/README.md b/tokio-xmpp/README.md similarity index 100% rename from README.md rename to tokio-xmpp/README.md diff --git a/examples/contact_addr.rs b/tokio-xmpp/examples/contact_addr.rs similarity index 100% rename from examples/contact_addr.rs rename to tokio-xmpp/examples/contact_addr.rs diff --git a/examples/download_avatars.rs b/tokio-xmpp/examples/download_avatars.rs similarity index 100% rename from examples/download_avatars.rs rename to tokio-xmpp/examples/download_avatars.rs diff --git a/examples/echo_bot.rs b/tokio-xmpp/examples/echo_bot.rs similarity index 100% rename from examples/echo_bot.rs rename to tokio-xmpp/examples/echo_bot.rs diff --git a/examples/echo_component.rs b/tokio-xmpp/examples/echo_component.rs similarity index 100% rename from examples/echo_component.rs rename to tokio-xmpp/examples/echo_component.rs diff --git a/logo.svg b/tokio-xmpp/logo.svg similarity index 100% rename from logo.svg rename to tokio-xmpp/logo.svg diff --git a/src/client/auth.rs b/tokio-xmpp/src/client/auth.rs similarity index 100% rename from src/client/auth.rs rename to tokio-xmpp/src/client/auth.rs diff --git a/src/client/bind.rs b/tokio-xmpp/src/client/bind.rs similarity index 100% rename from src/client/bind.rs rename to tokio-xmpp/src/client/bind.rs diff --git a/src/client/mod.rs b/tokio-xmpp/src/client/mod.rs similarity index 100% rename from src/client/mod.rs rename to tokio-xmpp/src/client/mod.rs diff --git a/src/component/auth.rs b/tokio-xmpp/src/component/auth.rs similarity index 100% rename from src/component/auth.rs rename to tokio-xmpp/src/component/auth.rs diff --git a/src/component/mod.rs b/tokio-xmpp/src/component/mod.rs similarity index 100% rename from src/component/mod.rs rename to tokio-xmpp/src/component/mod.rs diff --git a/src/error.rs b/tokio-xmpp/src/error.rs similarity index 100% rename from src/error.rs rename to tokio-xmpp/src/error.rs diff --git a/src/event.rs b/tokio-xmpp/src/event.rs similarity index 100% rename from src/event.rs rename to tokio-xmpp/src/event.rs diff --git a/src/happy_eyeballs.rs b/tokio-xmpp/src/happy_eyeballs.rs similarity index 100% rename from src/happy_eyeballs.rs rename to tokio-xmpp/src/happy_eyeballs.rs diff --git a/src/lib.rs b/tokio-xmpp/src/lib.rs similarity index 100% rename from src/lib.rs rename to tokio-xmpp/src/lib.rs diff --git a/src/starttls.rs b/tokio-xmpp/src/starttls.rs similarity index 100% rename from src/starttls.rs rename to tokio-xmpp/src/starttls.rs diff --git a/src/stream_start.rs b/tokio-xmpp/src/stream_start.rs similarity index 100% rename from src/stream_start.rs rename to tokio-xmpp/src/stream_start.rs diff --git a/src/xmpp_codec.rs b/tokio-xmpp/src/xmpp_codec.rs similarity index 100% rename from src/xmpp_codec.rs rename to tokio-xmpp/src/xmpp_codec.rs diff --git a/src/xmpp_stream.rs b/tokio-xmpp/src/xmpp_stream.rs similarity index 100% rename from src/xmpp_stream.rs rename to tokio-xmpp/src/xmpp_stream.rs From 5a6a1d7c97da84cb877daa1228a7ec3815fcc69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 18 Oct 2019 14:23:21 +0200 Subject: [PATCH 1017/1020] Prepare for merge: Move all jid-rs files into jid-rs/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitignore => jid-rs/.gitignore | 0 .gitlab-ci.yml => jid-rs/.gitlab-ci.yml | 0 CHANGELOG.md => jid-rs/CHANGELOG.md | 0 Cargo.toml => jid-rs/Cargo.toml | 0 LICENSE => jid-rs/LICENSE | 0 README.md => jid-rs/README.md | 0 {src => jid-rs/src}/lib.rs | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename .gitignore => jid-rs/.gitignore (100%) rename .gitlab-ci.yml => jid-rs/.gitlab-ci.yml (100%) rename CHANGELOG.md => jid-rs/CHANGELOG.md (100%) rename Cargo.toml => jid-rs/Cargo.toml (100%) rename LICENSE => jid-rs/LICENSE (100%) rename README.md => jid-rs/README.md (100%) rename {src => jid-rs/src}/lib.rs (100%) diff --git a/.gitignore b/jid-rs/.gitignore similarity index 100% rename from .gitignore rename to jid-rs/.gitignore diff --git a/.gitlab-ci.yml b/jid-rs/.gitlab-ci.yml similarity index 100% rename from .gitlab-ci.yml rename to jid-rs/.gitlab-ci.yml diff --git a/CHANGELOG.md b/jid-rs/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to jid-rs/CHANGELOG.md diff --git a/Cargo.toml b/jid-rs/Cargo.toml similarity index 100% rename from Cargo.toml rename to jid-rs/Cargo.toml diff --git a/LICENSE b/jid-rs/LICENSE similarity index 100% rename from LICENSE rename to jid-rs/LICENSE diff --git a/README.md b/jid-rs/README.md similarity index 100% rename from README.md rename to jid-rs/README.md diff --git a/src/lib.rs b/jid-rs/src/lib.rs similarity index 100% rename from src/lib.rs rename to jid-rs/src/lib.rs From 57b2c6a13558c7da11a344047e738bc6ab86d203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 18 Oct 2019 14:27:53 +0200 Subject: [PATCH 1018/1020] Prepare for merge: Move all minidom-rs files into minidom-rs/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitignore => minidom-rs/.gitignore | 0 .gitlab-ci.yml => minidom-rs/.gitlab-ci.yml | 0 CHANGELOG.md => minidom-rs/CHANGELOG.md | 0 Cargo.toml => minidom-rs/Cargo.toml | 0 LICENSE => minidom-rs/LICENSE | 0 README.md => minidom-rs/README.md | 0 {examples => minidom-rs/examples}/articles.rs | 0 {src => minidom-rs/src}/convert.rs | 0 {src => minidom-rs/src}/element.rs | 0 {src => minidom-rs/src}/error.rs | 0 {src => minidom-rs/src}/lib.rs | 0 {src => minidom-rs/src}/namespace_set.rs | 0 {src => minidom-rs/src}/node.rs | 0 {src => minidom-rs/src}/tests.rs | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename .gitignore => minidom-rs/.gitignore (100%) rename .gitlab-ci.yml => minidom-rs/.gitlab-ci.yml (100%) rename CHANGELOG.md => minidom-rs/CHANGELOG.md (100%) rename Cargo.toml => minidom-rs/Cargo.toml (100%) rename LICENSE => minidom-rs/LICENSE (100%) rename README.md => minidom-rs/README.md (100%) rename {examples => minidom-rs/examples}/articles.rs (100%) rename {src => minidom-rs/src}/convert.rs (100%) rename {src => minidom-rs/src}/element.rs (100%) rename {src => minidom-rs/src}/error.rs (100%) rename {src => minidom-rs/src}/lib.rs (100%) rename {src => minidom-rs/src}/namespace_set.rs (100%) rename {src => minidom-rs/src}/node.rs (100%) rename {src => minidom-rs/src}/tests.rs (100%) diff --git a/.gitignore b/minidom-rs/.gitignore similarity index 100% rename from .gitignore rename to minidom-rs/.gitignore diff --git a/.gitlab-ci.yml b/minidom-rs/.gitlab-ci.yml similarity index 100% rename from .gitlab-ci.yml rename to minidom-rs/.gitlab-ci.yml diff --git a/CHANGELOG.md b/minidom-rs/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to minidom-rs/CHANGELOG.md diff --git a/Cargo.toml b/minidom-rs/Cargo.toml similarity index 100% rename from Cargo.toml rename to minidom-rs/Cargo.toml diff --git a/LICENSE b/minidom-rs/LICENSE similarity index 100% rename from LICENSE rename to minidom-rs/LICENSE diff --git a/README.md b/minidom-rs/README.md similarity index 100% rename from README.md rename to minidom-rs/README.md diff --git a/examples/articles.rs b/minidom-rs/examples/articles.rs similarity index 100% rename from examples/articles.rs rename to minidom-rs/examples/articles.rs diff --git a/src/convert.rs b/minidom-rs/src/convert.rs similarity index 100% rename from src/convert.rs rename to minidom-rs/src/convert.rs diff --git a/src/element.rs b/minidom-rs/src/element.rs similarity index 100% rename from src/element.rs rename to minidom-rs/src/element.rs diff --git a/src/error.rs b/minidom-rs/src/error.rs similarity index 100% rename from src/error.rs rename to minidom-rs/src/error.rs diff --git a/src/lib.rs b/minidom-rs/src/lib.rs similarity index 100% rename from src/lib.rs rename to minidom-rs/src/lib.rs diff --git a/src/namespace_set.rs b/minidom-rs/src/namespace_set.rs similarity index 100% rename from src/namespace_set.rs rename to minidom-rs/src/namespace_set.rs diff --git a/src/node.rs b/minidom-rs/src/node.rs similarity index 100% rename from src/node.rs rename to minidom-rs/src/node.rs diff --git a/src/tests.rs b/minidom-rs/src/tests.rs similarity index 100% rename from src/tests.rs rename to minidom-rs/src/tests.rs From fa118433df3ca5138a4ea3eb4260231a8a3d8352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 19 Oct 2019 17:57:43 +0200 Subject: [PATCH 1019/1020] Create Cargo.toml workspace file. Add patch directives to override path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 15 + jid-rs/.gitignore | 2 - minidom-rs/.gitignore | 2 - tokio-xmpp/Cargo.lock | 1731 ----------------------------------------- tokio-xmpp/Cargo.toml | 2 +- xmpp-rs/Cargo.toml | 2 +- 6 files changed, 17 insertions(+), 1737 deletions(-) create mode 100644 Cargo.toml delete mode 100644 jid-rs/.gitignore delete mode 100644 minidom-rs/.gitignore delete mode 100644 tokio-xmpp/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..120ee5ce4432a1f2a3c5c50c2b8299b6500f0f32 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] +members = [ # alphabetically sorted + "jid-rs", + "minidom-rs", + "tokio-xmpp", + "xmpp-parsers", + "xmpp-rs", +] + +[patch.crates-io] +jid = { path = "jid-rs" } +minidom = { path = "minidom-rs" } +tokio-xmpp = { path = "tokio-xmpp" } +xmpp-parsers = { path = "xmpp-parsers" } +xmpp = { path = "xmpp-rs" } diff --git a/jid-rs/.gitignore b/jid-rs/.gitignore deleted file mode 100644 index a9d37c560c6ab8d4afbf47eda643e8c42e857716..0000000000000000000000000000000000000000 --- a/jid-rs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock diff --git a/minidom-rs/.gitignore b/minidom-rs/.gitignore deleted file mode 100644 index a9d37c560c6ab8d4afbf47eda643e8c42e857716..0000000000000000000000000000000000000000 --- a/minidom-rs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock diff --git a/tokio-xmpp/Cargo.lock b/tokio-xmpp/Cargo.lock deleted file mode 100644 index de1c70ae4c095007dbad31a9e699e2d1b6c07919..0000000000000000000000000000000000000000 --- a/tokio-xmpp/Cargo.lock +++ /dev/null @@ -1,1731 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "autocfg" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "backtrace" -version = "0.3.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "blake2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-padding" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "c2-chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cc" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "chrono" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "core-foundation" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "core-foundation-sys" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crossbeam-deque" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "enum-as-inner" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fnv" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "generic-array" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "getrandom" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hmac" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hostname" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "idna" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ipconfig" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", - "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "itoa" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "jid" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "keccak" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.64" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "linked-hash-map" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lock_api" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "markup5ever" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "string_cache 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "string_cache_codegen 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memchr" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memoffset" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "minidom" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quick-xml 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio" -version = "0.6.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio-uds" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miow" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "native-tls" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "net2" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "num-integer" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num_cpus" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "openssl" -version = "0.10.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "openssl-probe" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "openssl-sys" -version = "0.9.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pbkdf2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "phf" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "phf_codegen" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "phf_generator" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "phf_shared" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pkg-config" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ppv-lite86" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro2" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quick-error" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quick-xml" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quick-xml" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_chacha" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_syscall" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "remove_dir_all" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "resolv-conf" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ryu" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sasl" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "schannel" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "security-framework" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "security-framework-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde_derive" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_json" -version = "1.0.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha-1" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha3" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "siphasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "slab" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "smallvec" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "socket2" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "string_cache" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "string_cache_codegen 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "string_cache_codegen" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "string_cache_shared" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synstructure" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tempfile" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tendril" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "time" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-codec" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-executor" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-fs" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-io" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-sync" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-timer" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-tls" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-udp" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-uds" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-xmpp" -version = "1.0.1" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-xml 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sasl 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-resolver 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "xml5ever 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "xmpp-parsers 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "trust-dns-proto" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "typenum" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "url" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "utf-8" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vcpkg" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasi" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "widestring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winreg" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winutil" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "xml5ever" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "markup5ever 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "xmpp-parsers" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" -"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" -"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" -"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" -"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" -"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d58266c97445680766be408285e798d3401c6d4c378ec5552e78737e681e37d" -"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" -"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" -"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" -"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" -"checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" -"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -"checksum ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f" -"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum jid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "32b4cfc8edfd18c386be7b4e7307e59471aed5e21cd8b80e1aaf070b42de163d" -"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)" = "74dfca3d9957906e8d1e6a0b641dc9a59848e793f1da2165889fd4f62d10d79c" -"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" -"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -"checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -"checksum markup5ever 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "65381d9d47506b8592b97c4efd936afcf673b09b059f2bef39c7211ee78b9d03" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" -"checksum minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "df1f29c97815c4328d6b533d2b8976e80cbed02d97063970aa4a80c100ecf29a" -"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" -"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30" -"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" -"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" -"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)" = "ba24190c8f0805d3bd2ce028f439fe5af1d55882bbe6261bed1dbc93b50dd6b1" -"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" -"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" -"checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" -"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" -"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -"checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" -"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" -"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0" -"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quick-xml 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd45021132c1cb5540995e93fcc2cf5a874ef84f9639168fb6819caa023d4be" -"checksum quick-xml 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcdba8c8d71275493d966ef052a88726ac8590c15a09968b32158205c672ef" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" -"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" -"checksum sasl 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e457758c85b736bbad56dc099406cd2a9c19554cf81880dba7a51d092929e600" -"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" -"checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2" -"checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" -"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" -"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" -"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" -"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" -"checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" -"checksum string_cache 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "96ccb3a75a3caf2d7f2eb9ada86ec1fbbd4c74ad2bd8dc00a96a0c2f93509ef0" -"checksum string_cache_codegen 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f45ed1b65bf9a4bf2f7b7dc59212d1926e9eaf00fa998988e420fd124467c6" -"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" -"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" -"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" -"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" -"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" -"checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac" -"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" -"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" -"checksum tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c56391be9805bc80163151c0b9e5164ee64f4b0200962c346fea12773158f22d" -"checksum tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d06554cce1ae4a50f42fba8023918afa931413aded705b560e29600ccf7c6d76" -"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2bd2c6a3885302581f4401c82af70d792bb9df1700e7437b0aeb4ada94d5388c" -"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" -"checksum tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" -"checksum tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f02298505547f73e60f568359ef0d016d5acd6e830ab9bc7c4a5b3403440121b" -"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" -"checksum trust-dns-proto 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05457ece29839d056d8cb66ec080209d34492b3d2e7e00641b486977be973db9" -"checksum trust-dns-resolver 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb1b3a41ee784f8da051cd342c6f42a3a75ee45818164acad867eac8f2f85332" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" -"checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" -"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" -"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" -"checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" -"checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum xml5ever 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b93ca59bfbbc3c0f807a61faea54e6a4c753f82857d3730e9afb5523b6149c7" -"checksum xmpp-parsers 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57991f57c0011b66caeed8ed4380489b42c39c41364e31e4bbe011649cb79002" diff --git a/tokio-xmpp/Cargo.toml b/tokio-xmpp/Cargo.toml index 7bee03f8fb0cb463381d1b677488da7aedc75860..0ea41d967d3a02046885d057b43822f7aeda240a 100644 --- a/tokio-xmpp/Cargo.toml +++ b/tokio-xmpp/Cargo.toml @@ -25,4 +25,4 @@ tokio-io = "0.1" tokio-tls = "0.2" quick-xml = "0.17" xml5ever = "0.15" -xmpp-parsers = "0.15" +xmpp-parsers = "0.16" diff --git a/xmpp-rs/Cargo.toml b/xmpp-rs/Cargo.toml index 27622a8347733ca847be5a8b219f3a19a966b587..f14c16ec84242ea1d4527013d1ca74348ee0857e 100644 --- a/xmpp-rs/Cargo.toml +++ b/xmpp-rs/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" [dependencies] tokio-xmpp = "1.0.1" -xmpp-parsers = "0.15" +xmpp-parsers = "0.16" futures = "0.1" tokio = "0.1" log = "0.4" From a8c33fe9116f5ee2cb13a99afa890c77efee3b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 19 Oct 2019 17:59:24 +0200 Subject: [PATCH 1020/1020] Update TokioXmppEvent::Online handler with update jid arg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- xmpp-rs/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmpp-rs/src/lib.rs b/xmpp-rs/src/lib.rs index 486d632649533c8ddb8c1035b1ca6bad9efa0e91..4c96db27c966aab663111933d7a2a7be4fa9730e 100644 --- a/xmpp-rs/src/lib.rs +++ b/xmpp-rs/src/lib.rs @@ -201,7 +201,7 @@ impl ClientBuilder<'_> { }; match event { - TokioXmppEvent::Online => { + TokioXmppEvent::Online(_) => { let presence = ClientBuilder::make_initial_presence(&disco, &node).into(); let packet = Packet::Stanza(presence); sender_tx.unbounded_send(packet)

) -> Item { + Item { + id, + publisher, + payload: payload.map(|payload| payload.into()), + } + } +} + /// This trait should be implemented on any element which can be included as a PubSub payload. pub trait PubSubPayload: crate::TryFrom + Into {} diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 637b050aea1d04716d867789effcec21d862f9ea..c7bb6466cd948e83aa21602ef6acf1a9804e0755 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -8,7 +8,7 @@ use crate::data_forms::DataForm; use crate::util::error::Error; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId}; +use crate::pubsub::{NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; use minidom::Element; use try_from::TryFrom; @@ -113,45 +113,11 @@ generate_element!( ] ); -/// An item from a PubSub node. +/// Response wrapper for a PubSub ``. #[derive(Debug, Clone)] -pub struct Item { - /// The payload of this item, in an arbitrary namespace. - pub payload: Option, +pub struct Item(pub PubSubItem); - /// The 'id' attribute of this item. - pub id: Option, -} - -impl TryFrom for Item { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "item", PUBSUB); - check_no_unknown_attributes!(elem, "item", ["id"]); - let mut payloads = elem.children().cloned().collect::>(); - let payload = payloads.pop(); - if !payloads.is_empty() { - return Err(Error::ParseError( - "More than a single payload in item element.", - )); - } - Ok(Item { - payload, - id: get_attr!(elem, "id", optional), - }) - } -} - -impl From for Element { - fn from(item: Item) -> Element { - Element::builder("item") - .ns(ns::PUBSUB) - .attr("id", item.id) - .append(item.payload) - .build() - } -} +impl_pubsub_item!(Item, PUBSUB); generate_element!( /// The options associated to a subscription request. diff --git a/src/util/macros.rs b/src/util/macros.rs index fd3a0ada1febe68c177c15e1a4e57dc7cf4968db..0c296abba07e94e05d7fb32db3b7dd749ea5a0a8 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -596,3 +596,54 @@ macro_rules! assert_size ( assert_eq!(::std::mem::size_of::<$t>(), $sz); ); ); + +// TODO: move that to src/pubsub/mod.rs, once we figure out how to use macros from there. +macro_rules! impl_pubsub_item { + ($item:ident, $ns:ident) => { + impl crate::TryFrom for $item { + type Err = Error; + + fn try_from(elem: crate::Element) -> Result<$item, Error> { + check_self!(elem, "item", $ns); + check_no_unknown_attributes!(elem, "item", ["id", "publisher"]); + let mut payloads = elem.children().cloned().collect::>(); + let payload = payloads.pop(); + if !payloads.is_empty() { + return Err(Error::ParseError( + "More than a single payload in item element.", + )); + } + Ok($item(crate::pubsub::Item { + id: get_attr!(elem, "id", optional), + publisher: get_attr!(elem, "publisher", optional), + payload, + })) + } + } + + impl From<$item> for crate::Element { + fn from(item: $item) -> crate::Element { + crate::Element::builder("item") + .ns(ns::$ns) + .attr("id", item.0.id) + .attr("publisher", item.0.publisher) + .append(item.0.payload) + .build() + } + } + + impl ::std::ops::Deref for $item { + type Target = crate::pubsub::Item; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl ::std::ops::DerefMut for $item { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + } +} From be78f6578befe107deabdf040d4297f994a52054 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 29 Jan 2019 01:34:44 +0100 Subject: [PATCH 0834/1020] Revert "client: add more state to make close() send ``" This reverts commit 6379f91e02fc0813e8478a1115bb9050d2a23cb6. --- src/client/mod.rs | 48 +++++++++-------------------------------------- 1 file changed, 9 insertions(+), 39 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 025b4395f173fbfe52507d6af4021631584197fe..9033b83bcb624f779ee5f2b7fc5ba974ff90450d 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -35,8 +35,6 @@ enum ClientState { Disconnected, Connecting(Box>), Connected(XMPPStream), - ClosingSendEnd(futures::sink::Send), - ClosingClose(XMPPStream), } impl Client { @@ -190,14 +188,6 @@ impl Stream for Client { Err(e) => Err(e)?, } } - ClientState::ClosingSendEnd(_) => { - self.state = state; - Ok(Async::NotReady) - } - ClientState::ClosingClose(_) => { - self.state = state; - Ok(Async::NotReady) - } } } } @@ -229,40 +219,20 @@ impl Sink for Client { fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { match self.state { ClientState::Connected(ref mut stream) => stream.poll_complete().map_err(|e| e.into()), - ClientState::ClosingSendEnd(ref mut send) => { - match send.poll()? { - Async::NotReady => - Ok(Async::NotReady), - Async::Ready(stream) => { - self.state = ClientState::ClosingClose(stream); - self.poll_complete() - } - } - } - ClientState::ClosingClose(ref mut stream) => { - match stream.close()? { - Async::NotReady => - Ok(Async::NotReady), - Async::Ready(()) => { - self.state = ClientState::Disconnected; - Ok(Async::Ready(())) - } - } - } _ => Ok(Async::Ready(())), } } - /// Send ` and later close the inner TCP stream. + /// This closes the inner TCP stream. + /// + /// To synchronize your shutdown with the server side, you should + /// first send `Packet::StreamEnd` and wait for the end of the + /// incoming stream before closing the connection. fn close(&mut self) -> Poll<(), Self::SinkError> { - let state = replace(&mut self.state, ClientState::Disconnected); - - match state { - ClientState::Connected(stream) => { - let send = stream.send(Packet::StreamEnd); - self.state = ClientState::ClosingSendEnd(send); - self.poll_complete() - } + match self.state { + ClientState::Connected(ref mut stream) => + stream.close() + .map_err(|e| e.into()), _ => Ok(Async::Ready(())), } From 39346506c89e82744f17f580eb145098a435b0c9 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 29 Jan 2019 01:50:55 +0100 Subject: [PATCH 0835/1020] xmpp_codec: remove packet encoding unwrap()s --- src/xmpp_codec.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 82eda4a614d63d20076bc6cf5a17ff0bf4cbff45..17894b3cc027a35daa9b60881bf1bcb1500857dd 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -277,20 +277,28 @@ impl Encoder for XMPPCodec { dst.reserve(max_stanza_size - remaining); } + fn to_io_err>>(e: E) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, e) + } + match item { Packet::StreamStart(start_attrs) => { let mut buf = String::new(); - write!(buf, "\n").unwrap(); + write!(buf, ">\n") + .map_err(to_io_err)?; // print!(">> {}", buf); - write!(dst, "{}", buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)) + write!(dst, "{}", buf) + .map_err(to_io_err) } Packet::Stanza(stanza) => { stanza @@ -299,7 +307,7 @@ impl Encoder for XMPPCodec { // println!(">> {:?}", dst); Ok(()) }) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, format!("{}", e))) + .map_err(|e| to_io_err(format!("{}", e))) } Packet::Text(text) => { write_text(&text, dst) @@ -307,7 +315,7 @@ impl Encoder for XMPPCodec { // println!(">> {:?}", dst); Ok(()) }) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, format!("{}", e))) + .map_err(to_io_err) } // TODO: Implement all _ => Ok(()), From 1e7031aa3109c1beb9d67abe996df8793774d70a Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 29 Jan 2019 01:51:22 +0100 Subject: [PATCH 0836/1020] xmpp_codec: encode --- src/xmpp_codec.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 17894b3cc027a35daa9b60881bf1bcb1500857dd..482cd78694603e912caa905258582aea65cdc8b3 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -317,8 +317,10 @@ impl Encoder for XMPPCodec { }) .map_err(to_io_err) } - // TODO: Implement all - _ => Ok(()), + Packet::StreamEnd => { + write!(dst, "\n") + .map_err(to_io_err) + } } } } From 2654722fa3a6ddfc634e7025484c6665042456af Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 29 Jan 2019 01:52:07 +0100 Subject: [PATCH 0837/1020] client: switch SinkItem to Packet this breaks backwards compatibility --- examples/echo_bot.rs | 10 +++++----- src/client/mod.rs | 19 ++++--------------- src/lib.rs | 1 + 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 0b69b9342e824c8a96492429e051618d0fb88c2c..9a47ccb5769a0d47e8cd7443f0ed8e891d489b7b 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -2,7 +2,7 @@ use futures::{future, Future, Sink, Stream}; use std::env::args; use std::process::exit; use tokio::runtime::current_thread::Runtime; -use tokio_xmpp::Client; +use tokio_xmpp::{Client, Packet}; use xmpp_parsers::{Jid, Element, TryFrom}; use xmpp_parsers::message::{Body, Message, MessageType}; use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType}; @@ -34,7 +34,7 @@ fn main() { let presence = make_presence(); let sink = sink_state.take().unwrap(); - sink_future = Some(Box::new(sink.send(presence))); + sink_future = Some(Box::new(sink.send(Packet::Stanza(presence)))); } else if let Some(message) = event .into_stanza() .and_then(|stanza| Message::try_from(stanza).ok()) @@ -42,15 +42,15 @@ fn main() { match (message.from, message.bodies.get("")) { (Some(ref from), Some(ref body)) if body.0 == "die" => { println!("Secret die command triggered by {}", from); - let sink = sink_state.as_mut().unwrap(); - sink.close().expect("close"); + let sink = sink_state.take().unwrap(); + sink_future = Some(Box::new(sink.send(Packet::StreamEnd))); } (Some(ref from), Some(ref body)) => { if message.type_ != MessageType::Error { // This is a message we'll echo let reply = make_reply(from.clone(), &body.0); let sink = sink_state.take().unwrap(); - sink_future = Some(Box::new(sink.send(reply))); + sink_future = Some(Box::new(sink.send(Packet::Stanza(reply)))); } } _ => {} diff --git a/src/client/mod.rs b/src/client/mod.rs index 9033b83bcb624f779ee5f2b7fc5ba974ff90450d..99965dc8ec695e4a1ee09a850dd343873a7e3182 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,6 +1,6 @@ use futures::{done, Async, AsyncSink, Future, Poll, Sink, StartSend, Stream}; use idna; -use xmpp_parsers::{Jid, JidParseError, Element}; +use xmpp_parsers::{Jid, JidParseError}; use sasl::common::{ChannelBinding, Credentials}; use std::mem::replace; use std::str::FromStr; @@ -193,24 +193,13 @@ impl Stream for Client { } impl Sink for Client { - type SinkItem = Element; + type SinkItem = Packet; type SinkError = Error; fn start_send(&mut self, item: Self::SinkItem) -> StartSend { match self.state { - ClientState::Connected(ref mut stream) => { - match stream.start_send(Packet::Stanza(item)) { - Ok(AsyncSink::NotReady(Packet::Stanza(stanza))) => - Ok(AsyncSink::NotReady(stanza)), - Ok(AsyncSink::NotReady(_)) => { - panic!("Client.start_send with stanza but got something else back") - } - Ok(AsyncSink::Ready) => - Ok(AsyncSink::Ready), - Err(e) => - Err(e)?, - } - } + ClientState::Connected(ref mut stream) => + Ok(stream.start_send(item)?), _ => Ok(AsyncSink::NotReady(item)), } diff --git a/src/lib.rs b/src/lib.rs index 39876b1917baaed46289aa35a5fd7e5d7997d36a..50fdcf7cecb62971b4498ab1f1b6fc6dee82007c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ extern crate derive_error; mod starttls; mod stream_start; pub mod xmpp_codec; +pub use crate::xmpp_codec::Packet; pub mod xmpp_stream; pub use crate::starttls::StartTlsClient; mod event; From 1d69b1a256f9d64deb4dce0422736056798b30be Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 29 Jan 2019 23:04:42 +0100 Subject: [PATCH 0838/1020] improve echo_bot example --- examples/echo_bot.rs | 47 ++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/examples/echo_bot.rs b/examples/echo_bot.rs index 9a47ccb5769a0d47e8cd7443f0ed8e891d489b7b..cda70536a2eab561d00ec4d4bfc89437669f73d6 100644 --- a/examples/echo_bot.rs +++ b/examples/echo_bot.rs @@ -24,17 +24,32 @@ fn main() { // Make the two interfaces for sending and receiving independent // of each other so we can move one into a closure. let (sink, stream) = client.split(); - let mut sink_state = Some(sink); + + // Create outgoing pipe + let (mut tx, rx) = futures::unsync::mpsc::unbounded(); + rt.spawn( + rx.forward( + sink.sink_map_err(|_| panic!("Pipe")) + ) + .map(|(rx, mut sink)| { + drop(rx); + let _ = sink.close(); + }) + .map_err(|e| { + panic!("Send error: {:?}", e); + }) + ); + // Main loop, processes events + let mut wait_for_stream_end = false; let done = stream.for_each(move |event| { - let mut sink_future = None; - - if event.is_online() { + if wait_for_stream_end { + /* Do nothing */ + } else if event.is_online() { println!("Online!"); let presence = make_presence(); - let sink = sink_state.take().unwrap(); - sink_future = Some(Box::new(sink.send(Packet::Stanza(presence)))); + tx.start_send(Packet::Stanza(presence)).unwrap(); } else if let Some(message) = event .into_stanza() .and_then(|stanza| Message::try_from(stanza).ok()) @@ -42,31 +57,21 @@ fn main() { match (message.from, message.bodies.get("")) { (Some(ref from), Some(ref body)) if body.0 == "die" => { println!("Secret die command triggered by {}", from); - let sink = sink_state.take().unwrap(); - sink_future = Some(Box::new(sink.send(Packet::StreamEnd))); + wait_for_stream_end = true; + tx.start_send(Packet::StreamEnd).unwrap(); } (Some(ref from), Some(ref body)) => { if message.type_ != MessageType::Error { // This is a message we'll echo let reply = make_reply(from.clone(), &body.0); - let sink = sink_state.take().unwrap(); - sink_future = Some(Box::new(sink.send(Packet::Stanza(reply)))); + tx.start_send(Packet::Stanza(reply)).unwrap(); } } _ => {} } - }; + } - sink_future - .map(|future| { - let wait_send: Box> = - Box::new(future - .map(|sink| { - sink_state = Some(sink); - })); - wait_send - }) - .unwrap_or_else(|| Box::new(future::ok(()))) + future::ok(()) }); // Start polling `done` From 12e2f4e7e75170a48ef9ae76e70533072abf7340 Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 29 Jan 2019 23:08:27 +0100 Subject: [PATCH 0839/1020] tokio-xmpp 1.0.0 --- Cargo.lock | 479 +++++++++++++++++++++++++++++------------------------ Cargo.toml | 2 +- 2 files changed, 265 insertions(+), 216 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88d2a7cb6b842c4606c5786b35f8772be98850ef..302b72327dbadbda9333b807ffbcd53ab03e9489 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,9 +1,9 @@ [[package]] name = "MacTypes-sys" -version = "1.3.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -16,7 +16,7 @@ dependencies = [ [[package]] name = "autocfg" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -24,10 +24,10 @@ name = "backtrace" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -38,24 +38,15 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "base64" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "base64" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -68,39 +59,39 @@ name = "blake2" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "block-buffer" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "block-padding" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "byte-tools" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" -version = "1.2.7" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -108,7 +99,7 @@ name = "bytes" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -151,7 +142,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -159,18 +150,31 @@ name = "core-foundation-sys" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "crossbeam-channel" -version = "0.3.6" +name = "crossbeam" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-channel" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -178,18 +182,18 @@ name = "crossbeam-deque" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -197,10 +201,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -232,7 +237,7 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -260,9 +265,9 @@ name = "failure_derive" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -284,6 +289,11 @@ name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fuchsia-cprng" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -320,12 +330,21 @@ dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hmac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hostname" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -336,7 +355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -344,7 +363,7 @@ name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -401,7 +420,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.46" +version = "0.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -446,9 +465,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -461,12 +480,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memchr" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -494,7 +512,7 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", @@ -508,7 +526,7 @@ version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -529,14 +547,14 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", "schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -546,7 +564,7 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -581,12 +599,12 @@ name = "num_cpus" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "opaque-debug" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -598,7 +616,7 @@ dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -613,7 +631,7 @@ version = "0.9.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -640,13 +658,22 @@ name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pbkdf2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "percent-encoding" version = "1.0.1" @@ -675,7 +702,7 @@ version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -698,7 +725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "0.4.24" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -714,10 +741,10 @@ name = "quick-xml" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "encoding_rs 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -727,36 +754,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -767,21 +795,21 @@ name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_core" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_core" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -789,7 +817,7 @@ name = "rand_hc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -797,18 +825,28 @@ name = "rand_isaac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rand_os" +name = "rand_jitter" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -818,7 +856,7 @@ name = "rand_pcg" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -827,7 +865,7 @@ name = "rand_xorshift" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -835,12 +873,12 @@ name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "redox_syscall" -version = "0.1.50" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -878,18 +916,17 @@ name = "ryu" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "safemem" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "sasl" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -908,23 +945,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "security-framework" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "security-framework-sys" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "MacTypes-sys 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -942,27 +979,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.84" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.84" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -970,10 +1007,10 @@ name = "sha-1" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -981,10 +1018,10 @@ name = "sha2" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -992,11 +1029,11 @@ name = "sha3" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1011,7 +1048,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1023,8 +1060,8 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1042,7 +1079,7 @@ dependencies = [ "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1054,8 +1091,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1081,11 +1118,11 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.24" +version = "0.15.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1102,9 +1139,9 @@ name = "synstructure" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1114,9 +1151,9 @@ version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1136,14 +1173,14 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1156,9 +1193,10 @@ dependencies = [ "tokio-fs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1187,7 +1225,7 @@ name = "tokio-executor" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1198,7 +1236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1216,7 +1254,7 @@ name = "tokio-reactor" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1228,6 +1266,14 @@ dependencies = [ "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio-sync" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio-tcp" version = "0.1.3" @@ -1243,25 +1289,27 @@ dependencies = [ [[package]] name = "tokio-threadpool" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-timer" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1299,7 +1347,7 @@ dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1310,7 +1358,7 @@ dependencies = [ [[package]] name = "tokio-xmpp" -version = "0.2.2" +version = "1.0.0" dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "derive-error 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1318,43 +1366,43 @@ dependencies = [ "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "quick-xml 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sasl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "sasl 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-resolver 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-resolver 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", "xml5ever 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "xmpp-parsers 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trust-dns-proto" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trust-dns-resolver" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1365,9 +1413,9 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1393,8 +1441,11 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "unicode-xid" @@ -1434,11 +1485,6 @@ name = "vcpkg" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "void" version = "1.0.2" @@ -1519,7 +1565,7 @@ name = "xmpp-parsers" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1532,19 +1578,18 @@ dependencies = [ ] [metadata] -"checksum MacTypes-sys 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7dbbe033994ae2198a18517c7132d952a29fb1db44249a1234779da7c50f4698" +"checksum MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf9f0d0b1cc33a4d2aee14fb4b2eac03462ef4db29c8ac4057327d8a71ad86f" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" -"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" +"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" "checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" -"checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2" -"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91721a6330935673395a0607df4d49a9cb90ae12d259f1b3e0a3f6e1d486872e" -"checksum block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49665c62e0e700857531fa5d3763e91b539ff1abeebd56808d378b495870d60d" -"checksum block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc4358306e344bf9775d0197fd00d2603e5afb0771bb353538630f022068ea3" -"checksum byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "980479e6fde23246dfb54d47580d66b4e99202e7579c5eaa9fe10ecb5ebd2182" -"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" +"checksum block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "509de513cca6d92b6aacf9c61acfe7eaa160837323a81068d690cc1f8e5740da" +"checksum block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d75255892aeb580d3c566f213a2b6fdc1c66667839f45719ee1d30ebf2aea591" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" "checksum case 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e88b166b48e29667f5443df64df3c61dc07dc2b1a0b0d231800e07f09a33ecc1" "checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" @@ -1553,25 +1598,28 @@ dependencies = [ "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" "checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" -"checksum crossbeam-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "137bc235f622ffaa0428e3854e24acb53291fc0b3ff6fb2cb75a8be6fb02f06b" +"checksum crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad4c7ea749d9fb09e23c5cb17e3b70650860553a0e2744e38446b1803bf7db94" +"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" -"checksum crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f10a4f8f409aaac4b16a5474fb233624238fcdeefb9ba50d5ea059aab63ba31c" -"checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" +"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" +"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" "checksum derive-error 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ec098440b29ea3b1ece3e641bac424c19cf996779b623c9e0f2171495425c2c8" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" -"checksum encoding_rs 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)" = "a69d152eaa438a291636c1971b0a370212165ca8a75759eb66818c5ce9b538f7" +"checksum encoding_rs 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)" = "fd251508d65030820f3a4317af2248180db337fdb25d89967956242580277813" "checksum error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6930e04918388a9a2e41d518c25cf679ccafe26733fb4127dbf21993f2575d46" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f7f8eb465745ea9b02e2704612a9946a59fa40572086c6fd49d6ddcf30bf31" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" "checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" "checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" +"checksum hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f127a908633569f208325f86f71255d3363c79721d7f9fe31cd5569908819771" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" @@ -1582,7 +1630,7 @@ dependencies = [ "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)" = "023a4cd09b2ff695f9734c1934145a315594b7986398496841c7031a5a1bbdbd" +"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" "checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" @@ -1590,7 +1638,7 @@ dependencies = [ "checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" "checksum markup5ever 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "897636f9850c3eef4905a5540683ed53dc9393860f0846cab2c2ddf9939862ff" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9" +"checksum memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e1dd4eaac298c32ce07eb6ed9242eda7d82955b9170b7d6db59b2e02cc63fcb8" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum minidom 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "275024eea6c6ff4ace22f2750843831183785288eec1cff91a4e6b8898cf94f9" "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" @@ -1603,13 +1651,14 @@ dependencies = [ "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238" -"checksum opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51ecbcb821e1bd256d456fe858aaa7f380b63863eab2eb86eee1bd9f33dd6682" +"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" "checksum openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ec7bd7ca4cce6dbdc77e7c1230682740d307d1218a87fb0349a571272be749f9" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)" = "1bb974e77de925ef426b6bc82fce15fd45bdcbeb5728bffcfc7cdeeb7ce1c2d6" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" @@ -1617,45 +1666,45 @@ dependencies = [ "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" +"checksum proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "38fddd23d98b2144d197c0eca5705632d4fe2667d14a6be5df8934f8d74f1978" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quick-xml 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98d8d2d671bd29c6122a98b45ce3106391e89ba378f731274de677f1eff06e5f" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" -"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" -"checksum rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3906503e80ac6cbcacb2c2973fa8e473f24d7e2747c8c92bb230c2441cad96b5" +"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" +"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" -"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46fbd5550acf75b0c2730f5dd1873751daf9beb8f11b44027778fae50d7feca" +"checksum rand_jitter 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "29fe7b8bc348249f3b1bbb9ab8baa6fa3419196ecfbf213408bca1b2494710de" +"checksum rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b7c690732391ae0abafced5015ffb53656abfaec61b342290e5eb56b286a679d" "checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2" +"checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" -"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" -"checksum sasl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4dbda5436dcd059da44fbf84b87a7164c5e3cad354efd0630c1f2be3c51c8859" +"checksum sasl 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e457758c85b736bbad56dc099406cd2a9c19554cf81880dba7a51d092929e600" "checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "697d3f3c23a618272ead9e1fb259c1411102b31c6af8b93f1d64cca9c3b0e8e0" -"checksum security-framework-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "40d95f3d7da09612affe897f320d78264f0d2320f3e8eea27d12bd1bd94445e2" +"checksum security-framework 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfab8dda0e7a327c696d893df9ffa19cadc4bd195797997f5223cf5831beaf05" +"checksum security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3d6696852716b589dff9e886ff83778bb635150168e83afa8ac6b8a78cb82abc" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "0e732ed5a5592c17d961555e3b552985baf98d50ce418b7b655f31f6ba7eb1b7" -"checksum serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d6115a3ca25c224e409185325afc16a0d5aaaabc15c42b09587d6f1ba39a5b" -"checksum serde_json 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb1277d4d0563e4593e0b8b5d23d744d277b55d2bc0bf1c38d0d8a6589d38aa" +"checksum serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)" = "534b8b91a95e0f71bca3ed5824752d558da048d4248c91af873b63bd60519752" +"checksum serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)" = "a915306b0f1ac5607797697148c223bedeaa36bcc2e28a01441cd638cc6567b4" +"checksum serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "4b90a9fbe1211e57d3e1c15670f1cb00802988fb23a1a4aad7a2b63544f1920e" "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" "checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" "checksum sha3 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34a5e54083ce2b934bf059fdf38e7330a154177e029ab6c4e18638f2f624053a" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db" +"checksum smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "88aea073965ab29f6edb5493faf96ad662fb18aa9eeb186a3b7057951605ed15" "checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" @@ -1663,38 +1712,38 @@ dependencies = [ "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)" = "734ecc29cd36e8123850d9bf21dfd62ef8300aaa8f879aabaa899721808be37c" +"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2" "checksum tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum tokio 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4790d0be6f4ba6ae4f48190efa2ed7780c9e3567796abdb285003cf39840d9c5" +"checksum tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "e0500b88064f08bebddd0c0bed39e19f5c567a5f30975bee52b0c0d3e2eeb38c" "checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" "checksum tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "331c8acc267855ec06eb0c94618dcbbfea45bed2d20b77252940095273fb58f6" "checksum tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30c6dbf2d1ad1de300b393910e8a3aa272b724a400b6531da03eed99e329fbf0" "checksum tokio-fs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9cbbc8a3698b7ab652340f46633364f9eaa928ddaaee79d8b8f356dd79a09d" "checksum tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b53aeb9d3f5ccf2ebb29e19788f96987fa1355f8fe45ea193928eaaaf3ae820f" "checksum tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "afbcdb0f0d2a1e4c440af82d7bbf0bf91a8a8c0575bcd20c05d15be7e9d3a02f" +"checksum tokio-sync 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d65a58e2215c13179e6eeb2cf00511e0aee455cad40a9bfaef15a2fd8aab1c7" "checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "17465013014410310f9f61fa10bf4724803c149ea1d51efece131c38efca93aa" -"checksum tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4f37f0111d76cc5da132fe9bc0590b9b9cfd079bc7e75ac3846278430a299ff8" +"checksum tokio-threadpool 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c3fd86cb15547d02daa2b21aadaf4e37dee3368df38a526178a5afa3c034d2fb" +"checksum tokio-timer 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "21c04a314a1f69f73c0227beba6250e06cdc1e9a62e7eff912bf54a59b6d1b94" "checksum tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" "checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" "checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" -"checksum trust-dns-proto 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30dde452f5d142d5e316a3b32386da95280c98b7e266639f8f3bc6fdf507d279" -"checksum trust-dns-resolver 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "de630f95a192f793436ffae5137e88253cc4142a97d9a8e73c8d804fa85ddf0a" +"checksum trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09144f0992b0870fa8d2972cc069cbf1e3c0fda64d1f3d45c4d68d0e0b52ad4e" +"checksum trust-dns-resolver 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8a9f877f7a1ad821ab350505e1f1b146a4960402991787191d6d8cab2ce2de2c" "checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" +"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum widestring 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7157704c2e12e3d2189c507b7482c52820a16dfa4465ba91add92f266667cadb" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" diff --git a/Cargo.toml b/Cargo.toml index afda4d2765c1a2d43e456f863bc26f71405d4c55..78e175fdd4816fd3e6c1ec80d2c844a457ced04c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-xmpp" -version = "0.2.3" +version = "1.0.0" authors = ["Astro ", "Emmanuel Gil Peyrot ", "pep ", "O01eg "] description = "Asynchronous XMPP for Rust with tokio" license = "MPL-2.0" From fc5569a7651d52f35aefaac10e4e17686144edb1 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 14 Feb 2019 20:02:37 +0100 Subject: [PATCH 0840/1020] add logo --- logo.svg | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 logo.svg diff --git a/logo.svg b/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..2e15a475a809525bbe1b0492a7e17ee840587287 --- /dev/null +++ b/logo.svg @@ -0,0 +1,172 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 63dcba03b2c381d82802b6a22624b15c943a6418 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 21 Feb 2019 20:48:02 +0100 Subject: [PATCH 0841/1020] =?UTF-8?q?iq:=20Make=20@id=20required,=20as=20p?= =?UTF-8?q?er=20RFC6120=20=C2=A78.1.3.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/iq.rs | 77 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index b155b97c928a7da5072e561d6d6d78888085adac..ccb503ac18f890ea74ae0758e8dcdda09b7cf46c 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -63,7 +63,7 @@ pub struct Iq { /// The @id attribute of this stanza, which is required in order to match a /// request with its result/error. - pub id: Option, + pub id: String, /// The payload content of this stanza. pub payload: IqType, @@ -71,41 +71,41 @@ pub struct Iq { impl Iq { /// Creates an `` stanza containing a get request. - pub fn from_get(payload: impl IqGetPayload) -> Iq { + pub fn from_get(id: String, payload: impl IqGetPayload) -> Iq { Iq { from: None, to: None, - id: None, + id, payload: IqType::Get(payload.into()), } } /// Creates an `` stanza containing a set request. - pub fn from_set(payload: impl IqSetPayload) -> Iq { + pub fn from_set(id: String, payload: impl IqSetPayload) -> Iq { Iq { from: None, to: None, - id: None, + id, payload: IqType::Set(payload.into()), } } /// Creates an `` stanza containing a result. - pub fn from_result(payload: Option) -> Iq { + pub fn from_result(id: String, payload: Option) -> Iq { Iq { from: None, to: None, - id: None, + id, payload: IqType::Result(payload.map(|payload| payload.into())), } } /// Creates an `` stanza containing an error. - pub fn from_error(payload: StanzaError) -> Iq { + pub fn from_error(id: String, payload: StanzaError) -> Iq { Iq { from: None, to: None, - id: None, + id, payload: IqType::Error(payload), } } @@ -124,7 +124,7 @@ impl Iq { /// Sets the id of this stanza, in order to later match its response. pub fn with_id(mut self, id: String) -> Iq { - self.id = Some(id); + self.id = id; self } } @@ -136,7 +136,7 @@ impl TryFrom for Iq { check_self!(root, "iq", DEFAULT_NS); let from = get_attr!(root, "from", optional); let to = get_attr!(root, "to", optional); - let id = get_attr!(root, "id", optional); + let id = get_attr!(root, "id", required); let type_: String = get_attr!(root, "type", required); let mut payload = None; @@ -247,19 +247,30 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; + assert_eq!(message, "Required attribute 'id' missing."); + + #[cfg(not(feature = "component"))] + let elem: Element = "".parse().unwrap(); + #[cfg(feature = "component")] + let elem: Element = "".parse().unwrap(); + let error = Iq::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; assert_eq!(message, "Required attribute 'type' missing."); } #[test] fn test_get() { #[cfg(not(feature = "component"))] - let elem: Element = " + let elem: Element = " " .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = " + let elem: Element = " " .parse() @@ -268,7 +279,7 @@ mod tests { let query: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); - assert_eq!(iq.id, None); + assert_eq!(&iq.id, "foo"); assert!(match iq.payload { IqType::Get(element) => element.compare_to(&query), _ => false, @@ -278,13 +289,13 @@ mod tests { #[test] fn test_set() { #[cfg(not(feature = "component"))] - let elem: Element = " + let elem: Element = " " .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = " + let elem: Element = " " .parse() @@ -293,7 +304,7 @@ mod tests { let vcard: Element = "".parse().unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); - assert_eq!(iq.id, None); + assert_eq!(&iq.id, "vcard"); assert!(match iq.payload { IqType::Set(element) => element.compare_to(&vcard), _ => false, @@ -303,15 +314,15 @@ mod tests { #[test] fn test_result_empty() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); - assert_eq!(iq.id, None); + assert_eq!(&iq.id, "res"); assert!(match iq.payload { IqType::Result(None) => true, _ => false, @@ -321,13 +332,13 @@ mod tests { #[test] fn test_result() { #[cfg(not(feature = "component"))] - let elem: Element = " + let elem: Element = " " .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = " + let elem: Element = " " .parse() @@ -338,7 +349,7 @@ mod tests { .unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); - assert_eq!(iq.id, None); + assert_eq!(&iq.id, "res"); assert!(match iq.payload { IqType::Result(Some(element)) => element.compare_to(&query), _ => false, @@ -348,7 +359,7 @@ mod tests { #[test] fn test_error() { #[cfg(not(feature = "component"))] - let elem: Element = " + let elem: Element = " @@ -357,7 +368,7 @@ mod tests { .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = " + let elem: Element = " @@ -368,7 +379,7 @@ mod tests { let iq = Iq::try_from(elem).unwrap(); assert_eq!(iq.from, None); assert_eq!(iq.to, None); - assert_eq!(iq.id, None); + assert_eq!(iq.id, "err1"); match iq.payload { IqType::Error(error) => { assert_eq!(error.type_, ErrorType::Cancel); @@ -387,11 +398,11 @@ mod tests { #[test] fn test_children_invalid() { #[cfg(not(feature = "component"))] - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); let error = Iq::try_from(elem).unwrap_err(); @@ -405,15 +416,15 @@ mod tests { #[test] fn test_serialise() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); let iq2 = Iq { from: None, to: None, - id: None, + id: String::from("res"), payload: IqType::Result(None), }; let elem2 = iq2.into(); @@ -423,9 +434,9 @@ mod tests { #[test] fn test_disco() { #[cfg(not(feature = "component"))] - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); #[cfg(feature = "component")] - let elem: Element = "".parse().unwrap(); + let elem: Element = "".parse().unwrap(); let iq = Iq::try_from(elem).unwrap(); let disco_info = match iq.payload { IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(), From 637c3eadd71def30897de32ab748a531c6f3050d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 21 Feb 2019 21:00:58 +0100 Subject: [PATCH 0842/1020] Fix clippy lints. --- src/bookmarks.rs | 6 ++---- src/caps.rs | 4 ++-- src/component.rs | 3 ++- src/data_forms.rs | 2 +- src/disco.rs | 4 ++-- src/ecaps2.rs | 2 +- src/iq.rs | 8 ++++---- src/jingle.rs | 8 ++++---- src/jingle_ft.rs | 12 ++---------- src/jingle_s5b.rs | 8 ++++---- src/message.rs | 18 +++++++++--------- src/muc/muc.rs | 16 ++++------------ src/muc/user.rs | 4 ++-- src/presence.rs | 2 +- src/pubsub/event.rs | 2 +- src/pubsub/mod.rs | 2 +- src/sasl.rs | 4 ++-- src/sm.rs | 6 ++---- src/stanza_error.rs | 10 +++++----- src/util/helpers.rs | 10 +++++----- 20 files changed, 56 insertions(+), 75 deletions(-) diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 12f95707fc8f7c7bfbcc61d938be68aae9aa7b2e..273f770875d538ebbbfc4f254a4a379d304fd89e 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -49,6 +49,7 @@ generate_element!( generate_element!( /// Container element for multiple bookmarks. + #[derive(Default)] Storage, "storage", BOOKMARKS, children: [ /// Conferences the user has expressed an interest in. @@ -62,10 +63,7 @@ generate_element!( impl Storage { /// Create an empty bookmarks storage. pub fn new() -> Storage { - Storage { - conferences: Vec::new(), - urls: Vec::new(), - } + Storage::default() } } diff --git a/src/caps.rs b/src/caps.rs index 419e72ccce184ed963e4ca271d10e0e412367e71..a7948d191909d55db79d2692fad3a340f1270665 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -54,7 +54,7 @@ impl TryFrom for Caps { Ok(Caps { ext: get_attr!(elem, "ext", optional), node: get_attr!(elem, "node", required), - hash: hash, + hash, }) } } @@ -202,7 +202,7 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result { } Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, - algo: algo, + algo, }) } diff --git a/src/component.rs b/src/component.rs index 722495b8a02b6aea322c16a990070586e299ea4b..4ab78c57c28bf9ffba5ad50cd4bf9588c69fad8b 100644 --- a/src/component.rs +++ b/src/component.rs @@ -10,6 +10,7 @@ use sha1::Sha1; generate_element!( /// The main authentication mechanism for components. + #[derive(Default)] Handshake, "handshake", COMPONENT, text: ( /// If Some, contains the hex-encoded SHA-1 of the concatenation of the @@ -25,7 +26,7 @@ generate_element!( impl Handshake { /// Creates a successful reply from a server. pub fn new() -> Handshake { - Handshake { data: None } + Handshake::default() } /// Creates an authentication request from the component. diff --git a/src/data_forms.rs b/src/data_forms.rs index 30dbd29aa478ac2c9dd918bb3de0e7c08f4196c7..bce941364d75e7f228bb3170a2c9852af5ccc1bd 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -222,7 +222,7 @@ impl TryFrom for DataForm { check_no_unknown_attributes!(elem, "x", ["type"]); let type_ = get_attr!(elem, "type", required); let mut form = DataForm { - type_: type_, + type_, form_type: None, title: None, instructions: None, diff --git a/src/disco.rs b/src/disco.rs index ee673b8f92f0bfa314f4b4abfdf903725c7893f6..9f72cec56677c7d072657a43a093d250f248940d 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -86,8 +86,8 @@ impl TryFrom for Identity { } Ok(Identity { - category: category, - type_: type_, + category, + type_, lang: get_attr!(elem, "xml:lang", optional), name: get_attr!(elem, "name", optional), }) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 846fa6da0d75e1720e4bdff06a9a3e33cd3ffad6..ef3a59325d5e6afb59ab8a8d40ad54f5575704d0 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -136,7 +136,7 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, - algo: algo, + algo, }) } diff --git a/src/iq.rs b/src/iq.rs index ccb503ac18f890ea74ae0758e8dcdda09b7cf46c..dd04e6b8f9d2c385ff25e2460583c59cd5df43cc 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -96,7 +96,7 @@ impl Iq { from: None, to: None, id, - payload: IqType::Result(payload.map(|payload| payload.into())), + payload: IqType::Result(payload.map(Into::into)), } } @@ -188,9 +188,9 @@ impl TryFrom for Iq { }; Ok(Iq { - from: from, - to: to, - id: id, + from, + to, + id, payload: type_, }) } diff --git a/src/jingle.rs b/src/jingle.rs index 63c78a0f808ec9b678502ca1a19ddcc2e44ba2a8..2596687218cca28b0f0c6a0cc4ba16bbc3dc19a4 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -398,8 +398,8 @@ impl TryFrom for ReasonElement { "Reason doesn’t contain a valid reason.", ))?; Ok(ReasonElement { - reason: reason, - text: text, + reason, + text, }) } } @@ -449,8 +449,8 @@ impl Jingle { /// Create a new Jingle element. pub fn new(action: Action, sid: SessionId) -> Jingle { Jingle { - action: action, - sid: sid, + action, + sid, initiator: None, responder: None, contents: Vec::new(), diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index fce18cdf47ff7d8eda6b0eb153b88364ab2249fb..2d61569cb6209bccfed5e75d5a0e1a013614dccb 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -47,7 +47,7 @@ generate_id!( ); /// Represents a file to be transferred. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct File { /// The date of last modification of this file. pub date: Option, @@ -74,15 +74,7 @@ pub struct File { impl File { /// Creates a new file descriptor. pub fn new() -> File { - File { - date: None, - media_type: None, - name: None, - descs: BTreeMap::new(), - size: None, - range: None, - hashes: Vec::new(), - } + File::default() } /// Sets the date of last modification on this file. diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 58c4a27dd36a705bb5a6bd159459a1700655116f..613842f900ca1b8d9179d6f675945a62aa24f600 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -232,10 +232,10 @@ impl TryFrom for Transport { } let payload = payload.unwrap_or(TransportPayload::None); Ok(Transport { - sid: sid, - dstaddr: dstaddr, - mode: mode, - payload: payload, + sid, + dstaddr, + mode, + payload, }) } } diff --git a/src/message.rs b/src/message.rs index fe32300e5d353a33aefda76822c6fbd66f436dfe..f2611accd1a2ee40c6412854593ed37c62928b28 100644 --- a/src/message.rs +++ b/src/message.rs @@ -98,7 +98,7 @@ impl Message { pub fn new(to: Option) -> Message { Message { from: None, - to: to, + to, id: None, type_: MessageType::Chat, bodies: BTreeMap::new(), @@ -192,14 +192,14 @@ impl TryFrom for Message { } } Ok(Message { - from: from, - to: to, - id: id, - type_: type_, - bodies: bodies, - subjects: subjects, - thread: thread, - payloads: payloads, + from, + to, + id, + type_, + bodies, + subjects, + thread, + payloads, }) } } diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 9d29cc963c924481d79855c4e7791aebfa4e63b3..a4e60a4dd5d0e5c01a76f197fb0e0f6dead86247 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -10,7 +10,7 @@ use crate::presence::PresencePayload; generate_element!( /// Represents the query for messages before our join. - #[derive(PartialEq)] + #[derive(PartialEq, Default)] History, "history", MUC, attributes: [ /// How many characters of history to send, in XML characters. @@ -30,12 +30,7 @@ generate_element!( impl History { /// Create a new empty history element. pub fn new() -> Self { - History { - maxchars: None, - maxstanzas: None, - seconds: None, - since: None, - } + History::default() } /// Set how many characters of history to send. @@ -65,7 +60,7 @@ impl History { generate_element!( /// Represents a room join request. - #[derive(PartialEq)] + #[derive(PartialEq, Default)] Muc, "x", MUC, children: [ /// Password to use when the room is protected by a password. password: Option = ("password", MUC) => String, @@ -80,10 +75,7 @@ impl PresencePayload for Muc {} impl Muc { /// Create a new MUC join element. pub fn new() -> Self { - Muc { - password: None, - history: None, - } + Muc::default() } /// Join a room with this password. diff --git a/src/muc/user.rs b/src/muc/user.rs index 46a893337ea7b8525c35c67850dcc31382a45487..8232baf0a875d42208003bb624d6ea0106fb040e 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -100,9 +100,9 @@ impl TryFrom for Actor { match (jid, nick) { (Some(_), Some(_)) | (None, None) => { - return Err(Error::ParseError( + Err(Error::ParseError( "Either 'jid' or 'nick' attribute is required.", - )); + )) } (Some(jid), _) => Ok(Actor::Jid(jid)), (_, Some(nick)) => Ok(Actor::Nick(nick)), diff --git a/src/presence.rs b/src/presence.rs index 4a464f9c3ced4dfb767f71f38404ef203479fb09..e7d2a55df7c6b0f5d80d0f38b488c236c9677d6c 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -197,7 +197,7 @@ impl Presence { from: None, to: None, id: None, - type_: type_, + type_, show: Show::None, statuses: BTreeMap::new(), priority: 0i8, diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index e0f2b598d06b1b3fb840fe99bb434f553516fbaf..0c616ab64225b2a5c894ad42e481ae8c425840c2 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -178,7 +178,7 @@ impl TryFrom for PubSubEvent { } else if child.is("subscription", ns::PUBSUB_EVENT) { check_no_children!(child, "subscription"); payload = Some(PubSubEvent::Subscription { - node: node, + node, expiry: get_attr!(child, "expiry", optional), jid: get_attr!(child, "jid", optional), subid: get_attr!(child, "subid", optional), diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index 81e4e359f14cd54d83f8aa72848a0f474e1d1d8e..cde7e77529d65e99dfd0b4f96bc7f198449cd8ee 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -67,7 +67,7 @@ impl Item { Item { id, publisher, - payload: payload.map(|payload| payload.into()), + payload: payload.map(Into::into), } } } diff --git a/src/sasl.rs b/src/sasl.rs index fecdbfa6b6ca7017593af6d3df9bd7839daa1e9d..8f63f092a937ba38e77b2984c04f239a0bd8b745 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -192,8 +192,8 @@ impl TryFrom for Failure { defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?; Ok(Failure { - defined_condition: defined_condition, - texts: texts, + defined_condition, + texts, }) } } diff --git a/src/sm.rs b/src/sm.rs index fc8e7377609a0db349799f2cc9bf033fafd19fe8..1f48615e30da58c541955deade8c912e86bc04de 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -31,6 +31,7 @@ generate_attribute!( generate_element!( /// Client request for enabling stream management. + #[derive(Default)] Enable, "enable", SM, attributes: [ /// The preferred resumption time in seconds by the client. @@ -45,10 +46,7 @@ generate_element!( impl Enable { /// Generates a new `` element. pub fn new() -> Self { - Enable { - max: None, - resume: ResumeAttr::False, - } + Enable::default() } /// Sets the preferred resumption time in seconds. diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 7a4d421aad5505ce9f02a93c2169a4180ba6bd1b..d254f6b48d9a0e17c9f8fdd8d97f294a557056f6 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -258,11 +258,11 @@ impl TryFrom for StanzaError { defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; Ok(StanzaError { - type_: type_, - by: by, - defined_condition: defined_condition, - texts: texts, - other: other, + type_, + by, + defined_condition, + texts, + other, }) } } diff --git a/src/util/helpers.rs b/src/util/helpers.rs index 2aa37013ea5a7607ca057dd72846721f27e812e6..b3832345c33fd41698c71cce88b7f7dd913aa0e0 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -19,7 +19,7 @@ impl PlainText { } pub fn encode(string: &Option) -> Option { - string.as_ref().map(|text| text.to_owned()) + string.as_ref().map(ToOwned::to_owned) } } @@ -34,7 +34,7 @@ impl TrimmedPlainText { }) } - pub fn encode(string: &String) -> String { + pub fn encode(string: &str) -> String { string.to_owned() } } @@ -47,7 +47,7 @@ impl Base64 { Ok(base64::decode(s)?) } - pub fn encode(b: &Vec) -> Option { + pub fn encode(b: &[u8]) -> Option { Some(base64::encode(b)) } } @@ -57,11 +57,11 @@ pub struct WhitespaceAwareBase64; impl WhitespaceAwareBase64 { pub fn decode(s: &str) -> Result, Error> { - let s: String = s.chars().into_iter().filter(|ch| *ch != ' ' && *ch != '\n' && *ch != '\t').collect(); + let s: String = s.chars().filter(|ch| *ch != ' ' && *ch != '\n' && *ch != '\t').collect(); Ok(base64::decode(&s)?) } - pub fn encode(b: &Vec) -> Option { + pub fn encode(b: &[u8]) -> Option { Some(base64::encode(b)) } } From f68826057b094969e1ab1a45bfca0991bf2b1f11 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 21 Feb 2019 21:06:23 +0100 Subject: [PATCH 0843/1020] Fix clippy lints --- src/convert.rs | 2 +- src/element.rs | 4 ++-- src/namespace_set.rs | 6 +++--- src/node.rs | 4 +--- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/convert.rs b/src/convert.rs index 534073298b3d94fb206c68800a4e750d1558f28b..7c6d314e56df62c13a3c6c9e62c6491859a14c54 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -44,7 +44,7 @@ impl<'a> IntoAttributeValue for &'a str { impl IntoAttributeValue for Option { fn into_attribute_value(self) -> Option { - self.and_then(|t| t.into_attribute_value()) + self.and_then(IntoAttributeValue::into_attribute_value) } } diff --git a/src/element.rs b/src/element.rs index 5db7727bec18581a013e339defad144279ed1c6e..a94c1cec998b0586d88a95e262adaba2637729dd 100644 --- a/src/element.rs +++ b/src/element.rs @@ -102,8 +102,8 @@ impl Element { Element { prefix, name, namespaces: Rc::new(namespaces.into()), - attributes: attributes, - children: children, + attributes, + children, } } diff --git a/src/namespace_set.rs b/src/namespace_set.rs index 767106280929bbccea5d4dbd4ce7c8b1e966b142..57d3156dc5883f75ce06faa70aa683892604e5b4 100644 --- a/src/namespace_set.rs +++ b/src/namespace_set.rs @@ -58,7 +58,7 @@ impl From, String>> for NamespaceSet { fn from(namespaces: BTreeMap, String>) -> Self { NamespaceSet { parent: RefCell::new(None), - namespaces: namespaces, + namespaces, } } } @@ -79,7 +79,7 @@ impl From for NamespaceSet { NamespaceSet { parent: RefCell::new(None), - namespaces: namespaces, + namespaces, } } } @@ -92,7 +92,7 @@ impl From<(Option, String)> for NamespaceSet { NamespaceSet { parent: RefCell::new(None), - namespaces: namespaces, + namespaces, } } } diff --git a/src/node.rs b/src/node.rs index 7fe5ddadc70ca316d807c87dc4c1936c4dd6a833..b136dbdce5bc44c002f8c6588415838aa006a8a3 100644 --- a/src/node.rs +++ b/src/node.rs @@ -165,13 +165,11 @@ impl Node { Node::Element(ref elmt) => elmt.write_to_inner(writer)?, Node::Text(ref s) => { writer.write_event(Event::Text(BytesText::from_plain_str(s)))?; - () }, Node::Comment(ref s) => { writer.write_event(Event::Comment(BytesText::from_plain_str(s)))?; - () }, - }; + } Ok(()) } From a076221c9a109fa1a1661330291bb547c35ab96a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 4 Feb 2019 16:05:05 +0000 Subject: [PATCH 0844/1020] Add rustdoc flag to sort modules by XEP number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `--sort-modules-by-appearance` needs to be passed to rustdoc for this to happen. I haven't found a way to make it so that we don't have to add this flag manually each time we build locally. This config option should at least fix it for docs.rs. Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index cf9ae10b937b6f662050aec715dd00e3749bdeb4..8aab21dc5c72636969f7a1ace092cb739cf4587c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,6 @@ try_from = "0.3.2" component = [] # Disable validation of unknown attributes. disable-validation = [] + +[package.metadata.docs.rs] +rustdoc-args = [ "--sort-modules-by-appearance", "-Z unstable-options" ] From f2c3f45a6fd30386737bcfba21432d07c833d84b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 24 Feb 2019 19:25:14 +0100 Subject: [PATCH 0845/1020] data_forms: Stop duplicating FORM_TYPE in memory. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FORM_TYPE is now only present once, as the form_type member of the DataForm struct, it isn’t duplicated in fields anymore. This removes the need to ignore this special field in every single protocol built on XEP-0128. --- src/caps.rs | 4 +--- src/data_forms.rs | 16 +++++++++++++--- src/ecaps2.rs | 31 ++++++++++++++++++++----------- src/ibr.rs | 12 ------------ 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/caps.rs b/src/caps.rs index a7948d191909d55db79d2692fad3a340f1270665..1a0d57694c2a51430ef61e6f8d92f01565a03b1a 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -329,9 +329,7 @@ mod tests { "# .parse() .unwrap(); - let data = b"client/pc/el/\xce\xa8 0.11 for DataForm { form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { let field = Field::try_from(child.clone())?; - if field.var == "FORM_TYPE" && field.type_ == FieldType::Hidden { + if field.var == "FORM_TYPE" { + let mut field = field; if form.form_type.is_some() { return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); } + if field.type_ != FieldType::Hidden { + return Err(Error::ParseError("Invalid field type for FORM_TYPE.")); + } if field.values.len() != 1 { return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); } - form.form_type = Some(field.values[0].clone()); + form.form_type = field.values.pop(); + } else { + form.fields.push(field); } - form.fields.push(field); } else { return Err(Error::ParseError("Unknown child in data form element.")); } @@ -279,6 +284,11 @@ impl From for Element { .ns(ns::DATA_FORMS) .append(text) })) + .append(if let Some(form_type) = form.form_type { + vec![Element::builder("field").ns(ns::DATA_FORMS).attr("var", "FORM_TYPE").attr("type", "hidden").append(Element::builder("value").ns(ns::DATA_FORMS).append(form_type).build()).build()] + } else { + vec![] + }) .append(form.fields) .build() } diff --git a/src/ecaps2.rs b/src/ecaps2.rs index ef3a59325d5e6afb59ab8a8d40ad54f5575704d0..352881f105e2149f8b367d33ff1d6e0cfaf662c6 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -69,31 +69,40 @@ fn compute_identities(identities: &[Identity]) -> Vec { }) } -fn compute_extensions(extensions: &[DataForm]) -> Vec { - compute_items(extensions, 0x1c, |extension| { - compute_items(&extension.fields, 0x1d, |field| { +fn compute_extensions(extensions: &[DataForm]) -> Result, ()> { + for extension in extensions { + if extension.form_type.is_none() { + return Err(()); + } + } + Ok(compute_items(extensions, 0x1c, |extension| { + let mut bytes = compute_item("FORM_TYPE"); + bytes.append(&mut compute_item(if let Some(ref form_type) = extension.form_type { form_type } else { unreachable!() })); + bytes.push(0x1e); + bytes.append(&mut compute_items(&extension.fields, 0x1d, |field| { let mut bytes = compute_item(&field.var); bytes.append(&mut compute_items(&field.values, 0x1e, |value| { compute_item(value) })); bytes - }) - }) + })); + bytes + })) } /// Applies the [algorithm from /// XEP-0390](https://xmpp.org/extensions/xep-0390.html#algorithm-input) on a /// [disco#info query element](../disco/struct.DiscoInfoResult.html). -pub fn compute_disco(disco: &DiscoInfoResult) -> Vec { +pub fn compute_disco(disco: &DiscoInfoResult) -> Result, ()> { let features_string = compute_features(&disco.features); let identities_string = compute_identities(&disco.identities); - let extensions_string = compute_extensions(&disco.extensions); + let extensions_string = compute_extensions(&disco.extensions)?; let mut final_string = vec![]; final_string.extend(features_string); final_string.extend(identities_string); final_string.extend(extensions_string); - final_string + Ok(final_string) } fn get_hash_vec(hash: &[u8]) -> Vec { @@ -204,7 +213,7 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = compute_disco(&disco); + let ecaps2 = compute_disco(&disco).unwrap(); assert_eq!(ecaps2.len(), 54); } @@ -263,7 +272,7 @@ mod tests { 117, 115, 77, 111, 100, 31, 30, 28, 28, ]; let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = compute_disco(&disco); + let ecaps2 = compute_disco(&disco).unwrap(); assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); @@ -424,7 +433,7 @@ mod tests { 98, 50, 41, 31, 30, 29, 28, ]; let disco = DiscoInfoResult::try_from(elem).unwrap(); - let ecaps2 = compute_disco(&disco); + let ecaps2 = compute_disco(&disco).unwrap(); assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); diff --git a/src/ibr.rs b/src/ibr.rs index f19c47c131ba137644addac47d35d03ee884ee63..c98c909369c4ba1a25e52ae603c6e7b59584b28c 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -207,18 +207,6 @@ mod tests { assert!(!query.fields["instructions"].is_empty()); let form = query.form.clone().unwrap(); assert!(!form.instructions.unwrap().is_empty()); - assert!(form - .fields - .binary_search_by(|field| field.var.cmp(&String::from("first"))) - .is_ok()); - assert!(form - .fields - .binary_search_by(|field| field.var.cmp(&String::from("x-gender"))) - .is_ok()); - assert!(form - .fields - .binary_search_by(|field| field.var.cmp(&String::from("coucou"))) - .is_err()); let elem2 = query.into(); assert!(elem1.compare_to(&elem2)); } From bcd42a26e3b8d27524d4eb5ffde14f3fd37e660e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 24 Feb 2019 20:26:40 +0100 Subject: [PATCH 0846/1020] macros: Use a nicer syntax when declaring attributes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous version had a => required|optional|default token, this was duplicating information for Option types and didn’t look very good. This new version looks like a type, which can be either Required<_>, Option<_> or Default<_>, and means the same thing. --- src/avatar.rs | 12 +++++----- src/bookmarks.rs | 10 ++++---- src/data_forms.rs | 2 +- src/delay.rs | 4 ++-- src/disco.rs | 14 +++++------ src/eme.rs | 4 ++-- src/hashes.rs | 2 +- src/ibb.rs | 12 +++++----- src/idle.rs | 2 +- src/jingle.rs | 8 +++---- src/jingle_ft.rs | 8 +++---- src/jingle_ibb.rs | 6 ++--- src/jingle_s5b.rs | 12 +++++----- src/mam.rs | 10 ++++---- src/media_element.rs | 6 ++--- src/message_correct.rs | 2 +- src/muc/muc.rs | 8 +++---- src/muc/user.rs | 10 ++++---- src/pubsub/pubsub.rs | 50 +++++++++++++++++++-------------------- src/receipts.rs | 2 +- src/roster.rs | 10 ++++---- src/sasl.rs | 2 +- src/sm.rs | 24 +++++++++---------- src/stanza_id.rs | 6 ++--- src/stream.rs | 10 ++++---- src/util/macros.rs | 53 +++++++++++++++++++++++++++++++----------- src/websocket.rs | 10 ++++---- 27 files changed, 162 insertions(+), 137 deletions(-) diff --git a/src/avatar.rs b/src/avatar.rs index 48893e4c55ce34762ac735548e9fe087221d281e..6e2d3ce03da49f5891ef45fa28050f1ddd9b47d8 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -24,22 +24,22 @@ generate_element!( Info, "info", AVATAR_METADATA, attributes: [ /// The size of the image data in bytes. - bytes: u16 = "bytes" => required, + bytes: Required = "bytes", /// The width of the image in pixels. - width: Option = "width" => optional, + width: Option = "width", /// The height of the image in pixels. - height: Option = "height" => optional, + height: Option = "height", /// The SHA-1 hash of the image data for the specified content-type. - id: Sha1HexAttribute = "id" => required, + id: Required = "id", /// The IANA-registered content type of the image data. - type_: String = "type" => required, + type_: Required = "type", /// The http: or https: URL at which the image data file is hosted. - url: Option = "url" => optional, + url: Option = "url", ] ); diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 273f770875d538ebbbfc4f254a4a379d304fd89e..b1c59b0e40d0372921eeb082ac29caa42d4c1116 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -18,13 +18,13 @@ generate_element!( Conference, "conference", BOOKMARKS, attributes: [ /// Whether a conference bookmark should be joined automatically. - autojoin: Autojoin = "autojoin" => default, + autojoin: Default = "autojoin", /// The JID of the conference. - jid: Jid = "jid" => required, + jid: Required = "jid", /// A user-defined name for this conference. - name: String = "name" => required + name: Required = "name", ], children: [ /// The nick the user will use to join this conference. @@ -40,10 +40,10 @@ generate_element!( Url, "url", BOOKMARKS, attributes: [ /// A user-defined name for this URL. - name: String = "name" => required, + name: Required = "name", /// The URL of this bookmark. - url: String = "url" => required + url: Required = "url", ] ); diff --git a/src/data_forms.rs b/src/data_forms.rs index 6e2d4e3d2c770198860c6dba11b6043e11a16572..9a1ed3e3297556ebd26cabe8be938fb64b97c8f7 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -15,7 +15,7 @@ generate_element!( Option_, "option", DATA_FORMS, attributes: [ /// The optional label to be displayed to the user for this option. - label: Option = "label" => optional + label: Option = "label" ], children: [ /// The value returned to the server when selecting this option. diff --git a/src/delay.rs b/src/delay.rs index a02b44b1a4338929806b75529c2af166cb945191..80f92edd00eb8b8023d8920df551de550493bed8 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -15,10 +15,10 @@ generate_element!( Delay, "delay", DELAY, attributes: [ /// The entity which delayed this message. - from: Option = "from" => optional, + from: Option = "from", /// The time at which this message got stored. - stamp: DateTime = "stamp" => required + stamp: Required = "stamp" ], text: ( /// The optional reason this message got delayed. diff --git a/src/disco.rs b/src/disco.rs index 9f72cec56677c7d072657a43a093d250f248940d..43c15903365dc900188ac0acc2801cc24ed2a1de 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -20,7 +20,7 @@ generate_element!( DiscoInfoQuery, "query", DISCO_INFO, attributes: [ /// Node on which we are doing the discovery. - node: Option = "node" => optional, + node: Option = "node", ]); impl IqGetPayload for DiscoInfoQuery {} @@ -31,7 +31,7 @@ generate_element!( Feature, "feature", DISCO_INFO, attributes: [ /// Namespace of the feature we want to represent. - var: String = "var" => required, + var: Required = "var", ]); impl Feature { @@ -206,7 +206,7 @@ generate_element!( DiscoItemsQuery, "query", DISCO_ITEMS, attributes: [ /// Node on which we are doing the discovery. - node: Option = "node" => optional, + node: Option = "node", ]); impl IqGetPayload for DiscoItemsQuery {} @@ -216,11 +216,11 @@ generate_element!( Item, "item", DISCO_ITEMS, attributes: [ /// JID of the entity pointed by this item. - jid: Jid = "jid" => required, + jid: Required = "jid", /// Node of the entity pointed by this item. - node: Option = "node" => optional, + node: Option = "node", /// Name of the entity pointed by this item. - name: Option = "name" => optional, + name: Option = "name", ]); generate_element!( @@ -232,7 +232,7 @@ generate_element!( DiscoItemsResult, "query", DISCO_ITEMS, attributes: [ /// Node on which we have done this discovery. - node: Option = "node" => optional + node: Option = "node" ], children: [ /// List of items pointed by this entity. diff --git a/src/eme.rs b/src/eme.rs index cd24e43d03b7bf95799d75b86b086d3b280d8ab1..20623ff2f2b628ed56310bb8d1110d6df20ead6d 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -11,11 +11,11 @@ generate_element!( ExplicitMessageEncryption, "encryption", EME, attributes: [ /// Namespace of the encryption scheme used. - namespace: String = "namespace" => required, + namespace: Required = "namespace", /// User-friendly name for the encryption scheme, should be `None` for OTR, /// legacy OpenPGP and OX. - name: Option = "name" => optional, + name: Option = "name", ] ); diff --git a/src/hashes.rs b/src/hashes.rs index d6a1f37baf3609a24a5ff68a84b837c1b0e4ea4d..c097d034690678a682b1dd9a73daa72a85e1657c 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -102,7 +102,7 @@ generate_element!( Hash, "hash", HASHES, attributes: [ /// The algorithm used to create this hash. - algo: Algo = "algo" => required + algo: Required = "algo" ], text: ( /// The hash value, as a vector of bytes. diff --git a/src/ibb.rs b/src/ibb.rs index 960761187becc66ba7d9e1f7dd2ec02922d635ba..aa1e07d3c2308cd767defbec47be9e80d72f3fd9 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -30,13 +30,13 @@ generate_element!( Open, "open", IBB, attributes: [ /// Maximum size in bytes for each chunk. - block_size: u16 = "block-size" => required, + block_size: Required = "block-size", /// The identifier to be used to create a stream. - sid: StreamId = "sid" => required, + sid: Required = "sid", /// Which stanza type to use to exchange data. - stanza: Stanza = "stanza" => default, + stanza: Default = "stanza", ]); impl IqSetPayload for Open {} @@ -46,10 +46,10 @@ generate_element!( Data, "data", IBB, attributes: [ /// Sequence number of this chunk, must wraparound after 65535. - seq: u16 = "seq" => required, + seq: Required = "seq", /// The identifier of the stream on which data is being exchanged. - sid: StreamId = "sid" => required + sid: Required = "sid" ], text: ( /// Vector of bytes to be exchanged. @@ -64,7 +64,7 @@ generate_element!( Close, "close", IBB, attributes: [ /// The identifier of the stream to be closed. - sid: StreamId = "sid" => required, + sid: Required = "sid", ]); impl IqSetPayload for Close {} diff --git a/src/idle.rs b/src/idle.rs index bbf3f15114546636f20c49de18edec939d15ce8e..89b62c0549f139c4832ea5feafbf9dc77efbe5e3 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -12,7 +12,7 @@ generate_element!( Idle, "idle", IDLE, attributes: [ /// The time at which the user stopped interacting. - since: DateTime = "since" => required, + since: Required = "since", ] ); diff --git a/src/jingle.rs b/src/jingle.rs index 2596687218cca28b0f0c6a0cc4ba16bbc3dc19a4..b891ab7caea1b7a2d7fbae352fd8ade108b645b4 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -169,16 +169,16 @@ generate_element!( Content, "content", JINGLE, attributes: [ /// Who created this content. - creator: Creator = "creator" => required, + creator: Required = "creator", /// How the content definition is to be interpreted by the recipient. - disposition: Disposition = "disposition" => default, + disposition: Default = "disposition", /// A per-session unique identifier for this content. - name: ContentId = "name" => required, + name: Required = "name", /// Who can send data for this content. - senders: Senders = "senders" => default + senders: Default = "senders", ], children: [ /// What to send. diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 2d61569cb6209bccfed5e75d5a0e1a013614dccb..67c055e23290eed991eb0a0add4a68761c123ccc 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -20,11 +20,11 @@ generate_element!( Range, "range", JINGLE_FT, attributes: [ /// The offset in bytes from the beginning of the file. - offset: u64 = "offset" => default, + offset: Default = "offset", /// The length in bytes of the range, or None to be the entire /// remaining of the file. - length: Option = "length" => optional + length: Option = "length" ], children: [ /// List of hashes for this range. @@ -344,10 +344,10 @@ generate_element!( Received, "received", JINGLE_FT, attributes: [ /// The content identifier of this Jingle session. - name: ContentId = "name" => required, + name: Required = "name", /// The creator of this file transfer. - creator: Creator = "creator" => required, + creator: Required = "creator", ] ); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 08370e1f7f9dd328abb9dd3c2194547c20422111..019f03429f520c8983b1f937b96ba4bf8b68a3e5 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -12,13 +12,13 @@ generate_element!( Transport, "transport", JINGLE_IBB, attributes: [ /// Maximum size in bytes for each chunk. - block_size: u16 = "block-size" => required, + block_size: Required = "block-size", /// The identifier to be used to create a stream. - sid: StreamId = "sid" => required, + sid: Required = "sid", /// Which stanza type to use to exchange data. - stanza: Stanza = "stanza" => default, + stanza: Default = "stanza", ]); #[cfg(test)] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 613842f900ca1b8d9179d6f675945a62aa24f600..1f3a3ee60fb3b7f67c3cbc472bd00237a333fe6e 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -55,23 +55,23 @@ generate_element!( Candidate, "candidate", JINGLE_S5B, attributes: [ /// The identifier for this candidate. - cid: CandidateId = "cid" => required, + cid: Required = "cid", /// The host to connect to. - host: IpAddr = "host" => required, + host: Required = "host", /// The JID to request at the given end. - jid: Jid = "jid" => required, + jid: Required = "jid", /// The port to connect to. - port: Option = "port" => optional, + port: Option = "port", /// The priority of this candidate, computed using this formula: /// priority = (2^16)*(type preference) + (local preference) - priority: u32 = "priority" => required, + priority: Required = "priority", /// The type of the connection being proposed by this candidate. - type_: Type = "type" => default, + type_: Default = "type", ] ); diff --git a/src/mam.rs b/src/mam.rs index 453b4f606deeafbf321d58867e27564969f7e94f..b68dabb272b8052bd173da25e7cb40fa38af7950 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -27,10 +27,10 @@ generate_element!( attributes: [ /// An optional identifier for matching forwarded messages to this /// query. - queryid: Option = "queryid" => optional, + queryid: Option = "queryid", /// Must be set to Some when querying a PubSub node’s archive. - node: Option = "node" => optional + node: Option = "node" ], children: [ /// Used for filtering the results. @@ -50,11 +50,11 @@ generate_element!( Result_, "result", MAM, attributes: [ /// The stanza-id under which the archive stored this stanza. - id: String = "id" => required, + id: Required = "id", /// The same queryid as the one requested in the /// [query](struct.Query.html). - queryid: Option = "queryid" => optional, + queryid: Option = "queryid", ], children: [ /// The actual stanza being forwarded. @@ -76,7 +76,7 @@ generate_element!( Fin, "fin", MAM, attributes: [ /// True when the end of a MAM query has been reached. - complete: Complete = "complete" => default + complete: Default = "complete", ], children: [ /// Describes the current page, it should contain at least [first] diff --git a/src/media_element.rs b/src/media_element.rs index 639b71cea3401fcf764ffba7a54e36c760328900..259e9d1226d5a14336f2697c58a4b0113ddfdaa3 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -17,7 +17,7 @@ generate_element!( /// accepted too. /// /// [1]: https://www.iana.org/assignments/media-types/media-types.xhtml - type_: String = "type" => required + type_: Required = "type" ], text: ( /// The actual URI contained. @@ -31,10 +31,10 @@ generate_element!( MediaElement, "media", MEDIA_ELEMENT, attributes: [ /// The recommended display width in pixels. - width: Option = "width" => optional, + width: Option = "width", /// The recommended display height in pixels. - height: Option = "height" => optional + height: Option = "height" ], children: [ /// A list of URIs referencing this media. diff --git a/src/message_correct.rs b/src/message_correct.rs index 05663c8daa34cb40755378242a03e74b1c37d586..b2600e0b9a021f227b0bef80065fb83fa1778a9f 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -12,7 +12,7 @@ generate_element!( Replace, "replace", MESSAGE_CORRECT, attributes: [ /// The 'id' attribute of the message getting corrected. - id: String = "id" => required, + id: Required = "id", ] ); diff --git a/src/muc/muc.rs b/src/muc/muc.rs index a4e60a4dd5d0e5c01a76f197fb0e0f6dead86247..898af67896f657699516615e46d3fcaa50e7a8af 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -14,16 +14,16 @@ generate_element!( History, "history", MUC, attributes: [ /// How many characters of history to send, in XML characters. - maxchars: Option = "maxchars" => optional, + maxchars: Option = "maxchars", /// How many messages to send. - maxstanzas: Option = "maxstanzas" => optional, + maxstanzas: Option = "maxstanzas", /// Only send messages received in these last seconds. - seconds: Option = "seconds" => optional, + seconds: Option = "seconds", /// Only send messages after this date. - since: Option = "since" => optional, + since: Option = "since", ] ); diff --git a/src/muc/user.rs b/src/muc/user.rs index 8232baf0a875d42208003bb624d6ea0106fb040e..73e910ae1bcccb82890fe7d9637756ec6d53486c 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -128,7 +128,7 @@ generate_element!( Continue, "continue", MUC_USER, attributes: [ /// The thread to continue in this room. - thread: Option = "thread" => optional, + thread: Option = "thread", ] ); @@ -187,16 +187,16 @@ generate_element!( /// An item representing a user in a room. Item, "item", MUC_USER, attributes: [ /// The affiliation of this user with the room. - affiliation: Affiliation = "affiliation" => required, + affiliation: Required = "affiliation", /// The real JID of this user, if you are allowed to see it. - jid: Option = "jid" => optional, + jid: Option = "jid", /// The current nickname of this user. - nick: Option = "nick" => optional, + nick: Option = "nick", /// The current role of this user. - role: Role = "role" => required + role: Required = "role", ], children: [ /// The actor affected by this item. actor: Option = ("actor", MUC_USER) => Actor, diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index c7bb6466cd948e83aa21602ef6acf1a9804e0755..5093b3e1512e692bbd02f47f779e311944ebf0e6 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -20,7 +20,7 @@ generate_element!( Affiliations, "affiliations", PUBSUB, attributes: [ /// The optional node name this request pertains to. - node: Option = "node" => optional, + node: Option = "node", ], children: [ /// The actual list of affiliation elements. @@ -56,10 +56,10 @@ generate_element!( Affiliation, "affiliation", PUBSUB, attributes: [ /// The node this affiliation pertains to. - node: NodeName = "node" => required, + node: Required = "node", /// The affiliation you currently have on this node. - affiliation: AffiliationAttribute = "affiliation" => required, + affiliation: Required = "affiliation", ] ); @@ -77,7 +77,7 @@ generate_element!( Create, "create", PUBSUB, attributes: [ /// The node name to create, if `None` the service will generate one. - node: Option = "node" => optional, + node: Option = "node", ] ); @@ -86,10 +86,10 @@ generate_element!( Default, "default", PUBSUB, attributes: [ /// The node targetted by this request, otherwise the entire service. - node: Option = "node" => optional, + node: Option = "node", // TODO: do we really want to support collection nodes? - // type: String = "type" => optional, + // type: Option = "type", ] ); @@ -99,13 +99,13 @@ generate_element!( attributes: [ // TODO: should be an xs:positiveInteger, that is, an unbounded int ≥ 1. /// Maximum number of items returned. - max_items: Option = "max_items" => optional, + max_items: Option = "max_items", /// The node queried by this request. - node: NodeName = "node" => required, + node: Required = "node", /// The subscription identifier related to this request. - subid: Option = "subid" => optional, + subid: Option = "subid", ], children: [ /// The actual list of items returned. @@ -124,13 +124,13 @@ generate_element!( Options, "options", PUBSUB, attributes: [ /// The JID affected by this request. - jid: Jid = "jid" => required, + jid: Required = "jid", /// The node affected by this request. - node: Option = "node" => optional, + node: Option = "node", /// The subscription identifier affected by this request. - subid: Option = "subid" => optional, + subid: Option = "subid", ], children: [ /// The form describing the subscription. @@ -143,7 +143,7 @@ generate_element!( Publish, "publish", PUBSUB, attributes: [ /// The target node for this operation. - node: NodeName = "node" => required, + node: Required = "node", ], children: [ /// The items you want to publish. @@ -172,10 +172,10 @@ generate_element!( Retract, "retract", PUBSUB, attributes: [ /// The node affected by this request. - node: NodeName = "node" => required, + node: Required = "node", /// Whether a retract request should notify subscribers or not. - notify: Notify = "notify" => default, + notify: Default = "notify", ], children: [ /// The items affected by this request. @@ -233,10 +233,10 @@ generate_element!( Subscribe, "subscribe", PUBSUB, attributes: [ /// The JID being subscribed. - jid: Jid = "jid" => required, + jid: Required = "jid", /// The node to subscribe to. - node: Option = "node" => optional, + node: Option = "node", ] ); @@ -245,7 +245,7 @@ generate_element!( Subscriptions, "subscriptions", PUBSUB, attributes: [ /// The node to query. - node: Option = "node" => optional, + node: Option = "node", ], children: [ /// The list of subscription elements returned. @@ -258,16 +258,16 @@ generate_element!( SubscriptionElem, "subscription", PUBSUB, attributes: [ /// The JID affected by this subscription. - jid: Jid = "jid" => required, + jid: Required = "jid", /// The node affected by this subscription. - node: Option = "node" => optional, + node: Option = "node", /// The subscription identifier for this subscription. - subid: Option = "subid" => optional, + subid: Option = "subid", /// The state of the subscription. - subscription: Option = "subscription" => optional, + subscription: Option = "subscription", ], children: [ /// The options related to this subscription. @@ -280,13 +280,13 @@ generate_element!( Unsubscribe, "unsubscribe", PUBSUB, attributes: [ /// The JID affected by this request. - jid: Jid = "jid" => required, + jid: Required = "jid", /// The node affected by this request. - node: Option = "node" => optional, + node: Option = "node", /// The subscription identifier for this subscription. - subid: Option = "subid" => optional, + subid: Option = "subid", ] ); diff --git a/src/receipts.rs b/src/receipts.rs index c97d3e118358630758fa253d5e0153fbea3cf8cc..e60900401ec1510473735441f10d17c24be516d3 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -22,7 +22,7 @@ generate_element!( Received, "received", RECEIPTS, attributes: [ /// The 'id' attribute of the received message. - id: Option = "id" => optional, + id: Option = "id", ] ); diff --git a/src/roster.rs b/src/roster.rs index 29086aec2ae7777b00ffebebcf072a4e2587c458..57d440e97733717d16ff33c86e9c16c8ee938235 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -50,16 +50,16 @@ generate_element!( Item, "item", ROSTER, attributes: [ /// JID of this contact. - jid: Jid = "jid" => required, + jid: Required = "jid", /// Name of this contact. - name: Option = "name" => optional_empty, + name: OptionEmpty = "name", /// Subscription status of this contact. - subscription: Subscription = "subscription" => default, + subscription: Default = "subscription", /// Indicates “Pending Out” sub-states for this contact. - ask: Ask = "ask" => default, + ask: Default = "ask", ], children: [ @@ -77,7 +77,7 @@ generate_element!( /// This is an opaque string that should only be sent back to the server on /// a new connection, if this client is storing the contact list between /// connections. - ver: Option = "ver" => optional + ver: Option = "ver" ], children: [ /// List of the contacts of the user. diff --git a/src/sasl.rs b/src/sasl.rs index 8f63f092a937ba38e77b2984c04f239a0bd8b745..9d35072e1c25b0bf4709c42e3f9ea4fc53432db1 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -49,7 +49,7 @@ generate_element!( Auth, "auth", SASL, attributes: [ /// The mechanism used. - mechanism: Mechanism = "mechanism" => required + mechanism: Required = "mechanism" ], text: ( /// The content of the handshake. diff --git a/src/sm.rs b/src/sm.rs index 1f48615e30da58c541955deade8c912e86bc04de..661d05b9d0c3f5f73e30a2a47cd8787f7dc84b28 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -11,7 +11,7 @@ generate_element!( A, "a", SM, attributes: [ /// The last handled stanza. - h: u32 = "h" => required, + h: Required = "h", ] ); @@ -36,10 +36,10 @@ generate_element!( attributes: [ /// The preferred resumption time in seconds by the client. // TODO: should be the infinite integer set ≥ 1. - max: Option = "max" => optional, + max: Option = "max", /// Whether the client wants to be allowed to resume the stream. - resume: ResumeAttr = "resume" => default, + resume: Default = "resume", ] ); @@ -72,18 +72,18 @@ generate_element!( Enabled, "enabled", SM, attributes: [ /// A random identifier used for stream resumption. - id: Option = "id" => optional, + id: Option = "id", /// The preferred IP, domain, IP:port or domain:port location for /// resumption. - location: Option = "location" => optional, + location: Option = "location", /// The preferred resumption time in seconds by the server. // TODO: should be the infinite integer set ≥ 1. - max: Option = "max" => optional, + max: Option = "max", /// Whether stream resumption is allowed. - resume: ResumeAttr = "resume" => default, + resume: Default = "resume", ] ); @@ -92,7 +92,7 @@ generate_element!( Failed, "failed", SM, attributes: [ /// The last handled stanza. - h: Option = "h" => optional, + h: Option = "h", ], children: [ /// The error returned. @@ -113,11 +113,11 @@ generate_element!( Resume, "resume", SM, attributes: [ /// The last handled stanza. - h: u32 = "h" => required, + h: Required = "h", /// The previous id given by the server on /// [enabled](struct.Enabled.html). - previd: StreamId = "previd" => required, + previd: Required = "previd", ] ); @@ -126,11 +126,11 @@ generate_element!( Resumed, "resumed", SM, attributes: [ /// The last handled stanza. - h: u32 = "h" => required, + h: Required = "h", /// The previous id given by the server on /// [enabled](struct.Enabled.html). - previd: StreamId = "previd" => required, + previd: Required = "previd", ] ); diff --git a/src/stanza_id.rs b/src/stanza_id.rs index c9cefad724a1c8583bfdc908a56d777068ab915b..dd8ed9e0a125044eb9587f5364793730f1a12127 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -13,10 +13,10 @@ generate_element!( StanzaId, "stanza-id", SID, attributes: [ /// The id associated to this stanza by another entity. - id: String = "id" => required, + id: Required = "id", /// The entity who stamped this stanza-id. - by: Jid = "by" => required, + by: Required = "by", ] ); @@ -28,7 +28,7 @@ generate_element!( OriginId, "origin-id", SID, attributes: [ /// The id this client set for this stanza. - id: String = "id" => required, + id: Required = "id", ] ); diff --git a/src/stream.rs b/src/stream.rs index fa416fd80dd7e361169cd909eb175e3a720de6f4..a77a73ea8ca36f59a55df520e085aa29cf9dff85 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -11,20 +11,20 @@ generate_element!( Stream, "stream", STREAM, attributes: [ /// The JID of the entity opening this stream. - from: Option = "from" => optional, + from: Option = "from", /// The JID of the entity receiving this stream opening. - to: Option = "to" => optional, + to: Option = "to", /// The id of the stream, used for authentication challenges. - id: Option = "id" => optional, + id: Option = "id", /// The XMPP version used during this stream. - version: Option = "version" => optional, + version: Option = "version", /// The default human language for all subsequent stanzas, which will /// be transmitted to other entities for better localisation. - xml_lang: Option = "xml:lang" => optional, + xml_lang: Option = "xml:lang", ] ); diff --git a/src/util/macros.rs b/src/util/macros.rs index 0c296abba07e94e05d7fb32db3b7dd749ea5a0a8..74e4009f93471cfdda0112adc51e5a47371bf27e 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -8,20 +8,20 @@ macro_rules! get_attr { ($elem:ident, $attr:tt, $type:tt) => { get_attr!($elem, $attr, $type, value, value.parse()?) }; - ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => { + ($elem:ident, $attr:tt, OptionEmpty, $value:ident, $func:expr) => { match $elem.attr($attr) { Some("") => None, Some($value) => Some($func), None => None, } }; - ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => { + ($elem:ident, $attr:tt, Option, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => Some($func), None => None, } }; - ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => { + ($elem:ident, $attr:tt, Required, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, None => { @@ -33,12 +33,22 @@ macro_rules! get_attr { } } }; - ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => { + ($elem:ident, $attr:tt, Default, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, None => ::std::default::Default::default(), } }; + // The remaining ones are only for backwards-compatibility. + ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => { + get_attr!($elem, $attr, Option, $value, $func) + }; + ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => { + get_attr!($elem, $attr, Required, $value, $func) + }; + ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => { + get_attr!($elem, $attr, Default, $value, $func) + }; } macro_rules! generate_attribute { @@ -398,6 +408,21 @@ macro_rules! generate_elem_id { ); } +macro_rules! decl_attr { + (OptionEmpty, $type:ty) => ( + Option<$type> + ); + (Option, $type:ty) => ( + Option<$type> + ); + (Required, $type:ty) => ( + $type + ); + (Default, $type:ty) => ( + $type + ); +} + macro_rules! start_decl { (Vec, $type:ty) => ( Vec<$type> @@ -503,31 +528,31 @@ macro_rules! generate_serialiser { } macro_rules! generate_element { - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => ( - generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+,]) => ( + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => ( - generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+]) => ( + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( - generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => ( + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]); ); ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => ( generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>)); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => ( - generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>)); + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => ( + generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>)); ); - ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => ( + ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => ( $(#[$meta])* #[derive(Debug, Clone)] pub struct $elem { $( $(#[$attr_meta])* - pub $attr: $attr_type, + pub $attr: decl_attr!($attr_action, $attr_type), )* $( $(#[$child_meta])* diff --git a/src/websocket.rs b/src/websocket.rs index 10693ca7d4bee4f9c6278e65c5a8815791a0123b..03a1d0401b97103d42e81ba45f10f110dae3bea5 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -11,20 +11,20 @@ generate_element!( Open, "open", WEBSOCKET, attributes: [ /// The JID of the entity opening this stream. - from: Option = "from" => optional, + from: Option = "from", /// The JID of the entity receiving this stream opening. - to: Option = "to" => optional, + to: Option = "to", /// The id of the stream, used for authentication challenges. - id: Option = "id" => optional, + id: Option = "id", /// The XMPP version used during this stream. - version: Option = "version" => optional, + version: Option = "version", /// The default human language for all subsequent stanzas, which will /// be transmitted to other entities for better localisation. - xml_lang: Option = "xml:lang" => optional, + xml_lang: Option = "xml:lang", ] ); From ae3a5b67252f870362716d7112b2dbc52d21cd0d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 24 Feb 2019 20:48:19 +0100 Subject: [PATCH 0847/1020] macros: Remove backwards-compatibility variants introduced in the previous commit. --- src/blocking.rs | 2 +- src/caps.rs | 8 ++++---- src/data_forms.rs | 8 ++++---- src/disco.rs | 10 +++++----- src/iq.rs | 8 ++++---- src/jingle.rs | 8 ++++---- src/jingle_ft.rs | 6 +++--- src/jingle_message.rs | 2 +- src/jingle_s5b.rs | 10 +++++----- src/mam.rs | 2 +- src/message.rs | 12 ++++++------ src/muc/user.rs | 4 ++-- src/presence.rs | 10 +++++----- src/pubsub/event.rs | 14 +++++++------- src/rsm.rs | 2 +- src/sasl.rs | 2 +- src/stanza_error.rs | 6 +++--- src/util/macros.rs | 16 +++------------- 18 files changed, 60 insertions(+), 70 deletions(-) diff --git a/src/blocking.rs b/src/blocking.rs index 2b021185995c8427f28bcf1c921a780a60f57076..74e84bc6f46d44921c832c643f4d702ce694a4ad 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -41,7 +41,7 @@ macro_rules! generate_blocking_element { check_self!(child, "item", BLOCKING); check_no_unknown_attributes!(child, "item", ["jid"]); check_no_children!(child, "item"); - items.push(get_attr!(child, "jid", required)); + items.push(get_attr!(child, "jid", Required)); } Ok($elem { items }) } diff --git a/src/caps.rs b/src/caps.rs index 1a0d57694c2a51430ef61e6f8d92f01565a03b1a..452fa6d56acfd8920e73916a0eab3a101f57db14 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -46,14 +46,14 @@ impl TryFrom for Caps { check_self!(elem, "c", CAPS, "caps"); check_no_children!(elem, "caps"); check_no_unknown_attributes!(elem, "caps", ["hash", "ver", "ext", "node"]); - let ver: String = get_attr!(elem, "ver", required); + let ver: String = get_attr!(elem, "ver", Required); let hash = Hash { - algo: get_attr!(elem, "hash", required), + algo: get_attr!(elem, "hash", Required), hash: base64::decode(&ver)?, }; Ok(Caps { - ext: get_attr!(elem, "ext", optional), - node: get_attr!(elem, "node", required), + ext: get_attr!(elem, "ext", Option), + node: get_attr!(elem, "node", Required), hash, }) } diff --git a/src/data_forms.rs b/src/data_forms.rs index 9a1ed3e3297556ebd26cabe8be938fb64b97c8f7..296614e975dfd118707a93e2c0d9e45e8ccf742f 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -106,9 +106,9 @@ impl TryFrom for Field { check_self!(elem, "field", DATA_FORMS); check_no_unknown_attributes!(elem, "field", ["label", "type", "var"]); let mut field = Field { - var: get_attr!(elem, "var", required), - type_: get_attr!(elem, "type", default), - label: get_attr!(elem, "label", optional), + var: get_attr!(elem, "var", Required), + type_: get_attr!(elem, "type", Default), + label: get_attr!(elem, "label", Option), required: false, options: vec![], values: vec![], @@ -220,7 +220,7 @@ impl TryFrom for DataForm { fn try_from(elem: Element) -> Result { check_self!(elem, "x", DATA_FORMS); check_no_unknown_attributes!(elem, "x", ["type"]); - let type_ = get_attr!(elem, "type", required); + let type_ = get_attr!(elem, "type", Required); let mut form = DataForm { type_, form_type: None, diff --git a/src/disco.rs b/src/disco.rs index 43c15903365dc900188ac0acc2801cc24ed2a1de..0acdcb86c30bf391f41f2cbc8e5e1b5e5309d67a 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -71,14 +71,14 @@ impl TryFrom for Identity { ["category", "type", "xml:lang", "name"] ); - let category = get_attr!(elem, "category", required); + let category = get_attr!(elem, "category", Required); if category == "" { return Err(Error::ParseError( "Identity must have a non-empty 'category' attribute.", )); } - let type_ = get_attr!(elem, "type", required); + let type_ = get_attr!(elem, "type", Required); if type_ == "" { return Err(Error::ParseError( "Identity must have a non-empty 'type' attribute.", @@ -88,8 +88,8 @@ impl TryFrom for Identity { Ok(Identity { category, type_, - lang: get_attr!(elem, "xml:lang", optional), - name: get_attr!(elem, "name", optional), + lang: get_attr!(elem, "xml:lang", Option), + name: get_attr!(elem, "name", Option), }) } } @@ -135,7 +135,7 @@ impl TryFrom for DiscoInfoResult { check_no_unknown_attributes!(elem, "disco#info result", ["node"]); let mut result = DiscoInfoResult { - node: get_attr!(elem, "node", optional), + node: get_attr!(elem, "node", Option), identities: vec![], features: vec![], extensions: vec![], diff --git a/src/iq.rs b/src/iq.rs index dd04e6b8f9d2c385ff25e2460583c59cd5df43cc..e62a4cd839c71af8d7d575cce4607d6cabbf7f35 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -134,10 +134,10 @@ impl TryFrom for Iq { fn try_from(root: Element) -> Result { check_self!(root, "iq", DEFAULT_NS); - let from = get_attr!(root, "from", optional); - let to = get_attr!(root, "to", optional); - let id = get_attr!(root, "id", required); - let type_: String = get_attr!(root, "type", required); + let from = get_attr!(root, "from", Option); + let to = get_attr!(root, "to", Option); + let id = get_attr!(root, "id", Required); + let type_: String = get_attr!(root, "type", Required); let mut payload = None; let mut error_payload = None; diff --git a/src/jingle.rs b/src/jingle.rs index b891ab7caea1b7a2d7fbae352fd8ade108b645b4..2c6c3fb896b2907d80b31a75f158450cc2b8448e 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -492,10 +492,10 @@ impl TryFrom for Jingle { check_no_unknown_attributes!(root, "Jingle", ["action", "initiator", "responder", "sid"]); let mut jingle = Jingle { - action: get_attr!(root, "action", required), - initiator: get_attr!(root, "initiator", optional), - responder: get_attr!(root, "responder", optional), - sid: get_attr!(root, "sid", required), + action: get_attr!(root, "action", Required), + initiator: get_attr!(root, "initiator", Option), + responder: get_attr!(root, "responder", Option), + sid: get_attr!(root, "sid", Required), contents: vec![], reason: None, other: vec![], diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 67c055e23290eed991eb0a0add4a68761c123ccc..3cd9e00abe7fdc94bc94ea778b36c40b1e812eb6 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -163,7 +163,7 @@ impl TryFrom for File { } file.name = Some(child.text()); } else if child.is("desc", ns::JINGLE_FT) { - let lang = get_attr!(child, "xml:lang", default); + let lang = get_attr!(child, "xml:lang", Default); let desc = Desc(child.text()); if file.descs.insert(lang, desc).is_some() { return Err(Error::ParseError( @@ -321,8 +321,8 @@ impl TryFrom for Checksum { )); } Ok(Checksum { - name: get_attr!(elem, "name", required), - creator: get_attr!(elem, "creator", required), + name: get_attr!(elem, "name", Required), + creator: get_attr!(elem, "creator", Required), file: file.unwrap(), }) } diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 1a520223bcfadfa680e3526435d2c7db0b40724d..8561a649b792a7d422ca10cb7380d6a2aafdf38a 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -39,7 +39,7 @@ pub enum JingleMI { fn get_sid(elem: Element) -> Result { check_no_unknown_attributes!(elem, "Jingle message", ["id"]); - Ok(SessionId(get_attr!(elem, "id", required))) + Ok(SessionId(get_attr!(elem, "id", Required))) } fn check_empty_and_get_sid(elem: Element) -> Result { diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 1f3a3ee60fb3b7f67c3cbc472bd00237a333fe6e..02b97361352b517dc471c7e1816826370a9ab98a 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -177,9 +177,9 @@ impl TryFrom for Transport { fn try_from(elem: Element) -> Result { check_self!(elem, "transport", JINGLE_S5B); check_no_unknown_attributes!(elem, "transport", ["sid", "dstaddr", "mode"]); - let sid = get_attr!(elem, "sid", required); - let dstaddr = get_attr!(elem, "dstaddr", optional); - let mode = get_attr!(elem, "mode", default); + let sid = get_attr!(elem, "sid", Required); + let dstaddr = get_attr!(elem, "dstaddr", Option); + let mode = get_attr!(elem, "mode", Default); let mut payload = None; for child in elem.children() { @@ -200,7 +200,7 @@ impl TryFrom for Transport { "Non-activated child already present in JingleS5B transport element.", )); } - let cid = get_attr!(child, "cid", required); + let cid = get_attr!(child, "cid", Required); TransportPayload::Activated(cid) } else if child.is("candidate-error", ns::JINGLE_S5B) { if payload.is_some() { @@ -215,7 +215,7 @@ impl TryFrom for Transport { "Non-candidate-used child already present in JingleS5B transport element.", )); } - let cid = get_attr!(child, "cid", required); + let cid = get_attr!(child, "cid", Required); TransportPayload::CandidateUsed(cid) } else if child.is("proxy-error", ns::JINGLE_S5B) { if payload.is_some() { diff --git a/src/mam.rs b/src/mam.rs index b68dabb272b8052bd173da25e7cb40fa38af7950..cc606923f19bc15fb9a2f59a658f6ff5c90f3386 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -152,7 +152,7 @@ impl TryFrom for Prefs { return Err(Error::ParseError("Unknown child in prefs element.")); } } - let default_ = get_attr!(elem, "default", required); + let default_ = get_attr!(elem, "default", Required); Ok(Prefs { default_, always, diff --git a/src/message.rs b/src/message.rs index f2611accd1a2ee40c6412854593ed37c62928b28..29708c32e1d2e710ca27eda4790b6b0f6af7c0ac 100644 --- a/src/message.rs +++ b/src/message.rs @@ -154,10 +154,10 @@ impl TryFrom for Message { fn try_from(root: Element) -> Result { check_self!(root, "message", DEFAULT_NS); - let from = get_attr!(root, "from", optional); - let to = get_attr!(root, "to", optional); - let id = get_attr!(root, "id", optional); - let type_ = get_attr!(root, "type", default); + let from = get_attr!(root, "from", Option); + let to = get_attr!(root, "to", Option); + let id = get_attr!(root, "id", Option); + let type_ = get_attr!(root, "type", Default); let mut bodies = BTreeMap::new(); let mut subjects = BTreeMap::new(); let mut thread = None; @@ -165,7 +165,7 @@ impl TryFrom for Message { for elem in root.children() { if elem.is("body", ns::DEFAULT_NS) { check_no_children!(elem, "body"); - let lang = get_attr!(elem, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", Default); let body = Body(elem.text()); if bodies.insert(lang, body).is_some() { return Err(Error::ParseError( @@ -174,7 +174,7 @@ impl TryFrom for Message { } } else if elem.is("subject", ns::DEFAULT_NS) { check_no_children!(elem, "subject"); - let lang = get_attr!(elem, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", Default); let subject = Subject(elem.text()); if subjects.insert(lang, subject).is_some() { return Err(Error::ParseError( diff --git a/src/muc/user.rs b/src/muc/user.rs index 73e910ae1bcccb82890fe7d9637756ec6d53486c..8965529fa9486a7629472c188634afb731fd8fa0 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -95,8 +95,8 @@ impl TryFrom for Actor { check_self!(elem, "actor", MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); check_no_children!(elem, "actor"); - let jid: Option = get_attr!(elem, "jid", optional); - let nick = get_attr!(elem, "nick", optional); + let jid: Option = get_attr!(elem, "jid", Option); + let nick = get_attr!(elem, "nick", Option); match (jid, nick) { (Some(_), Some(_)) | (None, None) => { diff --git a/src/presence.rs b/src/presence.rs index e7d2a55df7c6b0f5d80d0f38b488c236c9677d6c..2bf09d04df224d48f538ed651c018aec7dc811d0 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -263,10 +263,10 @@ impl TryFrom for Presence { let mut show = None; let mut priority = None; let mut presence = Presence { - from: get_attr!(root, "from", optional), - to: get_attr!(root, "to", optional), - id: get_attr!(root, "id", optional), - type_: get_attr!(root, "type", default), + from: get_attr!(root, "from", Option), + to: get_attr!(root, "to", Option), + id: get_attr!(root, "id", Option), + type_: get_attr!(root, "type", Default), show: Show::None, statuses: BTreeMap::new(), priority: 0i8, @@ -285,7 +285,7 @@ impl TryFrom for Presence { } else if elem.is("status", ns::DEFAULT_NS) { check_no_unknown_attributes!(elem, "status", ["xml:lang"]); check_no_children!(elem, "status"); - let lang = get_attr!(elem, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", Default); if presence.statuses.insert(lang, elem.text()).is_some() { return Err(Error::ParseError( "Status element present twice for the same xml:lang.", diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 0c616ab64225b2a5c894ad42e481ae8c425840c2..8f6951d9e67081f62ffbfe630553868021b0d2f7 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -115,7 +115,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { } check_no_children!(child, "retract"); check_no_unknown_attributes!(child, "retract", ["id"]); - let id = get_attr!(child, "id", required); + let id = get_attr!(child, "id", Required); retracts.push(id); } else { return Err(Error::ParseError("Invalid child in items element.")); @@ -140,7 +140,7 @@ impl TryFrom for PubSubEvent { let mut payload = None; for child in elem.children() { - let node = get_attr!(child, "node", required); + let node = get_attr!(child, "node", Required); if child.is("configuration", ns::PUBSUB_EVENT) { let mut payloads = child.children().cloned().collect::>(); let item = payloads.pop(); @@ -163,7 +163,7 @@ impl TryFrom for PubSubEvent { "More than one redirect in delete element.", )); } - let uri = get_attr!(item, "uri", required); + let uri = get_attr!(item, "uri", Required); redirect = Some(uri); } else { return Err(Error::ParseError("Unknown child in delete element.")); @@ -179,10 +179,10 @@ impl TryFrom for PubSubEvent { check_no_children!(child, "subscription"); payload = Some(PubSubEvent::Subscription { node, - expiry: get_attr!(child, "expiry", optional), - jid: get_attr!(child, "jid", optional), - subid: get_attr!(child, "subid", optional), - subscription: get_attr!(child, "subscription", optional), + expiry: get_attr!(child, "expiry", Option), + jid: get_attr!(child, "jid", Option), + subid: get_attr!(child, "subid", Option), + subscription: get_attr!(child, "subscription", Option), }); } else { return Err(Error::ParseError("Unknown child in event element.")); diff --git a/src/rsm.rs b/src/rsm.rs index 1dd5f47f71ccfd4c0d93971379a1385b1b6a6aa6..6b65e3b48d9cda6f193fb96005293f0cd4d46f1d 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -131,7 +131,7 @@ impl TryFrom for SetResult { if set.first.is_some() { return Err(Error::ParseError("Set can’t have more than one first.")); } - set.first_index = get_attr!(child, "index", optional); + set.first_index = get_attr!(child, "index", Option); set.first = Some(child.text()); } else if child.is("last", ns::RSM) { if set.last.is_some() { diff --git a/src/sasl.rs b/src/sasl.rs index 9d35072e1c25b0bf4709c42e3f9ea4fc53432db1..ec84fe6cb99df44fa0ea8cf9b76111239eca044b 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -164,7 +164,7 @@ impl TryFrom for Failure { if child.is("text", ns::SASL) { check_no_unknown_attributes!(child, "text", ["xml:lang"]); check_no_children!(child, "text"); - let lang = get_attr!(child, "xml:lang", default); + let lang = get_attr!(child, "xml:lang", Default); if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError( "Text element present twice for the same xml:lang in failure element.", diff --git a/src/stanza_error.rs b/src/stanza_error.rs index d254f6b48d9a0e17c9f8fdd8d97f294a557056f6..3488b9c13c96a9007b69592496744a1163b3cf5a 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -221,8 +221,8 @@ impl TryFrom for StanzaError { fn try_from(elem: Element) -> Result { check_self!(elem, "error", DEFAULT_NS); - let type_ = get_attr!(elem, "type", required); - let by = get_attr!(elem, "by", optional); + let type_ = get_attr!(elem, "type", Required); + let by = get_attr!(elem, "by", Option); let mut defined_condition = None; let mut texts = BTreeMap::new(); let mut other = None; @@ -230,7 +230,7 @@ impl TryFrom for StanzaError { for child in elem.children() { if child.is("text", ns::XMPP_STANZAS) { check_no_children!(child, "text"); - let lang = get_attr!(elem, "xml:lang", default); + let lang = get_attr!(elem, "xml:lang", Default); if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError( "Text element present twice for the same xml:lang.", diff --git a/src/util/macros.rs b/src/util/macros.rs index 74e4009f93471cfdda0112adc51e5a47371bf27e..a67afe1f835818f220e1b6d5529f61d2313e00b9 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -39,16 +39,6 @@ macro_rules! get_attr { None => ::std::default::Default::default(), } }; - // The remaining ones are only for backwards-compatibility. - ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => { - get_attr!($elem, $attr, Option, $value, $func) - }; - ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => { - get_attr!($elem, $attr, Required, $value, $func) - }; - ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => { - get_attr!($elem, $attr, Default, $value, $func) - }; } macro_rules! generate_attribute { @@ -242,7 +232,7 @@ macro_rules! generate_attribute_enum { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); - Ok(match get_attr!(elem, $attr, required) { + Ok(match get_attr!(elem, $attr, Required) { $($enum_name => $elem::$enum,)+ _ => return Err(crate::util::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), }) @@ -639,8 +629,8 @@ macro_rules! impl_pubsub_item { )); } Ok($item(crate::pubsub::Item { - id: get_attr!(elem, "id", optional), - publisher: get_attr!(elem, "publisher", optional), + id: get_attr!(elem, "id", Option), + publisher: get_attr!(elem, "publisher", Option), payload, })) } From 82eda09ca5a54e37b072449901ba95e96c78fb61 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Feb 2019 19:25:43 +0100 Subject: [PATCH 0848/1020] =?UTF-8?q?iq:=20Allow=20any=20Into=20fo?= =?UTF-8?q?r=20the=20constructors=E2=80=99=20id.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/iq.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index e62a4cd839c71af8d7d575cce4607d6cabbf7f35..bca785e90af177b7f351c55ea368056ce3913335 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -71,41 +71,41 @@ pub struct Iq { impl Iq { /// Creates an `` stanza containing a get request. - pub fn from_get(id: String, payload: impl IqGetPayload) -> Iq { + pub fn from_get>(id: S, payload: impl IqGetPayload) -> Iq { Iq { from: None, to: None, - id, + id: id.into(), payload: IqType::Get(payload.into()), } } /// Creates an `` stanza containing a set request. - pub fn from_set(id: String, payload: impl IqSetPayload) -> Iq { + pub fn from_set>(id: S, payload: impl IqSetPayload) -> Iq { Iq { from: None, to: None, - id, + id: id.into(), payload: IqType::Set(payload.into()), } } /// Creates an `` stanza containing a result. - pub fn from_result(id: String, payload: Option) -> Iq { + pub fn from_result>(id: S, payload: Option) -> Iq { Iq { from: None, to: None, - id, + id: id.into(), payload: IqType::Result(payload.map(Into::into)), } } /// Creates an `` stanza containing an error. - pub fn from_error(id: String, payload: StanzaError) -> Iq { + pub fn from_error>(id: S, payload: StanzaError) -> Iq { Iq { from: None, to: None, - id, + id: id.into(), payload: IqType::Error(payload), } } From dd80f55c5f5bcdfa52e3a2da9bbe262c64efa9d4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Feb 2019 19:43:29 +0100 Subject: [PATCH 0849/1020] =?UTF-8?q?disco:=20Add=20constructors=20for=20I?= =?UTF-8?q?dentity,=20and=20fix=20Feature=E2=80=99s.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/disco.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index 0acdcb86c30bf391f41f2cbc8e5e1b5e5309d67a..b958cce573ee03f96675fc6b16b238d82cb61d0f 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -36,9 +36,9 @@ attributes: [ impl Feature { /// Create a new `` with the according `@var`. - pub fn new(var: &str) -> Feature { + pub fn new>(var: S) -> Feature { Feature { - var: String::from(var) + var: var.into(), } } } @@ -59,6 +59,36 @@ pub struct Identity { pub name: Option, } +impl Identity { + /// Create a new ``. + pub fn new(category: C, type_: T, lang: L, name: N) -> Identity + where C: Into, + T: Into, + L: Into, + N: Into, + { + Identity { + category: category.into(), + type_: type_.into(), + lang: Some(lang.into()), + name: Some(name.into()), + } + } + + /// Create a new `` without a name. + pub fn new_anonymous(category: C, type_: T) -> Identity + where C: Into, + T: Into, + { + Identity { + category: category.into(), + type_: type_.into(), + lang: None, + name: None, + } + } +} + impl TryFrom for Identity { type Err = Error; From ab28824b1a653a2a1052db8ccafca374e42ca87d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Feb 2019 19:51:41 +0100 Subject: [PATCH 0850/1020] presence: Simplify Presence::set_status with Into. --- src/presence.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index 2bf09d04df224d48f538ed651c018aec7dc811d0..fba1773c917fca83a3f6c3fc2738f738873b26cc 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -245,8 +245,11 @@ impl Presence { } /// Set the availability information of this presence. - pub fn set_status(&mut self, lang: Lang, status: Status) { - self.statuses.insert(lang, status); + pub fn set_status(&mut self, lang: L, status: S) + where L: Into, + S: Into, + { + self.statuses.insert(lang.into(), status.into()); } /// Add a payload to this presence. From 017fb0fbd1d4c803c865d2abd23e7305bcceeb74 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Feb 2019 20:21:26 +0100 Subject: [PATCH 0851/1020] stanza_error: Add a constructor. --- src/stanza_error.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 3488b9c13c96a9007b69592496744a1163b3cf5a..4c09eea792bedf03dbd0e18c996d3bf23f60d03f 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -215,6 +215,26 @@ pub struct StanzaError { impl MessagePayload for StanzaError {} impl PresencePayload for StanzaError {} +impl StanzaError { + /// Create a new `` with the according content. + pub fn new(type_: ErrorType, defined_condition: DefinedCondition, lang: L, text: T) -> StanzaError + where L: Into, + T: Into, + { + StanzaError { + type_, + by: None, + defined_condition, + texts: { + let mut map = BTreeMap::new(); + map.insert(lang.into(), text.into()); + map + }, + other: None, + } + } +} + impl TryFrom for StanzaError { type Err = Error; From 40d397c1fe32dd929998b22ad97be798ff97d0ed Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Feb 2019 18:13:06 +0100 Subject: [PATCH 0852/1020] jingle_rtp: Add a new parser/serialiser for XEP-0167. --- src/jingle_rtp.rs | 141 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 7 +++ 3 files changed, 151 insertions(+) create mode 100644 src/jingle_rtp.rs diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs new file mode 100644 index 0000000000000000000000000000000000000000..1ab50d4d325446993488378162c3a251a2111756 --- /dev/null +++ b/src/jingle_rtp.rs @@ -0,0 +1,141 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::num::ParseIntError; +use std::str::FromStr; +use minidom::IntoAttributeValue; + +generate_element!( + /// Wrapper element describing an RTP session. + Description, "description", JINGLE_RTP, + attributes: [ + /// Namespace of the encryption scheme used. + media: Required = "media", + + /// User-friendly name for the encryption scheme, should be `None` for OTR, + /// legacy OpenPGP and OX. + // XXX: is this a String or an u32?! Refer to RFC 3550. + ssrc: Option = "ssrc", + ], + children: [ + /// List of encodings that can be used for this RTP stream. + payload_types: Vec = ("payload-type", JINGLE_RTP) => PayloadType + + // TODO: Add support for and . + ] +); + +/// The number of channels. +#[derive(Debug, Clone)] +pub struct Channels(pub u8); + +impl Default for Channels { + fn default() -> Channels { + Channels(1) + } +} + +impl FromStr for Channels { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + Ok(Channels(u8::from_str(s)?)) + } +} + +impl IntoAttributeValue for Channels { + fn into_attribute_value(self) -> Option { + if self.0 == 1 { + None + } else { + Some(format!("{}", self.0)) + } + } +} + +generate_element!( + /// An encoding that can be used for an RTP stream. + PayloadType, "payload-type", JINGLE_RTP, + attributes: [ + /// The number of channels. + channels: Default = "channels", + + /// The sampling frequency in Hertz. + clockrate: Option = "clockrate", + + /// The payload identifier. + id: Required = "id", + + /// Maximum packet time as specified in RFC 4566. + maxptime: Option = "maxptime", + + /// The appropriate subtype of the MIME type. + name: Option = "name", + + /// Packet time as specified in RFC 4566. + ptime: Option = "ptime", + ], + children: [ + /// List of parameters specifying this payload-type. + /// + /// Their order MUST be ignored. + parameters: Vec = ("parameter", JINGLE_RTP) => Parameter + ] +); + +generate_element!( + /// Parameter related to a payload. + Parameter, "parameter", JINGLE_RTP, + attributes: [ + /// The name of the parameter, from the list at + /// https://www.iana.org/assignments/sdp-parameters/sdp-parameters.xhtml + name: Required = "name", + + /// The value of this parameter. + value: Required = "value", + ] +); + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use try_from::TryFrom; + + #[test] + fn test_simple() { + let elem: Element = " + + + + + + + + + + + + + + + + + + + + + + + +" + .parse() + .unwrap(); + let desc = Description::try_from(elem).unwrap(); + assert_eq!(desc.media, "audio"); + assert_eq!(desc.ssrc, None); + } +} diff --git a/src/lib.rs b/src/lib.rs index 765c9a15cc14a641c05000eac73915ea4f28b949..773c120211c95ccc264b1393a80d5fd7bcea99c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,6 +104,9 @@ pub mod caps; /// XEP-0166: Jingle pub mod jingle; +/// XEP-0167: Jingle RTP Sessions +pub mod jingle_rtp; + /// XEP-0172: User Nickname pub mod nick; diff --git a/src/ns.rs b/src/ns.rs index 7aa4768cfa4a85bfc061d59368cf0640c7494e49..f825d4dca701baff75dfd1830ba1fc7f58916bdf 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -82,6 +82,13 @@ pub const CAPS: &str = "http://jabber.org/protocol/caps"; /// XEP-0166: Jingle pub const JINGLE: &str = "urn:xmpp:jingle:1"; +/// XEP-0167: Jingle RTP Sessions +pub const JINGLE_RTP: &str = "urn:xmpp:jingle:apps:rtp:1"; +/// XEP-0167: Jingle RTP Sessions +pub const JINGLE_RTP_AUDIO: &str = "urn:xmpp:jingle:apps:rtp:audio"; +/// XEP-0167: Jingle RTP Sessions +pub const JINGLE_RTP_VIDEO: &str = "urn:xmpp:jingle:apps:rtp:video"; + /// XEP-0172: User Nickname pub const NICK: &str = "http://jabber.org/protocol/nick"; From beacbaeb86cf28f57f8cae956416bcf93c38b85e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Feb 2019 18:13:18 +0100 Subject: [PATCH 0853/1020] jingle_ice_udp: Add a new parser/serialiser for XEP-0176. --- src/lib.rs | 3 +++ src/ns.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 773c120211c95ccc264b1393a80d5fd7bcea99c3..fa1153fee2d9301a465ca7e4db88f114d7ba981a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,6 +110,9 @@ pub mod jingle_rtp; /// XEP-0172: User Nickname pub mod nick; +/// XEP-0176: Jingle ICE-UDP Transport Method +pub mod jingle_ice_udp; + /// XEP-0184: Message Delivery Receipts pub mod receipts; diff --git a/src/ns.rs b/src/ns.rs index f825d4dca701baff75dfd1830ba1fc7f58916bdf..321c6fa8362ee14bc8a1035638350c90db2419f4 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -92,6 +92,9 @@ pub const JINGLE_RTP_VIDEO: &str = "urn:xmpp:jingle:apps:rtp:video"; /// XEP-0172: User Nickname pub const NICK: &str = "http://jabber.org/protocol/nick"; +/// XEP-0176: Jingle ICE-UDP Transport Method +pub const JINGLE_ICE_UDP: &str = "urn:xmpp:jingle:transports:ice-udp:1"; + /// XEP-0184: Message Delivery Receipts pub const RECEIPTS: &str = "urn:xmpp:receipts"; From f8aedb0da13bd34a30ef1784353840416e39e1da Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Feb 2019 18:13:18 +0100 Subject: [PATCH 0854/1020] =?UTF-8?q?jingle=5Fice=5Fudp:=20Add=20missing?= =?UTF-8?q?=20file=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jingle_ice_udp.rs | 122 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/jingle_ice_udp.rs diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs new file mode 100644 index 0000000000000000000000000000000000000000..5713c2e66949b350ab5d4c9326cdeeb161f5c110 --- /dev/null +++ b/src/jingle_ice_udp.rs @@ -0,0 +1,122 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::net::IpAddr; + +generate_element!( + /// Wrapper element for an ICE-UDP transport. + Transport, "transport", JINGLE_ICE_UDP, + attributes: [ + /// A Password as defined in ICE-CORE. + pwd: Option = "pwd", + + /// A User Fragment as defined in ICE-CORE. + ufrag: Option = "ufrag", + ], + children: [ + /// List of candidates for this ICE-UDP session. + candidates: Vec = ("candidate", JINGLE_ICE_UDP) => Candidate + ] +); + +generate_attribute!( + /// A Candidate Type as defined in ICE-CORE. + Type, "type", { + /// Host candidate. + Host => "host", + + /// Peer reflexive candidate. + Prflx => "prflx", + + /// Relayed candidate. + Relay => "relay", + + /// Server reflexive candidate. + Srflx => "srflx", + } +); + +generate_element!( + /// A candidate for an ICE-UDP session. + Candidate, "candidate", JINGLE_ICE_UDP, + attributes: [ + /// A Component ID as defined in ICE-CORE. + component: Required = "component", + + /// A Foundation as defined in ICE-CORE. + foundation: Required = "foundation", + + /// An index, starting at 0, that enables the parties to keep track of updates to the + /// candidate throughout the life of the session. + generation: Required = "generation", + + /// A unique identifier for the candidate. + id: Required = "id", + + /// The Internet Protocol (IP) address for the candidate transport mechanism; this can be + /// either an IPv4 address or an IPv6 address. + ip: Required = "ip", + + /// An index, starting at 0, referencing which network this candidate is on for a given + /// peer. + network: Required = "network", + + /// The port at the candidate IP address. + port: Required = "port", + + /// A Priority as defined in ICE-CORE. + priority: Required = "priority", + + /// The protocol to be used. The only value defined by this specification is "udp". + protocol: Required = "protocol", + + /// A related address as defined in ICE-CORE. + rel_addr: Option = "rel-addr", + + /// A related port as defined in ICE-CORE. + rel_port: Option = "rel-port", + + /// A Candidate Type as defined in ICE-CORE. + type_: Required = "type", + ] +); + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use try_from::TryFrom; + + #[test] + fn test_simple() { + let elem: Element = " + + + + + + + + + + + + + + + + + + + +" + .parse() + .unwrap(); + let transport = Transport::try_from(elem).unwrap(); + assert_eq!(transport.pwd.unwrap(), "wakMJ8Ydd5rqnPaFerws5o"); + assert_eq!(transport.ufrag.unwrap(), "aeXX"); + } +} From 149681954625097448616ac88c277a5ba77b6638 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Feb 2019 18:04:16 +0100 Subject: [PATCH 0855/1020] Implement a nicer Debug for NamespaceSet The existing one was quite hard to parse visually, this makes it a lot easier to understand what is what. --- src/namespace_set.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/namespace_set.rs b/src/namespace_set.rs index 57d3156dc5883f75ce06faa70aa683892604e5b4..a5401d1e5c94b6f599dab68efd6c7c022353f863 100644 --- a/src/namespace_set.rs +++ b/src/namespace_set.rs @@ -1,9 +1,10 @@ use std::collections::BTreeMap; use std::cell::RefCell; +use std::fmt; use std::rc::Rc; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub struct NamespaceSet { parent: RefCell>>, namespaces: BTreeMap, String>, @@ -18,6 +19,19 @@ impl Default for NamespaceSet { } } +impl fmt::Debug for NamespaceSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "NamespaceSet(")?; + for (prefix, namespace) in &self.namespaces { + write!(f, "xmlns{}={:?}, ", match prefix { + None => String::new(), + Some(prefix) => format!(":{}", prefix), + }, namespace)?; + } + write!(f, "parent: {:?})", *self.parent.borrow()) + } +} + impl NamespaceSet { pub fn declared_ns(&self) -> &BTreeMap, String> { &self.namespaces @@ -107,7 +121,6 @@ impl From<(String, String)> for NamespaceSet { #[cfg(test)] mod tests { use super::*; - use std::rc::Rc; #[test] fn get_has() { @@ -147,4 +160,11 @@ mod tests { } } + #[test] + fn debug_looks_correct() { + let parent = NamespaceSet::from("http://www.w3.org/2000/svg".to_owned()); + let namespaces = NamespaceSet::from(("xhtml".to_owned(), "http://www.w3.org/1999/xhtml".to_owned())); + namespaces.set_parent(Rc::new(parent)); + assert_eq!(format!("{:?}", namespaces), "NamespaceSet(xmlns:xhtml=\"http://www.w3.org/1999/xhtml\", parent: Some(NamespaceSet(xmlns=\"http://www.w3.org/2000/svg\", parent: None)))"); + } } From c4d867571e6d82751283308961e4c4a0ce692612 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 27 Feb 2019 23:27:40 +0100 Subject: [PATCH 0856/1020] Also reexport TryInto from try_from. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index fa1153fee2d9301a465ca7e4db88f114d7ba981a..ca7840b165c34ebe920b7db035759f15aa06517b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ pub use minidom::Element; pub use jid::{Jid, JidParseError}; -pub use try_from::TryFrom; +pub use try_from::{TryFrom, TryInto}; pub use crate::util::error::Error; /// XML namespace definitions used through XMPP. From b56582c8b5983b40eb64686abe15194803d4b2fb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:26:10 +0100 Subject: [PATCH 0857/1020] disco: Use a macro for . --- src/disco.rs | 79 +++++++++++----------------------------------- src/util/macros.rs | 22 +++++++++++++ 2 files changed, 40 insertions(+), 61 deletions(-) diff --git a/src/disco.rs b/src/disco.rs index b958cce573ee03f96675fc6b16b238d82cb61d0f..87145208d96aeb819e913eb7ed8a7981665bf718 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -43,21 +43,25 @@ impl Feature { } } -/// Structure representing an `` element. -#[derive(Debug, Clone)] -pub struct Identity { - /// Category of this identity. - pub category: String, // TODO: use an enum here. +generate_element!( + /// Structure representing an `` element. + Identity, "identity", DISCO_INFO, + attributes: [ + /// Category of this identity. + // TODO: use an enum here. + category: RequiredNonEmpty = "category", - /// Type of this identity. - pub type_: String, // TODO: use an enum here. + /// Type of this identity. + // TODO: use an enum here. + type_: RequiredNonEmpty = "type", - /// Lang of the name of this identity. - pub lang: Option, + /// Lang of the name of this identity. + lang: Option = "xml:lang", - /// Name of this identity. - pub name: Option, -} + /// Name of this identity. + name: Option = "name", + ] +); impl Identity { /// Create a new ``. @@ -89,53 +93,6 @@ impl Identity { } } -impl TryFrom for Identity { - type Err = Error; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "identity", DISCO_INFO, "disco#info identity"); - check_no_children!(elem, "disco#info identity"); - check_no_unknown_attributes!( - elem, - "disco#info identity", - ["category", "type", "xml:lang", "name"] - ); - - let category = get_attr!(elem, "category", Required); - if category == "" { - return Err(Error::ParseError( - "Identity must have a non-empty 'category' attribute.", - )); - } - - let type_ = get_attr!(elem, "type", Required); - if type_ == "" { - return Err(Error::ParseError( - "Identity must have a non-empty 'type' attribute.", - )); - } - - Ok(Identity { - category, - type_, - lang: get_attr!(elem, "xml:lang", Option), - name: get_attr!(elem, "name", Option), - }) - } -} - -impl From for Element { - fn from(identity: Identity) -> Element { - Element::builder("identity") - .ns(ns::DISCO_INFO) - .attr("category", identity.category) - .attr("type", identity.type_) - .attr("xml:lang", identity.lang) - .attr("name", identity.name) - .build() - } -} - /// Structure representing a `` element. /// /// It should only be used in an ``, as it can only @@ -385,7 +342,7 @@ mod tests { }; assert_eq!( message, - "Identity must have a non-empty 'category' attribute." + "Required attribute 'category' must not be empty." ); let elem: Element = "".parse().unwrap(); @@ -402,7 +359,7 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Identity must have a non-empty 'type' attribute."); + assert_eq!(message, "Required attribute 'type' must not be empty."); } #[test] diff --git a/src/util/macros.rs b/src/util/macros.rs index a67afe1f835818f220e1b6d5529f61d2313e00b9..bc2b8342ea3a7503bbc23839b8cd63081485220d 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -33,6 +33,25 @@ macro_rules! get_attr { } } }; + ($elem:ident, $attr:tt, RequiredNonEmpty, $value:ident, $func:expr) => { + match $elem.attr($attr) { + Some("") => { + return Err(crate::util::error::Error::ParseError(concat!( + "Required attribute '", + $attr, + "' must not be empty." + ))); + }, + Some($value) => $func, + None => { + return Err(crate::util::error::Error::ParseError(concat!( + "Required attribute '", + $attr, + "' missing." + ))); + } + } + }; ($elem:ident, $attr:tt, Default, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, @@ -408,6 +427,9 @@ macro_rules! decl_attr { (Required, $type:ty) => ( $type ); + (RequiredNonEmpty, $type:ty) => ( + $type + ); (Default, $type:ty) => ( $type ); From 2b9a6d57b63538b3e6d3627d92cfc995a89362c0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:40:04 +0100 Subject: [PATCH 0858/1020] jingle: Support more than one with different @xml:lang. --- src/jingle.rs | 62 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/jingle.rs b/src/jingle.rs index 2c6c3fb896b2907d80b31a75f158450cc2b8448e..9edb318ff43b5903366fc0b1ee7c7186ac708c72 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -9,6 +9,7 @@ use crate::iq::IqSetPayload; use crate::ns; use jid::Jid; use minidom::Element; +use std::collections::BTreeMap; use std::str::FromStr; use try_from::TryFrom; @@ -354,6 +355,8 @@ impl From for Element { } } +type Lang = String; + /// Informs the recipient of something. #[derive(Debug, Clone)] pub struct ReasonElement { @@ -361,7 +364,7 @@ pub struct ReasonElement { pub reason: Reason, /// A human-readable description of this reason. - pub text: Option, + pub texts: BTreeMap, } impl TryFrom for ReasonElement { @@ -369,29 +372,30 @@ impl TryFrom for ReasonElement { fn try_from(elem: Element) -> Result { check_self!(elem, "reason", JINGLE); + check_no_attributes!(elem, "reason"); let mut reason = None; - let mut text = None; + let mut texts = BTreeMap::new(); for child in elem.children() { - if !child.has_ns(ns::JINGLE) { - return Err(Error::ParseError("Reason contains a foreign element.")); - } - match child.name() { - "text" => { - if text.is_some() { - return Err(Error::ParseError( - "Reason must not have more than one text.", - )); - } - text = Some(child.text()); + if child.is("text", ns::JINGLE) { + check_no_children!(child, "text"); + check_no_unknown_attributes!(child, "text", ["xml:lang"]); + let lang = get_attr!(elem, "xml:lang", Default); + if texts.insert(lang, child.text()).is_some() { + return Err(Error::ParseError( + "Text element present twice for the same xml:lang.", + )); } - name => { - if reason.is_some() { - return Err(Error::ParseError( - "Reason must not have more than one reason.", - )); - } - reason = Some(name.parse()?); + } else if child.has_ns(ns::JINGLE) { + if reason.is_some() { + return Err(Error::ParseError( + "Reason must not have more than one reason.", + )); } + check_no_children!(child, "reason"); + check_no_attributes!(child, "reason"); + reason = Some(child.name().parse()?); + } else { + return Err(Error::ParseError("Reason contains a foreign element.")); } } let reason = reason.ok_or(Error::ParseError( @@ -399,7 +403,7 @@ impl TryFrom for ReasonElement { ))?; Ok(ReasonElement { reason, - text, + texts, }) } } @@ -407,8 +411,16 @@ impl TryFrom for ReasonElement { impl From for Element { fn from(reason: ReasonElement) -> Element { Element::builder("reason") + .ns(ns::JINGLE) .append(Element::from(reason.reason)) - .append(reason.text) + .append( + reason.texts.into_iter().map(|(lang, text)| { + Element::builder("text") + .ns(ns::JINGLE) + .attr("xml:lang", lang) + .append(text) + .build() + }).collect::>()) .build() } } @@ -679,13 +691,13 @@ mod tests { let jingle = Jingle::try_from(elem).unwrap(); let reason = jingle.reason.unwrap(); assert_eq!(reason.reason, Reason::Success); - assert_eq!(reason.text, None); + assert_eq!(reason.texts, BTreeMap::new()); let elem: Element = "coucou".parse().unwrap(); let jingle = Jingle::try_from(elem).unwrap(); let reason = jingle.reason.unwrap(); assert_eq!(reason.reason, Reason::Success); - assert_eq!(reason.text, Some(String::from("coucou"))); + assert_eq!(reason.texts.get(""), Some(&String::from("coucou"))); } #[test] @@ -728,6 +740,6 @@ mod tests { Error::ParseError(string) => string, _ => panic!(), }; - assert_eq!(message, "Reason must not have more than one text."); + assert_eq!(message, "Text element present twice for the same xml:lang."); } } From 4f64754bdcd45712d37a71587fe538d84287b060 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:44:31 +0100 Subject: [PATCH 0859/1020] stanza_error: Simplify serialisation. --- src/stanza_error.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 4c09eea792bedf03dbd0e18c996d3bf23f60d03f..fdc0d52253c28f40afc01631c512d5b7608137a0 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -289,24 +289,21 @@ impl TryFrom for StanzaError { impl From for Element { fn from(err: StanzaError) -> Element { - let mut root = Element::builder("error") + Element::builder("error") .ns(ns::DEFAULT_NS) .attr("type", err.type_) .attr("by", err.by) .append(err.defined_condition) - .build(); - for (lang, text) in err.texts { - let elem = Element::builder("text") - .ns(ns::XMPP_STANZAS) - .attr("xml:lang", lang) - .append(text) - .build(); - root.append_child(elem); - } - if let Some(other) = err.other { - root.append_child(other); - } - root + .append( + err.texts.into_iter().map(|(lang, text)| { + Element::builder("text") + .ns(ns::XMPP_STANZAS) + .attr("xml:lang", lang) + .append(text) + .build() + }).collect::>()) + .append(err.other) + .build() } } From fb4a3dcba86a48073fc15e6160fbafe78c3d497f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:48:50 +0100 Subject: [PATCH 0860/1020] stanza_error: Add missing attribute checks. --- src/stanza_error.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index fdc0d52253c28f40afc01631c512d5b7608137a0..fd0522d7f65c51f2bb41b29d51db48e78c125ff9 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -240,6 +240,7 @@ impl TryFrom for StanzaError { fn try_from(elem: Element) -> Result { check_self!(elem, "error", DEFAULT_NS); + check_no_unknown_attributes!(elem, "error", ["type", "by"]); let type_ = get_attr!(elem, "type", Required); let by = get_attr!(elem, "by", Option); @@ -250,6 +251,7 @@ impl TryFrom for StanzaError { for child in elem.children() { if child.is("text", ns::XMPP_STANZAS) { check_no_children!(child, "text"); + check_no_unknown_attributes!(child, "text", ["xml:lang"]); let lang = get_attr!(elem, "xml:lang", Default); if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError( @@ -263,6 +265,7 @@ impl TryFrom for StanzaError { )); } check_no_children!(child, "defined-condition"); + check_no_attributes!(child, "defined-condition"); let condition = DefinedCondition::try_from(child.clone())?; defined_condition = Some(condition); } else { From a62b184d5474d430ab920cc6dba07cf94d225211 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 02:54:13 +0100 Subject: [PATCH 0861/1020] stanza_error: Use a mutable struct first. --- src/stanza_error.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/stanza_error.rs b/src/stanza_error.rs index fd0522d7f65c51f2bb41b29d51db48e78c125ff9..dcaf5cb884f6146839ccf557426eef424cfa3960 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -242,18 +242,21 @@ impl TryFrom for StanzaError { check_self!(elem, "error", DEFAULT_NS); check_no_unknown_attributes!(elem, "error", ["type", "by"]); - let type_ = get_attr!(elem, "type", Required); - let by = get_attr!(elem, "by", Option); + let mut stanza_error = StanzaError { + type_: get_attr!(elem, "type", Required), + by: get_attr!(elem, "by", Option), + defined_condition: DefinedCondition::UndefinedCondition, + texts: BTreeMap::new(), + other: None, + }; let mut defined_condition = None; - let mut texts = BTreeMap::new(); - let mut other = None; for child in elem.children() { if child.is("text", ns::XMPP_STANZAS) { check_no_children!(child, "text"); check_no_unknown_attributes!(child, "text", ["xml:lang"]); let lang = get_attr!(elem, "xml:lang", Default); - if texts.insert(lang, child.text()).is_some() { + if stanza_error.texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError( "Text element present twice for the same xml:lang.", )); @@ -269,24 +272,18 @@ impl TryFrom for StanzaError { let condition = DefinedCondition::try_from(child.clone())?; defined_condition = Some(condition); } else { - if other.is_some() { + if stanza_error.other.is_some() { return Err(Error::ParseError( "Error must not have more than one other element.", )); } - other = Some(child.clone()); + stanza_error.other = Some(child.clone()); } } - let defined_condition = + stanza_error.defined_condition = defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; - Ok(StanzaError { - type_, - by, - defined_condition, - texts, - other, - }) + Ok(stanza_error) } } From ba875cfd2a23459233b0e7a19874d3ac8fe71f2c Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 03:10:21 +0100 Subject: [PATCH 0862/1020] jingle_rtp: Put Channel code into a macro, to generate it automatically. --- src/jingle_rtp.rs | 35 ++++------------------------------- src/util/macros.rs | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 1ab50d4d325446993488378162c3a251a2111756..95194edc2b9fe5afa64f539db6a77d11135dd5e9 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -4,10 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use std::num::ParseIntError; -use std::str::FromStr; -use minidom::IntoAttributeValue; - generate_element!( /// Wrapper element describing an RTP session. Description, "description", JINGLE_RTP, @@ -28,33 +24,10 @@ generate_element!( ] ); -/// The number of channels. -#[derive(Debug, Clone)] -pub struct Channels(pub u8); - -impl Default for Channels { - fn default() -> Channels { - Channels(1) - } -} - -impl FromStr for Channels { - type Err = ParseIntError; - - fn from_str(s: &str) -> Result { - Ok(Channels(u8::from_str(s)?)) - } -} - -impl IntoAttributeValue for Channels { - fn into_attribute_value(self) -> Option { - if self.0 == 1 { - None - } else { - Some(format!("{}", self.0)) - } - } -} +generate_attribute!( + /// The number of channels. + Channels, "channels", u8, Default = 1 +); generate_element!( /// An encoding that can be used for an RTP stream. diff --git a/src/util/macros.rs b/src/util/macros.rs index bc2b8342ea3a7503bbc23839b8cd63081485220d..f4b08335fa29301d1d263e4315ea3938dfdfaa2a 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -191,6 +191,30 @@ macro_rules! generate_attribute { } } ); + ($(#[$meta:meta])* $elem:ident, $name:tt, $type:tt, Default = $default:expr) => ( + $(#[$meta])* + #[derive(Debug, Clone, PartialEq)] + pub struct $elem(pub $type); + impl ::std::str::FromStr for $elem { + type Err = crate::util::error::Error; + fn from_str(s: &str) -> Result { + Ok($elem($type::from_str(s)?)) + } + } + impl ::minidom::IntoAttributeValue for $elem { + fn into_attribute_value(self) -> Option { + match self { + $elem($default) => None, + $elem(value) => Some(format!("{}", value)), + } + } + } + impl ::std::default::Default for $elem { + fn default() -> $elem { + $elem($default) + } + } + ); } macro_rules! generate_element_enum { From 5592c11745e26d57071d24129e1374a5ef994927 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 03:36:04 +0100 Subject: [PATCH 0863/1020] jingle_ice_udp: Use an IpAddr for rel-addr too. --- src/jingle_ice_udp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 5713c2e66949b350ab5d4c9326cdeeb161f5c110..8cb47da825567c0dbbbbe79b2f6db4da44cbdc2a 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -74,7 +74,7 @@ generate_element!( protocol: Required = "protocol", /// A related address as defined in ICE-CORE. - rel_addr: Option = "rel-addr", + rel_addr: Option = "rel-addr", /// A related port as defined in ICE-CORE. rel_port: Option = "rel-port", From 7fc5dea4d690ca6bfb498757eb3657bfd8415474 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 03:47:04 +0100 Subject: [PATCH 0864/1020] jingle_ice_udp, jingle_rtp: Add a size test. --- src/jingle_ice_udp.rs | 16 ++++++++++++++++ src/jingle_rtp.rs | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 8cb47da825567c0dbbbbe79b2f6db4da44cbdc2a..40a07a62cd2f5e9f99e1237e0862d56af6ed91e5 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -90,6 +90,22 @@ mod tests { use minidom::Element; use try_from::TryFrom; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Transport, 36); + assert_size!(Type, 1); + assert_size!(Candidate, 72); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Transport, 72); + assert_size!(Type, 1); + assert_size!(Candidate, 104); + } + #[test] fn test_simple() { let elem: Element = " diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 95194edc2b9fe5afa64f539db6a77d11135dd5e9..366cfeeca4397a5e5d5d4cb4db12efb3b2b4141d 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -78,6 +78,24 @@ mod tests { use minidom::Element; use try_from::TryFrom; + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Description, 36); + assert_size!(Channels, 1); + assert_size!(PayloadType, 52); + assert_size!(Parameter, 24); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Description, 72); + assert_size!(Channels, 1); + assert_size!(PayloadType, 80); + assert_size!(Parameter, 48); + } + #[test] fn test_simple() { let elem: Element = " From 429fc387b5090e4e681f9075c12af9d28d4cd6dd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 13:32:52 +0100 Subject: [PATCH 0865/1020] jingle_drls_srtp: Add a new parser and serialiser. --- src/hashes.rs | 4 +-- src/jingle_dtls_srtp.rs | 78 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/util/helpers.rs | 25 +++++++++++++ 5 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 src/jingle_dtls_srtp.rs diff --git a/src/hashes.rs b/src/hashes.rs index c097d034690678a682b1dd9a73daa72a85e1657c..e1be62b1ceb967a7517f563a4814b6aaca109e44 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -133,8 +133,8 @@ impl FromStr for Sha1HexAttribute { fn from_str(hex: &str) -> Result { let mut bytes = vec![]; for i in 0..hex.len() / 2 { - let byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16); - bytes.push(byte?); + let byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)?; + bytes.push(byte); } Ok(Sha1HexAttribute(Hash::new(Algo::Sha_1, bytes))) } diff --git a/src/jingle_dtls_srtp.rs b/src/jingle_dtls_srtp.rs new file mode 100644 index 0000000000000000000000000000000000000000..29b6417392e73c6165ef193c307e922b6817c13b --- /dev/null +++ b/src/jingle_dtls_srtp.rs @@ -0,0 +1,78 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::util::helpers::ColonSeparatedHex; +use crate::hashes::Algo; + +generate_attribute!( + /// Indicates which of the end points should initiate the TCP connection establishment. + Setup, "setup", { + /// The endpoint will initiate an outgoing connection. + Active => "active", + + /// The endpoint will accept an incoming connection. + Passive => "passive", + + /// The endpoint is willing to accept an incoming connection or to initiate an outgoing + /// connection. + Actpass => "actpass", + + /* + /// The endpoint does not want the connection to be established for the time being. + /// + /// Note that this value isn’t used, as per the XEP. + Holdconn => "holdconn", + */ + } +); + +// TODO: use a hashes::Hash instead of two different fields here. +generate_element!( + /// Fingerprint of the key used for a DTLS handshake. + Fingerprint, "fingerprint", JINGLE_DTLS, + attributes: [ + /// The hash algorithm used for this fingerprint. + hash: Required = "hash", + + /// Indicates which of the end points should initiate the TCP connection establishment. + setup: Required = "setup" + ], + text: ( + /// Hash value of this fingerprint. + value: ColonSeparatedHex> + ) +); + +mod tests { + use super::*; + use minidom::Element; + use try_from::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Setup, 1); + assert_size!(Fingerprint, 32); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Setup, 1); + assert_size!(Fingerprint, 64); + } + + #[test] + fn test_ex1() { + let elem: Element = "02:1A:CC:54:27:AB:EB:9C:53:3F:3E:4B:65:2E:7D:46:3F:54:42:CD:54:F1:7A:03:A2:7D:F9:B0:7F:46:19:B2" + .parse() + .unwrap(); + let fingerprint = Fingerprint::try_from(elem).unwrap(); + assert_eq!(fingerprint.setup, Setup::Actpass); + assert_eq!(fingerprint.hash, Algo::Sha_256); + assert_eq!(fingerprint.value, [2, 26, 204, 84, 39, 171, 235, 156, 83, 63, 62, 75, 101, 46, 125, 70, 63, 84, 66, 205, 84, 241, 122, 3, 162, 125, 249, 176, 127, 70, 25, 178]); + } +} diff --git a/src/lib.rs b/src/lib.rs index ca7840b165c34ebe920b7db035759f15aa06517b..2a6c3abb2cf8d82836babedc4c2d00cdd4868c3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -158,6 +158,9 @@ pub mod mam; /// XEP-0319: Last User Interaction in Presence pub mod idle; +/// XEP-0320: Use of DTLS-SRTP in Jingle Sessions +pub mod jingle_dtls_srtp; + /// XEP-0353: Jingle Message Initiation pub mod jingle_message; diff --git a/src/ns.rs b/src/ns.rs index 321c6fa8362ee14bc8a1035638350c90db2419f4..1897dbb48dba0b37a7a77d628db93d8c510e112a 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -156,6 +156,9 @@ pub const MAM: &str = "urn:xmpp:mam:2"; /// XEP-0319: Last User Interaction in Presence pub const IDLE: &str = "urn:xmpp:idle:1"; +/// XEP-0320: Use of DTLS-SRTP in Jingle Sessions +pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0"; + /// XEP-0353: Jingle Message Initiation pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; diff --git a/src/util/helpers.rs b/src/util/helpers.rs index b3832345c33fd41698c71cce88b7f7dd913aa0e0..b7bdc8443cbaf4cbf1f5d3f3f6b055bd7ef16550 100644 --- a/src/util/helpers.rs +++ b/src/util/helpers.rs @@ -65,3 +65,28 @@ impl WhitespaceAwareBase64 { Some(base64::encode(b)) } } + +/// Codec for colon-separated bytes of uppercase hexadecimal. +pub struct ColonSeparatedHex; + +impl ColonSeparatedHex { + pub fn decode(s: &str) -> Result, Error> { + let mut bytes = vec![]; + for i in 0..(1 + s.len()) / 3 { + let byte = u8::from_str_radix(&s[3 * i..3 * i + 2], 16)?; + if 3 * i + 2 < s.len() { + assert_eq!(&s[3 * i + 2..3 * i + 3], ":"); + } + bytes.push(byte); + } + Ok(bytes) + } + + pub fn encode(b: &[u8]) -> Option { + let mut bytes = vec![]; + for byte in b { + bytes.push(format!("{:02X}", byte)); + } + Some(bytes.join(":")) + } +} From 36d6169386e04b6ebdb75489fce3b4d216292148 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 28 Feb 2019 13:35:09 +0100 Subject: [PATCH 0866/1020] jingle_ice_udp: Add DTLS-SRTP support. --- src/jingle_ice_udp.rs | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 40a07a62cd2f5e9f99e1237e0862d56af6ed91e5..3b386705362b9ee6cb545c29540f74263c5cb19e 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use crate::jingle_dtls_srtp::Fingerprint; use std::net::IpAddr; generate_element!( @@ -17,8 +18,11 @@ generate_element!( ufrag: Option = "ufrag", ], children: [ - /// List of candidates for this ICE-UDP session. - candidates: Vec = ("candidate", JINGLE_ICE_UDP) => Candidate + /// List of candidates for this ICE-UDP session. + candidates: Vec = ("candidate", JINGLE_ICE_UDP) => Candidate, + + /// Fingerprint of the key used for the DTLS handshake. + fingerprint: Option = ("fingerprint", JINGLE_DTLS) => Fingerprint ] ); @@ -89,25 +93,27 @@ mod tests { use super::*; use minidom::Element; use try_from::TryFrom; + use crate::hashes::Algo; + use crate::jingle_dtls_srtp::Setup; #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Transport, 36); + assert_size!(Transport, 68); assert_size!(Type, 1); - assert_size!(Candidate, 72); + assert_size!(Candidate, 80); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Transport, 72); + assert_size!(Transport, 136); assert_size!(Type, 1); assert_size!(Candidate, 104); } #[test] - fn test_simple() { + fn test_gajim() { let elem: Element = " @@ -135,4 +141,25 @@ mod tests { assert_eq!(transport.pwd.unwrap(), "wakMJ8Ydd5rqnPaFerws5o"); assert_eq!(transport.ufrag.unwrap(), "aeXX"); } + + #[test] + fn test_jitsi_meet() { + let elem: Element = " + + 97:F2:B5:BE:DB:A6:00:B1:3E:40:B2:41:3C:0D:FC:E0:BD:B2:A0:E8 + + + +" + .parse() + .unwrap(); + let transport = Transport::try_from(elem).unwrap(); + assert_eq!(transport.pwd.unwrap(), "7lk9uul39gckit6t02oavv2r9j"); + assert_eq!(transport.ufrag.unwrap(), "2acq51d4p07v2m"); + + let fingerprint = transport.fingerprint.unwrap(); + assert_eq!(fingerprint.hash, Algo::Sha_1); + assert_eq!(fingerprint.setup, Setup::Actpass); + assert_eq!(fingerprint.value, [151, 242, 181, 190, 219, 166, 0, 177, 62, 64, 178, 65, 60, 13, 252, 224, 189, 178, 160, 232]); + } } From af8cf177cef941959c52cf23029b476143906e11 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 3 Mar 2019 20:08:27 +0100 Subject: [PATCH 0867/1020] ecaps2: Add a constructor. --- src/ecaps2.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 352881f105e2149f8b367d33ff1d6e0cfaf662c6..36e55c2c34f6069bc5444664b9c836191369ee8e 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -28,6 +28,15 @@ generate_element!( impl PresencePayload for ECaps2 {} +impl ECaps2 { + /// Create an ECaps2 element from a list of hashes. + pub fn new(hashes: Vec) -> ECaps2 { + ECaps2 { + hashes, + } + } +} + fn compute_item(field: &str) -> Vec { let mut bytes = field.as_bytes().to_vec(); bytes.push(0x1f); From 41bcf2dce794019c3e50b447eb7f172ebc150f9b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 3 Mar 2019 20:13:51 +0100 Subject: [PATCH 0868/1020] examples: Add a caps/ecaps2 generator. --- examples/generate-caps.rs | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 examples/generate-caps.rs diff --git a/examples/generate-caps.rs b/examples/generate-caps.rs new file mode 100644 index 0000000000000000000000000000000000000000..f47715c1e6d7dd4f46871fd856e676b2e561c263 --- /dev/null +++ b/examples/generate-caps.rs @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::io::{self, Read}; +use std::env; +use xmpp_parsers::{ + TryFrom, + Element, + disco::DiscoInfoResult, + caps::{Caps, compute_disco as compute_disco_caps, hash_caps}, + ecaps2::{ECaps2, compute_disco as compute_disco_ecaps2, hash_ecaps2}, + hashes::Algo, +}; + +fn get_caps(disco: &DiscoInfoResult, node: String) -> Result { + let caps_data = compute_disco_caps(&disco); + let caps_hash = hash_caps(&caps_data, Algo::Sha_1)?; + Ok(Caps::new(node, caps_hash)) +} + +fn get_ecaps2(disco: &DiscoInfoResult) -> Result { + let ecaps2_data = compute_disco_ecaps2(&disco).unwrap(); + let ecaps2_sha256 = hash_ecaps2(&ecaps2_data, Algo::Sha_256)?; + let ecaps2_sha3_256 = hash_ecaps2(&ecaps2_data, Algo::Sha3_256)?; + Ok(ECaps2::new(vec![ecaps2_sha256, ecaps2_sha3_256])) +} + +fn main() -> Result<(), ()> { + let args: Vec<_> = env::args().collect(); + if args.len() != 2 { + println!("Usage: {} ", args[0]); + return Err(()); + } + let node = args[1].clone(); + + eprintln!("Reading a disco#info payload from stdin..."); + + // Read from stdin. + let stdin = io::stdin(); + let mut data = String::new(); + let mut handle = stdin.lock(); + handle.read_to_string(&mut data).unwrap(); + + // Parse the payload into a DiscoInfoResult. + let elem: Element = data.parse().unwrap(); + let disco = DiscoInfoResult::try_from(elem).unwrap(); + + // Compute both kinds of caps. + let caps = get_caps(&disco, node).unwrap(); + let ecaps2 = get_ecaps2(&disco).unwrap(); + + // Print them. + let caps_elem = Element::from(caps); + let ecaps2_elem = Element::from(ecaps2); + println!("{}", String::from(&caps_elem)); + println!("{}", String::from(&ecaps2_elem)); + + Ok(()) +} From 11a45a1330b0763429e41edf707cf08e4eeee504 Mon Sep 17 00:00:00 2001 From: Voker57 Date: Mon, 11 Mar 2019 19:00:25 +0000 Subject: [PATCH 0869/1020] Fix a typo in documentation --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 50fdcf7cecb62971b4498ab1f1b6fc6dee82007c..bfed02a5029575f7f05770728c8130f82452d3bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![deny(unsafe_code, unused, missing_docs)] -//! XMPP implemeentation with asynchronous I/O using Tokio. +//! XMPP implementation with asynchronous I/O using Tokio. #[macro_use] extern crate derive_error; From 3e41442702674b184024bab542e4966739d65680 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 17:48:37 +0100 Subject: [PATCH 0870/1020] jingle_drls_srtp: Add missing #[cfg(test)] on mod test. --- src/jingle_dtls_srtp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jingle_dtls_srtp.rs b/src/jingle_dtls_srtp.rs index 29b6417392e73c6165ef193c307e922b6817c13b..84f646506b3ca265650c3f03e8816ae8dfce000d 100644 --- a/src/jingle_dtls_srtp.rs +++ b/src/jingle_dtls_srtp.rs @@ -46,6 +46,7 @@ generate_element!( ) ); +#[cfg(test)] mod tests { use super::*; use minidom::Element; From 50579056502ad56cba1ae18b5ef2d445f7d16ce8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 18:52:20 +0100 Subject: [PATCH 0871/1020] ChangeLog: Add imminent 0.13.0 release. --- ChangeLog | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1c022a14b2a797e0628c581e90c616db9a8f496e..3a608458e89f42f90fadaa33b0e79fbe2c31ebbd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +Version 0.13.0: +2019-03-20 Emmanuel Gil Peyrot + * New parsers/serialisers: + - User Avatar (XEP-0084). + - Jingle RTP Sessions (XEP-0167). + - Jingle ICE-UDP Transport Method (XEP-0176). + - Use of DTLS-SRTP in Jingle Sessions (XEP-0320). + * Breaking changes: + - Make 'id' required on iq, as per RFC6120 §8.1.3. + - Refactor PubSub to have more type-safety. + - Treat FORM_TYPE as a special case in data forms, to avoid + duplicating it into a field. + - Add forgotten i18n to Jingle text element. + * Improvements: + - Add various helpers for hash representations. + - Add helpers constructors for multiple extensions (disco, caps, + pubsub, stanza_error). + - Use Into in more constructors. + - Internal change on attribute declaration in macros. + - Reexport missing try_from::TryInto. + Version 0.12.2: 2019-01-16 Emmanuel Gil Peyrot * Improvements: From 107bd90a87808e62c3bc5950430861902c5d510d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 26 Jan 2019 20:06:51 +0000 Subject: [PATCH 0872/1020] Change pep's email address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8aab21dc5c72636969f7a1ace092cb739cf4587c..f99e9a5c1ebfe255e3f7fd0e0cfd95d05ef07eb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "xmpp-parsers" version = "0.12.2" authors = [ "Emmanuel Gil Peyrot ", - "Maxime “pep” Buquet ", + "Maxime “pep” Buquet ", ] description = "Collection of parsers and serialisers for XMPP extensions" homepage = "https://gitlab.com/xmpp-rs/xmpp-parsers" From ba5a014de9c1c9247befef60a4e394d260176bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 26 Jan 2019 20:05:48 +0000 Subject: [PATCH 0873/1020] XEP-0157: Contact Addresses for XMPP Services. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/lib.rs | 3 + src/ns.rs | 3 + src/server_info.rs | 234 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 src/server_info.rs diff --git a/src/lib.rs b/src/lib.rs index 2a6c3abb2cf8d82836babedc4c2d00cdd4868c3b..eb3cc3b4aa52e818df45738a9f4a9fc058cca312 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,6 +101,9 @@ pub mod component; /// XEP-0115: Entity Capabilities pub mod caps; +/// XEP-0157: Contact Addresses for XMPP Services +pub mod server_info; + /// XEP-0166: Jingle pub mod jingle; diff --git a/src/ns.rs b/src/ns.rs index 1897dbb48dba0b37a7a77d628db93d8c510e112a..4f3d2d299d3dcd1f756427b03603fd8b0225aff6 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -79,6 +79,9 @@ pub const COMPONENT: &str = "jabber:component:accept"; /// XEP-0115: Entity Capabilities pub const CAPS: &str = "http://jabber.org/protocol/caps"; +/// XEP-0157: Contact Addresses for XMPP Services +pub const SERVER_INFO: &str = "http://jabber.org/network/serverinfo"; + /// XEP-0166: Jingle pub const JINGLE: &str = "urn:xmpp:jingle:1"; diff --git a/src/server_info.rs b/src/server_info.rs new file mode 100644 index 0000000000000000000000000000000000000000..cf1b75c4c6e8f12d4497dbf1c69d91c80d7abcf5 --- /dev/null +++ b/src/server_info.rs @@ -0,0 +1,234 @@ +// Copyright (C) 2019 Maxime “pep” Buquet +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::data_forms::{DataForm, DataFormType, Field, FieldType}; +use crate::ns; +use crate::util::error::Error; +use try_from::TryFrom; + +/// Structure representing a `http://jabber.org/network/serverinfo` form type. +#[derive(Debug, Clone)] +pub struct ServerInfo { + /// Abuse addresses + pub abuse: Vec, + + /// Admin addresses + pub admin: Vec, + + /// Feedback addresses + pub feedback: Vec, + + /// Sales addresses + pub sales: Vec, + + /// Security addresses + pub security: Vec, + + /// Support addresses + pub support: Vec, +} + +impl TryFrom for ServerInfo { + type Err = Error; + + fn try_from(form: DataForm) -> Result { + if form.type_ != DataFormType::Result_ { + return Err(Error::ParseError("Wrong type of form.")); + } + if form.form_type != Some(String::from(ns::SERVER_INFO)) { + return Err(Error::ParseError("Wrong FORM_TYPE for form.")); + } + let mut server_info = ServerInfo { + abuse: vec![], + admin: vec![], + feedback: vec![], + sales: vec![], + security: vec![], + support: vec![], + }; + for field in form.fields { + if field.type_ != FieldType::ListMulti { + return Err(Error::ParseError("Field is not of the required type.")); + } + if field.var == "abuse-addresses" { + server_info.abuse = field.values; + } else if field.var == "admin-addresses" { + server_info.admin = field.values; + } else if field.var == "feedback-addresses" { + server_info.feedback = field.values; + } else if field.var == "sales-addresses" { + server_info.sales = field.values; + } else if field.var == "security-addresses" { + server_info.security = field.values; + } else if field.var == "support-addresses" { + server_info.support = field.values; + } else { + return Err(Error::ParseError("Unknown form field var.")); + } + } + + Ok(server_info) + } +} + +impl From for DataForm { + fn from(server_info: ServerInfo) -> DataForm { + DataForm { + type_: DataFormType::Result_, + form_type: Some(String::from(ns::SERVER_INFO)), + title: None, + instructions: None, + fields: vec![ + Field { + var: String::from("FORM_TYPE"), + type_: FieldType::Hidden, + label: None, + required: false, + options: vec![], + values: vec![String::from(ns::SERVER_INFO)], + media: vec![], + }, + generate_address_field("abuse-addresses", server_info.abuse), + generate_address_field("admin-addresses", server_info.admin), + generate_address_field("feedback-addresses", server_info.feedback), + generate_address_field("sales-addresses", server_info.sales), + generate_address_field("security-addresses", server_info.security), + generate_address_field("support-addresses", server_info.support), + ], + } + } +} + +/// Generate `Field` for addresses +pub fn generate_address_field>(var: S, values: Vec) -> Field { + Field { + var: var.into(), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values, + media: vec![], + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::data_forms::{DataForm, DataFormType, Field, FieldType}; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(ServerInfo, 72); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(ServerInfo, 144); + } + + #[test] + fn test_simple() { + let form = DataForm { + type_: DataFormType::Result_, + form_type: Some(String::from(ns::SERVER_INFO)), + title: None, + instructions: None, + fields: vec![ + Field { + var: String::from("FORM_TYPE"), + type_: FieldType::Hidden, + label: None, + required: false, + options: vec![], + values: vec![String::from(ns::SERVER_INFO)], + media: vec![], + }, + Field { + var: String::from("abuse-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![], + media: vec![], + }, + Field { + var: String::from("admin-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![ + String::from("xmpp:admin@foo.bar"), + String::from("https://foo.bar/chat/"), + String::from("mailto:admin@foo.bar"), + ], + media: vec![], + }, + Field { + var: String::from("feedback-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![], + media: vec![], + }, + Field { + var: String::from("sales-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![], + media: vec![], + }, + Field { + var: String::from("security-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![ + String::from("xmpp:security@foo.bar"), + String::from("mailto:security@foo.bar"), + ], + media: vec![], + }, + Field { + var: String::from("support-addresses"), + type_: FieldType::ListMulti, + label: None, + required: false, + options: vec![], + values: vec![String::from("mailto:support@foo.bar")], + media: vec![], + }, + ], + }; + + let server_info = ServerInfo { + abuse: vec![], + admin: vec![ + String::from("xmpp:admin@foo.bar"), + String::from("https://foo.bar/chat/"), + String::from("mailto:admin@foo.bar"), + ], + feedback: vec![], + sales: vec![], + security: vec![ + String::from("xmpp:security@foo.bar"), + String::from("mailto:security@foo.bar"), + ], + support: vec![String::from("mailto:support@foo.bar")], + }; + + // assert_eq!(DataForm::from(server_info), form); + // assert_eq!(ServerInfo::try_from(form), Ok(form)); + } +} From cbc787a9d40868d650525849fb13120dff358716 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 19:09:14 +0100 Subject: [PATCH 0874/1020] server_info: Remove remaining FORM_TYPE handling. Also reenables the test. --- src/server_info.rs | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/server_info.rs b/src/server_info.rs index cf1b75c4c6e8f12d4497dbf1c69d91c80d7abcf5..6f18c7c92ab6203c81b075b955186544442f9e0f 100644 --- a/src/server_info.rs +++ b/src/server_info.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use try_from::TryFrom; /// Structure representing a `http://jabber.org/network/serverinfo` form type. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct ServerInfo { /// Abuse addresses pub abuse: Vec, @@ -81,15 +81,6 @@ impl From for DataForm { title: None, instructions: None, fields: vec![ - Field { - var: String::from("FORM_TYPE"), - type_: FieldType::Hidden, - label: None, - required: false, - options: vec![], - values: vec![String::from(ns::SERVER_INFO)], - media: vec![], - }, generate_address_field("abuse-addresses", server_info.abuse), generate_address_field("admin-addresses", server_info.admin), generate_address_field("feedback-addresses", server_info.feedback), @@ -139,15 +130,6 @@ mod tests { title: None, instructions: None, fields: vec![ - Field { - var: String::from("FORM_TYPE"), - type_: FieldType::Hidden, - label: None, - required: false, - options: vec![], - values: vec![String::from(ns::SERVER_INFO)], - media: vec![], - }, Field { var: String::from("abuse-addresses"), type_: FieldType::ListMulti, @@ -229,6 +211,6 @@ mod tests { }; // assert_eq!(DataForm::from(server_info), form); - // assert_eq!(ServerInfo::try_from(form), Ok(form)); + assert_eq!(ServerInfo::try_from(form).unwrap(), server_info); } } From 00e19012e5c063729cb0555c1592ff625a947119 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 19:10:01 +0100 Subject: [PATCH 0875/1020] server_info: Implement Default and use it. --- src/server_info.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/server_info.rs b/src/server_info.rs index 6f18c7c92ab6203c81b075b955186544442f9e0f..53cef3838bcd2cc55ad1e75de8827f8863183049 100644 --- a/src/server_info.rs +++ b/src/server_info.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use try_from::TryFrom; /// Structure representing a `http://jabber.org/network/serverinfo` form type. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct ServerInfo { /// Abuse addresses pub abuse: Vec, @@ -40,14 +40,7 @@ impl TryFrom for ServerInfo { if form.form_type != Some(String::from(ns::SERVER_INFO)) { return Err(Error::ParseError("Wrong FORM_TYPE for form.")); } - let mut server_info = ServerInfo { - abuse: vec![], - admin: vec![], - feedback: vec![], - sales: vec![], - security: vec![], - support: vec![], - }; + let mut server_info = ServerInfo::default(); for field in form.fields { if field.type_ != FieldType::ListMulti { return Err(Error::ParseError("Field is not of the required type.")); From d329a473d408fbe8a16d4960f473b15751d10b58 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 19:17:21 +0100 Subject: [PATCH 0876/1020] ChangeLog: Mention XEP-0157 support, thanks pep.! --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 3a608458e89f42f90fadaa33b0e79fbe2c31ebbd..12a14a0150956cedea332ec69094af3bd6174548 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version 0.13.0: 2019-03-20 Emmanuel Gil Peyrot * New parsers/serialisers: - User Avatar (XEP-0084). + - Contact Addresses for XMPP Services (XEP-0157). - Jingle RTP Sessions (XEP-0167). - Jingle ICE-UDP Transport Method (XEP-0176). - Use of DTLS-SRTP in Jingle Sessions (XEP-0320). From 98c4c5b9010d018b56cb69613f142b7be562527b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 20 Mar 2019 19:19:27 +0100 Subject: [PATCH 0877/1020] Cargo.toml: Release version 0.13.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f99e9a5c1ebfe255e3f7fd0e0cfd95d05ef07eb9..6ddde6f7b94773886e02ca0577893fdf43865699 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.12.2" +version = "0.13.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", From 63afd5d939beebcc1ca7ad03df1588da2e9b8ec7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Feb 2019 21:35:38 +0100 Subject: [PATCH 0878/1020] Bump xmpp-parsers to 0.13. Fixes #4. --- Cargo.toml | 2 +- src/client/bind.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 78e175fdd4816fd3e6c1ec80d2c844a457ced04c..3bcdbd2ce289807fda144beeb7c1a99cb5151241 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ tokio-tls = "0.2" sasl = "0.4" trust-dns-resolver = "0.10" trust-dns-proto = "0.6" -xmpp-parsers = "0.12.2" +xmpp-parsers = "0.13" idna = "0.1" quick-xml = "0.13" derive-error = "0.0.4" diff --git a/src/client/bind.rs b/src/client/bind.rs index 083650b9b94196fac4a797534f0083dc51222129..322458e524bdfbb793398fc103051eee3d0b5f9f 100644 --- a/src/client/bind.rs +++ b/src/client/bind.rs @@ -33,7 +33,7 @@ impl ClientBind { } Some(_) => { let resource = stream.jid.resource.clone(); - let iq = Iq::from_set(Bind::new(resource)).with_id(BIND_REQ_ID.to_string()); + let iq = Iq::from_set(BIND_REQ_ID, Bind::new(resource)); let send = stream.send_stanza(iq); ClientBind::WaitSend(send) } @@ -64,7 +64,7 @@ impl Future for ClientBind { ClientBind::WaitRecv(mut stream) => match stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => match Iq::try_from(stanza) { Ok(iq) => { - if iq.id == Some(BIND_REQ_ID.to_string()) { + if iq.id == BIND_REQ_ID { match iq.payload { IqType::Result(payload) => { payload From d04eb02d4c78e1e481f952df211f39818e3261e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 28 Jan 2019 21:39:54 +0000 Subject: [PATCH 0879/1020] Add "Contact Address for XMPP Services" (0157) example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.lock | 8 ++- examples/contact_addr.rs | 130 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 examples/contact_addr.rs diff --git a/Cargo.lock b/Cargo.lock index 302b72327dbadbda9333b807ffbcd53ab03e9489..306ce86be222bbce12d47add98d776a0e0422ce8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "MacTypes-sys" version = "2.1.0" @@ -1374,7 +1376,7 @@ dependencies = [ "trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "trust-dns-resolver 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", "xml5ever 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "xmpp-parsers 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "xmpp-parsers 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1562,7 +1564,7 @@ dependencies = [ [[package]] name = "xmpp-parsers" -version = "0.12.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1755,4 +1757,4 @@ dependencies = [ "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xml5ever 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cd7ebf0203c620906230ce22caa5df0b603c32b6fef72a275a48f6a2ae64b9" -"checksum xmpp-parsers 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "58b4400e1ae0d246044db5fa7f2e693fdfe9cc6e8eaa72ef2a68c5dc1d3c96de" +"checksum xmpp-parsers 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c467bb13e01718be793cb7a1c3d38d0e9ba45898db306aa43e70657a8aa3c2f2" diff --git a/examples/contact_addr.rs b/examples/contact_addr.rs new file mode 100644 index 0000000000000000000000000000000000000000..0d74f8ee16f329e8e1c091a1180d31be0d6ff2b3 --- /dev/null +++ b/examples/contact_addr.rs @@ -0,0 +1,130 @@ +use futures::{future, Sink, Stream}; +use std::env::args; +use std::process::exit; +use tokio::runtime::current_thread::Runtime; +use tokio_xmpp::{Client, xmpp_codec::Packet}; +use xmpp_parsers::{ + Element, + Jid, + TryFrom, + ns, + iq::{ + Iq, + IqType, + }, + disco::{ + DiscoInfoResult, + DiscoInfoQuery, + }, + server_info::ServerInfo, +}; + +fn main() { + let args: Vec = args().collect(); + if args.len() != 4 { + println!("Usage: {} ", args[0]); + exit(1); + } + let jid = &args[1]; + let password = &args[2]; + let target = &args[3]; + + // tokio_core context + let mut rt = Runtime::new().unwrap(); + // Client instance + let client = Client::new(jid, password).unwrap(); + + // Make the two interfaces for sending and receiving independent + // of each other so we can move one into a closure. + let (mut sink, stream) = client.split(); + // Wrap sink in Option so that we can take() it for the send(self) + // to consume and return it back when ready. + let mut send = move |packet| { + sink.start_send(packet).expect("start_send"); + }; + // Main loop, processes events + let mut wait_for_stream_end = false; + let done = stream.for_each(|event| { + if wait_for_stream_end { + /* Do Nothing. */ + } else if event.is_online() { + println!("Online!"); + + let target_jid: Jid = target.clone().parse().unwrap(); + let iq = make_disco_iq(target_jid); + println!("Sending disco#info request to {}", target.clone()); + println!(">> {}", String::from(&iq)); + send(Packet::Stanza(iq)); + } else if let Some(stanza) = event.into_stanza() { + if stanza.is("iq", "jabber:client") { + let iq = Iq::try_from(stanza).unwrap(); + if let IqType::Result(Some(payload)) = iq.payload { + if payload.is("query", ns::DISCO_INFO) { + if let Ok(disco_info) = DiscoInfoResult::try_from(payload) { + for ext in disco_info.extensions { + if let Ok(server_info) = ServerInfo::try_from(ext) { + print_server_info(server_info); + wait_for_stream_end = true; + send(Packet::StreamEnd); + } + } + } + } + } + } + } + + Box::new(future::ok(())) + }); + + // Start polling `done` + match rt.block_on(done) { + Ok(_) => (), + Err(e) => { + println!("Fatal: {}", e); + () + } + } +} + +fn make_disco_iq(target: Jid) -> Element { + Iq::from_get("disco", DiscoInfoQuery { node: None }) + .with_id(String::from("contact")) + .with_to(target) + .into() +} + +fn convert_field(field: Vec) -> String { + field.iter() + .fold((field.len(), String::new()), |(l, mut acc), s| { + acc.push('<'); + acc.push_str(&s); + acc.push('>'); + if l > 1 { + acc.push(','); + acc.push(' '); + } + (0, acc) + }).1 +} + +fn print_server_info(server_info: ServerInfo) { + if server_info.abuse.len() != 0 { + println!("abuse: {}", convert_field(server_info.abuse)); + } + if server_info.admin.len() != 0 { + println!("admin: {}", convert_field(server_info.admin)); + } + if server_info.feedback.len() != 0 { + println!("feedback: {}", convert_field(server_info.feedback)); + } + if server_info.sales.len() != 0 { + println!("sales: {}", convert_field(server_info.sales)); + } + if server_info.security.len() != 0 { + println!("security: {}", convert_field(server_info.security)); + } + if server_info.support.len() != 0 { + println!("support: {}", convert_field(server_info.support)); + } +} From 2e10ff8091e579c6efe3f8316df620609d87f1bb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Feb 2019 19:28:41 +0100 Subject: [PATCH 0880/1020] download_avatars: new example client --- examples/download_avatars.rs | 230 +++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 examples/download_avatars.rs diff --git a/examples/download_avatars.rs b/examples/download_avatars.rs new file mode 100644 index 0000000000000000000000000000000000000000..06ff8c343e330cfa7c2220ff150d2731683ba048 --- /dev/null +++ b/examples/download_avatars.rs @@ -0,0 +1,230 @@ +use futures::{future, Future, Sink, Stream}; +use std::env::args; +use std::fs::{create_dir_all, File}; +use std::io::{self, Write}; +use std::process::exit; +use std::str::FromStr; +use tokio::runtime::current_thread::Runtime; +use tokio_xmpp::{Client, Packet}; +use xmpp_parsers::{ + avatar::{Data as AvatarData, Metadata as AvatarMetadata}, + caps::{compute_disco, hash_caps, Caps}, + disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}, + hashes::Algo, + iq::{Iq, IqType}, + message::Message, + ns, + presence::{Presence, Type as PresenceType}, + pubsub::{ + event::PubSubEvent, + pubsub::{Items, PubSub}, + NodeName, + }, + stanza_error::{StanzaError, ErrorType, DefinedCondition}, + Jid, TryFrom, +}; + +fn main() { + let args: Vec = args().collect(); + if args.len() != 3 { + println!("Usage: {} ", args[0]); + exit(1); + } + let jid = &args[1]; + let password = &args[2]; + + // tokio_core context + let mut rt = Runtime::new().unwrap(); + // Client instance + let client = Client::new(jid, password).unwrap(); + + // Make the two interfaces for sending and receiving independent + // of each other so we can move one into a closure. + let (sink, stream) = client.split(); + + // Create outgoing pipe + let (mut tx, rx) = futures::unsync::mpsc::unbounded(); + rt.spawn( + rx.forward( + sink.sink_map_err(|_| panic!("Pipe")) + ) + .map(|(rx, mut sink)| { + drop(rx); + let _ = sink.close(); + }) + .map_err(|e| { + panic!("Send error: {:?}", e); + }) + ); + + let disco_info = make_disco(); + + // Main loop, processes events + let mut wait_for_stream_end = false; + let done = stream.for_each(move |event| { + // Helper function to send an iq error. + let mut send_error = |to, id, type_, condition, text: &str| { + let error = StanzaError::new(type_, condition, "en", text); + let iq = Iq::from_error(id, error) + .with_to(to); + tx.start_send(Packet::Stanza(iq.into())).unwrap(); + }; + + if wait_for_stream_end { + /* Do nothing */ + } else if event.is_online() { + println!("Online!"); + + let caps = get_disco_caps(&disco_info, "https://gitlab.com/xmpp-rs/tokio-xmpp"); + let presence = make_presence(caps); + tx.start_send(Packet::Stanza(presence.into())).unwrap(); + } else if let Some(stanza) = event.into_stanza() { + if stanza.is("iq", "jabber:client") { + let iq = Iq::try_from(stanza).unwrap(); + if let IqType::Get(payload) = iq.payload { + if payload.is("query", ns::DISCO_INFO) { + let query = DiscoInfoQuery::try_from(payload); + match query { + Ok(query) => { + let mut disco = disco_info.clone(); + disco.node = query.node; + let iq = Iq::from_result(iq.id, Some(disco)) + .with_to(iq.from.unwrap()); + tx.start_send(Packet::Stanza(iq.into())).unwrap(); + }, + Err(err) => { + send_error(iq.from.unwrap(), iq.id, ErrorType::Modify, DefinedCondition::BadRequest, &format!("{}", err)); + }, + } + } else { + // We MUST answer unhandled get iqs with a service-unavailable error. + send_error(iq.from.unwrap(), iq.id, ErrorType::Cancel, DefinedCondition::ServiceUnavailable, "No handler defined for this kind of iq."); + } + } else if let IqType::Result(Some(payload)) = iq.payload { + if payload.is("pubsub", ns::PUBSUB) { + let pubsub = PubSub::try_from(payload).unwrap(); + let from = + iq.from.clone().unwrap_or(Jid::from_str(jid).unwrap()); + handle_iq_result(pubsub, &from); + } + } else if let IqType::Set(_) = iq.payload { + // We MUST answer unhandled set iqs with a service-unavailable error. + send_error(iq.from.unwrap(), iq.id, ErrorType::Cancel, DefinedCondition::ServiceUnavailable, "No handler defined for this kind of iq."); + } + } else if stanza.is("message", "jabber:client") { + let message = Message::try_from(stanza).unwrap(); + let from = message.from.clone().unwrap(); + if let Some(body) = message.get_best_body(vec!["en"]) { + if body.1 .0 == "die" { + println!("Secret die command triggered by {}", from); + wait_for_stream_end = true; + tx.start_send(Packet::StreamEnd).unwrap(); + } + } + for child in message.payloads { + if child.is("event", ns::PUBSUB_EVENT) { + let event = PubSubEvent::try_from(child).unwrap(); + if let PubSubEvent::PublishedItems { node, items } = event { + if node.0 == ns::AVATAR_METADATA { + for item in items { + let payload = item.payload.clone().unwrap(); + if payload.is("metadata", ns::AVATAR_METADATA) { + // TODO: do something with these metadata. + let _metadata = AvatarMetadata::try_from(payload).unwrap(); + println!( + "{} has published an avatar, downloading...", + from + ); + let iq = download_avatar(&from); + tx.start_send(Packet::Stanza(iq.into())).unwrap(); + } + } + } + } + } + } + } else if stanza.is("presence", "jabber:client") { + // Nothing to do here. + } else { + panic!("Unknown stanza: {}", String::from(&stanza)); + } + } + + future::ok(()) + }); + + // Start polling `done` + match rt.block_on(done) { + Ok(_) => (), + Err(e) => { + println!("Fatal: {}", e); + () + } + } +} + +fn make_disco() -> DiscoInfoResult { + let identities = vec![Identity::new("client", "bot", "en", "tokio-xmpp")]; + let features = vec![ + Feature::new(ns::DISCO_INFO), + Feature::new(format!("{}+notify", ns::AVATAR_METADATA)), + ]; + DiscoInfoResult { + node: None, + identities, + features, + extensions: vec![], + } +} + +fn get_disco_caps(disco: &DiscoInfoResult, node: &str) -> Caps { + let caps_data = compute_disco(disco); + let hash = hash_caps(&caps_data, Algo::Sha_1).unwrap(); + Caps::new(node, hash) +} + +// Construct a +fn make_presence(caps: Caps) -> Presence { + let mut presence = Presence::new(PresenceType::None) + .with_priority(-1); + presence.set_status("en", "Downloading avatars."); + presence.add_payload(caps); + presence +} + +fn download_avatar(from: &Jid) -> Iq { + Iq::from_get("coucou", PubSub::Items(Items { + max_items: None, + node: NodeName(String::from(ns::AVATAR_DATA)), + subid: None, + items: Vec::new(), + })) + .with_to(from.clone()) +} + +fn handle_iq_result(pubsub: PubSub, from: &Jid) { + if let PubSub::Items(items) = pubsub { + if items.node.0 == ns::AVATAR_DATA { + for item in items.items { + if let Some(id) = item.id.clone() { + if let Some(payload) = &item.payload { + let data = AvatarData::try_from(payload.clone()).unwrap(); + save_avatar(from, id.0, &data.data).unwrap(); + } + } + } + } + } +} + +fn save_avatar(from: &Jid, id: String, data: &[u8]) -> io::Result<()> { + let directory = format!("data/{}", from); + let filename = format!("data/{}/{}", from, id); + println!( + "Saving avatar from {} to {}.", + from, filename + ); + create_dir_all(directory)?; + let mut file = File::create(filename)?; + file.write_all(data) +} From 12265e9237787d3cbf13cfb0939f9bd62c25fad1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 21 Mar 2019 01:39:21 +0100 Subject: [PATCH 0881/1020] presence: Fix serialisation of priority. --- src/presence.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/presence.rs b/src/presence.rs index fba1773c917fca83a3f6c3fc2738f738873b26cc..c6a1a102974f2e2701e42d89318f9f821c3ca2fa 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -347,7 +347,11 @@ impl From for Element { .append(if presence.priority == 0 { None } else { - Some(format!("{}", presence.priority)) + Some( + Element::builder("priority") + .append(format!("{}", presence.priority)) + .build() + ) }) .append(presence.payloads) .build() @@ -622,4 +626,15 @@ mod tests { assert!(elem.is("presence", ns::DEFAULT_NS)); assert!(elem.children().next().unwrap().is("status", ns::DEFAULT_NS)); } + + #[test] + fn test_serialise_priority() { + let presence = Presence::new(Type::None) + .with_priority(42); + let elem: Element = presence.into(); + assert!(elem.is("presence", ns::DEFAULT_NS)); + let priority = elem.children().next().unwrap(); + assert!(priority.is("priority", ns::DEFAULT_NS)); + assert_eq!(priority.text(), "42"); + } } From 0d893edb69860eaa33caeb1dbd7b986810353e03 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 8 Apr 2019 21:32:57 +0200 Subject: [PATCH 0882/1020] avatar: Bump width/height to u16 This mirrors XEP revision 1.1.2, which made width and height xs:unsignedShort instead of xs:unsignedByte, as per common usage. --- src/avatar.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/avatar.rs b/src/avatar.rs index 6e2d3ce03da49f5891ef45fa28050f1ddd9b47d8..7693c05c16ea954eb90e8ce6ddad287ef8a81d8a 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -27,10 +27,10 @@ generate_element!( bytes: Required = "bytes", /// The width of the image in pixels. - width: Option = "width", + width: Option = "width", /// The height of the image in pixels. - height: Option = "height", + height: Option = "height", /// The SHA-1 hash of the image data for the specified content-type. id: Required = "id", @@ -66,7 +66,7 @@ mod tests { #[test] fn test_size() { assert_size!(Metadata, 12); - assert_size!(Info, 60); + assert_size!(Info, 64); assert_size!(Data, 12); } @@ -74,7 +74,7 @@ mod tests { #[test] fn test_size() { assert_size!(Metadata, 24); - assert_size!(Info, 112); + assert_size!(Info, 120); assert_size!(Data, 24); } From 5bf14b0b22f0020463de381179c71f4b57f75a6e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 12 Apr 2019 10:58:42 +0200 Subject: [PATCH 0883/1020] Drop dependency on try_from. This bumps the minimum supported stable Rust version to 1.34. The TryFrom and TryInto traits are still reexported to not break the API, but these reexports are deprecated and will be removed in a future release. --- Cargo.toml | 1 - examples/generate-caps.rs | 2 +- src/attention.rs | 2 +- src/avatar.rs | 2 +- src/bind.rs | 4 ++-- src/blocking.rs | 4 ++-- src/bookmarks.rs | 2 +- src/caps.rs | 4 ++-- src/chatstates.rs | 2 +- src/component.rs | 2 +- src/data_forms.rs | 6 +++--- src/delay.rs | 2 +- src/disco.rs | 4 ++-- src/ecaps2.rs | 2 +- src/eme.rs | 2 +- src/forwarding.rs | 2 +- src/hashes.rs | 2 +- src/ibb.rs | 2 +- src/ibr.rs | 4 ++-- src/idle.rs | 2 +- src/iq.rs | 4 ++-- src/jingle.rs | 6 +++--- src/jingle_dtls_srtp.rs | 2 +- src/jingle_ft.rs | 8 ++++---- src/jingle_ibb.rs | 2 +- src/jingle_ice_udp.rs | 2 +- src/jingle_message.rs | 4 ++-- src/jingle_rtp.rs | 2 +- src/jingle_s5b.rs | 4 ++-- src/lib.rs | 2 +- src/mam.rs | 4 ++-- src/media_element.rs | 2 +- src/message.rs | 4 ++-- src/message_correct.rs | 2 +- src/mood.rs | 2 +- src/muc/muc.rs | 2 +- src/muc/user.rs | 4 ++-- src/nick.rs | 2 +- src/ping.rs | 2 +- src/presence.rs | 4 ++-- src/pubsub/event.rs | 4 ++-- src/pubsub/pubsub.rs | 6 +++--- src/receipts.rs | 2 +- src/roster.rs | 2 +- src/rsm.rs | 6 +++--- src/sasl.rs | 6 +++--- src/server_info.rs | 4 ++-- src/sm.rs | 2 +- src/stanza_error.rs | 4 ++-- src/stanza_id.rs | 2 +- src/stream.rs | 2 +- src/util/macros.rs | 22 +++++++++++----------- src/version.rs | 2 +- src/websocket.rs | 2 +- 54 files changed, 91 insertions(+), 92 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6ddde6f7b94773886e02ca0577893fdf43865699..36ed320c4e0249f5a33d6830a166d41688366258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ sha2 = "0.8" sha3 = "0.8" blake2 = "0.8" chrono = "0.4.5" -try_from = "0.3.2" [features] # Build xmpp-parsers to make components instead of clients. diff --git a/examples/generate-caps.rs b/examples/generate-caps.rs index f47715c1e6d7dd4f46871fd856e676b2e561c263..80ba863f12be8e5f9f229638966fef8ac1d8f800 100644 --- a/examples/generate-caps.rs +++ b/examples/generate-caps.rs @@ -4,10 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::convert::TryFrom; use std::io::{self, Read}; use std::env; use xmpp_parsers::{ - TryFrom, Element, disco::DiscoInfoResult, caps::{Caps, compute_disco as compute_disco_caps, hash_caps}, diff --git a/src/attention.rs b/src/attention.rs index 1910c8c13bf75657512b3db9b07c6e549bed0f89..a2af3a9cd79a9be54cef033e5b7468da397c1ccc 100644 --- a/src/attention.rs +++ b/src/attention.rs @@ -21,7 +21,7 @@ mod tests { #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[test] fn test_size() { diff --git a/src/avatar.rs b/src/avatar.rs index 7693c05c16ea954eb90e8ce6ddad287ef8a81d8a..e5c09d2a14957737c50a266ced9db2d1f25be35f 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -60,7 +60,7 @@ mod tests { use crate::hashes::Algo; use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/bind.rs b/src/bind.rs index 8ded3e6ac10c8bb01e61ba9bb0d3c56b1f1d4d24..1d509638c8fb0545f80a996bcc81a4d8f652e69b 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -10,7 +10,7 @@ use crate::ns; use jid::Jid; use minidom::Element; use std::str::FromStr; -use try_from::TryFrom; +use std::convert::TryFrom; /// The request for resource binding, which is the process by which a client /// can obtain a full JID and start exchanging on the XMPP network. @@ -43,7 +43,7 @@ impl IqSetPayload for Bind {} impl IqResultPayload for Bind {} impl TryFrom for Bind { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "bind", BIND); diff --git a/src/blocking.rs b/src/blocking.rs index 74e84bc6f46d44921c832c643f4d702ce694a4ad..b44c4111936b1c2af458f09bc8383c2a01f6351a 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -9,7 +9,7 @@ use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; generate_empty_element!( /// The element requesting the blocklist, the result iq will contain a @@ -31,7 +31,7 @@ macro_rules! generate_blocking_element { } impl TryFrom for $elem { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result<$elem, Error> { check_self!(elem, $name, BLOCKING); diff --git a/src/bookmarks.rs b/src/bookmarks.rs index b1c59b0e40d0372921eeb082ac29caa42d4c1116..d8b5a9ccc1bbce7141144df5ca699ef1e2bd9754 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -72,7 +72,7 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/caps.rs b/src/caps.rs index 452fa6d56acfd8920e73916a0eab3a101f57db14..e5e998b1df7561085baa580473f576030cd1c0af 100644 --- a/src/caps.rs +++ b/src/caps.rs @@ -17,7 +17,7 @@ use minidom::Element; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; -use try_from::TryFrom; +use std::convert::TryFrom; /// Represents a capability hash for a given client. #[derive(Debug, Clone)] @@ -40,7 +40,7 @@ pub struct Caps { impl PresencePayload for Caps {} impl TryFrom for Caps { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "c", CAPS, "caps"); diff --git a/src/chatstates.rs b/src/chatstates.rs index 2bf2d1b1fe0cbd91cc4803101bb941d03add554a..d7ae1e37d8a6dbe94bdf41b76ae6351a4da26b1b 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -35,7 +35,7 @@ mod tests { use crate::util::error::Error; use crate::ns; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[test] fn test_size() { diff --git a/src/component.rs b/src/component.rs index 4ab78c57c28bf9ffba5ad50cd4bf9588c69fad8b..a0588177fecc45c3c133b319ea4d2fb8238bdcef 100644 --- a/src/component.rs +++ b/src/component.rs @@ -44,7 +44,7 @@ impl Handshake { mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/data_forms.rs b/src/data_forms.rs index 296614e975dfd118707a93e2c0d9e45e8ccf742f..323e91ed091b7411dc1a2eb82e40b28fc4357853 100644 --- a/src/data_forms.rs +++ b/src/data_forms.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::media_element::MediaElement; use crate::ns; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; generate_element!( /// Represents one of the possible values for a list- field. @@ -100,7 +100,7 @@ impl Field { } impl TryFrom for Field { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "field", DATA_FORMS); @@ -215,7 +215,7 @@ pub struct DataForm { } impl TryFrom for DataForm { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "x", DATA_FORMS); diff --git a/src/delay.rs b/src/delay.rs index 80f92edd00eb8b8023d8920df551de550493bed8..97a32d2598933c069a0215d152e42ae5ae4b93c4 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -35,7 +35,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::str::FromStr; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/disco.rs b/src/disco.rs index 87145208d96aeb819e913eb7ed8a7981665bf718..9d8781dcdb95d99ae78f7178b68e6620c5e66fba 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -10,7 +10,7 @@ use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; generate_element!( /// Structure representing a `` element. @@ -115,7 +115,7 @@ pub struct DiscoInfoResult { impl IqResultPayload for DiscoInfoResult {} impl TryFrom for DiscoInfoResult { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "query", DISCO_INFO, "disco#info result"); diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 36e55c2c34f6069bc5444664b9c836191369ee8e..d408b28de48f1ccc74c09ad074e0322b35b278b0 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -176,7 +176,7 @@ mod tests { use super::*; use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/eme.rs b/src/eme.rs index 20623ff2f2b628ed56310bb8d1110d6df20ead6d..623a0cd1eb1378c6b24a9fb55e15eb72ee633167 100644 --- a/src/eme.rs +++ b/src/eme.rs @@ -26,7 +26,7 @@ mod tests { use super::*; use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/forwarding.rs b/src/forwarding.rs index 5dfa4aea0413a754808f3ddbbf4f1601ac85c76e..4adb47964422e79070a494dcb8b1264c567e7ced 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -28,7 +28,7 @@ mod tests { use super::*; use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/hashes.rs b/src/hashes.rs index e1be62b1ceb967a7517f563a4814b6aaca109e44..f4b19faad26f62324286715e220194fcd66af5f5 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -168,7 +168,7 @@ impl Deref for Sha1HexAttribute { mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/ibb.rs b/src/ibb.rs index aa1e07d3c2308cd767defbec47be9e80d72f3fd9..8a81b651826903733a271e3aab7c9953302ace4d 100644 --- a/src/ibb.rs +++ b/src/ibb.rs @@ -75,7 +75,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/ibr.rs b/src/ibr.rs index c98c909369c4ba1a25e52ae603c6e7b59584b28c..911b800228b76fe5b2038b1e0198df01e3ae9e1a 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -10,7 +10,7 @@ use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use minidom::Element; use std::collections::HashMap; -use try_from::TryFrom; +use std::convert::TryFrom; /// Query for registering against a service. #[derive(Debug, Clone)] @@ -36,7 +36,7 @@ impl IqSetPayload for Query {} impl IqResultPayload for Query {} impl TryFrom for Query { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "query", REGISTER, "IBR query"); diff --git a/src/idle.rs b/src/idle.rs index 89b62c0549f139c4832ea5feafbf9dc77efbe5e3..cba3698b4db829c20cc6afd7232cd7cef1d7e07b 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -25,7 +25,7 @@ mod tests { use minidom::Element; use std::error::Error as StdError; use std::str::FromStr; - use try_from::TryFrom; + use std::convert::TryFrom; #[test] fn test_size() { diff --git a/src/iq.rs b/src/iq.rs index bca785e90af177b7f351c55ea368056ce3913335..c5a195d404b68001960cb65b2edc20791a79c1fe 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -11,7 +11,7 @@ use crate::stanza_error::StanzaError; use jid::Jid; use minidom::Element; use minidom::IntoAttributeValue; -use try_from::TryFrom; +use std::convert::TryFrom; /// Should be implemented on every known payload of an ``. pub trait IqGetPayload: TryFrom + Into {} @@ -130,7 +130,7 @@ impl Iq { } impl TryFrom for Iq { - type Err = Error; + type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "iq", DEFAULT_NS); diff --git a/src/jingle.rs b/src/jingle.rs index 9edb318ff43b5903366fc0b1ee7c7186ac708c72..ed4fe4a8609e920d87f99e4b28c0a92b610986d1 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -11,7 +11,7 @@ use jid::Jid; use minidom::Element; use std::collections::BTreeMap; use std::str::FromStr; -use try_from::TryFrom; +use std::convert::TryFrom; generate_attribute!( /// The action attribute. @@ -368,7 +368,7 @@ pub struct ReasonElement { } impl TryFrom for ReasonElement { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "reason", JINGLE); @@ -497,7 +497,7 @@ impl Jingle { } impl TryFrom for Jingle { - type Err = Error; + type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "jingle", JINGLE, "Jingle"); diff --git a/src/jingle_dtls_srtp.rs b/src/jingle_dtls_srtp.rs index 84f646506b3ca265650c3f03e8816ae8dfce000d..e65c368170a229208e130dc0a9f4d8b4fb63449c 100644 --- a/src/jingle_dtls_srtp.rs +++ b/src/jingle_dtls_srtp.rs @@ -50,7 +50,7 @@ generate_element!( mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index 3cd9e00abe7fdc94bc94ea778b36c40b1e812eb6..f0b7cf32461fe8e1982bc9a309f67f3fa3db5f74 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -12,7 +12,7 @@ use crate::ns; use minidom::Element; use std::collections::BTreeMap; use std::str::FromStr; -use try_from::TryFrom; +use std::convert::TryFrom; generate_element!( /// Represents a range in a file. @@ -128,7 +128,7 @@ impl File { } impl TryFrom for File { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "file", JINGLE_FT); @@ -253,7 +253,7 @@ pub struct Description { } impl TryFrom for Description { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "description", JINGLE_FT, "JingleFT description"); @@ -301,7 +301,7 @@ pub struct Checksum { } impl TryFrom for Checksum { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "checksum", JINGLE_FT); diff --git a/src/jingle_ibb.rs b/src/jingle_ibb.rs index 019f03429f520c8983b1f937b96ba4bf8b68a3e5..d1fe61f9d295271c6582a3285f3e178f32cccb70 100644 --- a/src/jingle_ibb.rs +++ b/src/jingle_ibb.rs @@ -27,7 +27,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/jingle_ice_udp.rs b/src/jingle_ice_udp.rs index 3b386705362b9ee6cb545c29540f74263c5cb19e..323014380d89a17a26d30390ae96e337e58d075c 100644 --- a/src/jingle_ice_udp.rs +++ b/src/jingle_ice_udp.rs @@ -92,7 +92,7 @@ generate_element!( mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; use crate::hashes::Algo; use crate::jingle_dtls_srtp::Setup; diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 8561a649b792a7d422ca10cb7380d6a2aafdf38a..bf0478cf4801aed8018eae1a115a7069ac3626a1 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -8,7 +8,7 @@ use crate::util::error::Error; use crate::jingle::SessionId; use crate::ns; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; /// Defines a protocol for broadcasting Jingle requests to all of the clients /// of a user. @@ -48,7 +48,7 @@ fn check_empty_and_get_sid(elem: Element) -> Result { } impl TryFrom for JingleMI { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { if !elem.has_ns(ns::JINGLE_MESSAGE) { diff --git a/src/jingle_rtp.rs b/src/jingle_rtp.rs index 366cfeeca4397a5e5d5d4cb4db12efb3b2b4141d..108010d00404ae87fdc3ccbf4086c146ed6660da 100644 --- a/src/jingle_rtp.rs +++ b/src/jingle_rtp.rs @@ -76,7 +76,7 @@ generate_element!( mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 02b97361352b517dc471c7e1816826370a9ab98a..0aa797211481d754a269176c9a86019754516659 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -9,7 +9,7 @@ use crate::ns; use jid::Jid; use minidom::Element; use std::net::IpAddr; -use try_from::TryFrom; +use std::convert::TryFrom; generate_attribute!( /// The type of the connection being proposed by this candidate. @@ -172,7 +172,7 @@ impl Transport { } impl TryFrom for Transport { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "transport", JINGLE_S5B); diff --git a/src/lib.rs b/src/lib.rs index eb3cc3b4aa52e818df45738a9f4a9fc058cca312..38546ace262a5aaaa1df7a9f206b48040b167e3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ pub use minidom::Element; pub use jid::{Jid, JidParseError}; -pub use try_from::{TryFrom, TryInto}; +pub use std::convert::{TryFrom, TryInto}; pub use crate::util::error::Error; /// XML namespace definitions used through XMPP. diff --git a/src/mam.rs b/src/mam.rs index cc606923f19bc15fb9a2f59a658f6ff5c90f3386..bd97ed9902943262f05fc1ef94eabae70ac7cdb9 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -14,7 +14,7 @@ use crate::pubsub::NodeName; use crate::rsm::{SetQuery, SetResult}; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; generate_id!( /// An identifier matching a result message to the query requesting it. @@ -126,7 +126,7 @@ impl IqSetPayload for Prefs {} impl IqResultPayload for Prefs {} impl TryFrom for Prefs { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "prefs", MAM); diff --git a/src/media_element.rs b/src/media_element.rs index 259e9d1226d5a14336f2697c58a4b0113ddfdaa3..f632e48c13428c4e722e9871a62019f07d3f2b9b 100644 --- a/src/media_element.rs +++ b/src/media_element.rs @@ -49,7 +49,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::error::Error as StdError; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/message.rs b/src/message.rs index 29708c32e1d2e710ca27eda4790b6b0f6af7c0ac..582c7f1a70e7763d53c2748302b35b20e544e418 100644 --- a/src/message.rs +++ b/src/message.rs @@ -9,7 +9,7 @@ use crate::ns; use jid::Jid; use minidom::Element; use std::collections::BTreeMap; -use try_from::TryFrom; +use std::convert::TryFrom; /// Should be implemented on every known payload of a ``. pub trait MessagePayload: TryFrom + Into {} @@ -150,7 +150,7 @@ impl Message { } impl TryFrom for Message { - type Err = Error; + type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "message", DEFAULT_NS); diff --git a/src/message_correct.rs b/src/message_correct.rs index b2600e0b9a021f227b0bef80065fb83fa1778a9f..1055af9bd261bc0e3a9cb0b72c0a5996aab92fed 100644 --- a/src/message_correct.rs +++ b/src/message_correct.rs @@ -23,7 +23,7 @@ mod tests { use super::*; use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/mood.rs b/src/mood.rs index da07bf50a296d6a5190c56b6bc24cab306eb11ab..8d27c4f0629db50ff50675003532f7923535e4be 100644 --- a/src/mood.rs +++ b/src/mood.rs @@ -272,7 +272,7 @@ generate_elem_id!( mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 898af67896f657699516615e46d3fcaa50e7a8af..5428a60278e7cd220d10275ba89882f211ac3468 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -98,7 +98,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::str::FromStr; - use try_from::TryFrom; + use std::convert::TryFrom; #[test] fn test_muc_simple() { diff --git a/src/muc/user.rs b/src/muc/user.rs index 8965529fa9486a7629472c188634afb731fd8fa0..bc8012d6b90801239f25601358ed25578ac55eca 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -9,7 +9,7 @@ use crate::util::error::Error; use crate::ns; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; generate_attribute_enum!( /// Lists all of the possible status codes used in MUC presences. @@ -89,7 +89,7 @@ pub enum Actor { } impl TryFrom for Actor { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "actor", MUC_USER); diff --git a/src/nick.rs b/src/nick.rs index 1fbeb1026752abc44799299b2b271f1fa3f450f2..07f07f770125550a41cbd5e53674ff842f8586c1 100644 --- a/src/nick.rs +++ b/src/nick.rs @@ -17,7 +17,7 @@ mod tests { #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/ping.rs b/src/ping.rs index 62bd0b1a2e6c215f644864d43c277833ca84787a..f110088b33edf9a717437ba4b80bcdf2c522b226 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -23,7 +23,7 @@ mod tests { #[cfg(not(feature = "disable-validation"))] use crate::util::error::Error; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[test] fn test_size() { diff --git a/src/presence.rs b/src/presence.rs index c6a1a102974f2e2701e42d89318f9f821c3ca2fa..de2b77c37dd54423c79cceb436c5c24334d32d09 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -11,7 +11,7 @@ use jid::Jid; use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements}; use std::collections::BTreeMap; use std::str::FromStr; -use try_from::TryFrom; +use std::convert::TryFrom; /// Should be implemented on every known payload of a ``. pub trait PresencePayload: TryFrom + Into {} @@ -259,7 +259,7 @@ impl Presence { } impl TryFrom for Presence { - type Err = Error; + type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "presence", DEFAULT_NS); diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 8f6951d9e67081f62ffbfe630553868021b0d2f7..5d65eb783d9411505b26065f8aee07c25e476605 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -11,7 +11,7 @@ use crate::ns; use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; /// Event wrapper for a PubSub ``. #[derive(Debug, Clone)] @@ -132,7 +132,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { } impl TryFrom for PubSubEvent { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "event", PUBSUB_EVENT); diff --git a/src/pubsub/pubsub.rs b/src/pubsub/pubsub.rs index 5093b3e1512e692bbd02f47f779e311944ebf0e6..0e166bd3ebcf86cd17e2327f864e5f53a0ba670f 100644 --- a/src/pubsub/pubsub.rs +++ b/src/pubsub/pubsub.rs @@ -11,7 +11,7 @@ use crate::ns; use crate::pubsub::{NodeName, Subscription, SubscriptionId, Item as PubSubItem}; use jid::Jid; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; // TODO: a better solution would be to split this into a query and a result elements, like for // XEP-0030. @@ -191,7 +191,7 @@ pub struct SubscribeOptions { } impl TryFrom for SubscribeOptions { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "subscribe-options", PUBSUB); @@ -340,7 +340,7 @@ impl IqSetPayload for PubSub {} impl IqResultPayload for PubSub {} impl TryFrom for PubSub { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "pubsub", PUBSUB); diff --git a/src/receipts.rs b/src/receipts.rs index e60900401ec1510473735441f10d17c24be516d3..3b54d3cddb1663a8c4084f15493c29ae12667393 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -33,7 +33,7 @@ mod tests { use super::*; use crate::ns; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/roster.rs b/src/roster.rs index 57d440e97733717d16ff33c86e9c16c8ee938235..65c1fdf57f593b6a927728dfb1a550c99241e62b 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -96,7 +96,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::str::FromStr; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/rsm.rs b/src/rsm.rs index 6b65e3b48d9cda6f193fb96005293f0cd4d46f1d..a4f97365388da78a6e47e3ebc7555afd0a2623f2 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::ns; use minidom::Element; -use try_from::TryFrom; +use std::convert::TryFrom; /// Requests paging through a potentially big set of items (represented by an /// UID). @@ -29,7 +29,7 @@ pub struct SetQuery { } impl TryFrom for SetQuery { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "set", RSM, "RSM set"); @@ -116,7 +116,7 @@ pub struct SetResult { } impl TryFrom for SetResult { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "set", RSM, "RSM set"); diff --git a/src/sasl.rs b/src/sasl.rs index ec84fe6cb99df44fa0ea8cf9b76111239eca044b..3deed9e1aa6b0fface1dcd41319f8eafc96641c0 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -9,7 +9,7 @@ use crate::util::helpers::Base64; use crate::ns; use minidom::Element; use std::collections::BTreeMap; -use try_from::TryFrom; +use std::convert::TryFrom; generate_attribute!( /// The list of available SASL mechanisms. @@ -151,7 +151,7 @@ pub struct Failure { } impl TryFrom for Failure { - type Err = Error; + type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "failure", SASL); @@ -224,7 +224,7 @@ impl From for Element { mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/server_info.rs b/src/server_info.rs index 53cef3838bcd2cc55ad1e75de8827f8863183049..3ad9e0008786092f03391d6a1617095447ec54dc 100644 --- a/src/server_info.rs +++ b/src/server_info.rs @@ -6,7 +6,7 @@ use crate::data_forms::{DataForm, DataFormType, Field, FieldType}; use crate::ns; use crate::util::error::Error; -use try_from::TryFrom; +use std::convert::TryFrom; /// Structure representing a `http://jabber.org/network/serverinfo` form type. #[derive(Debug, Clone, PartialEq, Default)] @@ -31,7 +31,7 @@ pub struct ServerInfo { } impl TryFrom for ServerInfo { - type Err = Error; + type Error = Error; fn try_from(form: DataForm) -> Result { if form.type_ != DataFormType::Result_ { diff --git a/src/sm.rs b/src/sm.rs index 661d05b9d0c3f5f73e30a2a47cd8787f7dc84b28..b483514023031ca34bdf02fe07ddf1c75daacdfb 100644 --- a/src/sm.rs +++ b/src/sm.rs @@ -146,7 +146,7 @@ generate_empty_element!( mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/stanza_error.rs b/src/stanza_error.rs index dcaf5cb884f6146839ccf557426eef424cfa3960..f2ae500e2592ed59662542e9e20d839bd2c14e8c 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -11,7 +11,7 @@ use crate::presence::PresencePayload; use jid::Jid; use minidom::Element; use std::collections::BTreeMap; -use try_from::TryFrom; +use std::convert::TryFrom; generate_attribute!( /// The type of the error. @@ -236,7 +236,7 @@ impl StanzaError { } impl TryFrom for StanzaError { - type Err = Error; + type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "error", DEFAULT_NS); diff --git a/src/stanza_id.rs b/src/stanza_id.rs index dd8ed9e0a125044eb9587f5364793730f1a12127..2178d7a94014034f91b14f3d8c7d6859ed31265d 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -40,7 +40,7 @@ mod tests { use crate::util::error::Error; use minidom::Element; use std::str::FromStr; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/stream.rs b/src/stream.rs index a77a73ea8ca36f59a55df520e085aa29cf9dff85..1d4715f2d1c164cfc158500f12348bccfd808ab1 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -74,7 +74,7 @@ impl Stream { mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/util/macros.rs b/src/util/macros.rs index f4b08335fa29301d1d263e4315ea3938dfdfaa2a..010cde52bb48675b7ba8a43cbb2b45365c734923 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -230,8 +230,8 @@ macro_rules! generate_element_enum { $enum ),+ } - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::util::error::Error; + impl ::std::convert::TryFrom<::minidom::Element> for $elem { + type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); @@ -269,8 +269,8 @@ macro_rules! generate_attribute_enum { $enum ),+ } - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::util::error::Error; + impl ::std::convert::TryFrom<::minidom::Element> for $elem { + type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); @@ -367,8 +367,8 @@ macro_rules! generate_empty_element { #[derive(Debug, Clone)] pub struct $elem; - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::util::error::Error; + impl ::std::convert::TryFrom<::minidom::Element> for $elem { + type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); @@ -420,8 +420,8 @@ macro_rules! generate_elem_id { Ok($elem(String::from(s))) } } - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::util::error::Error; + impl ::std::convert::TryFrom<::minidom::Element> for $elem { + type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); @@ -600,8 +600,8 @@ macro_rules! generate_element { )* } - impl ::try_from::TryFrom<::minidom::Element> for $elem { - type Err = crate::util::error::Error; + impl ::std::convert::TryFrom<::minidom::Element> for $elem { + type Error = crate::util::error::Error; fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> { check_self!(elem, $name, $ns); @@ -662,7 +662,7 @@ macro_rules! assert_size ( macro_rules! impl_pubsub_item { ($item:ident, $ns:ident) => { impl crate::TryFrom for $item { - type Err = Error; + type Error = Error; fn try_from(elem: crate::Element) -> Result<$item, Error> { check_self!(elem, "item", $ns); diff --git a/src/version.rs b/src/version.rs index 5aa4d2fbb2acbfe5e3d3d2e8207e57d43958a195..afc617897adf033e0dfd46865165e04af4e551f2 100644 --- a/src/version.rs +++ b/src/version.rs @@ -43,7 +43,7 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] diff --git a/src/websocket.rs b/src/websocket.rs index 03a1d0401b97103d42e81ba45f10f110dae3bea5..b8c02e2b44f736f76c9447e97d25ebcef6e16bcb 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -73,7 +73,7 @@ impl Open { mod tests { use super::*; use minidom::Element; - use try_from::TryFrom; + use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] From 8db6b5602eda1c50b7ba2b7cb824d8c27ef76d45 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 12 Apr 2019 12:15:40 +0200 Subject: [PATCH 0884/1020] Cargo.toml, ChangeLog: Release version 0.13.1. --- Cargo.toml | 2 +- ChangeLog | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 36ed320c4e0249f5a33d6830a166d41688366258..08e98139f06bec43948da8873f3a6715e074fa6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.13.0" +version = "0.13.1" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", diff --git a/ChangeLog b/ChangeLog index 12a14a0150956cedea332ec69094af3bd6174548..f0129488451a36c12b719a150a95dc5792e2633d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Version 0.13.1: +2019-04-12 Emmanuel Gil Peyrot + * Bugfixes: + - Fix invalid serialisation of priority in presence. + - Bump image size to u16 from u8, as per XEP-0084 version 1.1.2. + * Improvements: + - Drop try_from dependency, as std::convert::TryFrom got + stabilised. + Version 0.13.0: 2019-03-20 Emmanuel Gil Peyrot * New parsers/serialisers: From 395d4480278d87c8c6ae2ec71d1fb170b0c3c236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sun, 21 Apr 2019 23:52:02 +0100 Subject: [PATCH 0885/1020] Split Jid struct into BareJid and FullJid. Jid is now an enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will help with being able to enforce the usage of bare or full at compile time. It is still possible to allow one or the other with the `Jid` enum. Thanks to O01eg (from xmpp-rs@muc.linkmauve.fr) for the help. This commit also contains code from them. Signed-off-by: Maxime “pep” Buquet --- src/lib.rs | 597 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 414 insertions(+), 183 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dd9a5f1b0bb1d82e8fe17232f9ec18d6312f9520..8a243b69e4bba6a002a678f59cd084058ca3a60c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,10 @@ pub enum JidParseError { #[fail(display = "no domain found in this JID")] NoDomain, + /// Happens when there is no resource, that is string contains no /. + #[fail(display = "no resource found in this JID")] + NoResource, + /// Happens when the node is empty, that is the string starts with a @. #[fail(display = "nodepart empty despite the presence of a @")] EmptyNode, @@ -28,46 +32,129 @@ pub enum JidParseError { EmptyResource, } -/// A struct representing a Jabber ID. +/// An enum representing a Jabber ID. It can be either a `FullJid` or a `BareJid`. +#[derive(Debug, Clone, PartialEq)] +pub enum Jid { + /// Bare Jid + Bare(BareJid), + + /// Full Jid + Full(FullJid), +} + +impl FromStr for Jid { + type Err = JidParseError; + + fn from_str(s: &str) -> Result { + let (ns, ds, rs): StringJid = _from_str(s)?; + Ok(match rs { + Some(rs) => Jid::Full(FullJid { + node: ns, + domain: ds, + resource: rs, + }), + None => Jid::Bare(BareJid { + node: ns, + domain: ds, + }) + }) + } +} + +impl From for String { + fn from(jid: Jid) -> String { + match jid { + Jid::Bare(bare) => String::from(bare), + Jid::Full(full) => String::from(full), + } + } +} + +/// A struct representing a Full Jabber ID. /// -/// A Jabber ID is composed of 3 components, of which 2 are optional: +/// A Full Jabber ID is composed of 3 components, of which one is optional: /// /// - A node/name, `node`, which is the optional part before the @. /// - A domain, `domain`, which is the mandatory part after the @ but before the /. -/// - A resource, `resource`, which is the optional part after the /. +/// - A resource, `resource`, which is the part after the /. #[derive(Clone, PartialEq, Eq, Hash)] -pub struct Jid { +pub struct FullJid { /// The node part of the Jabber ID, if it exists, else None. pub node: Option, /// The domain of the Jabber ID. pub domain: String, - /// The resource of the Jabber ID, if it exists, else None. - pub resource: Option, + /// The resource of the Jabber ID. + pub resource: String, } -impl From for String { - fn from(jid: Jid) -> String { +/// A struct representing a Bare Jabber ID. +/// +/// A Bare Jabber ID is composed of 2 components, of which one is optional: +/// +/// - A node/name, `node`, which is the optional part before the @. +/// - A domain, `domain`, which is the mandatory part after the @ but before the /. +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct BareJid { + /// The node part of the Jabber ID, if it exists, else None. + pub node: Option, + /// The domain of the Jabber ID. + pub domain: String, +} + +impl From for String { + fn from(jid: FullJid) -> String { let mut string = String::new(); if let Some(ref node) = jid.node { string.push_str(node); string.push('@'); } string.push_str(&jid.domain); - if let Some(ref resource) = jid.resource { - string.push('/'); - string.push_str(resource); + string.push('/'); + string.push_str(&jid.resource); + string + } +} + +impl From for String { + fn from(jid: BareJid) -> String { + let mut string = String::new(); + if let Some(ref node) = jid.node { + string.push_str(node); + string.push('@'); } + string.push_str(&jid.domain); string } } -impl fmt::Debug for Jid { +impl Into for FullJid { + fn into(self) -> BareJid { + BareJid { + node: self.node, + domain: self.domain, + } + } +} + +impl fmt::Debug for FullJid { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "JID({})", self) + write!(fmt, "FullJID({})", self) } } -impl fmt::Display for Jid { +impl fmt::Debug for BareJid { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "BareJID({})", self) + } +} + +impl fmt::Display for FullJid { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt.write_str(String::from(self.clone()).as_ref()) + } +} + +impl fmt::Display for BareJid { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt.write_str(String::from(self.clone()).as_ref()) } @@ -79,237 +166,262 @@ enum ParserState { Resource, } -impl FromStr for Jid { - type Err = JidParseError; - - fn from_str(s: &str) -> Result { - // TODO: very naive, may need to do it differently - let iter = s.chars(); - let mut buf = String::with_capacity(s.len()); - let mut state = ParserState::Node; - let mut node = None; - let mut domain = None; - let mut resource = None; - for c in iter { - match state { - ParserState::Node => { - match c { - '@' => { - if buf == "" { - return Err(JidParseError::EmptyNode); - } - state = ParserState::Domain; - node = Some(buf.clone()); // TODO: performance tweaks, do not need to copy it - buf.clear(); - } - '/' => { - if buf == "" { - return Err(JidParseError::NoDomain); - } - state = ParserState::Resource; - domain = Some(buf.clone()); // TODO: performance tweaks - buf.clear(); +type StringJid = (Option, String, Option); +fn _from_str(s: &str) -> Result { + // TODO: very naive, may need to do it differently + let iter = s.chars(); + let mut buf = String::with_capacity(s.len()); + let mut state = ParserState::Node; + let mut node = None; + let mut domain = None; + let mut resource = None; + for c in iter { + match state { + ParserState::Node => { + match c { + '@' => { + if buf == "" { + return Err(JidParseError::EmptyNode); } - c => { - buf.push(c); + state = ParserState::Domain; + node = Some(buf.clone()); // TODO: performance tweaks, do not need to copy it + buf.clear(); + } + '/' => { + if buf == "" { + return Err(JidParseError::NoDomain); } + state = ParserState::Resource; + domain = Some(buf.clone()); // TODO: performance tweaks + buf.clear(); + } + c => { + buf.push(c); } } - ParserState::Domain => { - match c { - '/' => { - if buf == "" { - return Err(JidParseError::NoDomain); - } - state = ParserState::Resource; - domain = Some(buf.clone()); // TODO: performance tweaks - buf.clear(); - } - c => { - buf.push(c); + } + ParserState::Domain => { + match c { + '/' => { + if buf == "" { + return Err(JidParseError::NoDomain); } + state = ParserState::Resource; + domain = Some(buf.clone()); // TODO: performance tweaks + buf.clear(); + } + c => { + buf.push(c); } } - ParserState::Resource => { - buf.push(c); - } + } + ParserState::Resource => { + buf.push(c); } } - if !buf.is_empty() { - match state { - ParserState::Node => { - domain = Some(buf); - } - ParserState::Domain => { - domain = Some(buf); - } - ParserState::Resource => { - resource = Some(buf); - } + } + if !buf.is_empty() { + match state { + ParserState::Node => { + domain = Some(buf); + } + ParserState::Domain => { + domain = Some(buf); + } + ParserState::Resource => { + resource = Some(buf); } - } else if let ParserState::Resource = state { - return Err(JidParseError::EmptyResource); } - Ok(Jid { - node: node, - domain: domain.ok_or(JidParseError::NoDomain)?, - resource: resource, + } else if let ParserState::Resource = state { + return Err(JidParseError::EmptyResource); + } + Ok(( + node, + domain.ok_or(JidParseError::NoDomain)?, + resource, + )) +} + +impl FromStr for FullJid { + type Err = JidParseError; + + fn from_str(s: &str) -> Result { + let (ns, ds, rs): StringJid = _from_str(s)?; + Ok(FullJid { + node: ns, + domain: ds, + resource: rs.ok_or(JidParseError::NoResource)?, }) } } -impl Jid { - /// Constructs a Jabber ID containing all three components. +impl FullJid { + /// Constructs a Full Jabber ID containing all three components. /// /// This is of the form `node`@`domain`/`resource`. /// /// # Examples /// /// ``` - /// use jid::Jid; + /// use jid::FullJid; /// - /// let jid = Jid::full("node", "domain", "resource"); + /// let jid = FullJid::new("node", "domain", "resource"); /// /// assert_eq!(jid.node, Some("node".to_owned())); /// assert_eq!(jid.domain, "domain".to_owned()); - /// assert_eq!(jid.resource, Some("resource".to_owned())); + /// assert_eq!(jid.resource, "resource".to_owned()); /// ``` - pub fn full(node: NS, domain: DS, resource: RS) -> Jid + pub fn new(node: NS, domain: DS, resource: RS) -> FullJid where NS: Into, DS: Into, RS: Into, { - Jid { + FullJid { node: Some(node.into()), domain: domain.into(), - resource: Some(resource.into()), + resource: resource.into() } } - /// Constructs a Jabber ID containing only the `node` and `domain` components. - /// - /// This is of the form `node`@`domain`. + /// Constructs a new Jabber ID from an existing one, with the node swapped out with a new one. /// /// # Examples /// /// ``` - /// use jid::Jid; + /// use jid::FullJid; /// - /// let jid = Jid::bare("node", "domain"); + /// let jid = FullJid::new("node", "domain", "resource"); /// /// assert_eq!(jid.node, Some("node".to_owned())); - /// assert_eq!(jid.domain, "domain".to_owned()); - /// assert_eq!(jid.resource, None); + /// + /// let new_jid = jid.with_node("new_node"); + /// + /// assert_eq!(new_jid.node, Some("new_node".to_owned())); /// ``` - pub fn bare(node: NS, domain: DS) -> Jid + pub fn with_node(&self, node: NS) -> FullJid where NS: Into, - DS: Into, { - Jid { + FullJid { node: Some(node.into()), - domain: domain.into(), - resource: None, + domain: self.domain.clone(), + resource: self.resource.clone(), } } - /// Returns a new Jabber ID from the current one with only node and domain. - /// - /// This is of the form `node`@`domain`. + /// Constructs a new Jabber ID from an existing one, with the domain swapped out with a new one. /// /// # Examples /// /// ``` - /// use jid::Jid; + /// use jid::FullJid; /// - /// let jid = Jid::full("node", "domain", "resource").into_bare_jid(); + /// let jid = FullJid::new("node", "domain", "resource"); /// - /// assert_eq!(jid.node, Some("node".to_owned())); /// assert_eq!(jid.domain, "domain".to_owned()); - /// assert_eq!(jid.resource, None); + /// + /// let new_jid = jid.with_domain("new_domain"); + /// + /// assert_eq!(new_jid.domain, "new_domain"); /// ``` - pub fn into_bare_jid(self) -> Jid { - Jid { - node: self.node, - domain: self.domain, - resource: None, + pub fn with_domain(&self, domain: DS) -> FullJid + where + DS: Into, + { + FullJid { + node: self.node.clone(), + domain: domain.into(), + resource: self.resource.clone(), } } - /// Constructs a Jabber ID containing only a `domain`. - /// - /// This is of the form `domain`. + /// Constructs a Full Jabber ID from a Bare Jabber ID, specifying a `resource`. /// /// # Examples /// /// ``` - /// use jid::Jid; + /// use jid::FullJid; /// - /// let jid = Jid::domain("domain"); + /// let jid = FullJid::new("node", "domain", "resource"); /// - /// assert_eq!(jid.node, None); - /// assert_eq!(jid.domain, "domain".to_owned()); - /// assert_eq!(jid.resource, None); + /// assert_eq!(jid.resource, "resource".to_owned()); + /// + /// let new_jid = jid.with_resource("new_resource"); + /// + /// assert_eq!(new_jid.resource, "new_resource"); /// ``` - pub fn domain(domain: DS) -> Jid + pub fn with_resource(&self, resource: RS) -> FullJid where - DS: Into, + RS: Into, { - Jid { - node: None, - domain: domain.into(), - resource: None, + FullJid { + node: self.node.clone(), + domain: self.domain.clone(), + resource: resource.into(), } } +} + +impl FromStr for BareJid { + type Err = JidParseError; + + fn from_str(s: &str) -> Result { + let (ns, ds, _rs): StringJid = _from_str(s)?; + Ok(BareJid { + node: ns, + domain: ds, + }) + } +} - /// Returns a new Jabber ID from the current one with only domain. +impl BareJid { + /// Constructs a Bare Jabber ID, containing two components. /// - /// This is of the form `domain`. + /// This is of the form `node`@`domain`. /// /// # Examples /// /// ``` - /// use jid::Jid; + /// use jid::BareJid; /// - /// let jid = Jid::full("node", "domain", "resource").into_domain_jid(); + /// let jid = BareJid::new("node", "domain"); /// - /// assert_eq!(jid.node, None); + /// assert_eq!(jid.node, Some("node".to_owned())); /// assert_eq!(jid.domain, "domain".to_owned()); - /// assert_eq!(jid.resource, None); /// ``` - pub fn into_domain_jid(self) -> Jid { - Jid { - node: None, - domain: self.domain, - resource: None, + pub fn new(node: NS, domain: DS) -> BareJid + where + NS: Into, + DS: Into, + { + BareJid { + node: Some(node.into()), + domain: domain.into(), } } - /// Constructs a Jabber ID containing the `domain` and `resource` components. + /// Constructs a Bare Jabber ID containing only a `domain`. /// - /// This is of the form `domain`/`resource`. + /// This is of the form `domain`. /// /// # Examples /// /// ``` - /// use jid::Jid; + /// use jid::BareJid; /// - /// let jid = Jid::domain_with_resource("domain", "resource"); + /// let jid = BareJid::domain("domain"); /// /// assert_eq!(jid.node, None); /// assert_eq!(jid.domain, "domain".to_owned()); - /// assert_eq!(jid.resource, Some("resource".to_owned())); /// ``` - pub fn domain_with_resource(domain: DS, resource: RS) -> Jid + pub fn domain(domain: DS) -> BareJid where DS: Into, - RS: Into, { - Jid { + BareJid { node: None, domain: domain.into(), - resource: Some(resource.into()), } } @@ -318,9 +430,9 @@ impl Jid { /// # Examples /// /// ``` - /// use jid::Jid; + /// use jid::BareJid; /// - /// let jid = Jid::domain("domain"); + /// let jid = BareJid::domain("domain"); /// /// assert_eq!(jid.node, None); /// @@ -328,14 +440,13 @@ impl Jid { /// /// assert_eq!(new_jid.node, Some("node".to_owned())); /// ``` - pub fn with_node(&self, node: S) -> Jid + pub fn with_node(&self, node: NS) -> BareJid where - S: Into, + NS: Into, { - Jid { + BareJid { node: Some(node.into()), domain: self.domain.clone(), - resource: self.resource.clone(), } } @@ -344,9 +455,9 @@ impl Jid { /// # Examples /// /// ``` - /// use jid::Jid; + /// use jid::BareJid; /// - /// let jid = Jid::domain("domain"); + /// let jid = BareJid::domain("domain"); /// /// assert_eq!(jid.domain, "domain"); /// @@ -354,40 +465,38 @@ impl Jid { /// /// assert_eq!(new_jid.domain, "new_domain"); /// ``` - pub fn with_domain(&self, domain: S) -> Jid + pub fn with_domain(&self, domain: DS) -> BareJid where - S: Into, + DS: Into, { - Jid { + BareJid { node: self.node.clone(), domain: domain.into(), - resource: self.resource.clone(), } } - /// Constructs a new Jabber ID from an existing one, with the resource swapped out with a new one. + /// Constructs a Full Jabber ID from a Bare Jabber ID, specifying a `resource`. /// /// # Examples /// /// ``` - /// use jid::Jid; + /// use jid::BareJid; /// - /// let jid = Jid::domain("domain"); + /// let bare = BareJid::new("node", "domain"); + /// let full = bare.with_resource("resource"); /// - /// assert_eq!(jid.resource, None); - /// - /// let new_jid = jid.with_resource("resource"); - /// - /// assert_eq!(new_jid.resource, Some("resource".to_owned())); + /// assert_eq!(full.node, Some("node".to_owned())); + /// assert_eq!(full.domain, "domain".to_owned()); + /// assert_eq!(full.resource, "resource".to_owned()); /// ``` - pub fn with_resource(&self, resource: S) -> Jid + pub fn with_resource(self, resource: RS) -> FullJid where - S: Into, + RS: Into, { - Jid { - node: self.node.clone(), - domain: self.domain.clone(), - resource: Some(resource.into()), + FullJid { + node: self.node, + domain: self.domain, + resource: resource.into(), } } } @@ -409,6 +518,34 @@ impl IntoElements for Jid { } } +#[cfg(feature = "minidom")] +impl IntoAttributeValue for FullJid { + fn into_attribute_value(self) -> Option { + Some(String::from(self)) + } +} + +#[cfg(feature = "minidom")] +impl IntoElements for FullJid { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_text_node(String::from(self)) + } +} + +#[cfg(feature = "minidom")] +impl IntoAttributeValue for BareJid { + fn into_attribute_value(self) -> Option { + Some(String::from(self)) + } +} + +#[cfg(feature = "minidom")] +impl IntoElements for BareJid { + fn into_elements(self, emitter: &mut ElementEmitter) { + emitter.append_text_node(String::from(self)) + } +} + #[cfg(test)] mod tests { use super::*; @@ -416,31 +553,88 @@ mod tests { use std::str::FromStr; #[test] - fn can_parse_jids() { - assert_eq!(Jid::from_str("a@b.c/d"), Ok(Jid::full("a", "b.c", "d"))); - assert_eq!(Jid::from_str("a@b.c"), Ok(Jid::bare("a", "b.c"))); - assert_eq!(Jid::from_str("b.c"), Ok(Jid::domain("b.c"))); + fn can_parse_full_jids() { + assert_eq!(FullJid::from_str("a@b.c/d"), Ok(FullJid::new("a", "b.c", "d"))); + assert_eq!( + FullJid::from_str("b.c/d"), + Ok(FullJid { + node: None, + domain: "b.c".to_owned(), + resource: "d".to_owned(), + }) + ); + + assert_eq!(FullJid::from_str("a@b.c"), Err(JidParseError::NoResource)); + assert_eq!(FullJid::from_str("b.c"), Err(JidParseError::NoResource)); + } + + #[test] + fn can_parse_bare_jids() { + assert_eq!(BareJid::from_str("a@b.c/d"), Ok(BareJid::new("a", "b.c"))); + assert_eq!( + BareJid::from_str("b.c/d"), + Ok(BareJid { + node: None, + domain: "b.c".to_owned(), + }) + ); + + assert_eq!(BareJid::from_str("a@b.c"), Ok(BareJid::new("a", "b.c"))); assert_eq!( - Jid::from_str("a/b@c"), - Ok(Jid::domain_with_resource("a", "b@c")) + BareJid::from_str("b.c"), + Ok(BareJid { + node: None, + domain: "b.c".to_owned(), + }) ); } + #[test] + fn can_parse_jids() { + let full = FullJid::from_str("a@b.c/d").unwrap(); + let bare = BareJid::from_str("e@f.g").unwrap(); + + assert_eq!(Jid::from_str("a@b.c/d"), Ok(Jid::Full(full))); + assert_eq!(Jid::from_str("e@f.g"), Ok(Jid::Bare(bare))); + } + + #[test] + fn full_to_bare_jid() { + let bare: BareJid = FullJid::new("a", "b.c", "d").into(); + assert_eq!(bare, BareJid::new("a", "b.c")); + } + + #[test] + fn bare_to_full_jid() { + assert_eq!(BareJid::new("a", "b.c").with_resource("d"), FullJid::new("a", "b.c", "d")); + } + #[test] fn serialise() { assert_eq!( - String::from(Jid::full("a", "b", "c")), + String::from(FullJid::new("a", "b", "c")), String::from("a@b/c") ); + assert_eq!( + String::from(BareJid::new("a", "b")), + String::from("a@b") + ); } #[test] fn invalid_jids() { - assert_eq!(Jid::from_str(""), Err(JidParseError::NoDomain)); - assert_eq!(Jid::from_str("/c"), Err(JidParseError::NoDomain)); - assert_eq!(Jid::from_str("a@/c"), Err(JidParseError::NoDomain)); - assert_eq!(Jid::from_str("@b"), Err(JidParseError::EmptyNode)); - assert_eq!(Jid::from_str("b/"), Err(JidParseError::EmptyResource)); + assert_eq!(BareJid::from_str(""), Err(JidParseError::NoDomain)); + assert_eq!(BareJid::from_str("/c"), Err(JidParseError::NoDomain)); + assert_eq!(BareJid::from_str("a@/c"), Err(JidParseError::NoDomain)); + assert_eq!(BareJid::from_str("@b"), Err(JidParseError::EmptyNode)); + assert_eq!(BareJid::from_str("b/"), Err(JidParseError::EmptyResource)); + + assert_eq!(FullJid::from_str(""), Err(JidParseError::NoDomain)); + assert_eq!(FullJid::from_str("/c"), Err(JidParseError::NoDomain)); + assert_eq!(FullJid::from_str("a@/c"), Err(JidParseError::NoDomain)); + assert_eq!(FullJid::from_str("@b"), Err(JidParseError::EmptyNode)); + assert_eq!(FullJid::from_str("b/"), Err(JidParseError::EmptyResource)); + assert_eq!(FullJid::from_str("a@b"), Err(JidParseError::NoResource)); } #[cfg(feature = "minidom")] @@ -448,6 +642,43 @@ mod tests { fn minidom() { let elem: minidom::Element = "".parse().unwrap(); let to: Jid = elem.attr("from").unwrap().parse().unwrap(); - assert_eq!(to, Jid::full("a", "b", "c")); + assert_eq!(to, Jid::Full(FullJid::new("a", "b", "c"))); + + let elem: minidom::Element = "".parse().unwrap(); + let to: Jid = elem.attr("from").unwrap().parse().unwrap(); + assert_eq!(to, Jid::Bare(BareJid::new("a", "b"))); + + let elem: minidom::Element = "".parse().unwrap(); + let to: FullJid = elem.attr("from").unwrap().parse().unwrap(); + assert_eq!(to, FullJid::new("a", "b", "c")); + + let elem: minidom::Element = "".parse().unwrap(); + let to: BareJid = elem.attr("from").unwrap().parse().unwrap(); + assert_eq!(to, BareJid::new("a", "b")); + } + + #[cfg(feature = "minidom")] + #[test] + fn minidom_into_attr() { + let full = FullJid::new("a", "b", "c"); + let elem = minidom::Element::builder("message") + .ns("jabber:client") + .attr("from", full.clone()) + .build(); + assert_eq!(elem.attr("from"), Some(String::from(full).as_ref())); + + let bare = BareJid::new("a", "b"); + let elem = minidom::Element::builder("message") + .ns("jabber:client") + .attr("from", bare.clone()) + .build(); + assert_eq!(elem.attr("from"), Some(String::from(bare.clone()).as_ref())); + + let jid = Jid::Bare(bare.clone()); + let _elem = minidom::Element::builder("message") + .ns("jabber:client") + .attr("from", jid) + .build(); + assert_eq!(elem.attr("from"), Some(String::from(bare).as_ref())); } } From 2d7bf32ad452cea31d9c64e71142cb2a0179e911 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 22 Apr 2019 11:58:13 +0200 Subject: [PATCH 0886/1020] Add a XEP-0202 implementation. Fixes #7. --- ChangeLog | 5 +++ src/date.rs | 15 +++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ src/time.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 src/time.rs diff --git a/ChangeLog b/ChangeLog index f0129488451a36c12b719a150a95dc5792e2633d..82253779eb3c2f54efcbd543c328a72cbd5a8632 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Version TODO: +TODO Emmanuel Gil Peyrot + * New parsers/serialisers: + - Entity Time (XEP-0202). + Version 0.13.1: 2019-04-12 Emmanuel Gil Peyrot * Bugfixes: diff --git a/src/date.rs b/src/date.rs index 1d3389b0651623ffc7ec802a64e9de6106032b1a..f50a1c4f55eda931c0c3c0a23977879d988fde32 100644 --- a/src/date.rs +++ b/src/date.rs @@ -15,6 +15,20 @@ use std::str::FromStr; #[derive(Debug, Clone, PartialEq)] pub struct DateTime(ChronoDateTime); +impl DateTime { + pub fn timezone(&self) -> FixedOffset { + self.0.timezone() + } + + pub fn with_timezone(&self, tz: &FixedOffset) -> DateTime { + DateTime(self.0.with_timezone(tz)) + } + + pub fn format(&self, fmt: &str) -> String { + format!("{}", self.0.format(fmt)) + } +} + impl FromStr for DateTime { type Err = Error; @@ -41,6 +55,7 @@ mod tests { use chrono::{Datelike, Timelike}; use std::error::Error as StdError; + // DateTime’s size doesn’t depend on the architecture. #[test] fn test_size() { assert_size!(DateTime, 16); diff --git a/src/lib.rs b/src/lib.rs index 38546ace262a5aaaa1df7a9f206b48040b167e3b..ebeddf2faf51dc7de0388affc41c9698a4b359b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,6 +128,9 @@ pub mod sm; /// XEP-0199: XMPP Ping pub mod ping; +/// XEP-0202: Entity Time +pub mod time; + /// XEP-0203: Delayed Delivery pub mod delay; diff --git a/src/ns.rs b/src/ns.rs index 4f3d2d299d3dcd1f756427b03603fd8b0225aff6..978a85233d6557ffe1e4f4ddf27f2aa85175dde8 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -112,6 +112,9 @@ pub const SM: &str = "urn:xmpp:sm:3"; /// XEP-0199: XMPP Ping pub const PING: &str = "urn:xmpp:ping"; +/// XEP-0202: Entity Time +pub const TIME: &str = "urn:xmpp:time"; + /// XEP-0203: Delayed Delivery pub const DELAY: &str = "urn:xmpp:delay"; diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000000000000000000000000000000000000..c107393856d8559f926658143d6a8d240ff25b33 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,112 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use chrono::FixedOffset; +use crate::date::DateTime; +use crate::iq::{IqGetPayload, IqResultPayload}; +use crate::ns; +use crate::util::error::Error; +use minidom::Element; +use std::convert::TryFrom; +use std::str::FromStr; + +generate_empty_element!( + TimeQuery, "time", TIME +); + +impl IqGetPayload for TimeQuery {} + +#[derive(Debug, Clone)] +pub struct TimeResult(pub DateTime); + +impl IqResultPayload for TimeResult {} + +impl TryFrom for TimeResult { + type Error = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "time", TIME); + check_no_attributes!(elem, "time"); + + let mut tzo = None; + let mut utc = None; + + for child in elem.children() { + if child.is("tzo", ns::TIME) { + if tzo.is_some() { + return Err(Error::ParseError("More than one tzo element in time.")); + } + check_no_children!(child, "tzo"); + check_no_attributes!(child, "tzo"); + // TODO: Add a FromStr implementation to FixedOffset to avoid this hack. + let fake_date = String::from("2019-04-22T11:38:00") + &child.text(); + let date_time = DateTime::from_str(&fake_date)?; + tzo = Some(date_time.timezone()); + } else if child.is("utc", ns::TIME) { + if utc.is_some() { + return Err(Error::ParseError("More than one utc element in time.")); + } + check_no_children!(child, "utc"); + check_no_attributes!(child, "utc"); + let date_time = DateTime::from_str(&child.text())?; + if date_time.timezone() != FixedOffset::east(0) { + return Err(Error::ParseError("Non-UTC timezone for utc element.")); + } + utc = Some(date_time); + } else { + return Err(Error::ParseError( + "Unknown child in time element.", + )); + } + } + + let tzo = tzo.ok_or(Error::ParseError("Missing tzo child in time element."))?; + let utc = utc.ok_or(Error::ParseError("Missing utc child in time element."))?; + let date = utc.with_timezone(&tzo); + + Ok(TimeResult(date)) + } +} + +impl From for Element { + fn from(time: TimeResult) -> Element { + Element::builder("time") + .ns(ns::TIME) + .append(Element::builder("tzo") + .append(format!("{}", time.0.timezone())) + .build()) + .append(Element::builder("utc") + .append(time.0.with_timezone(&FixedOffset::east(0)).format("%FT%TZ")) + .build()) + .build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // DateTime’s size doesn’t depend on the architecture. + #[test] + fn test_size() { + assert_size!(TimeQuery, 0); + assert_size!(TimeResult, 16); + } + + #[test] + fn parse_response() { + let elem: Element = + "" + .parse() + .unwrap(); + let elem1 = elem.clone(); + let time = TimeResult::try_from(elem).unwrap(); + assert_eq!(time.0.timezone(), FixedOffset::west(6 * 3600)); + assert_eq!(time.0, DateTime::from_str("2006-12-19T12:58:35-05:00").unwrap()); + let elem2 = Element::from(time); + assert_eq!(elem1, elem2); + } +} From e1bd0086f19d6da0c1b9980e65c2fd0dc965abfc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 22 Apr 2019 13:33:29 +0200 Subject: [PATCH 0887/1020] time, date: Add missing docs. --- src/date.rs | 3 +++ src/time.rs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/date.rs b/src/date.rs index f50a1c4f55eda931c0c3c0a23977879d988fde32..73dd3bd7e9dbb7777a376afd2ae8d2c4328663f2 100644 --- a/src/date.rs +++ b/src/date.rs @@ -16,14 +16,17 @@ use std::str::FromStr; pub struct DateTime(ChronoDateTime); impl DateTime { + /// Retrieves the associated timezone. pub fn timezone(&self) -> FixedOffset { self.0.timezone() } + /// Returns a new `DateTime` with a different timezone. pub fn with_timezone(&self, tz: &FixedOffset) -> DateTime { DateTime(self.0.with_timezone(tz)) } + /// Formats this `DateTime` with the specified format string. pub fn format(&self, fmt: &str) -> String { format!("{}", self.0.format(fmt)) } diff --git a/src/time.rs b/src/time.rs index c107393856d8559f926658143d6a8d240ff25b33..91866660acaf01c034bd3e0e3169d53b6cba77d0 100644 --- a/src/time.rs +++ b/src/time.rs @@ -14,11 +14,13 @@ use std::convert::TryFrom; use std::str::FromStr; generate_empty_element!( + /// An entity time query. TimeQuery, "time", TIME ); impl IqGetPayload for TimeQuery {} +/// An entity time result, containing an unique DateTime. #[derive(Debug, Clone)] pub struct TimeResult(pub DateTime); From dabdc1db68330e36d85850ee6c9a31134c88c181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 22 Apr 2019 12:43:22 +0100 Subject: [PATCH 0888/1020] Fix rustdoc-args option for docs.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 08e98139f06bec43948da8873f3a6715e074fa6b..69b94a5725af853a6f10ab74c451d9cb360a5f6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,4 +31,4 @@ component = [] disable-validation = [] [package.metadata.docs.rs] -rustdoc-args = [ "--sort-modules-by-appearance", "-Z unstable-options" ] +rustdoc-args = [ "--sort-modules-by-appearance", "-Zunstable-options" ] From e6542fdb6be9702f8d4a8c2b6a60b7e1fda22a80 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 9 May 2019 16:21:28 +0200 Subject: [PATCH 0889/1020] =?UTF-8?q?ibr:=20Ignore=20size=20tests,=20HashM?= =?UTF-8?q?ap=20implementation=20changed=20and=20this=20added=2016=C2=A0by?= =?UTF-8?q?tes=20to=20every=20instance.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ibr.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ibr.rs b/src/ibr.rs index 911b800228b76fe5b2038b1e0198df01e3ae9e1a..2d41a6fa2eb1deb86b70c50a778f460155d4d621 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -120,14 +120,21 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; + // TODO: These size tests are sensible to the size of HashMap, which recently grew of two + // pointers and is thus different on stable and nightly. Let’s wait for this issue before + // attempting a fix: + // https://github.com/rust-lang/hashbrown/issues/69 + #[cfg(target_pointer_width = "32")] #[test] + #[ignore] fn test_size() { assert_size!(Query, 88); } #[cfg(target_pointer_width = "64")] #[test] + #[ignore] fn test_size() { assert_size!(Query, 152); } From 80bb6635a9e17b17bf2d601cdf78e572ab6ec7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 19 Apr 2019 23:03:50 +0100 Subject: [PATCH 0890/1020] ns: add XEP-0277 microblog namespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/ns.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ns.rs b/src/ns.rs index 978a85233d6557ffe1e4f4ddf27f2aa85175dde8..9d59435d2b48db9b92965bcf537b097f71e76292 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -135,6 +135,9 @@ pub const JINGLE_S5B: &str = "urn:xmpp:jingle:transports:s5b:1"; /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub const JINGLE_IBB: &str = "urn:xmpp:jingle:transports:ibb:1"; +/// XEP-0277: Microblogging over XMPP +pub const MICROBLOG: &str = "urn:xmpp:microblog:0"; + /// XEP-0297: Stanza Forwarding pub const FORWARD: &str = "urn:xmpp:forward:0"; From 24aef813b3913cfca2166f3db7338e2bea671cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 10 Jun 2019 22:04:03 +0200 Subject: [PATCH 0891/1020] rustfmt pass after split-jids merge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/lib.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8a243b69e4bba6a002a678f59cd084058ca3a60c..fcd6f5517bd19baa507429899b97b3f80804e87d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ impl FromStr for Jid { None => Jid::Bare(BareJid { node: ns, domain: ds, - }) + }), }) } } @@ -235,11 +235,7 @@ fn _from_str(s: &str) -> Result { } else if let ParserState::Resource = state { return Err(JidParseError::EmptyResource); } - Ok(( - node, - domain.ok_or(JidParseError::NoDomain)?, - resource, - )) + Ok((node, domain.ok_or(JidParseError::NoDomain)?, resource)) } impl FromStr for FullJid { @@ -280,7 +276,7 @@ impl FullJid { FullJid { node: Some(node.into()), domain: domain.into(), - resource: resource.into() + resource: resource.into(), } } @@ -554,7 +550,10 @@ mod tests { #[test] fn can_parse_full_jids() { - assert_eq!(FullJid::from_str("a@b.c/d"), Ok(FullJid::new("a", "b.c", "d"))); + assert_eq!( + FullJid::from_str("a@b.c/d"), + Ok(FullJid::new("a", "b.c", "d")) + ); assert_eq!( FullJid::from_str("b.c/d"), Ok(FullJid { @@ -606,7 +605,10 @@ mod tests { #[test] fn bare_to_full_jid() { - assert_eq!(BareJid::new("a", "b.c").with_resource("d"), FullJid::new("a", "b.c", "d")); + assert_eq!( + BareJid::new("a", "b.c").with_resource("d"), + FullJid::new("a", "b.c", "d") + ); } #[test] @@ -615,10 +617,7 @@ mod tests { String::from(FullJid::new("a", "b", "c")), String::from("a@b/c") ); - assert_eq!( - String::from(BareJid::new("a", "b")), - String::from("a@b") - ); + assert_eq!(String::from(BareJid::new("a", "b")), String::from("a@b")); } #[test] From 6b17aacd8e9252370616c9970b49cb2ab1a6817c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 10 Jun 2019 22:06:57 +0200 Subject: [PATCH 0892/1020] Prepare for release 0.6.0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 358538efda87382fa2a0276cf4877137872a7a5c..c88c32c61212dbe6251849737b07cb3dcaf36b55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +Version 0.6.0, released 2019-06-19: + * Updates + - Jid is now an enum, with two variants, Bare(BareJid) and Full(FullJid) + Version 0.5.3, released 2019-01-16: * Updates - Link Mauve bumped the minidom dependency version. diff --git a/Cargo.toml b/Cargo.toml index b29a3c3c9e52f6d6a07b81c1fda28800b2e257d8..04fb6d4f6dd9f113411fd825f0194fa60fa40698 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.5.3" +version = "0.6.0" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From 6b1ad1ca9bd4e7f16b98d4a99c53bd9b6926f7a0 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 10 Jun 2019 22:49:57 +0200 Subject: [PATCH 0893/1020] Make the NoResource error description less ambiguous. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index fcd6f5517bd19baa507429899b97b3f80804e87d..05ea4817decbb0bec691d590e6f616e81878a306 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ pub enum JidParseError { NoDomain, /// Happens when there is no resource, that is string contains no /. - #[fail(display = "no resource found in this JID")] + #[fail(display = "no resource found in this full JID")] NoResource, /// Happens when the node is empty, that is the string starts with a @. From b12487a5b1261cf21417983307495c921c943206 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 10 Jun 2019 22:54:52 +0200 Subject: [PATCH 0894/1020] =?UTF-8?q?Standardise=20the=20casing=20of=20?= =?UTF-8?q?=E2=80=9Cfull=20JID=E2=80=9D=20and=20=E2=80=9Cbare=20JID?= =?UTF-8?q?=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 05ea4817decbb0bec691d590e6f616e81878a306..8ef7a3d17defdceac59f01d78528dffb1f7be5a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,9 +70,9 @@ impl From for String { } } -/// A struct representing a Full Jabber ID. +/// A struct representing a full Jabber ID. /// -/// A Full Jabber ID is composed of 3 components, of which one is optional: +/// A full Jabber ID is composed of 3 components, of which one is optional: /// /// - A node/name, `node`, which is the optional part before the @. /// - A domain, `domain`, which is the mandatory part after the @ but before the /. @@ -87,9 +87,9 @@ pub struct FullJid { pub resource: String, } -/// A struct representing a Bare Jabber ID. +/// A struct representing a bare Jabber ID. /// -/// A Bare Jabber ID is composed of 2 components, of which one is optional: +/// A bare Jabber ID is composed of 2 components, of which one is optional: /// /// - A node/name, `node`, which is the optional part before the @. /// - A domain, `domain`, which is the mandatory part after the @ but before the /. @@ -252,7 +252,7 @@ impl FromStr for FullJid { } impl FullJid { - /// Constructs a Full Jabber ID containing all three components. + /// Constructs a full Jabber ID containing all three components. /// /// This is of the form `node`@`domain`/`resource`. /// @@ -332,7 +332,7 @@ impl FullJid { } } - /// Constructs a Full Jabber ID from a Bare Jabber ID, specifying a `resource`. + /// Constructs a full Jabber ID from a bare Jabber ID, specifying a `resource`. /// /// # Examples /// @@ -372,7 +372,7 @@ impl FromStr for BareJid { } impl BareJid { - /// Constructs a Bare Jabber ID, containing two components. + /// Constructs a bare Jabber ID, containing two components. /// /// This is of the form `node`@`domain`. /// @@ -397,7 +397,7 @@ impl BareJid { } } - /// Constructs a Bare Jabber ID containing only a `domain`. + /// Constructs a bare Jabber ID containing only a `domain`. /// /// This is of the form `domain`. /// @@ -471,7 +471,7 @@ impl BareJid { } } - /// Constructs a Full Jabber ID from a Bare Jabber ID, specifying a `resource`. + /// Constructs a full Jabber ID from a bare Jabber ID, specifying a `resource`. /// /// # Examples /// From d7a74b2f28b6812ec8ae1889151fbda105c607d7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 10 Jun 2019 22:55:15 +0200 Subject: [PATCH 0895/1020] Specify what is a bare and a full JID, and when to use something else. --- src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 8ef7a3d17defdceac59f01d78528dffb1f7be5a8..ad9ac023852cc0c3d8e36c88401f346772167d80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,9 @@ impl From for String { /// - A node/name, `node`, which is the optional part before the @. /// - A domain, `domain`, which is the mandatory part after the @ but before the /. /// - A resource, `resource`, which is the part after the /. +/// +/// Unlike a `BareJid`, it always contains a resource, and should only be used when you are certain +/// there is no case where a resource can be missing. Otherwise, use a `Jid` enum. #[derive(Clone, PartialEq, Eq, Hash)] pub struct FullJid { /// The node part of the Jabber ID, if it exists, else None. @@ -92,7 +95,10 @@ pub struct FullJid { /// A bare Jabber ID is composed of 2 components, of which one is optional: /// /// - A node/name, `node`, which is the optional part before the @. -/// - A domain, `domain`, which is the mandatory part after the @ but before the /. +/// - A domain, `domain`, which is the mandatory part after the @. +/// +/// Unlike a `FullJid`, it can’t contain a resource, and should only be used when you are certain +/// there is no case where a resource can be set. Otherwise, use a `Jid` enum. #[derive(Clone, PartialEq, Eq, Hash)] pub struct BareJid { /// The node part of the Jabber ID, if it exists, else None. From e2b5696beb9a81290dcc1bc8973070bf887eb2c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 10 Jun 2019 22:55:53 +0200 Subject: [PATCH 0896/1020] Add BareJid and FullJid to the ChangeLog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c88c32c61212dbe6251849737b07cb3dcaf36b55..ccd5e619161d046d1598df23d004ff77c0929411 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ Version 0.6.0, released 2019-06-19: * Updates - Jid is now an enum, with two variants, Bare(BareJid) and Full(FullJid) + - BareJid and FullJid are two specialised variants of a JID. Version 0.5.3, released 2019-01-16: * Updates From 20a7d4fc5505395a5a6b50f2fefc21b0430451d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 10 Jun 2019 23:09:20 +0200 Subject: [PATCH 0897/1020] Switch from LGPLv3 to MPL-2.0. --- COPYING | 675 ------------------------------------------------- COPYING.LESSER | 166 ------------ Cargo.toml | 2 +- LICENSE | 373 +++++++++++++++++++++++++++ README.md | 20 +- src/lib.rs | 10 + 6 files changed, 385 insertions(+), 861 deletions(-) delete mode 100644 COPYING delete mode 100644 COPYING.LESSER create mode 100644 LICENSE diff --git a/COPYING b/COPYING deleted file mode 100644 index a737dcfed5db21fb99a8fcb812e995937d38401b..0000000000000000000000000000000000000000 --- a/COPYING +++ /dev/null @@ -1,675 +0,0 @@ - - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/COPYING.LESSER b/COPYING.LESSER deleted file mode 100644 index 5f5ff16a4a0f6104fadb6a5beef527573e46b425..0000000000000000000000000000000000000000 --- a/COPYING.LESSER +++ /dev/null @@ -1,166 +0,0 @@ - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/Cargo.toml b/Cargo.toml index 04fb6d4f6dd9f113411fd825f0194fa60fa40698..633207237c7b988a6e57270faa9122f473a825e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://gitlab.com/xmpp-rs/jid-rs" documentation = "https://docs.rs/jid" readme = "README.md" keywords = ["xmpp", "jid"] -license = "LGPL-3.0+" +license = "MPL-2.0" edition = "2018" [badges] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..14e2f777f6c395e7e04ab4aa306bbcc4b0c1120e --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/README.md b/README.md index e4b4fd370f7172f0fc2bed9773a095d2178fef54..905c3c3a70d45598297eb2bac8450c074ce78c1d 100644 --- a/README.md +++ b/README.md @@ -10,27 +10,9 @@ can of course use this. What license is it under? ------------------------- -LGPLv3 or later. See `COPYING` and `COPYING.LESSER`. +MPL-2.0 or later, see the `LICENSE` file. Notes ----- This library does not yet implement RFC7622. - -License yadda yadda. --------------------- - - Copyright 2017, jid-rs contributors. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . diff --git a/src/lib.rs b/src/lib.rs index ad9ac023852cc0c3d8e36c88401f346772167d80..29982c714a19b798b7db69b5f99ba3ab475f2a0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,13 @@ +// Copyright (c) 2017, 2018 lumi +// Copyright (c) 2017, 2018, 2019 Emmanuel Gil Peyrot +// Copyright (c) 2017, 2018, 2019 Maxime “pep” Buquet +// Copyright (c) 2017, 2018 Astro +// Copyright (c) 2017 Bastien Orivel +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + #![deny(missing_docs)] //! Provides a type for Jabber IDs. From 8ca35d81a450656d5178788ba8f51578e9d25371 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 10 Jun 2019 23:12:57 +0200 Subject: [PATCH 0898/1020] Fix CHANGELOG date for 0.6.0. --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccd5e619161d046d1598df23d004ff77c0929411..642a521da05487c4ef185dcf9709540c34d851ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ -Version 0.6.0, released 2019-06-19: +Version 0.6.0, released 2019-06-10: * Updates - - Jid is now an enum, with two variants, Bare(BareJid) and Full(FullJid) + - Jid is now an enum, with two variants, Bare(BareJid) and Full(FullJid). - BareJid and FullJid are two specialised variants of a JID. Version 0.5.3, released 2019-01-16: From 78b0d016f125d60727c1d9a5756df85b493749b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 10 Jun 2019 23:17:24 +0200 Subject: [PATCH 0899/1020] Release version 0.6.1, with the MPL-2.0 relicense. --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 642a521da05487c4ef185dcf9709540c34d851ca..34a0a9b1e1a7d360eece46ca6f20c220124d047c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +Version 0.6.1, released 2019-06-10: + * Updates + - Change the license from LGPLv3 to MPL-2.0. + Version 0.6.0, released 2019-06-10: * Updates - Jid is now an enum, with two variants, Bare(BareJid) and Full(FullJid). diff --git a/Cargo.toml b/Cargo.toml index 633207237c7b988a6e57270faa9122f473a825e9..8a211a972da3a96843516f04fb603ad28826b821 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.6.0" +version = "0.6.1" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From 1e3f940db9f812591d7b34f9e68989b9079ec6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 10 Jun 2019 23:17:09 +0200 Subject: [PATCH 0900/1020] Update jid dependency to 0.6.0: Jid split change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- src/bind.rs | 6 +++--- src/blocking.rs | 11 +++++------ src/bookmarks.rs | 6 +++--- src/lib.rs | 6 +++--- src/muc/user.rs | 10 +++++----- src/roster.rs | 14 +++++++------- src/stream.rs | 12 ++++++------ src/websocket.rs | 10 +++++----- 9 files changed, 38 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 69b94a5725af853a6f10ab74c451d9cb360a5f6f..b1e84c36487e3eaff92260380c7c84b53db56a6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" [dependencies] minidom = "0.10.0" -jid = { version = "0.5.3", features = ["minidom"] } +jid = { version = "0.6.0", features = ["minidom"] } base64 = "0.10" digest = "0.8" sha-1 = "0.8" diff --git a/src/bind.rs b/src/bind.rs index 1d509638c8fb0545f80a996bcc81a4d8f652e69b..408c08bb56ce4e2b3612d07b394561646d4e7b38 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; -use jid::Jid; +use jid::FullJid; use minidom::Element; use std::str::FromStr; use std::convert::TryFrom; @@ -26,7 +26,7 @@ pub enum Bind { Resource(String), /// The full JID returned by the server for this client. - Jid(Jid), + Jid(FullJid), } impl Bind { @@ -61,7 +61,7 @@ impl TryFrom for Bind { } else if child.is("jid", ns::BIND) { check_no_attributes!(child, "jid"); check_no_children!(child, "jid"); - bind = Bind::Jid(Jid::from_str(&child.text())?); + bind = Bind::Jid(FullJid::from_str(&child.text())?); } else { return Err(Error::ParseError("Unknown element in bind.")); } diff --git a/src/blocking.rs b/src/blocking.rs index b44c4111936b1c2af458f09bc8383c2a01f6351a..f218b21d4985dd4894d56d1c3e1e9dcc34c16902 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -101,6 +101,7 @@ generate_empty_element!( #[cfg(test)] mod tests { use super::*; + use jid::BareJid; #[cfg(target_pointer_width = "32")] #[test] @@ -143,16 +144,14 @@ mod tests { fn test_items() { let elem: Element = "".parse().unwrap(); let two_items = vec![ - Jid { + Jid::Bare(BareJid { node: Some(String::from("coucou")), domain: String::from("coucou"), - resource: None, - }, - Jid { + }), + Jid::Bare(BareJid { node: None, domain: String::from("domain"), - resource: None, - }, + }), ]; let result_elem = elem.clone(); diff --git a/src/bookmarks.rs b/src/bookmarks.rs index d8b5a9ccc1bbce7141144df5ca699ef1e2bd9754..1424686b0fcbf7a75402e3ff51b348a4d74f29cd 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use jid::Jid; +use jid::BareJid; generate_attribute!( /// Whether a conference bookmark should be joined automatically. @@ -21,7 +21,7 @@ generate_element!( autojoin: Default = "autojoin", /// The JID of the conference. - jid: Required = "jid", + jid: Required = "jid", /// A user-defined name for this conference. name: Required = "name", @@ -113,7 +113,7 @@ mod tests { assert_eq!(storage.conferences[0].autojoin, Autojoin::True); assert_eq!( storage.conferences[0].jid, - Jid::bare("test-muc", "muc.localhost") + BareJid::new("test-muc", "muc.localhost") ); assert_eq!(storage.conferences[0].name, "Test MUC"); assert_eq!(storage.conferences[0].clone().nick.unwrap(), "Coucou"); diff --git a/src/lib.rs b/src/lib.rs index ebeddf2faf51dc7de0388affc41c9698a4b359b7..6863afec8de05478a5f207251cf55c3cb75b37ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,8 +15,8 @@ //! [`TryFrom`]: ../try_from/trait.TryFrom.html //! [`Element`]: ../minidom/element/struct.Element.html -// Copyright (c) 2017-2018 Emmanuel Gil Peyrot -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017-2019 Emmanuel Gil Peyrot +// Copyright (c) 2017-2019 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -25,7 +25,7 @@ #![deny(missing_docs)] pub use minidom::Element; -pub use jid::{Jid, JidParseError}; +pub use jid::{Jid, BareJid, FullJid, JidParseError}; pub use std::convert::{TryFrom, TryInto}; pub use crate::util::error::Error; diff --git a/src/muc/user.rs b/src/muc/user.rs index bc8012d6b90801239f25601358ed25578ac55eca..3621450ab89263a396d5a77a0c417d40a286aa18 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -7,7 +7,7 @@ use crate::util::error::Error; use crate::ns; -use jid::Jid; +use jid::FullJid; use minidom::Element; use std::convert::TryFrom; @@ -82,7 +82,7 @@ Status, "status", MUC_USER, "code", { #[derive(Debug, Clone, PartialEq)] pub enum Actor { /// The full JID associated with this user. - Jid(Jid), + Jid(FullJid), /// The nickname of this user. Nick(String), @@ -95,7 +95,7 @@ impl TryFrom for Actor { check_self!(elem, "actor", MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); check_no_children!(elem, "actor"); - let jid: Option = get_attr!(elem, "jid", Option); + let jid: Option = get_attr!(elem, "jid", Option); let nick = get_attr!(elem, "nick", Option); match (jid, nick) { @@ -190,7 +190,7 @@ generate_element!( affiliation: Required = "affiliation", /// The real JID of this user, if you are allowed to see it. - jid: Option = "jid", + jid: Option = "jid", /// The current nickname of this user. nick: Option = "nick", @@ -448,7 +448,7 @@ mod tests { Actor::Jid(jid) => jid, _ => panic!(), }; - assert_eq!(jid, "foo@bar/baz".parse::().unwrap()); + assert_eq!(jid, "foo@bar/baz".parse::().unwrap()); } #[test] diff --git a/src/roster.rs b/src/roster.rs index 65c1fdf57f593b6a927728dfb1a550c99241e62b..dc615c786f3a6174d15145b8421f14c6766bc291 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -5,7 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; -use jid::Jid; +use jid::BareJid; generate_elem_id!( /// Represents a group a contact is part of. @@ -50,7 +50,7 @@ generate_element!( Item, "item", ROSTER, attributes: [ /// JID of this contact. - jid: Required = "jid", + jid: Required = "jid", /// Name of this contact. name: OptionEmpty = "name", @@ -172,7 +172,7 @@ mod tests { assert_eq!(roster.items.len(), 4); assert_eq!( roster.items[0].jid, - Jid::from_str("romeo@example.net").unwrap() + BareJid::from_str("romeo@example.net").unwrap() ); assert_eq!(roster.items[0].name, Some(String::from("Romeo"))); assert_eq!(roster.items[0].subscription, Subscription::Both); @@ -184,7 +184,7 @@ mod tests { assert_eq!( roster.items[3].jid, - Jid::from_str("contact@example.org").unwrap() + BareJid::from_str("contact@example.org").unwrap() ); assert_eq!(roster.items[3].name, Some(String::from("MyContact"))); assert_eq!(roster.items[3].subscription, Subscription::None); @@ -213,7 +213,7 @@ mod tests { assert_eq!(roster.items.len(), 1); assert_eq!( roster.items[0].jid, - Jid::from_str("test@example.org").unwrap() + BareJid::from_str("test@example.org").unwrap() ); assert_eq!(roster.items[0].name, None); assert_eq!(roster.items[0].groups.len(), 2); @@ -248,7 +248,7 @@ mod tests { assert_eq!(roster.items.len(), 1); assert_eq!( roster.items[0].jid, - Jid::from_str("nurse@example.com").unwrap() + BareJid::from_str("nurse@example.com").unwrap() ); assert_eq!(roster.items[0].name, Some(String::from("Nurse"))); assert_eq!(roster.items[0].groups.len(), 1); @@ -270,7 +270,7 @@ mod tests { assert_eq!(roster.items.len(), 1); assert_eq!( roster.items[0].jid, - Jid::from_str("nurse@example.com").unwrap() + BareJid::from_str("nurse@example.com").unwrap() ); assert!(roster.items[0].name.is_none()); assert!(roster.items[0].groups.is_empty()); diff --git a/src/stream.rs b/src/stream.rs index 1d4715f2d1c164cfc158500f12348bccfd808ab1..021b64251c897d6b68bc135a0a5d827f309b8025 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -4,17 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use jid::Jid; +use jid::BareJid; generate_element!( /// The stream opening for client-server communications. Stream, "stream", STREAM, attributes: [ /// The JID of the entity opening this stream. - from: Option = "from", + from: Option = "from", /// The JID of the entity receiving this stream opening. - to: Option = "to", + to: Option = "to", /// The id of the stream, used for authentication challenges. id: Option = "id", @@ -30,7 +30,7 @@ generate_element!( impl Stream { /// Creates a simple client→server `` element. - pub fn new(to: Jid) -> Stream { + pub fn new(to: BareJid) -> Stream { Stream { from: None, to: Some(to), @@ -42,7 +42,7 @@ impl Stream { /// Sets the [@from](#structfield.from) attribute on this `` /// element. - pub fn with_from(mut self, from: Jid) -> Stream { + pub fn with_from(mut self, from: BareJid) -> Stream { self.from = Some(from); self } @@ -92,7 +92,7 @@ mod tests { fn test_simple() { let elem: Element = "".parse().unwrap(); let stream = Stream::try_from(elem).unwrap(); - assert_eq!(stream.from, Some(Jid::domain("some-server.example"))); + assert_eq!(stream.from, Some(BareJid::domain("some-server.example"))); assert_eq!(stream.to, None); assert_eq!(stream.id, Some(String::from("abc"))); assert_eq!(stream.version, Some(String::from("1.0"))); diff --git a/src/websocket.rs b/src/websocket.rs index b8c02e2b44f736f76c9447e97d25ebcef6e16bcb..dc2670f74ed100744eec3b3dc76d6255be7c8a0d 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -4,17 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use jid::Jid; +use jid::BareJid; generate_element!( /// The stream opening for WebSocket. Open, "open", WEBSOCKET, attributes: [ /// The JID of the entity opening this stream. - from: Option = "from", + from: Option = "from", /// The JID of the entity receiving this stream opening. - to: Option = "to", + to: Option = "to", /// The id of the stream, used for authentication challenges. id: Option = "id", @@ -30,7 +30,7 @@ generate_element!( impl Open { /// Creates a simple client→server `` element. - pub fn new(to: Jid) -> Open { + pub fn new(to: BareJid) -> Open { Open { from: None, to: Some(to), @@ -42,7 +42,7 @@ impl Open { /// Sets the [@from](#structfield.from) attribute on this `` /// element. - pub fn with_from(mut self, from: Jid) -> Open { + pub fn with_from(mut self, from: BareJid) -> Open { self.from = Some(from); self } From 3178aaa1cbb2177b94513b2cef65db899d8d4229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 22 Apr 2019 11:24:11 +0100 Subject: [PATCH 0901/1020] Update test_size tests 64bit archs with jid-rs change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/bookmarks.rs | 2 +- src/delay.rs | 2 +- src/disco.rs | 2 +- src/forwarding.rs | 2 +- src/iq.rs | 4 ++-- src/jingle.rs | 2 +- src/jingle_s5b.rs | 2 +- src/mam.rs | 2 +- src/message.rs | 2 +- src/presence.rs | 2 +- src/roster.rs | 2 +- src/stanza_error.rs | 2 +- src/stanza_id.rs | 2 +- src/stream.rs | 2 +- src/websocket.rs | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 1424686b0fcbf7a75402e3ff51b348a4d74f29cd..b40482c6aa4b3c683069ec32cd56e150b6a99c07 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -85,7 +85,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Conference, 152); + assert_size!(Conference, 128); assert_size!(Url, 48); assert_size!(Storage, 48); } diff --git a/src/delay.rs b/src/delay.rs index 97a32d2598933c069a0215d152e42ae5ae4b93c4..892578c2e8619b5aa35d596f0b6e3bf21eb620c0 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -46,7 +46,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Delay, 112); + assert_size!(Delay, 120); } #[test] diff --git a/src/disco.rs b/src/disco.rs index 9d8781dcdb95d99ae78f7178b68e6620c5e66fba..2cacc2a752510d559e7378ef621c3a4d36026d1a 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -256,7 +256,7 @@ mod tests { assert_size!(DiscoInfoQuery, 24); assert_size!(DiscoInfoResult, 96); - assert_size!(Item, 120); + assert_size!(Item, 128); assert_size!(DiscoItemsQuery, 24); assert_size!(DiscoItemsResult, 48); } diff --git a/src/forwarding.rs b/src/forwarding.rs index 4adb47964422e79070a494dcb8b1264c567e7ced..906f016e9ea95c3c0ac1e4ba70aadb4de473e866 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -39,7 +39,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Forwarded, 392); + assert_size!(Forwarded, 408); } #[test] diff --git a/src/iq.rs b/src/iq.rs index c5a195d404b68001960cb65b2edc20791a79c1fe..68d585d087a393311608094a4dcb5aadbf742f8f 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -232,8 +232,8 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(IqType, 216); - assert_size!(Iq, 384); + assert_size!(IqType, 224); + assert_size!(Iq, 408); } #[test] diff --git a/src/jingle.rs b/src/jingle.rs index ed4fe4a8609e920d87f99e4b28c0a92b610986d1..14574fdb47d84d7f0bdf0454140f7d8eef247447 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -579,7 +579,7 @@ mod tests { assert_size!(Reason, 1); assert_size!(ReasonElement, 32); assert_size!(SessionId, 24); - assert_size!(Jingle, 256); + assert_size!(Jingle, 272); } #[test] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index 0aa797211481d754a269176c9a86019754516659..a34b5e319b43a6553a90f0a4a5ed449ae27a92d5 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -297,7 +297,7 @@ mod tests { assert_size!(Mode, 1); assert_size!(CandidateId, 24); assert_size!(StreamId, 24); - assert_size!(Candidate, 128); + assert_size!(Candidate, 136); assert_size!(TransportPayload, 32); assert_size!(Transport, 88); } diff --git a/src/mam.rs b/src/mam.rs index bd97ed9902943262f05fc1ef94eabae70ac7cdb9..f0bd0448b30ee1423dba5a240e4149185c75a594 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -211,7 +211,7 @@ mod tests { fn test_size() { assert_size!(QueryId, 24); assert_size!(Query, 232); - assert_size!(Result_, 440); + assert_size!(Result_, 456); assert_size!(Complete, 1); assert_size!(Fin, 88); assert_size!(DefaultPrefs, 1); diff --git a/src/message.rs b/src/message.rs index 582c7f1a70e7763d53c2748302b35b20e544e418..fb11e91d98ef614476e2c3ac49c69fd813ef0a2c 100644 --- a/src/message.rs +++ b/src/message.rs @@ -274,7 +274,7 @@ mod tests { assert_size!(Body, 24); assert_size!(Subject, 24); assert_size!(Thread, 24); - assert_size!(Message, 272); + assert_size!(Message, 288); } #[test] diff --git a/src/presence.rs b/src/presence.rs index de2b77c37dd54423c79cceb436c5c24334d32d09..b4e1a1e046b2f36761a560ce175c6edad26a116a 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -376,7 +376,7 @@ mod tests { fn test_size() { assert_size!(Show, 1); assert_size!(Type, 1); - assert_size!(Presence, 224); + assert_size!(Presence, 240); } #[test] diff --git a/src/roster.rs b/src/roster.rs index dc615c786f3a6174d15145b8421f14c6766bc291..073ecfa7e770462e92ec9f519a306d87c04fa05d 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -114,7 +114,7 @@ mod tests { assert_size!(Group, 24); assert_size!(Subscription, 1); assert_size!(Ask, 1); - assert_size!(Item, 128); + assert_size!(Item, 104); assert_size!(Roster, 48); } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index f2ae500e2592ed59662542e9e20d839bd2c14e8c..18087bafa385e5265c0f0cc2647bcc44a392b461 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -324,7 +324,7 @@ mod tests { fn test_size() { assert_size!(ErrorType, 1); assert_size!(DefinedCondition, 1); - assert_size!(StanzaError, 208); + assert_size!(StanzaError, 216); } #[test] diff --git a/src/stanza_id.rs b/src/stanza_id.rs index 2178d7a94014034f91b14f3d8c7d6859ed31265d..eac29085f0a828818ef448f1b0a3f9cef6958111 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -52,7 +52,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(StanzaId, 96); + assert_size!(StanzaId, 104); assert_size!(OriginId, 24); } diff --git a/src/stream.rs b/src/stream.rs index 021b64251c897d6b68bc135a0a5d827f309b8025..112f1b94ac4572f9176009eb6802c9fd3b445bdf 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -85,7 +85,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Stream, 216); + assert_size!(Stream, 168); } #[test] diff --git a/src/websocket.rs b/src/websocket.rs index dc2670f74ed100744eec3b3dc76d6255be7c8a0d..d94d81fd1f89c4321ca1be9effa36f891dba788d 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -84,7 +84,7 @@ mod tests { #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Open, 216); + assert_size!(Open, 168); } #[test] From bc480f8e7c966a4c9bdc1a97896268c5e78771fd Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 22 Apr 2019 14:34:25 +0200 Subject: [PATCH 0902/1020] Update test_size tests for 32-bit architectures --- src/bookmarks.rs | 2 +- src/delay.rs | 2 +- src/disco.rs | 2 +- src/forwarding.rs | 2 +- src/iq.rs | 4 ++-- src/jingle.rs | 2 +- src/jingle_s5b.rs | 2 +- src/mam.rs | 2 +- src/message.rs | 2 +- src/presence.rs | 2 +- src/roster.rs | 2 +- src/stanza_error.rs | 2 +- src/stanza_id.rs | 2 +- src/stream.rs | 2 +- src/websocket.rs | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/bookmarks.rs b/src/bookmarks.rs index b40482c6aa4b3c683069ec32cd56e150b6a99c07..c00195a526bd6b8568d94fe241e5660916a8d854 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -77,7 +77,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Conference, 76); + assert_size!(Conference, 64); assert_size!(Url, 24); assert_size!(Storage, 24); } diff --git a/src/delay.rs b/src/delay.rs index 892578c2e8619b5aa35d596f0b6e3bf21eb620c0..e987a4f0e013352c8221ce586f4c67fc49d74df9 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -40,7 +40,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Delay, 64); + assert_size!(Delay, 68); } #[cfg(target_pointer_width = "64")] diff --git a/src/disco.rs b/src/disco.rs index 2cacc2a752510d559e7378ef621c3a4d36026d1a..abf0e238f152afcdab91ca8eb270d9cc32e4f7aa 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -243,7 +243,7 @@ mod tests { assert_size!(DiscoInfoQuery, 12); assert_size!(DiscoInfoResult, 48); - assert_size!(Item, 60); + assert_size!(Item, 64); assert_size!(DiscoItemsQuery, 12); assert_size!(DiscoItemsResult, 24); } diff --git a/src/forwarding.rs b/src/forwarding.rs index 906f016e9ea95c3c0ac1e4ba70aadb4de473e866..5286323cfe1bcb21228b886c230acd69f8ca7436 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -33,7 +33,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Forwarded, 204); + assert_size!(Forwarded, 212); } #[cfg(target_pointer_width = "64")] diff --git a/src/iq.rs b/src/iq.rs index 68d585d087a393311608094a4dcb5aadbf742f8f..c8d62257fb70e882b3b39f372b963ddb2025a277 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -225,8 +225,8 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(IqType, 108); - assert_size!(Iq, 192); + assert_size!(IqType, 112); + assert_size!(Iq, 204); } #[cfg(target_pointer_width = "64")] diff --git a/src/jingle.rs b/src/jingle.rs index 14574fdb47d84d7f0bdf0454140f7d8eef247447..98e5fcbb46db4620ed82b2efae8a6a4026ba5892 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -564,7 +564,7 @@ mod tests { assert_size!(Reason, 1); assert_size!(ReasonElement, 16); assert_size!(SessionId, 12); - assert_size!(Jingle, 128); + assert_size!(Jingle, 136); } #[cfg(target_pointer_width = "64")] diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index a34b5e319b43a6553a90f0a4a5ed449ae27a92d5..969b83f30ed562ef354660cecea560ab88786a5b 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -285,7 +285,7 @@ mod tests { assert_size!(Mode, 1); assert_size!(CandidateId, 12); assert_size!(StreamId, 12); - assert_size!(Candidate, 80); + assert_size!(Candidate, 84); assert_size!(TransportPayload, 16); assert_size!(Transport, 44); } diff --git a/src/mam.rs b/src/mam.rs index f0bd0448b30ee1423dba5a240e4149185c75a594..6889f2edf85cd5d1139748b7cf2aa893c15cc570 100644 --- a/src/mam.rs +++ b/src/mam.rs @@ -199,7 +199,7 @@ mod tests { fn test_size() { assert_size!(QueryId, 12); assert_size!(Query, 116); - assert_size!(Result_, 228); + assert_size!(Result_, 236); assert_size!(Complete, 1); assert_size!(Fin, 44); assert_size!(DefaultPrefs, 1); diff --git a/src/message.rs b/src/message.rs index fb11e91d98ef614476e2c3ac49c69fd813ef0a2c..f9bae2b16ee3351555b469534481f18f79324a14 100644 --- a/src/message.rs +++ b/src/message.rs @@ -264,7 +264,7 @@ mod tests { assert_size!(Body, 12); assert_size!(Subject, 12); assert_size!(Thread, 12); - assert_size!(Message, 136); + assert_size!(Message, 144); } #[cfg(target_pointer_width = "64")] diff --git a/src/presence.rs b/src/presence.rs index b4e1a1e046b2f36761a560ce175c6edad26a116a..e501f8839a4a5149470132450da4b70ecd554326 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -368,7 +368,7 @@ mod tests { fn test_size() { assert_size!(Show, 1); assert_size!(Type, 1); - assert_size!(Presence, 112); + assert_size!(Presence, 120); } #[cfg(target_pointer_width = "64")] diff --git a/src/roster.rs b/src/roster.rs index 073ecfa7e770462e92ec9f519a306d87c04fa05d..6c871ba651d84cfaf9f22668143c68ed732e3219 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -104,7 +104,7 @@ mod tests { assert_size!(Group, 12); assert_size!(Subscription, 1); assert_size!(Ask, 1); - assert_size!(Item, 64); + assert_size!(Item, 52); assert_size!(Roster, 24); } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 18087bafa385e5265c0f0cc2647bcc44a392b461..db9230ebbddee84e8035c673291404c3365fa886 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -316,7 +316,7 @@ mod tests { fn test_size() { assert_size!(ErrorType, 1); assert_size!(DefinedCondition, 1); - assert_size!(StanzaError, 104); + assert_size!(StanzaError, 108); } #[cfg(target_pointer_width = "64")] diff --git a/src/stanza_id.rs b/src/stanza_id.rs index eac29085f0a828818ef448f1b0a3f9cef6958111..bbbfb48f2c5bfbfef5f0f4baba3946c3b1d522e0 100644 --- a/src/stanza_id.rs +++ b/src/stanza_id.rs @@ -45,7 +45,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(StanzaId, 48); + assert_size!(StanzaId, 52); assert_size!(OriginId, 12); } diff --git a/src/stream.rs b/src/stream.rs index 112f1b94ac4572f9176009eb6802c9fd3b445bdf..e6f5be009b0dfce49565f359c4b193f4dd43c589 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -79,7 +79,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Stream, 108); + assert_size!(Stream, 84); } #[cfg(target_pointer_width = "64")] diff --git a/src/websocket.rs b/src/websocket.rs index d94d81fd1f89c4321ca1be9effa36f891dba788d..1092423a10cddb9612823af7865ff81d62774b97 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -78,7 +78,7 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Open, 108); + assert_size!(Open, 84); } #[cfg(target_pointer_width = "64")] From 956193e0daa217a780360683fbf13f621adc6ef6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 12 Jun 2019 16:29:55 +0200 Subject: [PATCH 0903/1020] Fix all bare_trait_objects warnings, and deny them. --- src/client/auth.rs | 6 +++--- src/client/mod.rs | 2 +- src/component/mod.rs | 2 +- src/error.rs | 2 +- src/lib.rs | 2 +- src/xmpp_codec.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/client/auth.rs b/src/client/auth.rs index d961f7a0988b1b26ef11b99038e8bebab312d499..81210dc7539c85ebfada329cc7b8accec7f404e4 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -16,12 +16,12 @@ use crate::{AuthError, Error, ProtocolError}; const NS_XMPP_SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; pub struct ClientAuth { - future: Box, Error = Error>>, + future: Box, Error = Error>>, } impl ClientAuth { pub fn new(stream: XMPPStream, creds: Credentials) -> Result { - let local_mechs: Vec Box>> = vec![ + let local_mechs: Vec Box>> = vec![ Box::new(|| Box::new(Scram::::from_credentials(creds.clone()).unwrap())), Box::new(|| Box::new(Scram::::from_credentials(creds.clone()).unwrap())), Box::new(|| Box::new(Plain::from_credentials(creds.clone()).unwrap())), @@ -62,7 +62,7 @@ impl ClientAuth { Err(AuthError::NoMechanism)? } - fn handle_challenge(stream: XMPPStream, mut mechanism: Box) -> Box, Error = Error>> { + fn handle_challenge(stream: XMPPStream, mut mechanism: Box) -> Box, Error = Error>> { Box::new( stream.into_future() .map_err(|(e, _stream)| e.into()) diff --git a/src/client/mod.rs b/src/client/mod.rs index 99965dc8ec695e4a1ee09a850dd343873a7e3182..37085788976a041c07b8339b991ddea0c0f849c7 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -33,7 +33,7 @@ const NS_JABBER_CLIENT: &str = "jabber:client"; enum ClientState { Invalid, Disconnected, - Connecting(Box>), + Connecting(Box>), Connected(XMPPStream), } diff --git a/src/component/mod.rs b/src/component/mod.rs index cfc1ee5e2452504a6a39daa06fa26b3e0a61db5f..8738111780499591c021ffa9a9693d55d8b8c322 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -30,7 +30,7 @@ const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; enum ComponentState { Invalid, Disconnected, - Connecting(Box>), + Connecting(Box>), Connected(XMPPStream), } diff --git a/src/error.rs b/src/error.rs index 375c5178c6c4ff908c5a8bde3af12227b025ef6f..d8b2063d473b44039e3d271472d5f27cb855ec6f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -59,7 +59,7 @@ impl StdError for ParseError { fn description(&self) -> &str { self.0.as_ref() } - fn cause(&self) -> Option<&StdError> { + fn cause(&self) -> Option<&dyn StdError> { None } } diff --git a/src/lib.rs b/src/lib.rs index bfed02a5029575f7f05770728c8130f82452d3bf..07ae878437455e1694c95b9cf41c8582ba73014f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![deny(unsafe_code, unused, missing_docs)] +#![deny(unsafe_code, unused, missing_docs, bare_trait_objects)] //! XMPP implementation with asynchronous I/O using Tokio. diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 482cd78694603e912caa905258582aea65cdc8b3..3920ba416145de670773b7c2ff53a2aaec4e7305 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -219,7 +219,7 @@ impl Decoder for XMPPCodec { type Error = ParserError; fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { - let buf1: Box> = if !self.buf.is_empty() && !buf.is_empty() { + let buf1: Box> = if !self.buf.is_empty() && !buf.is_empty() { let mut prefix = std::mem::replace(&mut self.buf, vec![]); prefix.extend_from_slice(buf.take().as_ref()); Box::new(prefix) From c6ab5a22b89467b1390247a18fc93f487e29d197 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 12 Jun 2019 16:44:13 +0200 Subject: [PATCH 0904/1020] Update xml5ever. --- Cargo.toml | 2 +- src/xmpp_codec.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3bcdbd2ce289807fda144beeb7c1a99cb5151241..db18052106adb775cc50b474b8f01a3e59703f5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ tokio = "0.1" tokio-io = "0.1" tokio-codec = "0.1" bytes = "0.4" -xml5ever = "0.12" +xml5ever = "0.14" native-tls = "0.2" tokio-tls = "0.2" sasl = "0.4" diff --git a/src/xmpp_codec.rs b/src/xmpp_codec.rs index 3920ba416145de670773b7c2ff53a2aaec4e7305..0002087361b4d9341e630b15afc772b90642f813 100644 --- a/src/xmpp_codec.rs +++ b/src/xmpp_codec.rs @@ -18,6 +18,7 @@ use std::borrow::Cow; use tokio_codec::{Decoder, Encoder}; use xml5ever::interface::Attribute; use xml5ever::tokenizer::{Tag, TagKind, Token, TokenSink, XmlTokenizer}; +use xml5ever::buffer_queue::BufferQueue; /// Anything that can be sent or received on an XMPP/XML stream #[derive(Debug, Clone, PartialEq, Eq)] @@ -231,8 +232,10 @@ impl Decoder for XMPPCodec { Ok(s) => { if !s.is_empty() { // println!("<< {}", s); + let mut buffer_queue = BufferQueue::new(); let tendril = FromIterator::from_iter(s.chars()); - self.parser.feed(tendril); + buffer_queue.push_back(tendril); + self.parser.feed(&mut buffer_queue); } } // Remedies for truncated utf8 From 1eaf06bf1cd97d1cab5d01fdbd1114a967002b52 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 12 Jun 2019 16:48:39 +0200 Subject: [PATCH 0905/1020] Update trust-dns. --- Cargo.toml | 4 ++-- src/happy_eyeballs.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index db18052106adb775cc50b474b8f01a3e59703f5f..a62b4ba1d696ff9e8e4047315844274c9af2b1e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,8 @@ xml5ever = "0.14" native-tls = "0.2" tokio-tls = "0.2" sasl = "0.4" -trust-dns-resolver = "0.10" -trust-dns-proto = "0.6" +trust-dns-resolver = "0.11" +trust-dns-proto = "0.7" xmpp-parsers = "0.13" idna = "0.1" quick-xml = "0.13" diff --git a/src/happy_eyeballs.rs b/src/happy_eyeballs.rs index fc4e0280501f1444f2f8b43439d68612352d5036..7696f65e2cfa42f8903259b341719f381cc3b9b3 100644 --- a/src/happy_eyeballs.rs +++ b/src/happy_eyeballs.rs @@ -79,7 +79,7 @@ impl Connecter { // Initialize state match &self_.srv_domain { &Some(ref srv_domain) => { - let srv_lookup = resolver.lookup_srv(srv_domain); + let srv_lookup = resolver.lookup_srv(srv_domain.clone()); self_.state = State::ResolveSrv(resolver, srv_lookup); } None => { From aacfcc58b9c7a44bfa9932740bf5bd3781718cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 12 Jun 2019 18:22:51 +0200 Subject: [PATCH 0906/1020] Change unnecessary mut variable to default immutable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/element.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/element.rs b/src/element.rs index a94c1cec998b0586d88a95e262adaba2637729dd..47c8de96ba5589a5899e4ec9f15ef27aa1e370e4 100644 --- a/src/element.rs +++ b/src/element.rs @@ -347,14 +347,14 @@ impl Element { Event::Text(s) => { let text = s.unescape_and_decode(reader)?; if text != "" { - let mut current_elem = stack.last_mut().unwrap(); + let current_elem = stack.last_mut().unwrap(); current_elem.append_text_node(text); } }, Event::CData(s) => { let text = reader.decode(&s).into_owned(); if text != "" { - let mut current_elem = stack.last_mut().unwrap(); + let current_elem = stack.last_mut().unwrap(); current_elem.append_text_node(text); } }, From 769821141b94c295d035c85d9e813f3ecfcac030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 12 Jun 2019 18:23:32 +0200 Subject: [PATCH 0907/1020] Update quick-xml dependency to 0.14 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fa66cfe377938713b8b89f75ae8a726ce78a861d..1ffa9b7921766ed93090e186c0bdc00108caf704 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,6 @@ license = "MIT" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -quick-xml = "0.13" +quick-xml = "0.14" failure = "0.1.1" failure_derive = "0.1.1" From 019e700ab46797cae00c54603c1ba124bd64cd8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 12 Jun 2019 18:42:20 +0200 Subject: [PATCH 0908/1020] CHANGELOG: Add missing 0.10.0 entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc1fadc82edd186ee157db2277ec5d78ba611390..020f3f39cfbc0479da7a60a3ad306efd3692b1c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +Version 0.10.0, released 2018-10-21: + * Changes + * Update quick-xml to 0.13 + * Update doc to reflect switch from xml-rs to quick-xml. Version 0.9.1, released 2018-05-29: * Fixes * Lumi fixed CDATA handling, minidom will not unescape CDATA bodies anymore. From c5c330d77a9b9d92b7a40964c8e995b6ab1ddf8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 12 Jun 2019 18:43:42 +0200 Subject: [PATCH 0909/1020] Prepare for 0.11.0 release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- CHANGELOG.md | 9 +++++++++ Cargo.toml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 020f3f39cfbc0479da7a60a3ad306efd3692b1c9..65a72f9cb32445a0bee253077da633bc1c330f08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +Version 0.11.0, released 2019-06-14: + * Breaking + * Get rid of IntoElements, replace with `Into` and ` IntoIterator>` + * Fixes + * Remote unused `mut` attribute on variable + * Changes + * Update quick-xml to 0.14 + * Split Node into its own module + * Nicer Debug implementation for NamespaceSet Version 0.10.0, released 2018-10-21: * Changes * Update quick-xml to 0.13 diff --git a/Cargo.toml b/Cargo.toml index 1ffa9b7921766ed93090e186c0bdc00108caf704..231949e761f9b8c3e16819627780aefc1c5bb4f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "minidom" -version = "0.10.0" +version = "0.11.0" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From 868164700aee90d812c38bafebbeb71555153447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 26 Jun 2019 02:06:38 +0200 Subject: [PATCH 0910/1020] Fix pep email address in copyright once and for all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/iq.rs | 2 +- src/muc/mod.rs | 2 +- src/muc/muc.rs | 2 +- src/muc/user.rs | 2 +- src/ns.rs | 2 +- src/ping.rs | 2 +- src/presence.rs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/iq.rs b/src/iq.rs index c8d62257fb70e882b3b39f372b963ddb2025a277..f3aa7f5d17a8f2f477b4b8ed79bdbdc53d6d876b 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -1,5 +1,5 @@ // Copyright (c) 2017 Emmanuel Gil Peyrot -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/muc/mod.rs b/src/muc/mod.rs index a12bd565f8ee2fd77201a77325d71a298df84747..5875e3bab750c79f8dc9e48e8e4c6f8de4ef79f0 100644 --- a/src/muc/mod.rs +++ b/src/muc/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/muc/muc.rs b/src/muc/muc.rs index 5428a60278e7cd220d10275ba89882f211ac3468..a4d13f8a90a082740069319f6d1e92a041253c52 100644 --- a/src/muc/muc.rs +++ b/src/muc/muc.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // Copyright (c) 2017 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public diff --git a/src/muc/user.rs b/src/muc/user.rs index 3621450ab89263a396d5a77a0c417d40a286aa18..44af3a9c734ff689a01f8f63e6846c8bd919b88a 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // Copyright (c) 2017 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public diff --git a/src/ns.rs b/src/ns.rs index 9d59435d2b48db9b92965bcf537b097f71e76292..e89ad67a0ad5379bcc2c223f2fb030201c57c3cc 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -1,5 +1,5 @@ // Copyright (c) 2017-2018 Emmanuel Gil Peyrot -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/ping.rs b/src/ping.rs index f110088b33edf9a717437ba4b80bcdf2c522b226..be8ac48c2be8f2ef4625bc683fd89428135dda7e 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -1,5 +1,5 @@ // Copyright (c) 2017 Emmanuel Gil Peyrot -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/presence.rs b/src/presence.rs index e501f8839a4a5149470132450da4b70ecd554326..b53b6714811edfd859daf234cf8bd485c8fd53e6 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -1,5 +1,5 @@ // Copyright (c) 2017 Emmanuel Gil Peyrot -// Copyright (c) 2017 Maxime “pep” Buquet +// Copyright (c) 2017 Maxime “pep” Buquet // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this From 03a1d915a4e33d7be62a867f0e0e1a4a5ac0c7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 26 Jun 2019 01:50:52 +0200 Subject: [PATCH 0911/1020] presence: Remove Show::None and make presence.show Option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should make it easier to upgrade to minidom 0.11, to avoid having to implement an Iterator for Show, and just implement Into. It also makes a bit more sense to me semantically. Signed-off-by: Maxime “pep” Buquet --- src/presence.rs | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/presence.rs b/src/presence.rs index b53b6714811edfd859daf234cf8bd485c8fd53e6..ffb734c596be6d0b7575f85e2079b51693108cf2 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -19,9 +19,6 @@ pub trait PresencePayload: TryFrom + Into {} /// Specifies the availability of an entity or resource. #[derive(Debug, Clone, PartialEq)] pub enum Show { - /// Not an actual show value, but an indication there is no show set. - None, - /// The entity or resource is temporarily away. Away, @@ -36,12 +33,6 @@ pub enum Show { Xa, } -impl Default for Show { - fn default() -> Show { - Show::None - } -} - impl FromStr for Show { type Err = Error; @@ -59,13 +50,9 @@ impl FromStr for Show { impl IntoElements for Show { fn into_elements(self, emitter: &mut ElementEmitter) { - if self == Show::None { - return; - } emitter.append_child( Element::builder("show") .append(match self { - Show::None => unreachable!(), Show::Away => Some("away"), Show::Chat => Some("chat"), Show::Dnd => Some("dnd"), @@ -177,7 +164,7 @@ pub struct Presence { pub type_: Type, /// The availability of the sender of this presence. - pub show: Show, + pub show: Option, /// A localised list of statuses defined in this presence. pub statuses: BTreeMap, @@ -198,7 +185,7 @@ impl Presence { to: None, id: None, type_, - show: Show::None, + show: None, statuses: BTreeMap::new(), priority: 0i8, payloads: vec![], @@ -228,7 +215,7 @@ impl Presence { /// Set the availability information of this presence. pub fn with_show(mut self, show: Show) -> Presence { - self.show = show; + self.show = Some(show); self } @@ -270,7 +257,7 @@ impl TryFrom for Presence { to: get_attr!(root, "to", Option), id: get_attr!(root, "id", Option), type_: get_attr!(root, "type", Default), - show: Show::None, + show: None, statuses: BTreeMap::new(), priority: 0i8, payloads: vec![], @@ -307,9 +294,7 @@ impl TryFrom for Presence { presence.payloads.push(elem.clone()); } } - if let Some(show) = show { - presence.show = show; - } + presence.show = show; if let Some(priority) = priority { presence.priority = priority; } @@ -423,7 +408,21 @@ mod tests { .unwrap(); let presence = Presence::try_from(elem).unwrap(); assert_eq!(presence.payloads.len(), 0); - assert_eq!(presence.show, Show::Chat); + assert_eq!(presence.show, Some(Show::Chat)); + } + + #[test] + fn test_empty_show_value() { + #[cfg(not(feature = "component"))] + let elem: Element = "" + .parse() + .unwrap(); + #[cfg(feature = "component")] + let elem: Element = "" + .parse() + .unwrap(); + let presence = Presence::try_from(elem).unwrap(); + assert_eq!(presence.show, None); } #[test] From 8f0d5c7ca1368bae1c02b89250cd4df99daa459a Mon Sep 17 00:00:00 2001 From: lumi Date: Sat, 6 Jul 2019 14:55:19 +0200 Subject: [PATCH 0912/1020] Implement From and From for Jid. --- src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 29982c714a19b798b7db69b5f99ba3ab475f2a0b..2401792bc08bf3a2879721093249293ca14ea3c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,18 @@ impl From for String { } } +impl From for Jid { + fn from(bare_jid: BareJid) -> Jid { + Jid::Bare(bare_jid) + } +} + +impl From for Jid { + fn from(full_jid: FullJid) -> Jid { + Jid::Full(full_jid) + } +} + /// A struct representing a full Jabber ID. /// /// A full Jabber ID is composed of 3 components, of which one is optional: From fb71acd8d13db734d8a742e0b8b9604b171333c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 13 Jul 2019 17:57:17 +0200 Subject: [PATCH 0913/1020] Cargo.toml, ChangeLog: Release version 0.14.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- ChangeLog | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b1e84c36487e3eaff92260380c7c84b53db56a6c..fba43cb47e76b689fe177309fbca26f4e52d0241 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xmpp-parsers" -version = "0.13.1" +version = "0.14.0" authors = [ "Emmanuel Gil Peyrot ", "Maxime “pep” Buquet ", diff --git a/ChangeLog b/ChangeLog index 82253779eb3c2f54efcbd543c328a72cbd5a8632..5637b0b24397442d0c79a51ea60a3d9d1a58e439 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,15 @@ -Version TODO: -TODO Emmanuel Gil Peyrot +Version 0.14.0: +2019-07-13 Emmanuel Gil Peyrot , Maxime “pep” Buquet * New parsers/serialisers: - Entity Time (XEP-0202). + * Improvements: + - Microblog NS (XEP-0227). + - Update jid-rs dependency with jid split change (Jid, FullJid, + BareJid) and reexport them. + - Fix rustdoc options in Cargo.toml for docs.rs + * Breaking changes: + - Presence's show attribute is now Option and Show::None is no + more. Version 0.13.1: 2019-04-12 Emmanuel Gil Peyrot From 9d6a43f6209863840c60308e679e742a077f7dd9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 20:27:20 +0200 Subject: [PATCH 0914/1020] Add a DOAP file. --- doap.xml | 560 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 560 insertions(+) create mode 100644 doap.xml diff --git a/doap.xml b/doap.xml new file mode 100644 index 0000000000000000000000000000000000000000..d4d449fcb218859c5adb093911c03af097caf4da --- /dev/null +++ b/doap.xml @@ -0,0 +1,560 @@ + + + + + xmpp-parsers + + 2017-04-18 + + Collection of parsers and serialisers for XMPP extensions + Collection de parseurs et de sérialiseurs pour extensions XMPP + + TODO + TODO + + + + + + + + + + + + + + + + Rust + + + + + + + + + Link Mauve + + aaa4dac2b31c1be4ee8f8e2ab986d34fb261974f + + + + + pep. + + TODO + + + + + + + + + + + + + + + + + + + + complete + 0.1.0 + + + + + + complete + 0.1.0 + + + + + + complete + 0.10.0 + + + + + + partial + 2.9 + 0.1.0 + + + + + + complete + 2.5rc3 + 0.1.0 + + + + + + complete + 1.32.0 + 0.5.0 + + + + + + complete + 2.0 + 0.1.0 + + + + + + complete + 1.1 + 0.10.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + partial + 1.15.8 + 0.5.0 + + + + + + complete + 2.4 + 0.6.0 + + + + + + complete + 1.1 + 0.9.0 + + + + + + complete + 1.1.2 + 0.13.0 + + + + + + complete + 2.1 + 0.1.0 + + + + + + complete + 1.1 + 0.8.0 + + + + + + complete + 1.2.1 + 0.9.0 + + + + + + complete + 1.6 + 0.10.0 + + + + + + complete + 1.5.1 + 0.4.0 + + + + + + complete + 1.0.1 + 0.13.0 + + + + + + complete + 1.1.2 + 0.1.0 + + + + + + complete + 1.1.1 + 0.13.0 + + + + + + complete + 1.1 + 0.10.0 + + + + + + complete + 1.0 + 0.13.0 + + + + + + complete + 1.3.0 + 0.1.0 + + + + + + complete + 1.3 + 0.9.0 + + + + + + complete + 1.6 + 0.10.0 + + + + + + complete + 2.0.1 + 0.1.0 + + + + + + complete + 2.0 + 0.14.0 + + + + + + complete + 2.0 + 0.1.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 0.19.1 + 0.1.0 + + + + + + complete + 1.0.3 + 0.2.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + partial + 0.6.3 + 0.14.0 + only the namespace is included for now + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 0.6.0 + 0.1.0 + + + + + + complete + 1.1.0 + 0.1.0 + + + + + + complete + 0.6.3 + 0.1.0 + + + + + + complete + 1.0.2 + 0.3.0 + + + + + + complete + 0.3.1 + 0.13.0 + + + + + + complete + 0.3 + 0.7.0 + + + + + + complete + 0.6.0 + 0.1.0 + + + + + + complete + 0.2.0 + 0.1.0 + + + + + + complete + 0.3.0 + 0.1.0 + + + + + + + + 0.14.0 + 2019-07-13 + + + + + + + 0.13.1 + 2019-04-12 + + + + + + 0.13.0 + 2019-03-20 + + + + + + 0.12.2 + 2019-01-16 + + + + + + 0.12.1 + 2019-01-16 + + + + + + 0.12.0 + 2019-01-16 + + + + + + 0.11.1 + 2018-09-20 + + + + + + 0.11.0 + 2018-08-02 + + + + + + 0.10.0 + 2018-07-31 + + + + + + 0.9.0 + 2017-12-27 + + + + + + 0.8.0 + 2017-11-30 + + + + + + 0.7.1 + 2017-11-30 + + + + + + 0.7.0 + 2017-11-30 + + + + + + 0.6.0 + 2017-11-30 + + + + + + 0.5.0 + 2017-11-30 + + + + + + 0.4.0 + 2017-11-30 + + + + + + 0.3.0 + 2017-11-30 + + + + + + 0.2.0 + 2017-11-30 + + + + + + 0.1.0 + 2017-11-30 + + + + + From 329afabb66cf1913ddc5461877e1ee3b57d47371 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 21:55:16 +0200 Subject: [PATCH 0915/1020] Implement Message Carbons. --- doap.xml | 8 ++++ src/carbons.rs | 127 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/ns.rs | 3 ++ 4 files changed, 141 insertions(+) create mode 100644 src/carbons.rs diff --git a/doap.xml b/doap.xml index d4d449fcb218859c5adb093911c03af097caf4da..241965cdf9e007ff897d57c4c5af1ef0676ec88f 100644 --- a/doap.xml +++ b/doap.xml @@ -339,6 +339,14 @@ only the namespace is included for now + + + + complete + 0.13.0 + NEXT + + diff --git a/src/carbons.rs b/src/carbons.rs new file mode 100644 index 0000000000000000000000000000000000000000..b40c1e2e7563c8b1f02e4c3311079c7567cb619e --- /dev/null +++ b/src/carbons.rs @@ -0,0 +1,127 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::forwarding::Forwarded; +use crate::iq::IqSetPayload; +use crate::message::MessagePayload; + +generate_empty_element!( + /// Enable carbons for this session. + Enable, + "enable", + CARBONS +); + +impl IqSetPayload for Enable {} + +generate_empty_element!( + /// Disable a previously-enabled carbons. + Disable, + "disable", + CARBONS +); + +impl IqSetPayload for Disable {} + +generate_empty_element!( + /// Request the enclosing message to not be copied to other carbons-enabled + /// resources of the user. + Private, + "private", + CARBONS +); + +impl MessagePayload for Private {} + +generate_element!( + /// Wrapper for a message received on another resource. + Received, "received", CARBONS, + + children: [ + /// Wrapper for the enclosed message. + forwarded: Required = ("forwarded", FORWARD) => Forwarded + ] +); + +impl MessagePayload for Received {} + +generate_element!( + /// Wrapper for a message sent from another resource. + Sent, "sent", CARBONS, + + children: [ + /// Wrapper for the enclosed message. + forwarded: Required = ("forwarded", FORWARD) => Forwarded + ] +); + +impl MessagePayload for Sent {} + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Enable, 0); + assert_size!(Disable, 0); + assert_size!(Private, 0); + assert_size!(Received, 212); + assert_size!(Sent, 212); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Enable, 0); + assert_size!(Disable, 0); + assert_size!(Private, 0); + assert_size!(Received, 408); + assert_size!(Sent, 408); + } + + #[test] + fn empty_elements() { + let elem: Element = "".parse().unwrap(); + Enable::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Disable::try_from(elem).unwrap(); + + let elem: Element = "".parse().unwrap(); + Private::try_from(elem).unwrap(); + } + + #[test] + fn forwarded_elements() { + let elem: Element = " + + + +" + .parse() + .unwrap(); + let received = Received::try_from(elem).unwrap(); + assert!(received.forwarded.stanza.is_some()); + + let elem: Element = " + + + +" + .parse() + .unwrap(); + let sent = Sent::try_from(elem).unwrap(); + assert!(sent.forwarded.stanza.is_some()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 6863afec8de05478a5f207251cf55c3cb75b37ac..607ea1bea6872e03cb2569d33148e2132378e856 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -149,6 +149,9 @@ pub mod jingle_s5b; /// XEP-0261: Jingle In-Band Bytestreams Transport Method pub mod jingle_ibb; +/// XEP-0280: Message Carbons +pub mod carbons; + /// XEP-0297: Stanza Forwarding pub mod forwarding; diff --git a/src/ns.rs b/src/ns.rs index e89ad67a0ad5379bcc2c223f2fb030201c57c3cc..c1a5e96c21d60a999918ff5fa8f15de2c816c64f 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -138,6 +138,9 @@ pub const JINGLE_IBB: &str = "urn:xmpp:jingle:transports:ibb:1"; /// XEP-0277: Microblogging over XMPP pub const MICROBLOG: &str = "urn:xmpp:microblog:0"; +/// XEP-0280: Message Carbons +pub const CARBONS: &str = "urn:xmpp:carbons:2"; + /// XEP-0297: Stanza Forwarding pub const FORWARD: &str = "urn:xmpp:forward:0"; From a7dbaee3094a6e0fef3e256086745db70d4c5015 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 21:58:20 +0200 Subject: [PATCH 0916/1020] ChangeLog: Start the next entry. --- ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5637b0b24397442d0c79a51ea60a3d9d1a58e439..7a2b344409bfbefb425d3b49c649de86502d2dcd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Version NEXT: +DATE Emmanuel Gil Peyrot + * New parsers/serialisers: + - Message Carbons (XEP-0280) + * Improvements: + - New DOAP file for a machine-readable description of the features. + Version 0.14.0: 2019-07-13 Emmanuel Gil Peyrot , Maxime “pep” Buquet * New parsers/serialisers: From 0aa5f5f60fa75fd9f88679a2eddee6b22aed4d00 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 22:26:41 +0200 Subject: [PATCH 0917/1020] lib: Stop reexporting TryFrom and TryInto. They are available in std::convert nowadays, and should be imported from there. --- ChangeLog | 3 +++ src/lib.rs | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7a2b344409bfbefb425d3b49c649de86502d2dcd..a3b472e34d551a71561ff2276df24e44919cb9bb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,9 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - Message Carbons (XEP-0280) + * Breaking changes: + - Stop reexporting TryFrom and TryInto, they are available in + std::convert nowadays. * Improvements: - New DOAP file for a machine-readable description of the features. diff --git a/src/lib.rs b/src/lib.rs index 607ea1bea6872e03cb2569d33148e2132378e856..634cfd4fe29a1d96578150e7c52c8a4782c0db61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! A crate parsing common XMPP elements into Rust structures. //! -//! Each module implements the [`TryFrom`] trait, which takes a +//! Each module implements the `TryFrom` trait, which takes a //! minidom [`Element`] and returns a `Result` whose value is `Ok` if the //! element parsed correctly, `Err(error::Error)` otherwise. //! @@ -12,7 +12,6 @@ //! [`Element`], using either `From` or `Into`, which give you what //! you want to be sending on the wire. //! -//! [`TryFrom`]: ../try_from/trait.TryFrom.html //! [`Element`]: ../minidom/element/struct.Element.html // Copyright (c) 2017-2019 Emmanuel Gil Peyrot @@ -25,8 +24,7 @@ #![deny(missing_docs)] pub use minidom::Element; -pub use jid::{Jid, BareJid, FullJid, JidParseError}; -pub use std::convert::{TryFrom, TryInto}; +pub use jid::{BareJid, FullJid, Jid, JidParseError}; pub use crate::util::error::Error; /// XML namespace definitions used through XMPP. From 2234bb76f21540062f3cbc4bce24da7f58e91e94 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 17 Jul 2019 22:30:49 +0200 Subject: [PATCH 0918/1020] macros, pubsub: Fix build failure introduced in the previous commit. --- src/pubsub/mod.rs | 2 +- src/util/macros.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index cde7e77529d65e99dfd0b4f96bc7f198449cd8ee..4b998b9febddc61c341e82d28edf88a1ebde497d 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -73,4 +73,4 @@ impl Item { } /// This trait should be implemented on any element which can be included as a PubSub payload. -pub trait PubSubPayload: crate::TryFrom + Into {} +pub trait PubSubPayload: ::std::convert::TryFrom + Into {} diff --git a/src/util/macros.rs b/src/util/macros.rs index 010cde52bb48675b7ba8a43cbb2b45365c734923..5b54cf65d57d97cf38778f18cc70bc616776b410 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -661,7 +661,7 @@ macro_rules! assert_size ( // TODO: move that to src/pubsub/mod.rs, once we figure out how to use macros from there. macro_rules! impl_pubsub_item { ($item:ident, $ns:ident) => { - impl crate::TryFrom for $item { + impl ::std::convert::TryFrom for $item { type Error = Error; fn try_from(elem: crate::Element) -> Result<$item, Error> { From 750562cd966c3267ef58b663ab846a1526371d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 20 Jul 2019 19:01:25 +0200 Subject: [PATCH 0919/1020] add getters for node and domain on Jid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 2401792bc08bf3a2879721093249293ca14ea3c9..62847c597966475d7cdd84af0f8ae39c348bbe52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,6 +92,22 @@ impl From for Jid { } } +impl Jid { + /// The node part of the Jabber ID, if it exists, else None. + pub fn node(self) -> Option { + match self { + Jid::Bare(BareJid { node, .. }) | Jid::Full(FullJid { node, .. }) => node, + } + } + + /// The domain of the Jabber ID. + pub fn domain(self) -> String { + match self { + Jid::Bare(BareJid { domain, .. }) | Jid::Full(FullJid { domain, .. }) => domain, + } + } +} + /// A struct representing a full Jabber ID. /// /// A full Jabber ID is composed of 3 components, of which one is optional: @@ -639,6 +655,22 @@ mod tests { ); } + #[test] + fn node_from_jid() { + assert_eq!( + Jid::Full(FullJid::new("a", "b.c", "d")).node(), + Some(String::from("a")), + ); + } + + #[test] + fn domain_from_jid() { + assert_eq!( + Jid::Bare(BareJid::new("a", "b.c")).domain(), + String::from("b.c"), + ); + } + #[test] fn serialise() { assert_eq!( From 6b9e85850740bb0c4f1dafb0386b65e2686cebf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 20 Jul 2019 19:14:12 +0200 Subject: [PATCH 0920/1020] Release version 0.6.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34a0a9b1e1a7d360eece46ca6f20c220124d047c..40edcd5b457513229bd249d955506950b4991c7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +Version 0.6.2, released 2019-07-20: + * Updates + - Implement From and From for Jid + - Add node and domain getters on Jid + Version 0.6.1, released 2019-06-10: * Updates - Change the license from LGPLv3 to MPL-2.0. diff --git a/Cargo.toml b/Cargo.toml index 8a211a972da3a96843516f04fb603ad28826b821..f3235b3eff745e36c4a3f3d01b0a283c1150e654 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.6.1" +version = "0.6.2" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From 365f26523c91156ac130893418872bf9800a7359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 20 Jul 2019 19:21:08 +0200 Subject: [PATCH 0921/1020] Add minidom feature for CI tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b4e77f06d233074e4efced741958cc6a524498c..69b23118cdc68f3f16bfbfde4b1daca990e5e3dd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,11 +6,11 @@ rust-latest: image: rust:latest script: - cargo build --verbose - - cargo test --verbose + - cargo test --lib --verbose --features=minidom rust-nightly: stage: build image: rustlang/rust:nightly script: - cargo build --verbose - - cargo test --verbose + - cargo test --lib --verbose --features=minidom From 24d3d8696d0a72347894a95512fcf83644434908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 20 Jul 2019 19:27:31 +0200 Subject: [PATCH 0922/1020] Build CI with minidom feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 69b23118cdc68f3f16bfbfde4b1daca990e5e3dd..5a1213e308553ae38cf2d4ca5f295d4699792d5e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,12 +5,12 @@ rust-latest: stage: build image: rust:latest script: - - cargo build --verbose + - cargo build --verbose --features=minidom - cargo test --lib --verbose --features=minidom rust-nightly: stage: build image: rustlang/rust:nightly script: - - cargo build --verbose + - cargo build --verbose --features=minidom - cargo test --lib --verbose --features=minidom From 74759a7e399c53dcaf17041ee72ca7f1eea4c163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 20 Jul 2019 19:45:06 +0200 Subject: [PATCH 0923/1020] Update minidom dep to 0.11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- Cargo.toml | 2 +- src/lib.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f3235b3eff745e36c4a3f3d01b0a283c1150e654..4f6fe560171d271bb223f30ce1ef11ecab7d5991 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,4 @@ gitlab = { repository = "xmpp-rs/jid-rs" } [dependencies] failure = "0.1.1" failure_derive = "0.1.1" -minidom = { version = "0.10", optional = true } +minidom = { version = "0.11", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 62847c597966475d7cdd84af0f8ae39c348bbe52..c55c806731bc00b7de88369d535d77134b059c20 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -542,7 +542,7 @@ impl BareJid { } #[cfg(feature = "minidom")] -use minidom::{ElementEmitter, IntoAttributeValue, IntoElements}; +use minidom::{IntoAttributeValue, Node}; #[cfg(feature = "minidom")] impl IntoAttributeValue for Jid { @@ -552,9 +552,9 @@ impl IntoAttributeValue for Jid { } #[cfg(feature = "minidom")] -impl IntoElements for Jid { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(String::from(self)) +impl Into for Jid { + fn into(self) -> Node { + Node::Text(String::from(self)) } } @@ -566,9 +566,9 @@ impl IntoAttributeValue for FullJid { } #[cfg(feature = "minidom")] -impl IntoElements for FullJid { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(String::from(self)) +impl Into for FullJid { + fn into(self) -> Node { + Node::Text(String::from(self)) } } @@ -580,9 +580,9 @@ impl IntoAttributeValue for BareJid { } #[cfg(feature = "minidom")] -impl IntoElements for BareJid { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(String::from(self)) +impl Into for BareJid { + fn into(self) -> Node { + Node::Text(String::from(self)) } } From 72ebd217673f6bd31a830c1551aa902ca7b108cb Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 25 Jul 2019 17:51:05 +0200 Subject: [PATCH 0924/1020] hashes: Add base64, hex and colon-separated hex formatters on Hash. --- src/hashes.rs | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index f4b19faad26f62324286715e220194fcd66af5f5..8e96d517777d28613a70000addc4c1e911ab2af1 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -121,6 +121,29 @@ impl Hash { pub fn from_base64(algo: Algo, hash: &str) -> Result { Ok(Hash::new(algo, base64::decode(hash)?)) } + + /// Formats this hash into base64. + pub fn to_base64(&self) -> String { + base64::encode(&self.hash[..]) + } + + /// Formats this hash into hexadecimal. + pub fn to_hex(&self) -> String { + let mut bytes = vec![]; + for byte in self.hash.iter() { + bytes.push(format!("{:02x}", byte)); + } + bytes.join("") + } + + /// Formats this hash into colon-separated hexadecimal. + pub fn to_colon_hex(&self) -> String { + let mut bytes = vec![]; + for byte in self.hash.iter() { + bytes.push(format!("{:02x}", byte)); + } + bytes.join(":") + } } /// Helper for parsing and serialising a SHA-1 attribute. @@ -142,11 +165,7 @@ impl FromStr for Sha1HexAttribute { impl IntoAttributeValue for Sha1HexAttribute { fn into_attribute_value(self) -> Option { - let mut bytes = vec![]; - for byte in self.0.hash { - bytes.push(format!("{:02x}", byte)); - } - Some(bytes.join("")) + Some(self.to_hex()) } } @@ -195,6 +214,15 @@ mod tests { ); } + #[test] + fn value_serialisation() { + let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); + let hash = Hash::try_from(elem).unwrap(); + assert_eq!(hash.to_base64(), "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); + assert_eq!(hash.to_hex(), "d976ab9b04e53710c0324bf29a5a17dd2e7e55bca536b26dfe5e50c8f6be6285"); + assert_eq!(hash.to_colon_hex(), "d9:76:ab:9b:04:e5:37:10:c0:32:4b:f2:9a:5a:17:dd:2e:7e:55:bc:a5:36:b2:6d:fe:5e:50:c8:f6:be:62:85"); + } + #[test] fn test_unknown() { let elem: Element = "" From bf1c2bd48b515d5f4e85040fcf6f6df50d56e02b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Fri, 26 Jul 2019 01:43:57 +0200 Subject: [PATCH 0925/1020] Release version 0.7.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40edcd5b457513229bd249d955506950b4991c7a..d5a731d25b0013e60d0e25784c4020d346f69443 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +Version 0.7.0, released 2019-07-26: + * Breaking + - Update minidom dependency to 0.11 + Version 0.6.2, released 2019-07-20: * Updates - Implement From and From for Jid diff --git a/Cargo.toml b/Cargo.toml index 4f6fe560171d271bb223f30ce1ef11ecab7d5991..77cbae3d3acaac8e06265579d45926aa8a3ed513 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jid" -version = "0.6.2" +version = "0.7.0" authors = [ "lumi ", "Emmanuel Gil Peyrot ", From f167e8b591ede98ea334a4f708aa33fb1bd4a9d4 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 26 Jul 2019 01:54:26 +0200 Subject: [PATCH 0926/1020] date: Follow clippy and remove a harmful reference. --- src/date.rs | 4 ++-- src/time.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/date.rs b/src/date.rs index 73dd3bd7e9dbb7777a376afd2ae8d2c4328663f2..e5fd67e89dc1de926760f39aa1a107d2440ca011 100644 --- a/src/date.rs +++ b/src/date.rs @@ -22,8 +22,8 @@ impl DateTime { } /// Returns a new `DateTime` with a different timezone. - pub fn with_timezone(&self, tz: &FixedOffset) -> DateTime { - DateTime(self.0.with_timezone(tz)) + pub fn with_timezone(&self, tz: FixedOffset) -> DateTime { + DateTime(self.0.with_timezone(&tz)) } /// Formats this `DateTime` with the specified format string. diff --git a/src/time.rs b/src/time.rs index 91866660acaf01c034bd3e0e3169d53b6cba77d0..cbedb9883ed1fae397356c9536cbb58c40c7298b 100644 --- a/src/time.rs +++ b/src/time.rs @@ -67,7 +67,7 @@ impl TryFrom for TimeResult { let tzo = tzo.ok_or(Error::ParseError("Missing tzo child in time element."))?; let utc = utc.ok_or(Error::ParseError("Missing utc child in time element."))?; - let date = utc.with_timezone(&tzo); + let date = utc.with_timezone(tzo); Ok(TimeResult(date)) } @@ -81,7 +81,7 @@ impl From for Element { .append(format!("{}", time.0.timezone())) .build()) .append(Element::builder("utc") - .append(time.0.with_timezone(&FixedOffset::east(0)).format("%FT%TZ")) + .append(time.0.with_timezone(FixedOffset::east(0)).format("%FT%TZ")) .build()) .build() } From 1f96d82cb8a6fc5d4a185263ff674cff8372f631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Sat, 27 Jul 2019 13:14:37 +0200 Subject: [PATCH 0927/1020] Remove trailing whitespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/namespace_set.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/namespace_set.rs b/src/namespace_set.rs index a5401d1e5c94b6f599dab68efd6c7c022353f863..163a87304aadb1320e4ff4731f2a8f66614d97d6 100644 --- a/src/namespace_set.rs +++ b/src/namespace_set.rs @@ -36,7 +36,7 @@ impl NamespaceSet { pub fn declared_ns(&self) -> &BTreeMap, String> { &self.namespaces } - + pub fn get(&self, prefix: &Option) -> Option { match self.namespaces.get(prefix) { Some(ns) => Some(ns.clone()), From 1ded40b61476dea8e0580dfc65df9b4de0741b2f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 27 Jul 2019 15:52:41 +0200 Subject: [PATCH 0928/1020] doap: Update to the latest specification The xmpp-doap extension has be simplified to only expose the SupportedXep class and its children properties, as well as categories, and reuses DOAP to the maximum. --- doap.xml | 719 ++++++++++++++++++++++++++----------------------------- 1 file changed, 345 insertions(+), 374 deletions(-) diff --git a/doap.xml b/doap.xml index 241965cdf9e007ff897d57c4c5af1ef0676ec88f..c28d0f932eabe7e4dc43569db1d103ac387b1576 100644 --- a/doap.xml +++ b/doap.xml @@ -28,9 +28,6 @@ Rust - - - @@ -58,377 +55,351 @@ - - - - - - - complete - 0.1.0 - - - - - - complete - 0.1.0 - - - - - - complete - 0.10.0 - - - - - - partial - 2.9 - 0.1.0 - - - - - - complete - 2.5rc3 - 0.1.0 - - - - - - complete - 1.32.0 - 0.5.0 - - - - - - complete - 2.0 - 0.1.0 - - - - - - complete - 1.1 - 0.10.0 - - - - - - complete - 1.0 - 0.1.0 - - - - - - partial - 1.15.8 - 0.5.0 - - - - - - complete - 2.4 - 0.6.0 - - - - - - complete - 1.1 - 0.9.0 - - - - - - complete - 1.1.2 - 0.13.0 - - - - - - complete - 2.1 - 0.1.0 - - - - - - complete - 1.1 - 0.8.0 - - - - - - complete - 1.2.1 - 0.9.0 - - - - - - complete - 1.6 - 0.10.0 - - - - - - complete - 1.5.1 - 0.4.0 - - - - - - complete - 1.0.1 - 0.13.0 - - - - - - complete - 1.1.2 - 0.1.0 - - - - - - complete - 1.1.1 - 0.13.0 - - - - - - complete - 1.1 - 0.10.0 - - - - - - complete - 1.0 - 0.13.0 - - - - - - complete - 1.3.0 - 0.1.0 - - - - - - complete - 1.3 - 0.9.0 - - - - - - complete - 1.6 - 0.10.0 - - - - - - complete - 2.0.1 - 0.1.0 - - - - - - complete - 2.0 - 0.14.0 - - - - - - complete - 2.0 - 0.1.0 - - - - - - complete - 1.0 - 0.1.0 - - - - - - complete - 1.0 - 0.1.0 - - - - - - complete - 0.19.1 - 0.1.0 - - - - - - complete - 1.0.3 - 0.2.0 - - - - - - complete - 1.0 - 0.1.0 - - - - - - partial - 0.6.3 - 0.14.0 - only the namespace is included for now - - - - - - complete - 0.13.0 - NEXT - - - - - - complete - 1.0 - 0.1.0 - - - - - - complete - 0.6.0 - 0.1.0 - - - - - - complete - 1.1.0 - 0.1.0 - - - - - - complete - 0.6.3 - 0.1.0 - - - - - - complete - 1.0.2 - 0.3.0 - - - - - - complete - 0.3.1 - 0.13.0 - - - - - - complete - 0.3 - 0.7.0 - - - - - - complete - 0.6.0 - 0.1.0 - - - - - - complete - 0.2.0 - 0.1.0 - - - - - - complete - 0.3.0 - 0.1.0 - - - - + + + + partial + 2.9 + 0.1.0 + + + + + + complete + 2.5rc3 + 0.1.0 + + + + + + complete + 1.32.0 + 0.5.0 + + + + + + complete + 2.0 + 0.1.0 + + + + + + complete + 1.1 + 0.10.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + partial + 1.15.8 + 0.5.0 + + + + + + complete + 2.4 + 0.6.0 + + + + + + complete + 1.1 + 0.9.0 + + + + + + complete + 1.1.2 + 0.13.0 + + + + + + complete + 2.1 + 0.1.0 + + + + + + complete + 1.1 + 0.8.0 + + + + + + complete + 1.2.1 + 0.9.0 + + + + + + complete + 1.6 + 0.10.0 + + + + + + complete + 1.5.1 + 0.4.0 + + + + + + complete + 1.0.1 + 0.13.0 + + + + + + complete + 1.1.2 + 0.1.0 + + + + + + complete + 1.1.1 + 0.13.0 + + + + + + complete + 1.1 + 0.10.0 + + + + + + complete + 1.0 + 0.13.0 + + + + + + complete + 1.3.0 + 0.1.0 + + + + + + complete + 1.3 + 0.9.0 + + + + + + complete + 1.6 + 0.10.0 + + + + + + complete + 2.0.1 + 0.1.0 + + + + + + complete + 2.0 + 0.14.0 + + + + + + complete + 2.0 + 0.1.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 0.19.1 + 0.1.0 + + + + + + complete + 1.0.3 + 0.2.0 + + + + + + complete + 1.0 + 0.1.0 + + + + + + partial + 0.6.3 + 0.14.0 + only the namespace is included for now + + + + + + complete + 0.13.0 + NEXT + + + + + + complete + 1.0 + 0.1.0 + + + + + + complete + 0.6.0 + 0.1.0 + + + + + + complete + 1.1.0 + 0.1.0 + + + + + + complete + 0.6.3 + 0.1.0 + + + + + + complete + 1.0.2 + 0.3.0 + + + + + + complete + 0.3.1 + 0.13.0 + + + + + + complete + 0.3 + 0.7.0 + + + + + + complete + 0.6.0 + 0.1.0 + + + + + + complete + 0.2.0 + 0.1.0 + + + + + + complete + 0.3.0 + 0.1.0 + + From ecee3e9ee8a55179a8895eab3778a7926977e37e Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 30 Jul 2019 21:25:27 +0200 Subject: [PATCH 0929/1020] bind: Split Bind into request/response. --- src/bind.rs | 131 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 40 deletions(-) diff --git a/src/bind.rs b/src/bind.rs index 408c08bb56ce4e2b3612d07b394561646d4e7b38..35d2204533134bbf31ce9343f25d3883eaa10fe5 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -17,76 +17,111 @@ use std::convert::TryFrom; /// /// See https://xmpp.org/rfcs/rfc6120.html#bind #[derive(Debug, Clone, PartialEq)] -pub enum Bind { - /// Requests no particular resource, a random one will be affected by the - /// server. - None, - +pub struct BindRequest { /// Requests this resource, the server may associate another one though. - Resource(String), - - /// The full JID returned by the server for this client. - Jid(FullJid), + /// + /// If this is None, we request no particular resource, and a random one + /// will be affected by the server. + resource: Option, } -impl Bind { +impl BindRequest { /// Creates a resource binding request. - pub fn new(resource: Option) -> Bind { - match resource { - None => Bind::None, - Some(resource) => Bind::Resource(resource), - } + pub fn new(resource: Option) -> BindRequest { + BindRequest { resource } } } -impl IqSetPayload for Bind {} -impl IqResultPayload for Bind {} +impl IqSetPayload for BindRequest {} -impl TryFrom for Bind { +impl TryFrom for BindRequest { type Error = Error; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "bind", BIND); check_no_attributes!(elem, "bind"); - let mut bind = Bind::None; + let mut resource = None; for child in elem.children() { - if bind != Bind::None { + if resource.is_some() { return Err(Error::ParseError("Bind can only have one child.")); } if child.is("resource", ns::BIND) { check_no_attributes!(child, "resource"); check_no_children!(child, "resource"); - bind = Bind::Resource(child.text()); - } else if child.is("jid", ns::BIND) { - check_no_attributes!(child, "jid"); - check_no_children!(child, "jid"); - bind = Bind::Jid(FullJid::from_str(&child.text())?); + resource = Some(child.text()); } else { - return Err(Error::ParseError("Unknown element in bind.")); + return Err(Error::ParseError("Unknown element in bind request.")); } } - Ok(bind) + Ok(BindRequest { resource }) } } -impl From for Element { - fn from(bind: Bind) -> Element { +impl From for Element { + fn from(bind: BindRequest) -> Element { Element::builder("bind") .ns(ns::BIND) - .append(match bind { - Bind::None => vec![], - Bind::Resource(resource) => vec![Element::builder("resource") + .append(match bind.resource { + None => vec![], + Some(resource) => vec![Element::builder("resource") .ns(ns::BIND) .append(resource) .build()], - Bind::Jid(jid) => vec![Element::builder("jid").ns(ns::BIND).append(jid).build()], }) .build() } } +/// The response for resource binding, containing the client’s full JID. +/// +/// See https://xmpp.org/rfcs/rfc6120.html#bind +#[derive(Debug, Clone, PartialEq)] +pub struct BindResponse { + /// The full JID returned by the server for this client. + jid: FullJid, +} + +impl IqResultPayload for BindResponse {} + +impl TryFrom for BindResponse { + type Error = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "bind", BIND); + check_no_attributes!(elem, "bind"); + + let mut jid = None; + for child in elem.children() { + if jid.is_some() { + return Err(Error::ParseError("Bind can only have one child.")); + } + if child.is("jid", ns::BIND) { + check_no_attributes!(child, "jid"); + check_no_children!(child, "jid"); + jid = Some(FullJid::from_str(&child.text())?); + } else { + return Err(Error::ParseError("Unknown element in bind response.")); + } + } + + Ok(BindResponse { jid: match jid { + None => return Err(Error::ParseError("Bind response must contain a jid element.")), + Some(jid) => jid, + } }) + } +} + +impl From for Element { + fn from(bind: BindResponse) -> Element { + Element::builder("bind") + .ns(ns::BIND) + .append(Element::builder("jid").ns(ns::BIND).append(bind.jid).build()) + .build() + } +} + #[cfg(test)] mod tests { use super::*; @@ -94,13 +129,15 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(Bind, 40); + assert_size!(BindRequest, 12); + assert_size!(BindResponse, 36); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(Bind, 80); + assert_size!(BindRequest, 24); + assert_size!(BindResponse, 72); } #[test] @@ -108,8 +145,22 @@ mod tests { let elem: Element = "" .parse() .unwrap(); - let bind = Bind::try_from(elem).unwrap(); - assert_eq!(bind, Bind::None); + let bind = BindRequest::try_from(elem).unwrap(); + assert_eq!(bind.resource, None); + + let elem: Element = "Hello™" + .parse() + .unwrap(); + let bind = BindRequest::try_from(elem).unwrap(); + // FIXME: “™” should be resourceprep’d into “TM” here… + //assert_eq!(bind.resource.unwrap(), "HelloTM"); + assert_eq!(bind.resource.unwrap(), "Hello™"); + + let elem: Element = "coucou@linkmauve.fr/HelloTM" + .parse() + .unwrap(); + let bind = BindResponse::try_from(elem).unwrap(); + assert_eq!(bind.jid, FullJid::new("coucou", "linkmauve.fr", "HelloTM")); } #[cfg(not(feature = "disable-validation"))] @@ -118,7 +169,7 @@ mod tests { let elem: Element = "resource" .parse() .unwrap(); - let error = Bind::try_from(elem).unwrap_err(); + let error = BindRequest::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -128,7 +179,7 @@ mod tests { let elem: Element = "resource" .parse() .unwrap(); - let error = Bind::try_from(elem).unwrap_err(); + let error = BindRequest::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From a8628a7870f072428e6bc880886c111e90d83749 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 30 Jul 2019 21:30:34 +0200 Subject: [PATCH 0930/1020] ibr: Update the size tests. Hashbrown has been stabilised, so HashMap is now two pointers bigger, this reflects in this test. --- src/ibr.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/ibr.rs b/src/ibr.rs index 2d41a6fa2eb1deb86b70c50a778f460155d4d621..8ddca59dc4ebe34b3c1868f2d4d60f1dfbca9ce5 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -120,23 +120,16 @@ mod tests { use super::*; use crate::util::compare_elements::NamespaceAwareCompare; - // TODO: These size tests are sensible to the size of HashMap, which recently grew of two - // pointers and is thus different on stable and nightly. Let’s wait for this issue before - // attempting a fix: - // https://github.com/rust-lang/hashbrown/issues/69 - #[cfg(target_pointer_width = "32")] #[test] - #[ignore] fn test_size() { - assert_size!(Query, 88); + assert_size!(Query, 96); } #[cfg(target_pointer_width = "64")] #[test] - #[ignore] fn test_size() { - assert_size!(Query, 152); + assert_size!(Query, 168); } #[test] From 1eb8c781ab2c2076660f7a22b19da480179b3ac9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 30 Jul 2019 22:14:06 +0200 Subject: [PATCH 0931/1020] hashes: Add a from_hex constructor, and use it in Sha1HexAttribute. --- src/hashes.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/hashes.rs b/src/hashes.rs index 8e96d517777d28613a70000addc4c1e911ab2af1..e3f73ca83c51dfbf6187b90d78b4911915826e30 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -122,6 +122,16 @@ impl Hash { Ok(Hash::new(algo, base64::decode(hash)?)) } + /// Like [new](#method.new) but takes hex-encoded data before decoding it. + pub fn from_hex(algo: Algo, hex: &str) -> Result { + let mut bytes = vec![]; + for i in 0..hex.len() / 2 { + let byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)?; + bytes.push(byte); + } + Ok(Hash::new(algo, bytes)) + } + /// Formats this hash into base64. pub fn to_base64(&self) -> String { base64::encode(&self.hash[..]) @@ -154,12 +164,8 @@ impl FromStr for Sha1HexAttribute { type Err = ParseIntError; fn from_str(hex: &str) -> Result { - let mut bytes = vec![]; - for i in 0..hex.len() / 2 { - let byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)?; - bytes.push(byte); - } - Ok(Sha1HexAttribute(Hash::new(Algo::Sha_1, bytes))) + let hash = Hash::from_hex(Algo::Sha_1, hex)?; + Ok(Sha1HexAttribute(hash)) } } From 08fa36d1860ecfe5b725b00a6596c5a0db475c16 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 31 Jul 2019 13:51:18 +0200 Subject: [PATCH 0932/1020] Implement Bits of Binary. --- ChangeLog | 1 + doap.xml | 8 +++ src/bob.rs | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/ns.rs | 3 + 5 files changed, 183 insertions(+) create mode 100644 src/bob.rs diff --git a/ChangeLog b/ChangeLog index a3b472e34d551a71561ff2276df24e44919cb9bb..a855cb7495582789de67dba2342836834c072740 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - Message Carbons (XEP-0280) + - Bits of Binary (XEP-0231) * Breaking changes: - Stop reexporting TryFrom and TryInto, they are available in std::convert nowadays. diff --git a/doap.xml b/doap.xml index c28d0f932eabe7e4dc43569db1d103ac387b1576..b68a71673bd5df4f062dea4ec6eff41d32839712 100644 --- a/doap.xml +++ b/doap.xml @@ -279,6 +279,14 @@ 0.1.0 + + + + complete + 1.0 + NEXT + + diff --git a/src/bob.rs b/src/bob.rs new file mode 100644 index 0000000000000000000000000000000000000000..704ed0c94ef7c692f1cea8478f353306c9affdd8 --- /dev/null +++ b/src/bob.rs @@ -0,0 +1,168 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::hashes::{Hash, Algo}; +use crate::util::helpers::Base64; +use crate::util::error::Error; +use minidom::IntoAttributeValue; +use std::str::FromStr; + +/// A Content-ID, as defined in RFC2111. +/// +/// The text value SHOULD be of the form algo+hash@bob.xmpp.org, this struct +/// enforces that format. +#[derive(Clone, Debug)] +pub struct ContentId { + hash: Hash, +} + +impl FromStr for ContentId { + type Err = Error; + + fn from_str(s: &str) -> Result { + let temp: Vec<_> = s.splitn(2, '@').collect(); + let temp: Vec<_> = match temp[..] { + [lhs, rhs] => { + if rhs != "bob.xmpp.org" { + return Err(Error::ParseError("Wrong domain for cid URI.")) + } + lhs.splitn(2, '+').collect() + }, + _ => return Err(Error::ParseError("Missing @ in cid URI.")) + }; + let (algo, hex) = match temp[..] { + [lhs, rhs] => { + let algo = match lhs { + "sha1" => Algo::Sha_1, + "sha256" => Algo::Sha_256, + _ => unimplemented!(), + }; + (algo, rhs) + }, + _ => return Err(Error::ParseError("Missing + in cid URI.")) + }; + let hash = Hash::from_hex(algo, hex)?; + Ok(ContentId { hash }) + } +} + +impl IntoAttributeValue for ContentId { + fn into_attribute_value(self) -> Option { + let algo = match self.hash.algo { + Algo::Sha_1 => "sha1", + Algo::Sha_256 => "sha256", + _ => unimplemented!(), + }; + Some(format!("{}+{}@bob.xmpp.org", algo, self.hash.to_hex())) + } +} + +generate_element!( + /// Request for an uncached cid file. + Data, "data", BOB, + attributes: [ + /// The cid in question. + cid: Required = "cid", + + /// How long to cache it (in seconds). + max_age: Option = "max-age", + + /// The MIME type of the data being transmitted. + /// + /// See the [IANA MIME Media Types Registry][1] for a list of + /// registered types, but unregistered or yet-to-be-registered are + /// accepted too. + /// + /// [1]: https://www.iana.org/assignments/media-types/media-types.xhtml + type_: Option = "type" + ], + text: ( + /// The actual data. + data: Base64> + ) +); + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + use std::convert::TryFrom; + use std::error::Error as StdError; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(ContentId, 24); + assert_size!(Data, 24); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(ContentId, 56); + assert_size!(Data, 120); + } + + #[test] + fn test_simple() { + let cid: ContentId = "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org".parse().unwrap(); + assert_eq!(cid.hash.algo, Algo::Sha_1); + assert_eq!(cid.hash.hash, b"\x8f\x35\xfe\xf1\x10\xff\xc5\xdf\x08\xd5\x79\xa5\x00\x83\xff\x93\x08\xfb\x62\x42"); + assert_eq!(cid.into_attribute_value().unwrap(), "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org"); + + let elem: Element = "".parse().unwrap(); + let data = Data::try_from(elem).unwrap(); + assert_eq!(data.cid.hash.algo, Algo::Sha_1); + assert_eq!(data.cid.hash.hash, b"\x8f\x35\xfe\xf1\x10\xff\xc5\xdf\x08\xd5\x79\xa5\x00\x83\xff\x93\x08\xfb\x62\x42"); + assert!(data.max_age.is_none()); + assert!(data.type_.is_none()); + assert!(data.data.is_empty()); + } + + #[test] + fn invalid_cid() { + let error = "Hello world!".parse::().unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Missing @ in cid URI."); + + let error = "Hello world@bob.xmpp.org".parse::().unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Missing + in cid URI."); + + let error = "sha1+1234@coucou.linkmauve.fr".parse::().unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Wrong domain for cid URI."); + + let error = "sha1+invalid@bob.xmpp.org".parse::().unwrap_err(); + let message = match error { + Error::ParseIntError(error) => error, + _ => panic!(), + }; + assert_eq!(message.description(), "invalid digit found in string"); + } + + #[test] + fn unknown_child() { + let elem: Element = "" + .parse() + .unwrap(); + let error = Data::try_from(elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in data element."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 634cfd4fe29a1d96578150e7c52c8a4782c0db61..3a4a5dc3366d9b35b3ba39a25e1c2febd3658e89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,6 +138,9 @@ pub mod media_element; /// XEP-0224: Attention pub mod attention; +/// XEP-0231: Bits of Binary +pub mod bob; + /// XEP-0234: Jingle File Transfer pub mod jingle_ft; diff --git a/src/ns.rs b/src/ns.rs index c1a5e96c21d60a999918ff5fa8f15de2c816c64f..f5775d53843572200aacbb73f7d95789729ece7b 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -124,6 +124,9 @@ pub const MEDIA_ELEMENT: &str = "urn:xmpp:media-element"; /// XEP-0224: Attention pub const ATTENTION: &str = "urn:xmpp:attention:0"; +/// XEP-0231: Bits of Binary +pub const BOB: &str = "urn:xmpp:bob"; + /// XEP-0234: Jingle File Transfer pub const JINGLE_FT: &str = "urn:xmpp:jingle:apps:file-transfer:5"; /// XEP-0234: Jingle File Transfer From 08c3cb8c6fc9e79d4b22540ba18586e88d311775 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 31 Jul 2019 13:52:08 +0200 Subject: [PATCH 0933/1020] bind: Document the split in ChangeLog. --- ChangeLog | 1 + src/bind.rs | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index a855cb7495582789de67dba2342836834c072740..ea8f73c7f8cf0dd65f93ef06f3dc89ea2b4f6fb0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,7 @@ DATE Emmanuel Gil Peyrot * Breaking changes: - Stop reexporting TryFrom and TryInto, they are available in std::convert nowadays. + - Bind has been split into BindQuery and BindResponse. * Improvements: - New DOAP file for a machine-readable description of the features. diff --git a/src/bind.rs b/src/bind.rs index 35d2204533134bbf31ce9343f25d3883eaa10fe5..357a7326b4448352494b2923d7456294ce1d8395 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -17,7 +17,7 @@ use std::convert::TryFrom; /// /// See https://xmpp.org/rfcs/rfc6120.html#bind #[derive(Debug, Clone, PartialEq)] -pub struct BindRequest { +pub struct BindQuery { /// Requests this resource, the server may associate another one though. /// /// If this is None, we request no particular resource, and a random one @@ -25,19 +25,19 @@ pub struct BindRequest { resource: Option, } -impl BindRequest { +impl BindQuery { /// Creates a resource binding request. - pub fn new(resource: Option) -> BindRequest { - BindRequest { resource } + pub fn new(resource: Option) -> BindQuery { + BindQuery { resource } } } -impl IqSetPayload for BindRequest {} +impl IqSetPayload for BindQuery {} -impl TryFrom for BindRequest { +impl TryFrom for BindQuery { type Error = Error; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "bind", BIND); check_no_attributes!(elem, "bind"); @@ -55,12 +55,12 @@ impl TryFrom for BindRequest { } } - Ok(BindRequest { resource }) + Ok(BindQuery { resource }) } } -impl From for Element { - fn from(bind: BindRequest) -> Element { +impl From for Element { + fn from(bind: BindQuery) -> Element { Element::builder("bind") .ns(ns::BIND) .append(match bind.resource { @@ -129,14 +129,14 @@ mod tests { #[cfg(target_pointer_width = "32")] #[test] fn test_size() { - assert_size!(BindRequest, 12); + assert_size!(BindQuery, 12); assert_size!(BindResponse, 36); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { - assert_size!(BindRequest, 24); + assert_size!(BindQuery, 24); assert_size!(BindResponse, 72); } @@ -145,13 +145,13 @@ mod tests { let elem: Element = "" .parse() .unwrap(); - let bind = BindRequest::try_from(elem).unwrap(); + let bind = BindQuery::try_from(elem).unwrap(); assert_eq!(bind.resource, None); let elem: Element = "Hello™" .parse() .unwrap(); - let bind = BindRequest::try_from(elem).unwrap(); + let bind = BindQuery::try_from(elem).unwrap(); // FIXME: “™” should be resourceprep’d into “TM” here… //assert_eq!(bind.resource.unwrap(), "HelloTM"); assert_eq!(bind.resource.unwrap(), "Hello™"); @@ -169,7 +169,7 @@ mod tests { let elem: Element = "resource" .parse() .unwrap(); - let error = BindRequest::try_from(elem).unwrap_err(); + let error = BindQuery::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), @@ -179,7 +179,7 @@ mod tests { let elem: Element = "resource" .parse() .unwrap(); - let error = BindRequest::try_from(elem).unwrap_err(); + let error = BindQuery::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), From aa8018d9999496d287e70039cf0241a4b4e7ff38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 21 Aug 2019 10:32:32 +0200 Subject: [PATCH 0934/1020] Update to quick-xml 0.15 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- CHANGELOG.md | 3 +++ Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65a72f9cb32445a0bee253077da633bc1c330f08..f0dd53d3f9ea9714828896560a9a771b39c10e40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +Version XXX, released YYY: + * Changes + * Update to quick-xml 0.15 Version 0.11.0, released 2019-06-14: * Breaking * Get rid of IntoElements, replace with `Into` and ` IntoIterator>` diff --git a/Cargo.toml b/Cargo.toml index 231949e761f9b8c3e16819627780aefc1c5bb4f6..3c13bd807a65d57688f69944412d7ade58656741 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,6 @@ license = "MIT" gitlab = { repository = "lumi/minidom-rs" } [dependencies] -quick-xml = "0.14" +quick-xml = "0.15" failure = "0.1.1" failure_derive = "0.1.1" From a91252c861ee61a390aac0a7907ae34f4482ea41 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 22 Aug 2019 18:04:47 +0200 Subject: [PATCH 0935/1020] Make comments optional. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a default "comments" feature to transform comments into errors when unset. This is so that XMPP implementations don’t have to care about comments, as they can’t happen in the stream. --- CHANGELOG.md | 1 + Cargo.toml | 4 ++++ src/element.rs | 11 ++++++++++- src/error.rs | 5 +++++ src/node.rs | 8 ++++++++ src/tests.rs | 4 ++++ 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0dd53d3f9ea9714828896560a9a771b39c10e40..4dc9482e07e49389541adfa7b1f551533ab5e086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ Version XXX, released YYY: * Changes * Update to quick-xml 0.15 + * Add a default "comments" feature to transform comments into errors when unset. Version 0.11.0, released 2019-06-14: * Breaking * Get rid of IntoElements, replace with `Into` and ` IntoIterator>` diff --git a/Cargo.toml b/Cargo.toml index 3c13bd807a65d57688f69944412d7ade58656741..9ed80d59cf63cd19fe50694ffefe1e93f6d99e2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,7 @@ gitlab = { repository = "lumi/minidom-rs" } quick-xml = "0.15" failure = "0.1.1" failure_derive = "0.1.1" + +[features] +default = ["comments"] +comments = [] diff --git a/src/element.rs b/src/element.rs index 47c8de96ba5589a5899e4ec9f15ef27aa1e370e4..cc9f8e11d4409cdc3d05033687d59c2c15de937f 100644 --- a/src/element.rs +++ b/src/element.rs @@ -283,9 +283,14 @@ impl Element { Event::Eof => { return Err(Error::EndOfDocument); }, + #[cfg(not(feature = "comments"))] + Event::Comment { .. } => { + return Err(Error::CommentsDisabled); + } + #[cfg(feature = "comments")] + Event::Comment { .. } => (), Event::Text { .. } | Event::End { .. } | - Event::Comment { .. } | Event::CData { .. } | Event::Decl { .. } | Event::PI { .. } | @@ -361,6 +366,9 @@ impl Element { Event::Eof => { break; }, + #[cfg(not(feature = "comments"))] + Event::Comment(_) => return Err(Error::CommentsDisabled), + #[cfg(feature = "comments")] Event::Comment(s) => { let comment = reader.decode(&s).into_owned(); if comment != "" { @@ -569,6 +577,7 @@ impl Element { /// /// elem.append_comment_node("comment"); /// ``` + #[cfg(feature = "comments")] pub fn append_comment_node>(&mut self, child: S) { self.children.push(Node::Comment(child.into())); } diff --git a/src/error.rs b/src/error.rs index b2ba473cd88c987cef13a6c334f6a0a2dada33b2..773f214fa573a5434213c176ce6d03ee38b9a3b4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,6 +28,11 @@ pub enum Error { /// An error which is returned when an elemet's name contains more than one colon #[fail(display = "the XML element is invalid")] InvalidElement, + + /// An error which is returned when a comment is to be parsed by minidom + #[cfg(not(comments))] + #[fail(display = "a comment has been found even though comments are disabled by feature")] + CommentsDisabled, } impl From<::quick_xml::Error> for Error { diff --git a/src/node.rs b/src/node.rs index b136dbdce5bc44c002f8c6588415838aa006a8a3..56069d09f80f7a0960111a91914371389a00f717 100644 --- a/src/node.rs +++ b/src/node.rs @@ -16,6 +16,7 @@ pub enum Node { Element(Element), /// A text node. Text(String), + #[cfg(feature = "comments")] /// A comment node. Comment(String), } @@ -39,6 +40,7 @@ impl Node { match *self { Node::Element(ref e) => Some(e), Node::Text(_) => None, + #[cfg(feature = "comments")] Node::Comment(_) => None, } } @@ -61,6 +63,7 @@ impl Node { match *self { Node::Element(ref mut e) => Some(e), Node::Text(_) => None, + #[cfg(feature = "comments")] Node::Comment(_) => None, } } @@ -83,6 +86,7 @@ impl Node { match self { Node::Element(e) => Some(e), Node::Text(_) => None, + #[cfg(feature = "comments")] Node::Comment(_) => None, } } @@ -105,6 +109,7 @@ impl Node { match *self { Node::Element(_) => None, Node::Text(ref s) => Some(s), + #[cfg(feature = "comments")] Node::Comment(_) => None, } } @@ -133,6 +138,7 @@ impl Node { match *self { Node::Element(_) => None, Node::Text(ref mut s) => Some(s), + #[cfg(feature = "comments")] Node::Comment(_) => None, } } @@ -155,6 +161,7 @@ impl Node { match self { Node::Element(_) => None, Node::Text(s) => Some(s), + #[cfg(feature = "comments")] Node::Comment(_) => None, } } @@ -166,6 +173,7 @@ impl Node { Node::Text(ref s) => { writer.write_event(Event::Text(BytesText::from_plain_str(s)))?; }, + #[cfg(feature = "comments")] Node::Comment(ref s) => { writer.write_event(Event::Comment(BytesText::from_plain_str(s)))?; }, diff --git a/src/tests.rs b/src/tests.rs index 2acc97052ecc63c81761d0447dc9ae2c8eb6e0d0..1ff37e6ccfb816ea41f89d4f67cfd08da8b641ca 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -27,8 +27,10 @@ fn build_test_tree() -> Element { root } +#[cfg(feature = "comments")] const COMMENT_TEST_STRING: &'static str = r#""#; +#[cfg(feature = "comments")] fn build_comment_test_tree() -> Element { let mut root = Element::builder("root").build(); root.append_comment_node("This is a child."); @@ -211,12 +213,14 @@ fn namespace_inherited_prefixed2() { assert_eq!(child.ns(), Some("jabber:client".to_owned())); } +#[cfg(feature = "comments")] #[test] fn read_comments() { let mut reader = Reader::from_str(COMMENT_TEST_STRING); assert_eq!(Element::from_reader(&mut reader).unwrap(), build_comment_test_tree()); } +#[cfg(feature = "comments")] #[test] fn write_comments() { let root = build_comment_test_tree(); From 7ebfe3e88100fd91df72d6d25003f0a290a63b65 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 25 Aug 2019 19:01:51 +0200 Subject: [PATCH 0936/1020] New XHTML-IM parser (XEP-0071). --- ChangeLog | 3 +- doap.xml | 8 + src/lib.rs | 3 + src/ns.rs | 5 + src/xhtml.rs | 472 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 490 insertions(+), 1 deletion(-) create mode 100644 src/xhtml.rs diff --git a/ChangeLog b/ChangeLog index ea8f73c7f8cf0dd65f93ef06f3dc89ea2b4f6fb0..eec3bbb8cb21ca2b08b42a528f354d846e8f2aa6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,9 @@ Version NEXT: DATE Emmanuel Gil Peyrot * New parsers/serialisers: - - Message Carbons (XEP-0280) + - XHTML-IM (XEP-0071) - Bits of Binary (XEP-0231) + - Message Carbons (XEP-0280) * Breaking changes: - Stop reexporting TryFrom and TryInto, they are available in std::convert nowadays. diff --git a/doap.xml b/doap.xml index b68a71673bd5df4f062dea4ec6eff41d32839712..a3165dc0b94df64facbfe2696f0579e0efc62bab 100644 --- a/doap.xml +++ b/doap.xml @@ -111,6 +111,14 @@ 0.5.0 + + + + complete + 1.5.4 + NEXT + + diff --git a/src/lib.rs b/src/lib.rs index 3a4a5dc3366d9b35b3ba39a25e1c2febd3658e89..af2fc129566d64a4dc6ca7121fd95c0e56bbc6da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,6 +75,9 @@ pub mod rsm; /// XEP-0060: Publish-Subscribe pub mod pubsub; +/// XEP-0071: XHTML-IM +pub mod xhtml; + /// XEP-0077: In-Band Registration pub mod ibr; diff --git a/src/ns.rs b/src/ns.rs index f5775d53843572200aacbb73f7d95789729ece7b..5c59ec03cccbf36169f5bf355ca27e10ea377528 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -53,6 +53,11 @@ pub const PUBSUB_EVENT: &str = "http://jabber.org/protocol/pubsub#event"; /// XEP-0060: Publish-Subscribe pub const PUBSUB_OWNER: &str = "http://jabber.org/protocol/pubsub#owner"; +/// XEP-0071: XHTML-IM +pub const XHTML_IM: &str = "http://jabber.org/protocol/xhtml-im"; +/// XEP-0071: XHTML-IM +pub const XHTML: &str = "http://www.w3.org/1999/xhtml"; + /// XEP-0077: In-Band Registration pub const REGISTER: &str = "jabber:iq:register"; diff --git a/src/xhtml.rs b/src/xhtml.rs new file mode 100644 index 0000000000000000000000000000000000000000..c4fc0c05685448160a05fe6bc98f882676099492 --- /dev/null +++ b/src/xhtml.rs @@ -0,0 +1,472 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::util::error::Error; +use crate::message::MessagePayload; +use crate::ns; +use minidom::{Element, Node}; +use std::convert::TryFrom; +use std::collections::HashMap; + +// TODO: Use a proper lang type. +type Lang = String; + +/// Container for formatted text. +#[derive(Debug, Clone)] +pub struct XhtmlIm { + /// Map of language to body element. + bodies: HashMap, +} + +impl XhtmlIm { + /// Serialise formatted text to HTML. + pub fn to_html(self) -> String { + let mut html = Vec::new(); + // TODO: use the best language instead. + for (lang, body) in self.bodies { + if let Tag::Body { style: _, xml_lang, children } = body { + if lang.is_empty() { + assert!(xml_lang.is_none()); + } else { + assert_eq!(Some(lang), xml_lang); + } + for tag in children { + html.push(tag.to_html()); + } + break; + } else { + unreachable!(); + } + } + html.concat() + } +} + +impl MessagePayload for XhtmlIm {} + +impl TryFrom for XhtmlIm { + type Error = Error; + + fn try_from(elem: Element) -> Result { + check_self!(elem, "html", XHTML_IM); + check_no_attributes!(elem, "html"); + + let mut bodies = HashMap::new(); + for child in elem.children() { + if child.is("body", ns::XHTML) { + let child = child.clone(); + let lang = match child.attr("xml:lang") { + Some(lang) => lang, + None => "", + }.to_string(); + let body = Tag::try_from(child)?; + match bodies.insert(lang, body) { + None => (), + Some(_) => return Err(Error::ParseError("Two identical language bodies found in XHTML-IM.")) + } + } else { + return Err(Error::ParseError("Unknown element in XHTML-IM.")); + } + } + + Ok(XhtmlIm { bodies }) + } +} + +impl From for Element { + fn from(wrapper: XhtmlIm) -> Element { + Element::builder("html") + .ns(ns::XHTML_IM) + .append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { + if let Tag::Body { style, xml_lang, children } = body { + assert_eq!(Some(lang), xml_lang.as_ref()); + Element::builder("body") + .ns(ns::XHTML_IM) + .attr("style", get_style_string(style.clone())) + .attr("xml:lang", xml_lang.clone()) + .append(children_to_nodes(children.clone())) + } else { + unreachable!(); + } + }).collect::>()) + .build() + } +} + +#[derive(Debug, Clone)] +enum Child { + Tag(Tag), + Text(String), +} + +impl Child { + fn to_html(self) -> String { + match self { + Child::Tag(tag) => tag.to_html(), + Child::Text(text) => text, + } + } +} + +#[derive(Debug, Clone)] +struct Property { + key: String, + value: String, +} + +type Css = Vec; + +fn get_style_string(style: Css) -> Option { + let mut result = vec![]; + for Property { key, value } in style { + result.push(format!("{}: {}", key, value)); + } + if result.is_empty() { + return None; + } + Some(result.join("; ")) +} + +#[derive(Debug, Clone)] +enum Tag { + A { href: Option, style: Css, type_: Option, children: Vec }, + Blockquote { style: Css, children: Vec }, + Body { style: Css, xml_lang: Option, children: Vec }, + Br, + Cite { style: Css, children: Vec }, + Em { children: Vec }, + Img { src: Option, alt: Option }, // TODO: height, width, style + Li { style: Css, children: Vec }, + Ol { style: Css, children: Vec }, + P { style: Css, children: Vec }, + Span { style: Css, children: Vec }, + Strong { children: Vec }, + Ul { style: Css, children: Vec }, + Unknown(Vec), +} + +impl Tag { + fn to_html(self) -> String { + match self { + Tag::A { href, style, type_, children } => { + let href = write_attr(href, "href"); + let style = write_attr(get_style_string(style), "style"); + let type_ = write_attr(type_, "type"); + format!("{}", href, style, type_, children_to_html(children)) + }, + Tag::Blockquote { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + }, + Tag::Body { style, xml_lang: _, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + }, + Tag::Br => String::from("
"), + Tag::Cite { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + }, + Tag::Em { children } => format!("{}", children_to_html(children)), + Tag::Img { src, alt } => { + let src = write_attr(src, "src"); + let alt = write_attr(alt, "alt"); + format!("", src, alt) + } + Tag::Li { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + } + Tag::Ol { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}", style, children_to_html(children)) + } + Tag::P { style, children } => { + let style = write_attr(get_style_string(style), "style"); + format!("{}