Planet Jabber

March 31, 2020

Jérôme Poisson

SàT progress note 2020-W14

Hello, it's time for a new progress note.

attachments

As I've mentioned last time, I've continued the work on Cagou UX, with attachments. When you select a file to upload or send to your contact, it's not sent directly anymore, but appears in a "attachment" layout just above your message. This way, you can check that you'll send the right file to the right person (notably on touch screens, it could be easy to touch the wrong file).

When sharing large images, a "reduce image size" checkbox will appear, checked by default. I've chosen this way over automatic resizing, as a user may want to share HD images. Of course this behaviour may be adjusted in a future release, according to users feedback.

I've also worked on attachment on reception, images end-2-end encrypted (or e2ee) with OMEMO are now displayed instead of their links (it was already the case for non e2ee images), except if the contact is not your contact list (or "roster" in XMPP terms), in which case user has to click on the attachment (this is to avoid leaking your IP address to unknown people, as OMEMO encrypted images use HTTPS to retrieve the file).

There is now an integrated full-screen image viewer, where you can pinch or zoom the image. It's for now really basic, but I plan to add a menu to open the image in external editor, download it, or even doing basic edition.

When several images are linked in one message, a "collection" is shown : you see a grid of thumbnails of the first images, and clicking on it will open the editor, where all the images are in a carousel. Unfortunately, OMEMO Media Sharing, which is currently the only way to share e2ee files, allows only one file per message, so the collection is only usable with non encrypted images. This will be fixed thanks the recent work done by XMPP community on OMEMO : new version don't encrypt the body only anymore, this will open the way to a proper standard to share e2ee images, something to expect probably in the incoming months.

screenshot of a collections of images in Cagou

The menu to share a file has been reworked too, as you can see in the screenshot below. I've decided to let the 2 options to share a file : upload it, or direct transfer (which is P2P when possible). While upload is more user friendly, direct sending is more adapted when sending a large image to a single entity, and you may not want to let the file on your server (note that in some cases when P2P connection can't be established, the file may transit through your server).

screenshot of file sending panel on Android

There is a clear note indicating what will happen to the file (mainly telling if it goes to the server and if it is encrypted).

Dropping files is now handled on desktop, including dropping multiple files at once.

screencast of files drag & drop with Cagou

note: I've got feedback for my last note about the use of GIF images for screencast. I totally agree that's an issue : it's an outdated format, it's playing automatically which is annoying, it's heavy, not restoring all colors, etc. I've been using it so far because it's the only format which can be read without trouble everywhere (no thanks to some company with a fruit logo). I've got a pointer to ogv.js (thanks Link Mauve) which seems a reasonable solution used notably on Mediawiki websites, I plan to integrate it in Libervia, I just didn't got the time to do it for this note.

OMEMO

After attachments, I've moved back to OMEMO to improve UX. Now if you have an unencrypted session and somebody is writing to you using encryption, the encryption will be started automatically on your side too, so there is no risk anymore to accidentally answer without activating e2ee.

I've not yet implemented e2e encryption by default for several reasons:

  • not all XMPP clients manage OMEMO (Movim doesn't for instance), and activating OMEMO or any other algorithm by default would exclude them

  • there is already mandatory encryption by default with XMPP (between client and server, and between servers), it's just not end-2-end, but still your message are not traveling in clear text on the network

  • current de facto OMEMO standard, what I'll call now "OMEMO legacy" (see below), has plenty of issues, and notably it encrypts only the body, i.e. the plain text payload of the message. This prevent the use of many useful XMPP extensions.

  • authentication is a mess, fingerprints validation is often hidden or not used at all, which makes e2ee interest arguable. This situation is not specific to XMPP, it's really hard to make authentication user friendly, and most e2ee capable chat clients just hide it. In other words, without authenticating properly all your contacts devices (by a secured external channel, like being face to face), end-2-end encryption is only useful against so-called "passive" attacks on your server and your addressees servers, that means that it protects against an administrator only looking at the logs or archive without actively trying to spy on you. With active attacks, adding a fake device is enough to ruin the purpose of e2ee.

That doesn't mean that I wont do it at a later point. After the great work done by the XMPP community on OMEMO:1, I may switch to e2ee encryption by default once the novelties are implemented.

In Cagou, following what can be seen on other clients, I've added a lock to indicate if a message was encrypted or not. If you receive an unencrypted message in an encrypted session, the lock will not only be open, but will also appear in red, as this situation should not happen.

screencapture of lock indicating that a message has been encrypted

I've implemented Blind Trust Before Verification (BTBV) trust policy in SàT. BTBV has been introduced by Conversations, and as its name states it trusts by default any new device, until a manual verification is done (QR Code scanning is not implemented yet in Cagou, but it's on the TODO list). This improves a lot user experience, while letting open the option of a more secure workflow.

Automatically trusted devices are indicated in the trust UI (the panel shown when you manually trust a device), as well as an option to restore BTBV behaviour for this contact.

an automatically trusted device

note that this UI should be improved in future versions, with QR Code, colors, etc.

OMEMO legacy vs omemo:1

a couple of weeks ago, part of the XMPP community met in Germany to work on fixing OMEMO issues. Currently, most clients implement a de-facto "OMEMO" standard which has been done as a first draft many years ago and has many majors flaws (only the body is encrypted, dependency on libsignal, list of devices saved in a single pubsub item, etc.).

This resulted in new versions of XEP-0384, and the situation is far better now. Notably, the inclusion of Stanza Content Encryption is a game changer.

This new version has a new namespace urn:xmpp:omemo:1, that's why there is a distinction betweek omemo legacy and omemo:1. We can most probably expect a major adoption of omemo:1 before the end of the year.

You may want to check the nice blog post of vanitasvitae for more details.

Sprint

Finally, I've been participating to Berlin Sprint. While it was initially an in-person meeting, due to the pandemic situation that you're all aware of it has been converted to an online one. This has been a nice occasion to block the same time spot for several members of the XMPP community, and thanks to the precious help of Syndace (author of the python-omemo library used in SàT) a nasty bug has been fixed.

I've also made a lightning talk to quickly show SàT ecosystem.

Despite the difficult situation, Berlin sprint has been well organised and flexible enough to adapt, big thanks to organisers and participants.

now

That's it for this long note. Now I'm working on improving avatars handling, and then I plan to finally move to web dev with Libervia.

by goffi at March 31, 2020 10:16

March 30, 2020

Gajim

Development News March 2020

This is the third development news post this year, and it’s covering big changes. Starting with this month, the new connection code is available in Gajim’s master branch. It allows better connection handling in difficult network situations and significantly improves connection speed. Building on top of these changes, we’d like to introduce Gajim’s completely rewritten account creation wizard. This month also brings some fixes for voice/video calls, which are the first steps to make this feature available again in the future.

March 30, 2020 00:00

March 24, 2020

Prosodical Thoughts

Prosody 0.11.5 released

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

by The Prosody Team at March 24, 2020 21:41

March 21, 2020

Monal IM

New mac and iOS betas

Sorry for being off the grid for so long. Covid-19 is disrupting all of our lives. I haven’t worked on Monal a ton but thanks to some contributions from Thilo to try to resolve some xmpp duplication and performance issues, there are new iOS and Mac betas. I’ll fix a nick name issue for contacts and probably release this to production this week. Stay safe.

by Anu at March 21, 2020 13:58

March 17, 2020

ProcessOne

ProcessOne services continue as usual through COVID-19 outbreak

In case you were wondering, all ProcessOne services and operations are continuing as usual throughout COVID-19 outbreak. For more than a decade ProcessOne has been providing realtime messaging & communication services while its team was working 100% remotely, from various global locations. We continue to do so today without interruptions.

Realtime messaging is critical at this time and we are here to help. Let’s connect »

by Mickaël Rémond at March 17, 2020 15:08

March 16, 2020

Paul Schaub

Install Jitsi-Meet alongside ejabberd

Since the corona virus is forcing many of us into home office there is a high demand for video conference solutions. A popular free and open source tool for creating video conferences similar to Google’s hangouts is Jitsi Meet. It enables you to create a conference room from within your browser for which you can then share a link to your coworkers. No client software is needed at all (except mobile devices).

The installation of Jitsi Meet is super straight forward – if you have a dedicated server sitting around. Simply add the jitsi repository to your package manager and (in case of debian based systems) type

sudo apt-get install jitsi-meet

The installer will guide you through most of the process (setting up nginx / apache, installing dependencies, even do the letsencrypt setup) and in the end you can start video calling! The quick start guide does a better job explaining this than I do.

Jitsi Meet is a suite of different components that all play together (see Jitsi Meet manual). Part of the mix is a prosody XMPP server that is used for signalling. That means if you want to have the simple easy setup experience, your server must not already run another XMPP server. Otherwise you’ll have to do some manual configuration ahead of you.

I did that.

Since I already run a personal ejabberd XMPP server and don’t have any virtualization tools at hands, I wanted to make jitsi-meet use ejabberd instead of prosody. In the end both should be equally suited for the job.

Looking at the prosody configuration file that comes with Jitsi’s bundled prosody we can see that Jitsi Meet requires the XMPP server to serve two different virtual hosts.
The file is located under /etc/prosody/conf.d/meet.example.org.cfg.lua

VirtualHost "meet.example.org"
        authentication = "anonymous"
        ssl = {
                ...
        }
        modules_enabled = {
            "bosh";
            "pubsub";
            "ping";
        }
        c2s_require_encryption = false

Component "conference.meet.example.org" "muc"
    storage = "memory"
admins = { "focus@auth.meet.example.org" }

Component "jitsi-videobridge.meet.example.org"
    component_secret = "SECRET1"

VirtualHost "auth.meet.example.org"
    ssl = {
        ...
    }
    authentication = "internal_plain"

Component "focus.meet.example.org"
    component_secret = "SECRET2"

Remember to replace SECRET1 and SECRET2 with secure secrets! There are also some external components that need to be configured. This is where Jitsi Meet plugs into the XMPP server.

In my case I don’t want to server 3 virtual hosts with my ejabberd, so I decided to replace auth.meet.jabberhead.tk with my already existing main domain jabberhead.tk which already uses internal authentication. So all I had to do is to add the virtual host meet.jabberhead.tk to my ejabberd.yml and configure it to use anonymous authentication.
The ejabberd config file is located under /etc/ejabberd/ejabberd.yml or /opt/ejabberd/conf/ejabberd.yml depending on your ejabberd distribution.

hosts:
    ## serves as main host, as well as auth.meet.jabberhead.tk for focus user
  - "jabberhead.tk"
    ## serves as anonymous authentication host for meet.jabberhead.tk
  - "meet.jabberhead.tk"
...
host_config:
  meet.jabberhead.tk:
    auth_method: anonymous
    allow_multiple_connections: true
    anonymous_protocol: both

The syntax for external components is quite different for ejabberd than it is for prosody, so it took me some time to get it working.

listen:
 -
    port: 5280
    ip: "::"
    module: ejabberd_http
    request_handlers:
      ## Not sure if this is needed, but by default jitsi-meet uses http-bind for bosh
      "/http-bind": mod_bosh
      "/bosh": mod_bosh
    tls: true
    protocol_options: 'TLS_OPTIONS'
  -
    port: 5275
    ip: "::"
    module: ejabberd_service
    access: all
    shaper: fast
    hosts:
      "jitsi-videobridge.jabberhead.tk":
        password: "SECRET1"
  -
    port: 5347
    module: ejabberd_service
    hosts:
      "focus.jabberhead.tk":
        password: "SECRET2"

By re-reading the config files now, I wonder why I ended up placing the focus component under the host focus.jabberhead.tk and not focus.meet.jabberhead.tk, but hey – it works and I’m too scared to touch it again 😛

The configuration of the modules was a bit trickier on ejabberd, as the ejabberd config syntax seems to disallow duplicate entries. In this case I had to configure mod_muc for my main domain different than for the meet.jabberhead.tk domain, so I had to move the original mod_muc and mod_muc_admin configuration out of the modules: block and into an append_host_config: block with different settings per domain.

append_host_config:
  jabberhead.tk:
    modules:
        ## This is the original muc configuration I used before
        mod_muc:
          access:
            - allow
          access_admin:
            - allow: admin
          access_create: muc_create
          access_persistent: muc_create
          access_mam:
            - allow
          default_room_options:
            allow_private_messages: true
            mam: true
            persistent: true
        mod_muc_admin: {}
  meet.jabberhead.tk:
    modules:
      ## This is the config only for meet.jabberhead.tk
      mod_muc:
        host: conference.meet.jabberhead.tk
      mod_muc_admin: {}

mod_pubsub, mod_ping and mod_bosh all have to be enabled, but can stay in the global modules: block.

Last but not least we have to add the focus user as an admin and also generate (not discussed here) and add certificates for the meet.jabberhead.tk subdomain.

certfiles:
  - ...
  - "/etc/ssl/meet.jabberhead.tk/cert.pem"
  - "/etc/ssl/meet.jabberhead.tk/fullchain.pem"
  - "/etc/ssl/meet.jabberhead.tk/privkey.pem"
...
acl:
  admin:
    user:
      - "focus@jabberhead.tk"

That’s it for the ejabberd configuration. Now we have to configure the other Jitsi Meet components. Lets start with jicofo, the Jitsi Conference Focus component.

My /etc/jitsi/jicofo/config file looks as follows.

JICOFO_HOST=jabberhead.tk
JICOFO_HOSTNAME=jabberhead.tk
JICOFO_SECRET=SECRET2
JICOFO_PORT=5347
JICOFO_AUTH_DOMAIN=jabberhead.tk
JICOFO_AUTH_USER=focus
JICOFO_AUTH_PASSWORD=SECRET3
JICOFO_OPTS=""
# Below can be left as is.
JAVA_SYS_PROPS=...

Respectively the videobridge configuration (/etc/jitsi/videobridge/config) looks like this:

JVB_HOSTNAME=jabberhead.tk
JVB_HOST=localhost
JVB_PORT=5275
JVB_SECRET=SECRET1
## Leave below as originally was
JAVA_SYS_PROPS=...

Some changes had to be made to /etc/jitsi/videobridge/sip-communicator.properties:

org.jitsi.videobridge.AUTHORIZED_SOURCE_REGEXP=focus@jabberhead.tk/.*
org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<IP-OF-YOUR-SERVER>
org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<IP-OF-YOUR-SERVER>
org.jitsi.videobridge.TCP_HARVESTER_PORT=4443

Now we can wire it all together by modifying the Jitsi Meet config file found under /etc/jitsi/meet/meet.example.org-config.js:

var config = {
    hosts: {
        domain: 'jabberhead.tk',
        anonymousdomain: 'meet.jabberhead.tk',
        authdomain: 'jabberhead.tk',
        bridge: 'jitsi-videobridge.meet.jabberhead.tk',
        focus: 'focus.jabberhead.tk',
        muc: 'conference.meet.jabberhead.tk'
    },
    bosh: '//meet.jabberhead.tk/http-bind',
    clientNode: 'http://jitsi.org/jitsimeet',
    focusUserJid: 'focus@jabberhead.tk',

    testing: {
    ...
    }
...
}

Last but not least my nginx host configuration (/etc/nginx/sites-available/meet.example.org.conf) where all I changed was the bosh configuration (went from http to https, I heard people say this is not necessary though).

server {
    listen 80;
    server_name meet.jabberhead.tk;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl;
    server_name meet.jabberhead.tk;
    ...
    # BOSH
    location = /http-bind {
        proxy_pass      https://localhost:5280/http-bind;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $http_host;
    }
    ...
}

Finally of course, I also had to register the focus user as an XMPP account:

ejabberdctl register focus jabberhead.tk SECRET3

Remember to use a safe password instead of SECRET3 and also stop and disable the bundled prosody! That’s it!

I hope this lowers the bar for some to deploy Jitsi Meet next to their already existing ejabberd. Lastly please do not ask me for support, as I barely managed to get this working for myself 😛

by vanitasvitae at March 16, 2020 18:02

March 13, 2020

Ignite Realtime Blog

inVerse Openfire plugin 6.0.1.1 released

@wroot wrote:

The Ignite Realtime community is happy to announce the release of version 6.0.1.1 of the inVerse plugin for Openfire!

This update brings changes and fixes from Converse 6.0.0 and 6.0.1. There were also a few compatibility breaking changes, so please check the changelog before upgrading.

Your instance of Openfire should automatically display the availability of the update. Alternatively, you can download the new release of the plugin at the inVerse plugin’s archive page

For other release announcements and news follow us on Twitter

Posts: 1

Participants: 1

Read full topic

by @wroot wroot at March 13, 2020 16:59

March 10, 2020

ProcessOne

go-xmpp v0.4.0

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 request
rqCreate, 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 request
rqSubscribe, err := stanza.NewSubRq(serviceName, stanza.SubInfo{        
  Node: nodeName,
  Jid:  userJID,
})
// Send it
pubRespCh, _ := 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]Field
select {
  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 !

by Remi Corniere at March 10, 2020 19:42

go-xmpp v0.5.0

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

This version essentially adds support for Stream Management (XEP-0198), and post-connect/post-reconnect hooks for XMPP clients. Read more below !

Support for Stream Management (XEP-0198)

Acking

“Acking” is the tracking of stanzas at the client level, meaning at higher levels than TCP for instance.
The receiving entity keeps a counter of successfully processed stanzas for a given session, and can communicate this counter to the other entity.

In go-xmpp, if Client.Config.StreamManagementEnable is set to “true”, all sent stanzas will be both sent and put into a queue.
If an ack request is made to the server, depending on the response, some stanzas will be discarded, because the server already handled them, or resent because the server never got them.

Here’s how one would ask for acks :

r := stanza.SMRequest{}
client.Send(r)

Resumption

Stream resume capabilities were also added in this release.

Stream resumption allows to resume a session with the server without going through the entire ceremony of the protocol; it essentially skips some steps.

When a client is created using go-xmpp, one can specify a handler for the client’s event manager. Basically, every time the client changes states (connected -> disconnected for instance), the handler is executed.

Here’s we would use the handler to resume an interrupted stream :

  // Create a client. Skipped for brevity

  // Create a handler for that client. Triggered when changing state
  handler := func(e Event) error {
        switch e.State.state {
      // Some states are skipped. 
      // See "StreamManager" in the lib for a more thorough example)
        case StateDisconnected:
            // Reconnect on disconnection
            return client.Resume()
        case StatePermanentError:
            // Do not attempt to reconnect
        }
        return nil
    }

  // Add the handler to the client
  client.SetHandler(handler)

Now a disconnection because of network outage would trigger a stream resumption.

Post-(re)connect hooks

To help with designing clients, two post connection hooks were added to Client :

  • PostConnectHook
    Will be triggered when calling Client.Connect(). Can be used to retrieve the roster for instance.

  • PostResumeHook
    Will be triggered when using Client.Resume(). Do different operations when reconnecting, and skipping roster if we already have it for example.

One would just assign them as follows :

   config := Config{
        TransportConfiguration: TransportConfiguration{
            Address: testServerAddress,
        },
        Jid:        "test@localhost",
        Credential: Password("test"),
        Insecure:   true}

    var client *Client
    var err error
    router := NewRouter()
  // client default handler omitted for brevity
    if client, err = NewClient(&config, router, clientDefaultErrorHandler); err != nil {
    log.Fatalf("connect create XMPP client: %s", err)
    }

    // Assign hooks to the client
    client.PostConnectHook = func() error {
        go func() {
            // ask server for roster, omitted here
        }()
        return nil
    }
    client.PostResumeHook = func() error {
        go func() {
            // do something else when we try to resume. We already have the roster if this gets triggered
        }()
        return nil
    }

And that’s it ! Hooks will be triggered automatically when relevant.

by Remi Corniere at March 10, 2020 19:42

Paul Schaub

OMEMO Specification Sprint

The past weekend some members of the XMPP community gathered in Düsseldorf to work on the next iteration of the OMEMO Encryption Specification. All of us agree that the result – version 0.4 of XEP-0384 – is a huge step forward and better than ever!

On Saturday morning we met up at the Chaosdorf, a local Hacker Space who’s members kindly hosted the sprint. Huge thanks to them for having us!

Prior to the sprint we had collected a list of bullet points of topics we wanted to discuss. Among the more urging topics was proper specification of OMEMO for group chats, support for encrypting extension elements other than the body, as well as clarification on how to implement OMEMO without having to use libsignal. While the latter was technically already possible having a clear written documentation on how to do it is very important.

We spent most of the first day discussing several changes, features and problems and later started writing down the solutions we found. In between – in true Düsseldorf fashion – we snacked on some Onigiri and later went for some nice Ramen together. Saturday afternoon we started working in smaller groups on different parts of the specification. I’m amazed by the know-how and technical understanding that people brought to the table!

On the second day I had to leave relatively early after lunchtime due to family commitments, so I could only follow the remaining development of the XEP via git commits on the train.

Apart from further clarification, the updated spec now contains some additional features and tweaks. It is now possible to encrypt near arbitrary contents of messages with the help of Stanza Content Encryption. OMEMO now defines its own SCE profile. This enables workflows like fully end-to-end encrypted read markers and reactions. Thanks to Marvin and Klaus, the specification now also contains a section about how to opt-out of OMEMO encryption, both completely as well as on a per-conversation basis. Now you no longer have to manually disable OMEMO for that one contact on EVERY device you own.

The biggest part of the discussions went into properly specifying the cryptographic primitives for use with the Double Ratchet Algorithm. Tim and Andy did a great job of describing how to use hash functions and cipher algorithms to keep be able to re-implement OMEMO without having to rely on libsignal alone. Klaus and Marvin figured out some sane rules that help to decide when a device becomes active / inactive / stale. This should preserve the cryptographic guarantees of the encryption even if you don’t use one of your devices for a longer time.

Daniel properly described the workflow of recovering from broken sessions. This should improve OMEMO session stability. He also defined the exact form of OMEMO related XML elements. One notable feature from a users perspective are human readable labels for identity keys. This should make it easier for you to distinguish keys from another.

I’m really excited about the changes and can’t wait to see the first implementations in the real world!

One thing that’s left to do for now is to determine a smooth upgrade path. Clients will probably have to use both the new and old OMEMO in parallel for some time, as the changes are not backwards compatible. This would mean that we cannot immediately benefit from stanza content encryption and are bound to body-only encryption for some more time.

by vanitasvitae at March 10, 2020 18:29

March 06, 2020

Tigase Blog

BeagleIM 3.7 and SiskinIM 5.7 released

New versions of XMPP clients for Apple’s mobile and desktop platforms have been released.

Common changes

  • Improve bookmarks support (proper handling of bookmarks without a name)

BeagleIM 3.7

Since this version it’s possible to also install BeagleIM via Homebrew package manager.

First, you need to add our brew tap:

brew tap tigase/tigase

And later install BeagleIM Cask:

brew cask install beagleim

We also decided to start publishing test, beta versions of BeagleIM - you can find it on BeagleIM’s GitHub release page or get it via Homebrew (IMPORTANT: Beta versions may contain bugs and cause data loss and should be used only for testing purposes):

brew cask install beagleim-beta

See our Homebrew tap for details

beagleim-brew

SiskinIM 5.7

The stable release of SiskinIM 5.7 contains the following changes:

  • Fix crash when private chat of MUC is in contacts list
  • Improve reliability of establishing XMPP connection

You can download both application from their respective app-stores: Beagle IM from macOS appstore and Siskin IM from iOS appstore and star them on GitHub: Siskin IM on GitHub and Beagle IM on GitHub

Join the discussion

You can discuss all-things-Tigase (including our client apps) on our groupchat: tigase@muc.tigase.org

join-discussion-qr-code

March 06, 2020 12:40

March 05, 2020

ProcessOne

xmpp-notifer v1.0.0

We just released a new GitHub Action called xmpp-notifier! It allows sending notifications to XMPP, and uses our go-xmpp library under the hood.

Let’s go through an example showing how to use it.

Small example

Say we want to get notifications when tests fail on a pull-request for our Go project. In the /.github/workflows/ directory of our project, we may setup actions as YAML configuration files. Let’s make a file, call it for example xmpp_notif.yaml, and add the following contents to it.

First add the event we want to trigger the jobs on (as an array):

on:
  [pull_request]

Following this, we can add a job that will notify us on test failures. Let’s call it notif-script:

jobs:
  notif-script:
    runs-on: ubuntu-latest
    name: job that pushes test failures to xmpp
    steps:
      - name: Run tests
        run: |
        go test ./... -v -race

      # Now lets write the notification action
      - name: Tests failed notif
        # If the previous step fails, start this one. Otherwise, skip it.
        if: failure()  
        id: test_fail_notif
        # Use the xmpp-notifier action
        uses: processone/xmpp-notifier@master
        # Set the secrets as inputs
        with: 
          # Login expects the bot's bare jid (user@domain)
          jid: ${{ secrets.jid }}
          # The bot's password
          password: ${{ secrets.password }}
          # The server we wish to connect to. 
          server_host: ${{ secrets.server_host }}
          # Intended recipient of the notification. Can be a user or room. Bare jid expected.
          recipient: ${{ secrets.recipient }}
          # Port is optional. Defaults to 5222
          server_port: ${{ secrets.server_port }}
          # This message will be sent as a <message> payload to the recipient.
          # It contains a link to the Pull Request page that triggered the action.
          # Test logs are only one click away.
          message: |
            tests for the following PR have failed : ${{ github.event.pull_request.html_url }}
          # Boolean to indicate if correspondent should be treated as a room (true) or a single user (false)
          recipient_is_room: true

That’s it! Now if tests fail on a pull request to any branch of our project, we will get notified through XMPP.

by Remi Corniere at March 05, 2020 13:18

March 03, 2020

Profanity

How to run profanity instances within tmux

If you have more than one XMPP account, you may run one profanity process per account. For example profanity -a AccountName.

It’s possible to start a tmux session with one window for each profanity instance.

Create a tmux configuration file, e.g. in ~/.config/tmux.

mkdir ~/.config/tmux
touch ~/.config/tmux/profanity.conf

Within the configuration file, you create a tmux session named Profanity. Followed by one window per XMPP Account. Each window will run a profanity followed by -a accountname.

session-name Profanity
neww -n "Private" profanity -a private 
neww -n "Work" profanity -a work
neww -n "Movim" profanity -a Movim

If you are using bash, you may like to define a alias in ~/.bash_aliases.

alias profanitymux='tmux new-session "tmux source-file ~/.config/tmux/profanity.conf"'

Done! You can start your profanity instances within a tmux session by running profanitymux. tmux will create one window for each profanity instance. You can switch the tmux windows with CTRL-B + 1 or CTRL-B + 2,…

March 03, 2020 13:49

March 02, 2020

ProcessOne

Suggestions for assessing our carbon footprint

Every one of our consumer actions has an impact on the environment and contributes to carbon emissions into the atmosphere. We all agree that global warming is not yet under control and that we must act to reduce our own emissions.

However, our first tendency is frequently to hide our face as well. It is very tempting to be selective in the way we view our impact. Beyond transportation, we are still very poorly equipped to produce an objective estimate, because the evaluation tools are still lacking.

Where do we start?

We first need to measure our impact to estimate what we can accomplish at our level. As such, I find Le Monde‘s effort to measure the impact of its video production on the environment to be an excellent example.

The project is summarized in the article “How Le Monde tracks down and reduces the carbon footprint of its videos” (In French). Le Monde has developed a grid to estimate the carbon impact of each report with the help of a few simple input keys.

For the moment, the method focuses on the production phase of the video report, including both the impact of the transport used to make the report and the electricity used to film and edit the video. In the end, the carbon impact depends mainly on the type of transportation used. Using plane or car counts for a large part in the impact of the videos. On the basis of these findings, Le Monde can thus optimize its reporting rules to limit their environmental impact.

To provide transparent communications to its readers, the carbon impact is mentioned at the end of the video.

Le Monde‘s approach is replicable to other fields. As managers of IT companies, it is our duty to adopt a similar approach to:

  • Determine the overall carbon footprint of online service platforms and communicate it to users.
  • Assess each user’s share of this consumption to enable them to evaluate their use.
  • To engage in a continuous improvement process and explain the gains achieved.

This exercise in carbon assessment, traceability, corporate accountability and transparency needs to become more widespread to enable us, as consumers, to make informed choices. We are still in the early phases of awaking and ProcessOne is proud to be involved in the development of a methodology to measure the carbon impact of online services.

It now remains to generalize such transparency practices. If Le Monde has done it, surely we can all make it happen.

And you, are you going to convince your company to implement this carbon transparency?

Photo by Dan Meyers on Unsplash

by Mickaël Rémond at March 02, 2020 11:39

Monal IM

Monal 4.4 for iOS and Mac are out

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.

by Anu at March 02, 2020 04:09

February 28, 2020

Alexander Gnauck

Run Snikket on your NAS

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  Target
chat.gnauck.name.  600  IN     CNAME my-nas.dyndns.org.
groups.chat.gnauck.name  600  IN     CNAME  my-nas.dyndns.org
share.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 NAS
TCP 443 => TCP 5281 on NAS
TCP 5222 => TCP 5222 on NAS
TCP 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.com
SNIKKET_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-invite command. Of course you create your regular users without the --admin option.

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

by gnauck at February 28, 2020 16:08

February 26, 2020

Monal IM

Monal for Mac is out

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

by Anu at February 26, 2020 22:28

ProcessOne

ejabberd 20.02

We are pleased to announce ejabberd version 20.02. After a big release last month, this version is mostly a bugfix release.

Fixes

  • Fix problems when trying to use string format with Unicode values directly in XMPP nodes
  • Add missing oauth_client table declaration in lite.new.sql
  • Improve compatibility with CockroachDB
  • Fix importing of piefxis files that did use scram passwords
  • Fix importing of piefxis files that had multiple includes in them
  • Update jiffy dependency
  • Allow storage of emojis when using MSSQL database (Thanks to Christoph Scholz)
  • Make ejabberd_auth_http be able to use auth_opts
  • Make custom_headers options in HTTP modules correctly override built-in values
  • Fix return value of reload_config and dump_config commands

Downloads & Feedback

As usual, the release is tagged in the Git source code repository on Github.

The source package and binary installers are available at ProcessOne.

If you suspect that you’ve found a bug, please search or fill a bug report on Github.

by Jérôme Sautret at February 26, 2020 14:40

Gajim

Development News February 2020

Welcome to February’s development news post. This month’s development brought a new look and flow to group chat invitations, some color improvements, and many bug fixes. Gajim Nightly is now also available as a Flatpak app.

February 26, 2020 00:00

February 25, 2020

Paul Schaub

How to Implement a XEP for Smack.

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 ybat
gvzr 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 FallbackIndicationElementProvider
extends 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 FallbackIndicationElementProvider
extends 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 FallbackIndicationManager
getInstanceFor(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!

by vanitasvitae at February 25, 2020 20:54

Ignite Realtime Blog

REST API Openfire plugin 1.4.0 released

@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 a 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: 2

Participants: 2

Read full topic

by @wroot wroot at February 25, 2020 05:17

February 22, 2020

Monal IM

Burned by an old typo

At some point a long time ago while writing the OMEMO code I entered in the device features. While returning the features that Monal supported, I returned eu.siacs.conversations.axolotl.devicelist instead of eu.siacs.conversations.axolotl.devicelist+notify . The missing +notify meant I didn’t get notifications when the device list changed causing all sort of havoc. Thankfully its been fixed and there is a new Mac beta up as well as a TestFlight build. Thanks everyone for all of your testing, its been gradual but few things shine a light on bugs like testing.

by Anu at February 22, 2020 04:21

February 21, 2020

Monal IM

Mac Catalyst delayed in the Appstore.. it’s blocking the keychain

It seems that the mac app store is currently blocking the use of the keychain as a non public api. It’s clearly a bug because this has worked fine for a decade and until yesterday there were no issues. Until they resolve this, i can’t upload new catalyst builds — including the one intended for the appstore

ITMS-90338: Non-public API usage – The app references non-public symbols in Contents/Frameworks/SAMKeychain.framework/Versions/A/SAMKeychain: _SecItemAdd, _SecItemCopyMatching, _SecItemDelete, _SecItemUpdate, _kSecAttrAccessible, _kSecAttrAccount, _kSecAttrLabel, _kSecAttrService, _kSecAttrSynchronizableAny, _kSecClass, _kSecMatchLimit, _kSecMatchLimitAll, _kSecMatchLimitOne, _kSecReturnAttributes, _kSecReturnData, _kSecValueData,

by Anu at February 21, 2020 03:24

February 20, 2020

Monal IM

Removing Google talk

I am planning to remove Google talk support from 4.4 onwards for Mac and iOS. Its an sad milestone because Google talk was why I made Monal in 2008. However, given google themselves have deprecated it twice and even its replacement google chat has been deprecated, it seems increasingly less important. Additionally, the fact that it does not support any modern xmpp specs and doesn’t work with push means it is barely usable in current versions of Monal. I don’t track user stats but I suspect everyone who used to use this for google has moved on to other clients or slack.

If you *REALLY* need Google talk the legacy mac client will still be around for a little bit.

by Anu at February 20, 2020 20:24

New Mac and iOS beta bre updated

There was a bug in last nights iOS and Mac builds that caused it to crash on group chats. I have updated both clients now. I have also linked the old Monal-OSX.zip file to the catalyst version. Catalyst is the mac client now. This should allow brew users to get it as well. Unlike the old Monal for mac, the new one is sandboxed, hardened runtime and notarized.

by Anu at February 20, 2020 14:02