phone

    • chevron_right

      Monal IM: Update chat ui and new betas

      Anu • news.movim.eu / PlanetJabber • 19 July, 2020

    I’m going to say 4.7 is feature complete. There are new betas for iOS and mac. In addition to stability fixes, notable is typing notifications as well as the new top nav bar.

    Many thanks for Thilo and Friedrich who did most of the coding for this release.

    • wifi_tethering open_in_new

      This post is public

      monal.im /blog/update-chat-ui-and-new-betas/

    • chevron_right

      Monal IM: New betas out

      Anu • news.movim.eu / PlanetJabber • 24 June, 2020

    There are new mac and iOS betas out. Mac should resolve a badge issue where the wrong number of unread was shown. Both have debug logging enabled

    • wifi_tethering open_in_new

      This post is public

      monal.im /blog/new-betas-out/

    • chevron_right

      Prosodical Thoughts: Prosody 0.11.5 released

      The Prosody Team • news.movim.eu / PlanetJabber • 24 March, 2020

    We are pleased to announce a new minor release from our stable branch.This release mostly adds command line flags to force foreground or background operation, which replaces and deprecates the ‘daemonize’ option in the config file.A summary of changes in this release:Fixes and improvements prosody / mod_posix: Support for command-line flags to override ‘daemonize’ config option Minor changes mod_websocket: Clear mask bit when reflecting ping frames (fixes #1484: Websocket masks pong answer) Download As usual, download instructions for many platforms can be found on our download page
    • wifi_tethering open_in_new

      This post is public

      blog.prosody.im /prosody-0.11.5-released/

    • chevron_right

      ProcessOne: go-xmpp v0.4.0

      Remi Corniere • news.movim.eu / PlanetJabber • 5 March, 2020 • 6 minutes

    A new version of the go-xmpp library, which can be used to write XMPP clients or components in Go, has been released. It’s available on GitHub .

    You can find the full changelog here: CHANGELOG
    Some noteworthy features are the support of three new extensions:

    — XEP-0060: Publish-Subscribe
    — XEP-0050: Ad-Hoc Commands
    — XEP-0004: Data Forms

    for both component and client.

    Callbacks for error management were also added to the client and component.

    New Extensions

    PubSub

    Let’s create a very simple client that is both owner of a node, and subscribed to it.

    This example assumes that you have a locally running jabber server listening on port 5222, like ejabberd .

    First, let’s create the client. We need to provide a user JID, our identity, then the address of the server we wish to connect to.

    Let’s also get a little ahead and put the node name and the service name in our constants:

    const (      domain = "mycomponent.localhost"      address = "localhost:8888"      nodeName = "example_node"      serviceName = "pubsub.localhost"      )

    Now, we need to fill a Config struct, that will be passed to the NewClient method :

    config := xmpp.Config{      TransportConfiguration: xmpp.TransportConfiguration{      Address: serverAddress,      },      Jid: userJID,      Credential: xmpp.Password("pass123"), // For the sake brievety      Insecure: true,        }

    To process publications, we need to setup a route to catch messages and print them on screen:

    router := xmpp.NewRouter();router.NewRoute().     Packet("message").      HandlerFunc(func(s xmpp.Sender, p stanza.Packet) {// Packet type to go through this route      // Make the packet readable and print it      data, _ := xml.Marshal(p)      fmt.Println("Received a publication ! =>   n" + string(data))  })

    Let’s make the client ! (see next feature’s description for an explanation on the func(err error) argument)

     client, err := xmpp.NewClient(config, router, func(err error){ fmt.Println(err) })

    We can connect:

    err := c.Connect()

    Our client is live, let’s make it create a node on the service, using the previously defined constants:

    // Build the requestrqCreate, err := stanza.NewCreateNode(serviceName, nodeName)// Send it  ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)ch, err := client.SendIQ(ctx, rqCreate)

    Now we just have to wait for our response:

    select {    case iqResponse := <-ch:  // Got response from server  fmt.Print(iqResponse.Payload)  case <-time.After(100 * time.Millisecond):    cancel()    panic("No iq response was received in time")}

    The node is created! Let us subscribe to it before we publish :

    // Create a subscribe requestrqSubscribe, err := stanza.NewSubRq(serviceName, stanza.SubInfo{          Node: nodeName,  Jid:  userJID,})// Send itpubRespCh, _ := client.SendIQ(ctx, rqSubscribe)

    We can publish to that node. The publish model is very generic and can be a bit verbose, but it provides great flexibility.

    Let’s say we want to publish this item:

    <item id="62B5B8B3AB34">  <entry xmlns="http://www.w3.org/2005/Atom">    <title xmlns="http://www.w3.org/2005/Atom">My pub item title</title>    <summary xmlns="http://www.w3.org/2005/Atom">My pub item content summary</summary>    <link href="http://denmark.lit/2003/12/13/atom03" rel="alternate" type="text/html" xmlns="http://www.w3.org/2005/Atom"/>    <id xmlns="http://www.w3.org/2005/Atom">My pub item content ID</id>    <published xmlns="http://www.w3.org/2005/Atom">2003-12-13T18:30:02Z</published>    <updated xmlns="http://www.w3.org/2005/Atom">2003-12-13T18:30:02Z</updated>  </entry></item>

    This is how we would need to build the request:

     pub, err := stanza.NewPublishItemRq(serviceName, nodeName, "", stanza.Item{      Publisher: "testuser2",      Any: &stanza.Node{        XMLName: xml.Name{          Space: "http://www.w3.org/2005/Atom",          Local: "entry",        },        Nodes: []stanza.Node{          {            XMLName: xml.Name{Space: "", Local: "title"},            Attrs:   nil,            Content: "My pub item title",            Nodes:   nil,          },          {            XMLName: xml.Name{Space: "", Local: "summary"},            Attrs:   nil,            Content: "My pub item content summary",            Nodes:   nil,          },          {            XMLName: xml.Name{Space: "", Local: "link"},            Attrs: []xml.Attr{              {                Name:  xml.Name{Space: "", Local: "rel"},                Value: "alternate",              },              {                Name:  xml.Name{Space: "", Local: "type"},                Value: "text/html",              },              {                Name:  xml.Name{Space: "", Local: "href"},                Value: "http://denmark.lit/2003/12/13/atom03",              },            },          },          {            XMLName: xml.Name{Space: "", Local: "id"},            Attrs:   nil,            Content: "My pub item content ID",            Nodes:   nil,          },          {            XMLName: xml.Name{Space: "", Local: "published"},            Attrs:   nil,            Content: "2003-12-13T18:30:02Z",            Nodes:   nil,          },          {            XMLName: xml.Name{Space: "", Local: "updated"},            Attrs:   nil,            Content: "2003-12-13T18:30:02Z",            Nodes:   nil,          },        },      },    })

    Then we can send it !

    client.SendIQ(ctx, pub)

    As we are subscribed to it, the route that we setup earlier will catch a message from the server that contains this publication and print it on screen.

    You can find another use of this extension in our xmpp_jukebox example .

    Full example

    The full program that:

    — runs a client
    — connects it to a XMMP server
    — creates a node, subscribes to it
    — publishes to it
    — prints the notification from it

    is available in our library repository , with a few extras.

    XEP-0050 : Ad-Hoc Commands

    Using the example above, let’s say we already have a connected client.
    To request all pending subscriptions requests for all nodes on a PubSub
    service, we would just use :

    subR, err := stanza.NewGetPendingSubRequests(serviceName)subRCh, err := client.SendIQ(ctx, subR)

    Now we just need to listen to the channel (subRCh) to get the server response.

    This request uses the XEP-0050 under the hood, as specifed by XEP-0060: 8.7 Process Pending Subscription Requests.
    Support for XEP-0050 is currently provided without helper functions.

    XEP-0004: Data Forms

    Support of this extension was added partly to cover the 8.2 Configure a Node section of XEP-0060.
    In this process, the client must send a request like :

    <iq type='get'    from='hamlet@denmark.lit/elsinore'    to='pubsub.shakespeare.lit'    id='config1'>  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>    <configure node='princely_musings'/>  </pubsub></iq>

    to which the server responds with a form that should be returned with the new configuration values for the node. To request that form, we would need to use :

    confRq, err := stanza.NewConfigureNode(serviceName, nodeName)confReqCh, err := client.SendIQ(ctx, confRq)

    Then, catch the response from the server and extract the configuration items that you wish to update :

    var fields map[string]Fieldselect {  case confForm:=<-confReqCh:    fields, err = confForm.GetFormFields()  case <-time.After(100 * time.Millisecond):    cancel()    log.Fatal("No iq response was received in time")}

    Edit fields to your liking:

    fields["pubsub#max_payload_size"].ValuesList[0] = "200000" fields["pubsub#deliver_notifications"].ValuesList[0] = "1"

    and send them back to the service :

    submitConf, err := stanza.NewFormSubmissionOwner(      serviceName,      nodeName,      []*stanza.Field{      fields["pubsub#max_payload_size"],      fields["pubsub#notification_type"],    })client.SendIQ(ctx, submitConf)

    Error callbacks

    Overview

    The “NewClient” function changed signature from :

    func NewClient(config Config, r *Router) (c *Client, err error)

    to :

    func NewClient(config Config, r *Router, errorHandler func(error)) (c *Client, err error)

    Meaning you can now provide an errorHandler function, that will be called, currently, when:

    — A new session fails to start
    — Stanzas cannot be read properly
    — A stream error is recieved
    — There is a problem with the underlying connexion

    Simple example

    Here’s an example of how to use the handler. This very simple handler just prints the error that arises:

    import("gosrc.io/xmpp" "log")func main() {    // Create the error handler  errHandler := func(err error) {    fmt.Println(e)  }  // Create a client  var client *xmpp.Client  clientCfg := xmpp.Config{    TransportConfiguration: xmpp.TransportConfiguration{      Address: "serverAddress",    },    Jid:        "myJid",    Credential: xmpp.Password("myPassword"),    Insecure:   true  }  router := xmpp.NewRouter()  if client, err = xmpp.NewClient(clientCfg, router, errHandler); err != nil {    log.Panicln(fmt.Sprintf("Could not create a new client : %s", err))  }  // Connect your client    client.Connect()}

    That’s it !

    • chevron_right

      Monal IM: Monal 4.4 for iOS and Mac are out

      Anu • news.movim.eu / PlanetJabber • 2 March, 2020

    The latest versions of Monal are out for both apple platforms. 4.5 is in development and will probably come out next week.

    If you find the apps useful please remember to rate it and leave feedback on the Appstore’s. This helps discovery and encourages new users to try it. On a related note while its probably tiny numbers for apps these days, it seems the iOS App crossed 250k users recently 🎉 . I don’t really monitor or track these things so I didn’t even notice.

    • wifi_tethering open_in_new

      This post is public

      monal.im /blog/monal-4-4-for-ios-and-mac-are-out/

    • chevron_right

      Alexander Gnauck: Run Snikket on your NAS

      gnauck • news.movim.eu / PlanetJabber • 28 February, 2020 • 4 minutes

    Some weeks ago at FOSDEM Matthew Wild announced Snikket . You can find the introduction here in his post .

    Many admins are still struggling when they want to run their own XMPP server. One of the strength of XMPP is the extensibility. But this can be also a weakness. It can add a lot of complexity, and a variaty of options you need to choose from for your server install. Many extensions and modules you can choose from during the setup, often dependent on each other. Another burden is often getting your TLS certificate setup fully automated. ALl those pain points are addressed in Skicket.

    Snikket is:

    • dockerized
    • certificates are automated by default using Let’s Encrypt
    • all modules to run a modern XMPP server are enabled and perfectly configured out of the box

    One specific module to mention is shared roster module. This is what many people expect from a modern instant messaging system by default.
    When you run your own family or company team chat server you want all your users being able to communicate with each other out of the box. Without asking them to build the contact list on their own, and request authorization before starting a conversation.

    Now I will show how easy it is to install Snikket on a Synology NAS. This tutorial assumes that your Synology NAS has the Docker package already installed.

    Step 1: DNS

    XMPP is a decentralized system. Similar to Email, where many individual servers communicate to each other. There is no single point of failure. No single company which controls the network and own the users.

    This means you need a domain for your Snikket server. And its important that you have access to control the DNS setup. In this sample I am choosing the domain gnauck.name . If you websites and other services on your domain its always suggested to use a subdomain for the XMPP services. This is why I decided to use chat.gnauck.name as the Snikket domain.

    We need to add the following 3 DNS records. I am pointing chat.gnauck.name to the external IP address of my network.
    The other 2 sub domains for groups (group chat service) and share (file share) need to point to the same address or domain. This is why I just use a CNAME records for all of them.

    # Domain           TTL  Class  Type  Targetchat.gnauck.name.  600  IN     CNAME my-nas.dyndns.org.groups.chat.gnauck.name  600  IN     CNAME  my-nas.dyndns.orgshare.chat.gnauck.name   600  IN     CNAME  my-nas.dyndns.org

    Step 2: Firewall routes

    On the firewall the relevant ports get forwarded from the public internet to Snikket which will be running on the Synology NAS. The following 4 ports needs to get forwarded. I redirect ports on the firewall. YOu could to those port redirects also in the docker port setup later.

    TCP 80 => TCP 5280 on NASTCP 443 => TCP 5281 on NASTCP 5222 => TCP 5222 on NASTCP 5269 => TCP 5269 on NAS

    Step 3: Configure Snikket docker image

    Now we switch to our Synology NAS and go to the Docker UI. First we go to the registry tab and search for Snikket docker images. We download the image from the alpha channel.

    Once the image is downloaded we press the Launch button. We are getting asked to provide a container name. Lets choose snikket as the name and continue with the advanced settings.
    We Enable auto-restart there, to make sure Snikket it running 24/7.

    Under volume we mount a local directory from our NAS into the docker image. This is where Snikket will store all persistent data (user data, files, message history etc…)
    I create a new directory /docker/snikket_data and mount it as /snikket to our container.

    Under port settings we just expose all the required ports we need. In this section you could also redirect the 5280/5281 ports to 80/443. I kept them as is, because we already did the port mapping above on the firewall as described in step 2.

    Under environment add the 2 environment variables for the Snikket domain and admin email address.

    SNIKKET_ADMIN_EMAIL = yourmail@yourdomain.comSNIKKET_DOMAIN = chat.gnauck.name

    Now we can save our configuration and start the container. On the first startup Snikket is writing all the configuration files to the volume mount we created above. It also will request the TLS certificates for our subdomains. Lets give Snikket some time for the initial startup. You can look at the logs, or just wait 2-3 minutes.

    Now we can just got to the container terminal on the Synology and open a new Bash console. In the console we just type the command which gives us an onboarding link to create the admin user.

    create-invite --admin
    Copy the link, paste it to a browser on your Desktop or Android device. You should get to a webpage which is hosted on your newly created Snikket server. Just follow the instructions to download the Snikket Android app and create your new admin user.
    At any time you can create invites for additional users with the create-invitecommand. Of course you create your regular users without the --adminoption.

    That was easy! Now we have our own self hosted secure and federated WhatApp like instant messaging service. Or our company team chat service.

    • wifi_tethering open_in_new

      This post is public

      www.ag-software.net /2020/02/28/run-snikket-on-your-nas/

    • chevron_right

      Monal IM: Monal for Mac is out

      Anu • news.movim.eu / PlanetJabber • 26 February, 2020

    Monal for Mac 4.3 aka the catalyst build is now out in the App Store . Hopefully going forward iOS and Mac will come out at the same time. I will be phasing out the other older Mac client

    • wifi_tethering open_in_new

      This post is public

      monal.im /blog/monal-for-mac-is-out/

    • chevron_right

      Paul Schaub: How to Implement a XEP for Smack.

      vanitasvitae • news.movim.eu / PlanetJabber • 25 February, 2020 • 11 minutes

    Smack is a FLOSS XMPP client library for Java and Android app development. It takes away much of the burden a developer of a chat application would normally have to carry, so the developer can spend more time working on nice stuff like features instead of having to deal with the protocol stack.

    Many (80+ and counting) XMPP Extension Protocols (XEPs) are already implemented in Smack. Today I want to bring you along with me and add support for one more.

    What Smack does very well is to follow the Open-Closed-Principle of software architecture. That means while Smacks classes are closed for modification by the developer, it is pretty easy to extend Smack to add support for custom features. If Smack doesn’t fit your needs, don’t change it, extend it!

    The most important class in Smack is probably the XMPPConnection , as this is where messages coming from and going to. However, even more important for the developer is what is being sent.

    XMPP’s strength comes from the fact that arbitrary XML elements can be exchanged by clients and servers. Heck, the server doesn’t even have to understand what two clients are sending each other. That means that if you need to send some form of data from one device to another, you can simply use XMPP as the transport protocol, serialize your data as XML elements with a namespace that you control and send if off! It doesn’t matter, which XMPP server software you choose, as the server more or less just forwards the data from the sender to the receiver. Awesome!

    So lets see how we can extend Smack to add support for a new feature without changing (and therefore potentially breaking) any existing code!

    For this article, I chose XEP-0428: Fallback Indication as an example protocol extension. The goal of Fallback Indication is to explicitly mark <body/> elements in messages as fallback. For example some end-to-end encryption mechanisms might still add a body with an explanation that the message is encrypted, so that older clients that cannot decrypt the message due to lack of support still display the explanation text instead. This enables the user to switch to a better client 😛 Another example would be an emoji in the body as fallback for a reaction.

    XEP-0428 does this by adding a fallback element to the message:

    <message from="alice@example.org" to="bob@example.net" type="chat">  <fallback xmlns="urn:xmpp:fallback:0"/>  <-- THIS HERE  <encrypted xmlns="urn:example:crypto">Rgreavgl vf abg n irel ybatgvzr nccneragyl.</encrypted>  <body>This message is encrypted.</body></message>

    If a client or server encounter such an element, they can be certain that the body of the message is intended to be a fallback for legacy clients and act accordingly. So how to get this feature into Smack?

    After the XMPPConnection , the most important types of classes in Smack are the ExtensionElement interface and the ExtensionElementProvider class. The later defines a class responsible for deserializing or parsing incoming XML into the an object of the former class.

    The ExtensionElement is itself an empty interface in that it does not provide anything new, but it is composed from a hierarchy of other interfaces from which it inherits some methods. One notable super class is NamedElement , more on that in just a second. If we start our XEP-0428 implementation by creating a class that implements ExtensionElement , our IDE would create this class body for us:

    package tk.jabberhead.blog.wow.nice;import org.jivesoftware.smack.packet.ExtensionElement;import org.jivesoftware.smack.packet.XmlEnvironment;public class FallbackIndicationElement implements ExtensionElement {        @Override    public String getNamespace() {        return null;    }    @Override    public String getElementName() {        return null;    }    @Override    public CharSequence toXML(XmlEnvironment xmlEnvironment) {        return null;    }}

    The first thing we should do is to change the return type of the toXML() method to XmlStringBuilder , as that is more performant and gains us a nice API to work with. We could also leave it as is, but it is generally recommended to return an XmlStringBuilder instead of a boring old CharSequence .

    Secondly we should take a look at the XEP to identify what to return in getNamespace() and getElementName() .

    <fallback xmlns="urn:xmpp:fallback:0"/>[   ^    ]      [        ^          ]element name          namespace

    In XML, the part right after the opening bracket is the element name . The namespace follows as the value of the xmlns attribute. An element that has both an element name and a namespace is called fully qualified . That’s why ExtensionElement is inheriting from FullyQualifiedElement . In contrast, a NamedElement does only have an element name, but no explicit namespace. In good object oriented manner, Smacks ExtensionElement inherits from FullyQualifiedElement which in term is inheriting from NamedElement but also introduces the getNamespace() method.

    So lets turn our new knowledge into code!

    package tk.jabberhead.blog.wow.nice;import org.jivesoftware.smack.packet.ExtensionElement;import org.jivesoftware.smack.packet.XmlEnvironment;public class FallbackIndicationElement implements ExtensionElement {        @Override    public String getNamespace() {        return "urn:xmpp:fallback:0";    }    @Override    public String getElementName() {        return "fallback";    }    @Override    public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {        return null;    }}

    Hm, now what about this toXML() method? At this point it makes sense to follow good old test driven development practices and create a JUnit test case that verifies the correct serialization of our element.

    package tk.jabberhead.blog.wow.nice;import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar;import org.jivesoftware.smackx.pubsub.FallbackIndicationElement;import org.junit.jupiter.api.Test;public class FallbackIndicationElementTest {    @Test    public void serializationTest() {        FallbackIndicationElement element = new FallbackIndicationElement();        assertXmlSimilar("<fallback xmlns=\"urn:xmpp:fallback:0\"/>",element.toXML());    }}

    Now we can tweak our code until the output of toXml() is just right and we can be sure that if at some point someone starts messing with the code the test will inform us of any breakage. So what now?

    Well, we said it is better to use XmlStringBuilder instead of CharSequence , so lets create an instance. Oh! XmlStringBuilder can take an ExtensionElement as constructor argument! Lets do it! What happens if we return new XmlStringBuilder(this); and run the test case?

    <fallback xmlns="urn:xmpp:fallback:0"

    Almost! The test fails, but the builder already constructed most of the element for us. It prints an opening bracket, followed by the element name and adds an xmlns attribute with our namespace as value. This is typically the “head” of any XML element. What it forgot is to close the element. Lets see… Oh, there’s a closeElement() method that again takes our element as its argument. Lets try it out!

    <fallback xmlns="urn:xmpp:fallback:0"</fallback>

    Hm, this doesn’t look right either. Its not even valid XML! (ノಠ益ಠ)ノ彡┻━┻ Normally you’d use such a sequence to close an element which contained some child elements, but this one is an empty element. Oh, there it is! closeEmptyElement() . Perfect!

    <fallback xmlns="urn:xmpp:fallback:0"/>
    package tk.jabberhead.blog.wow.nice;import org.jivesoftware.smack.packet.ExtensionElement;import org.jivesoftware.smack.packet.XmlEnvironment;public class FallbackIndicationElement implements ExtensionElement {        @Override    public String getNamespace() {        return "urn:xmpp:fallback:0";    }    @Override    public String getElementName() {        return "fallback";    }    @Override    public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {        return new XmlStringBuilder(this).closeEmptyElement();    }}

    We can now serialize our ExtensionElement into valid XML! At this point we could start sending around FallbackIndications to all our friends and family by adding it to a message object and sending that off using the XMPPConnection . But what is sending without receiving? For this we need to create an implementation of the ExtensionElementProvider custom to our FallbackIndicationElement . So lets start.

    package tk.jabberhead.blog.wow.nice;import org.jivesoftware.smack.packet.XmlEnvironment;import org.jivesoftware.smack.provider.ExtensionElementProvider;import org.jivesoftware.smack.xml.XmlPullParser;public class FallbackIndicationElementProviderextends ExtensionElementProvider<FallbackIndicationElement> {        @Override    public FallbackIndicationElement parse(XmlPullParser parser,int initialDepth, XmlEnvironment xmlEnvironment) {        return null;    }}

    Normally implementing the deserialization part in form of a ExtensionElementProvider is tiring enough for me to always do that last, but luckily this is not the case with Fallback Indications. Every FallbackIndicationElement always looks the same. There are no special attributes or – shudder – nested named child elements that need special treating.

    Our implementation of the FallbackIndicationElementProvider looks simply like this:

    package tk.jabberhead.blog.wow.nice;import org.jivesoftware.smack.packet.XmlEnvironment;import org.jivesoftware.smack.provider.ExtensionElementProvider;import org.jivesoftware.smack.xml.XmlPullParser;public class FallbackIndicationElementProviderextends ExtensionElementProvider<FallbackIndicationElement> {        @Override    public FallbackIndicationElement parse(XmlPullParser parser,int initialDepth, XmlEnvironment xmlEnvironment) {        return new FallbackIndicationElement();    }}

    Very nice! Lets finish the element part by creating a test that makes sure that our provider does as it should by creating another JUnit test. Obviously we have done that before writing any code, right? We can simply put this test method into the same test class as the serialization test.

        @Test    public void deserializationTest()throws XmlPullParserException, IOException, SmackParsingException {        String xml = "<fallback xmlns=\"urn:xmpp:fallback:0\"/>";        FallbackIndicationElementProvider provider =new FallbackIndicationElementProvider();        XmlPullParser parser = TestUtils.getParser(xml);        FallbackIndicationElement element = provider.parse(parser);        assertEquals(new FallbackIndicationElement(), element);    }

    Boom! Working, tested code!

    But how does Smack learn about our shiny new FallbackIndicationElementProvider ? Internally Smack uses a Manager class to keep track of registered ExtensionElementProviders to choose from when processing incoming XML. Spoiler alert: Smack uses Manager classes for everything !

    If we have no way of modifying Smacks code base, we have to manually register our provider by calling

    ProviderManager.addExtensionProvider("fallback", "urn:xmpp:fallback:0",new FallbackIndicationElementProvider());

    Element providers that are part of Smacks codebase however are registered using an providers.xml file instead, but the concept stays the same.

    Now when receiving a stanza containing a fallback indication, Smack will parse said element into an object that we can acquire from the message object by calling

    FallbackIndicationElement element = message.getExtension("fallback","urn:xmpp:fallback:0");

    You should have noticed by now, that the element name and namespace are used and referred to in a number some places, so it makes sense to replace all the occurrences with references to a constant. We will put these into the FallbackIndicationElement where it is easy to find. Additionally we should provide a handy method to extract fallback indication elements from messages.

    ...public class FallbackIndicationElement implements ExtensionElement {        public static final String NAMESPACE = "urn:xmpp:fallback:0";    public static final String ELEMENT = "fallback";    @Override    public String getNamespace() {        return NAMESPACE;    }    @Override    public String getElementName() {        return ELEMENT;    }    ...    public static FallbackIndicationElement fromMessage(Message message) {        return message.getExtension(ELEMENT, NAMESPACE);    }}

    Did I say Smack uses Managers for everything ? Where is the FallbackIndicationManager then? Well, lets create it!

    package tk.jabberhead.blog.wow.nice;import java.util.Map;import java.util.WeakHashMap;import org.jivesoftware.smack.Manager;import org.jivesoftware.smack.XMPPConnection;public class FallbackIndicationManager extends Manager {    private static final Map<XMPPConnection, FallbackIndicationManager>INSTANCES = new WeakHashMap<>();    public static synchronized FallbackIndicationManagergetInstanceFor(XMPPConnection connection) {        FallbackIndicationManager manager = INSTANCES.get(connection);        if (manager == null) {            manager = new FallbackIndicationManager(connection);            INSTANCES.put(connection, manager);        }        return manager;    }    private FallbackIndicationManager(XMPPConnection connection) {        super(connection);    }}

    Woah, what happened here? Let me explain.

    Smack uses Managers to provide the user (the developer of an application) with an easy access to functionality that the user expects. In order to use some feature, the first thing the user does it to acquire an instance of the respective Manager class for their XMPPConnection . The returned instance is unique for the provided connection, meaning a different connection would get a different instance of the manager class, but the same connection will get the same instance anytime getInstanceFor(connection) is called.

    Now what does the user expect from the API we are designing? Probably being able to send fallback indications and being notified whenever we receive one. Lets do sending first!

        ...    private FallbackIndicationManager(XMPPConnection connection) {        super(connection);    }    public MessageBuilder addFallbackIndicationToMessage(MessageBuilder message, String fallbackBody) {        return message.setBody(fallbackBody)                .addExtension(new FallbackIndicationElement());}

    Easy!

    Now, in order to listen for incoming fallback indications, we have to somehow tell Smack to notify us whenever a FallbackIndicationElement comes in. Luckily there is a rather nice way of doing this.

        ...    private FallbackIndicationManager(XMPPConnection connection) {        super(connection);        registerStanzaListener();    }    private void registerStanzaListener() {        StanzaFilter filter = new AndFilter(StanzaTypeFilter.MESSAGE,                 new StanzaExtensionFilter(FallbackIndicationElement.ELEMENT,                         FallbackIndicationElement.NAMESPACE));        connection().addAsyncStanzaListener(stanzaListener, filter);    }    private final StanzaListener stanzaListener = new StanzaListener() {        @Override        public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException,SmackException.NotLoggedInException {            Message message = (Message) packet;            FallbackIndicationElement fallbackIndicator =FallbackIndicationElement.fromMessage(message);            String fallbackBody = message.getBody();            onFallbackIndicationReceived(message, fallbackIndicator,fallbackBody);        }    };    private void onFallbackIndicationReceived(Message message,FallbackIndicationElement fallbackIndicator, String fallbackBody) {        // do something, eg. notify registered listeners etc.    }

    Now that’s nearly it. One last, very important thing is left to do. XMPP is known for its extensibility (for the better or the worst). If your client supports some feature, it is a good idea to announce this somehow, so that the other end knows about it. That way features can be negotiated so that the sender doesn’t try to use some feature that the other client doesn’t support.

    Features are announced by using XEP-0115: Entity Capabilities , which is based on XEP-0030: Service Discovery . Smack supports this using the ServiceDiscoveryManager . We can announce support for Fallback Indications by letting our manager call

    ServiceDiscoveryManager.getInstanceFor(connection)        .addFeature(FallbackIndicationElement.NAMESPACE);

    somewhere, for example in its constructor. Now the world knows that we know what Fallback Indications are. We should however also provide our users with the possibility to check if their contacts support that feature as well! So lets add a method for that to our manager!

        public boolean userSupportsFallbackIndications(EntityBareJid jid)             throws XMPPException.XMPPErrorException,SmackException.NotConnectedException, InterruptedException,             SmackException.NoResponseException {        return ServiceDiscoveryManager.getInstanceFor(connection())                .supportsFeature(jid, FallbackIndicationElement.NAMESPACE);    }

    Done!

    I hope this little article brought you some insights into the XMPP protocol and especially into the development process of protocol libraries such as Smack, even though the demonstrated feature was not very spectacular.

    Quick reminder that the next Google Summer of Code is coming soon and the XMPP Standards Foundation got accepted 😉
    Check out the project ideas page!

    Happy Hacking!

    • wifi_tethering open_in_new

      This post is public

      blog.jabberhead.tk /2020/02/25/how-to-implement-a-xep-for-smack/

    • chevron_right

      Ignite Realtime Blog: REST API Openfire plugin 1.4.0 released

      @wroot wroot • news.movim.eu / PlanetJabber • 25 February, 2020

    @wroot wrote:

    The Ignite Realtime community is happy to announce the immediate release of version 1.4.0 of the REST API plugin for Openfire!

    The REST API plugin provides the ability to manage Openfire by sending an REST/HTTP request to the server.

    This update upgrades jackson library to 1.9.11 and also makes new version available in Admin Console again.

    Your instance of Openfire should automatically display the availability of the update in the next few hours. Alternatively, you can download the new release of the plugin at the REST API plugin archive page

    For other release announcements and news follow us on Twitter

    Posts: 1

    Participants: 1

    Read full topic