Quantcast
Channel: Planet Jabber
Viewing all 3042 articles
Browse latest View live

Ignite Realtime Blog: Search plugin 1.7.3 released

$
0
0

@wroot wrote:

The Ignite Realtime community is happy to announce the immediate release of version 1.7.3 of the Search plugin for Openfire!

This update adds protection against CSRF and XSS attacks.

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 Search plugin archive page.

For other release announcements and news follow us on Twitter

Posts: 1

Participants: 1

Read full topic


Ignite Realtime Blog: Monitoring Service plugin 1.8.1 released

$
0
0

@wroot wrote:

The Ignite Realtime community is happy to announce the immediate release of version 1.8.1 of the Monitoring Service plugin for Openfire!

This hotfix update adds protection against XSS attacks on Archiving Settings page.

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 Monitoring Service plugin archive page.

For other release announcements and news follow us on Twitter

Posts: 1

Participants: 1

Read full topic

Ignite Realtime Blog: Openfire 4.4.2 Release

$
0
0

@akrherz wrote:

The Ignite Realtime Community is happy to announce the promotion of release 4.4.2 of Openfire. This release signifies our effort to stablize a 4.4 branch of Openfire while work continues on the next feature release. A changelog exists denoting the 22 Jira issues resolved since the 4.4.1 release. This release should better support server to server (s2s) connections, fix a few admin console XSS-style issues, and improve client session stability.

You can find downloads available with the following sha1sum values for the release artifacts.

f0d116fa699cb0668cf5761e888b77031edbca75  openfire-4.4.2-1.i686.rpm
5ebb03c6d7531bf181fa70b86270f11b31650c5b  openfire-4.4.2-1.noarch.rpm
d332038208197fbdd6d2e96ade2262e82c3faa1a  openfire-4.4.2-1.x86_64.rpm
6b4796507f337536a0d2e138f482c5817a346911  openfire_4.4.2_all.deb
be3a7c14f9670dfcf3b34a125387420f277f7bd3  openfire_4_4_2_bundledJRE.exe
41661466dbff8611628edcdddf53025f3039fe80  openfire_4_4_2_bundledJRE_x64.exe
2eba17818b834fd7fce1a2e5610be1ca16c47df4  openfire_4_4_2.dmg
d9e7504d363df4534b02c87ffcacb3c70748809a  openfire_4_4_2.exe
47c80c7c365f6980e3719f07a3ac32a03cc6a20d  openfire_4_4_2.tar.gz
40281cbb650bdc45e899b533a4f44fb6d9d32dbd  openfire_4_4_2_x64.exe
a9ed8b67dc7a09807f1503722c1785f8c5f6da32  openfire_4_4_2.zip
eeffaa918c1de50c833cd33f22550024ab4fd40b  openfire_src_4_4_2.tar.gz
0e4e903557b6713d9f736f7cd1343044fc89135d  openfire_src_4_4_2.zip

Please let us know in the Community Forums of any issues you have and we are always looking for folks interested in helping out with development, documentation, and testing of Openfire. Considering stopping by our web support group chat and say hi!

For other release announcements and news follow us on Twitter

Posts: 1

Participants: 1

Read full topic

ProcessOne: ejabberd 19.09

$
0
0

We are pleased to announce ejabberd version 19.09. The main focus has been to improve automatic certificate management stack (Let’s Encrypt). We also fixed bugs that had been introduced during previous big refactoring of the configuration management file, as well as the usual various bug fixes.

New Features and improvements

Better ACME support

In this release ACME support has been significantly improved. ACME is used to automatically obtain SSL certificates for the domains served by ejabberd.

The newest version of ACME (so called ACMEv2) is now supported. The implementation is now much more robust, and is able to perform certificate requests and renewals in a fully automated mode.

The automated mode is enabled by default, however, since ACME requires HTTP challenges (i.e. an ACME server will connect to ejabberd server on HTTP port 80 during certificate issuance), some configuration of ejabberd is still required. Namely, an HTTP listener for ejabberd_http module should be configured on non-TLS port with so called “ACME well known” request handler:

listen:
...
  -
    module: ejabberd_http
    port: 5280
    request_handlers:
      /.well-known/acme-challenge: ejabberd_acme
      ...
...

Note that the ACME protocol requires challenges to be sent on port 80. Since this is a privileged port, ejabberd cannot listen on it directly without root privileges. Thus you need some mechanism to forward port 80 to the port defined by the listener (port 5280 in the example above).

There are several ways to do this: using NAT or HTTP front-ends (e.g. sslh, nginx, haproxy and so on). Pick one that fits your installation the best, but DON’T run ejabberd as root.

If you see errors in the logs with ACME server problem reports, it’s highly recommended to change ca_url option of section acme to the URL pointing to some staging ACME environment, fix the problems until you obtain a certificate, and then change the URL back and retry using request-certificate ejabberdctl command (see below).

This is needed because ACME servers typically have rate limits, preventing you from requesting certificates too rapidly and you can get stuck for several hours or even days.

By default, ejabberd uses Let’s Encrypt authority. Thus, the default value of ca_url option is
https://acme-v02.api.letsencrypt.org/directory

and the staging URL will be https://acme-staging-v02.api.letsencrypt.org/directory

acme:
  ## Staging environment
  ca_url: https://acme-staging-v02.api.letsencrypt.org/directory
  ## Production environment (the default):
  # ca_url: https://acme-v02.api.letsencrypt.org/directory

The automated mode can be disabled by setting auto option of section acme to false:

acme:
  auto: false
  ...

In this case automated renewals are still enabled, however, in order to request a new certificate,
you need to run request-certificate ejabberdctl command:

$ ejabberdctl request-certificate all

If you only want to request certificates for a subset of the domains, run:

$ ejabberdctl request-certificate domain.tld,pubsub.domain.tld,server.com,conference.server.com...

You can view the certificates obtained using ACME:

$ ejabberdctl list-certificates
domain.tld /path/to/cert/file1 true
server.com /path/to/cert/file2 false
...

The output is mostly self-explained: every line contains the domain, the corresponding certificate file, and whether this certificate file is used or not. A certificate might not be used for several reasons: mostly because ejabberd detects a better certificate (i.e. not expired, or having a longer lifetime). It’s recommended to revoke unused certificates if they are not yet expired (see below).

At any point you can revoke a certificate: pick the certificate file from the listing above and run:

$ ejabberdctl revoke-certificate /path/to/cert/file

If the commands return errors, consult the log files for details.

Webadmin

Some people have reported having issues to connect to the web administration console. To solve that, the need to connect using a URL with domain corresponding to an XMPP domain has been reverted.

Technical changes

Erlang/OTP requirement

Erlang/OTP 19.3 is now the minimum supported Erlang version for this release.

Database schema changes

There is no change to perform on the database to move from ejabberd 19.08 to ejabberd 19.09. Still, as usual, please, make a backup before upgrading.

Download and install ejabberd 19.09

The source package and binary installers are available at ProcessOne. If you installed a previous version, there are no additional upgrade steps, but as a good practice, plase backup your data.

As usual, the release is tagged in the Git source code repository on Github. If you suspect that you’ve found a bug, please search or fill a bug report in Issues.


Full changelog
===========

* Admin
– The minimum required Erlang/OTP version is now 19.3
– Fix API call using OAuth (#2982)
– Rename MUC command arguments from Host to Service (#2976)

* Webadmin
– Don’t treat ‘Host’ header as a virtual XMPP host (#2989)
– Fix some links to Guide in WebAdmin and add new ones (#3003)
– Use select fields to input host in WebAdmin Backup (#3000)
– Check account auth provided in WebAdmin is a local host (#3000)

* ACME
– Improve ACME implementation
– Fix IDA support in ACME requests
– Fix unicode formatting in ACME module
– Log an error message on IDNA failure
– Support IDN hostnames in ACME requests
– Don’t attempt to create ACME directory on ejabberd startup
– Don’t allow requesting certificates for localhost or IP-like domains
– Don’t auto request certificate for localhost and IP-like domains
– Add listener for ACME challenge in example config

* Authentication
– JWT-only authentication for some users (#3012)

* MUC
– Apply default role after revoking admin affiliation (#3023)
– Custom exit message is not broadcast (#3004)
– Revert “Affiliations other than admin and owner cannot invite to members_only rooms” (#2987)
– When join new room with password, set pass and password_protected (#2668)
– Improve rooms_* commands to accept ‘global’ as MUC service argument (#2976)
– Rename MUC command arguments from Host to Service (#2976)

* SQL
– Fix transactions for Microsoft SQL Server (#2978)
– Spawn SQL connections on demand only

* Misc
– Add support for XEP-0328: JID Prep
– Added gsfonts for captcha
– Log Mnesia table type on creation
– Replicate Mnesia ‘bosh’ table when nodes are joined
– Fix certificate selection for s2s (#3015)
– Provide meaningful error when adding non-local users to shared roster (#3000)
– Websocket: don’t treat ‘Host’ header as a virtual XMPP host (#2989)
– Fix sm ack related c2s error (#2984)
– Don’t hide the reason why c2s connection has failed
– Unicode support
– Correctly handle unicode in log messages
– Fix unicode processing in ejabberd.yml

Prosodical Thoughts: Prosody 0.11.3 released

$
0
0
We are pleased to announce a new minor release from our stable branch. This is a bugfix release for the stable 0.11 branch. It is recommended for all users of 0.11.x to upgrade. Important note for those upgrading: Previous releases did not automatically expire messages from group chat (MUC) archives, so if mod_muc_mam was loaded and enabled for a MUC, archives would grow indefinitely. This is not what most deployments want, therefore automatic expiry is now implemented and enabled with a default 7 day retention.

ProcessOne: Writing a Custom Scroll View with SwiftUI in a chat application

$
0
0

When you are writing a chat application, you need to be able to have some control on the chat view. The chat view typically starts aligned at the end of the conversation, which is the bottom of the screen. When you have received more messages and they cannot fit on one screen anymore, you can scroll back to display them.

However, using only SwiftUI standard ScrollView to build such a conversation view is not possible in the first release of SwiftUI (as of Xcode 11), as no API is provided to define the content offset and start with the content at the bottom. It means that you would be stuck to displaying your chat window and the top and scroll down to see the new messages, which is not acceptable.

In this article, I will show you how to write a custom scroll view to get the intended behaviour. It will not yet be a fully-featured scroll view, with all the bells and whistles you expect (like, for example, a scroll bar), but it will be a good example showing what is required to build SwiftUI custom views. You can then build on that example to add the features you need.

Note: The code was tested on Xcode 11.0.

What is a scroll view?

A scroll view is a view that lets you see more content that can fit on the screen by dragging the content on the screen to display more.

From a technical point of view, a scroll view contains another view that is larger than the screen. It will then handle the “drag” events to synchronize the displayed part of the content view.

Custom SwiftUI scroll view principles

To create a custom SwiftUI view, you generally need to master two SwiftUI concepts:

  • GeometryReader: GeometryReader is a view wrapper that let child views access sizing info of their parent view.
  • Preferences: Preferences are used for the reverse operation. They can be used to propagate information from the child views the parent. They are usually attached to the parent by creating a view modifier.

Creating an example project

We will be creating an example project, with an example conversation file in JSON format to illustrate the view rendering.

Create a new project for iOS, and select the Single View App template:

Choose a name for the new project (i.e. SwiftUI-ScrollView-Demo) and make sure you select SwiftUI for User Interface:

You are ready to start your example project.

Creating a basic view with the conversation loaded

Create a Models group in SwiftUI-ScrollView-Demo group and create a Swift file name Conversation.swift in that group.

It will contain a minimal model to allow rendering a conversation and populate a demo conversation with test messages, to test our ability to put those messages in a scroll view.

//
//  Conversation.swift
//  SwiftUI-ScrollView-Demo
//
struct Conversation: Hashable, Codable {
    var messages: [Message] = []
}

struct Message: Hashable, Codable, Identifiable {
    public var id: Int
    let body: String
    // TODO: add more fields (from, to, timestamp, read indicators, etc).
}

// Create demo conversation to test our custom scroll view.
let demoConversation: Conversation = {
    var conversation = Conversation()
    for index in 0..<40 {
        var message = Message(id: index, body: "message \(index)")
        conversation.messages.append(message)
    }
    return conversation
}()

Preparing the BubbleView

In this article, the message BubbleView will not look like a chat bubble. It will just be a raw cell with a gray background.

Create a new SwiftUI file named BubbleView,swift in the SwiftUI-ScrollView-Demo group.

The content of the file is as follows:

//
//  BubbleView.swift
//  SwiftUI-ScrollView-Demo
//
import SwiftUI

struct BubbleView: View {
    var message: String

    var body: some View {
        HStack {
            Spacer()
            Text(message)
        }
        .padding(10)
        .background(Color.gray)
    }
}

struct BubbleView_Previews: PreviewProvider {
    static var previews: some View {
        BubbleView(message: "Hello")
            .previewLayout(.sizeThatFits)
    }
}

It renders a right-aligned text message, with padding and gray background.

With the custom preview layout, the canvas preview will only show you the content of that view, with the preview message “Hello”:

Working on the main conversation view

You can now edit the ContentView.swift file to render your custom scroll view.

First rename your ContentView to ConversationView using the new Xcode refactoring.

Then, you can prepare your list of messages in the conversation and render then in a VStack. We put that VStack in a standard scroll view, to be able to see all the messages by scrolling the Vstack inside the scroll view.

//
//  ConversationView.swift
//  SwiftUI-ScrollView-Demo
//
import SwiftUI

struct ConversationView: View {
    var conversation: Conversation

    var body: some View {
        NavigationView {
            ScrollView {
                VStack(spacing: 8) {
                    ForEach(self.conversation.messages) { message in
                        return BubbleView(message: message.body)
                    }
                }
            }
            .navigationBarTitle(Text("Conversation"))
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ConversationView(conversation: demoConversation)
    }
}

The preview is getting our demoConversation to render our example conversation.

Note that you also need to edit your SceneDelegate to pass the demoConversation as a parameter when setting up your ConversationView:

//  SceneDelegate.swift
// ...
        // Create the SwiftUI view that provides the window contents.
        let contentView = ConversationView(conversation: demoConversation)
// ...

We now render all the message in our demo Conversation, but we see that the conversation is top aligned and there is no API at the moment to control the content offset to render the display from the bottom of the scroll view on init.

We will fix that in a moment by writing a custom scroll view.

Bootstraping our custom scroll view

Add a new SwiftUI file called ReverseScrollView.swift in the project.

You can then first create your ReverseScroll View to first adapt the VStack to the parent view geometry, thanks to GeometryReader. By wrapping the content of the ReverseScrollView inside a GeometryReader wrapper, you can access info about the “outer” geometry (like the height).

Here is an initial version of the ReverseScrollView:

//
//  ReverseScrollView.swift
//  SwiftUI-ScrollView-Demo
//
//  Created by Mickaël Rémond on 24/09/2019.
//  Copyright © 2019 ProcessOne. All rights reserved.
//
import SwiftUI

struct ReverseScrollView<Content>: View where Content: View {
    var content: () -> Content

    var body: some View {
        GeometryReader { outerGeometry in
            // Render the content
            //  ... and set its sizing inside the parent
            self.content()
            .frame(height: outerGeometry.size.height)
            .clipped()
        }
    }
}

struct ReverseScrollView_Previews: PreviewProvider {
    static var previews: some View {
        ReverseScrollView {
            BubbleView(message: "Hello")
        }
        .previewLayout(.sizeThatFits)
    }
}

You can also replace the ScrollView in ConversationView to use our ReverseScrollView:

//...
        NavigationView {
            ReverseScrollView {
                VStack {
//...

In the Canvas, you can see the view is not scrolling or displayed properly with the last message at the bottom, but it is now properly fitting inside its parent view.

Aligning our view content to the bottom of the ReverseScrollView

The next step is to use preferences to pass the size of the content view to our ReverseScrollView. This will allow us to align the content of the view to the bottom of our custom ScrollView.

To do that we will leverage a SwiftUI feature called preferences. The preferences will be used to track the content size in the ReverseScrollView to properly set the content offset so that it is bottom aligned.

To track the content view height, we need to define a PreferenceKey that will keep track of the total height of the view. It will sum up the value of the height of all subviews in its reduce static function. To do so, add the following code to your ReverseScrollView file:

struct ViewHeightKey: PreferenceKey {
    static var defaultValue: CGFloat { 0 }
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = value + nextValue()
    }
}

You then need to make that ValueHeightKey a view modifier that will use a few tricks to read the content size and propagate the value:

  • The view modifier is embedding a geometry reader in our content background to read the geometry. It will work, as the size of the background content is the same as the content itself.
  • The view modifier will then set the Color.clear preference for that key to propagate them to the parent, listening to them using onPreferenceChange event. We are setting Color.clear preference, as we need to generate a view here and we actually want to hide that background. This trick makes it possible to read and propagate the preference, using a “dummy” background view.

Here is the view modifier extension for our ViewHeightKey:

extension ViewHeightKey: ViewModifier {
    func body(content: Content) -> some View {
        return content.background(GeometryReader { proxy in
            Color.clear.preference(key: Self.self, value: proxy.size.height)
        })
    }
}

Finally, we need to keep track of that Content View Height in a ReverseScrollView state. To do so:

  • We add a ContentHeight state to our ReverseScrollView.
  • We apply our view modifier ViewHeightKey to the content view.
  • We set our contentHeight State in the onPreferenceChange event for the ViewHeightKey values.
  • We update the content offset to the y axis on the content. To calculate the offset, we use the following offset function. It is using scrollview height and content height to calculate the offset so that the content is bottom-aligned (see below).
    // Calculate content offset
    func offset(outerheight: CGFloat, innerheight: CGFloat) -> CGFloat {
        print("outerheight: \(outerheight) innerheight: \(innerheight)")

        let totalOffset = currentOffset + scrollOffset
        return -((innerheight/2 - outerheight/2) - totalOffset)
    }

The content view is now bottom-aligned and the last message (Message 39) is properly displayed at the bottom of our custom scroll view.

You can check the final code in ReverseScrollView.swift.

Making our scroll view scrollable

The final step is to make our custom scroll view able to scroll, synchronized with vertical drag events.

First, we need to add two new states to keep track of the scroll position:

  • The current scroll offset set the content offset after the drag event ended.
  • The scroll offset is used to synchronize the content offset while the user is still dragging the view.

We will update those two states during on drag events onChanged and onEnded.

Here is the operation on ongoing drag event:

    func onDragChanged(_ value: DragGesture.Value) {
        // Update rendered offset
        print("Start: \(value.startLocation.y)")
        print("Start: \(value.location.y)")
        self.scrollOffset = (value.location.y - value.startLocation.y)
        print("Scrolloffset: \(self.scrollOffset)")
    }

and when drag ends, we store the current position, enforcing top and bottom limits:

    func onDragEnded(_ value: DragGesture.Value, outerHeight: CGFloat) {
        // Update view to target position based on drag position
        let scrollOffset = value.location.y - value.startLocation.y
        print("Ended currentOffset=\(self.currentOffset) scrollOffset=\(scrollOffset)")

        let topLimit = self.contentHeight - outerHeight
        print("toplimit: \(topLimit)")

        // Negative topLimit => Content is smaller than screen size. We reset the scroll position on drag end:
        if topLimit < 0 {
             self.currentOffset = 0
        } else {
            // We cannot pass bottom limit (negative scroll)
            if self.currentOffset + scrollOffset < 0 {
                self.currentOffset = 0
            } else if self.currentOffset + scrollOffset > topLimit {
                self.currentOffset = topLimit
            } else {
                self.currentOffset += scrollOffset
            }
        }
        print("new currentOffset=\(self.currentOffset)")
        self.scrollOffset = 0
    }

We also need to update the offset calculation to take into account the drag states:

    // Calculate content offset
    func offset(outerheight: CGFloat, innerheight: CGFloat) -> CGFloat {
        print("outerheight: \(outerheight) innerheight: \(innerheight)")

        let totalOffset = currentOffset + scrollOffset
        return -((innerheight/2 - outerheight/2) - totalOffset)
    }

Finally, you need to track the gesture on the content view, and link those gestures to our drag function:

            self.content()
            // ...
            .animation(.easeInOut)
            .gesture(
                 DragGesture()
                    .onChanged({ self.onDragChanged($0) })
                    .onEnded({ self.onDragEnded($0, outerHeight: outerGeometry.size.height)}))

We used the opportunity to also apply some animation to smoothen the end drag scroll position correction when hitting the limit. We now have a custom scroll view that starts at the bottom and can be properly scrolled with proper top and bottom limits.

Final project

You can download the final project example from Github: SwiftUI-ScrollView-Demo.

What’s next?

Let us know in the comments if you are interested in follow-up blog posts. Here are possible additional features that could make sense to illustrate in detail:

  • Handle device rotation
  • Show how to add messages to the conversation
  • Kinetic scroll with deceleration
  • Better bounce when hitting limits
  • Add a scroll bar
  • Kinetic animation for chat bubbles when scrolling (bit of springy behaviour)

Photo by Alvaro Reyes, Unsplash

The XMPP Standards Foundation: XMPP Newsletter, 01 Oct 2019, FOSDEM 2020, modernization of XMPP, peer networks

$
0
0

Welcome to the XMPP newsletter covering the month of September.

New this month: we've made explicit that this newsletter can be shared and adapted as defined by the CC by-sa 4.0 license, and we've added the credits as this is a community effort.

Be kind, inform your friends and colleagues: forward this newsletter!

Please submit your XMPP/Jabber articles, tutorials or blog posts on our wiki.

Articles

Ralph Meijer, chairman of the Board of Directors of the XSF, has written an introductory piece about "XMPP Message Attaching, Fastening, References", specifications currently in progress.

In case of internet shut down, whether by disaster or voluntarily, peer networks are useful. Monal uses Airdrop (wifi and bluetooth) along with XMPP and OMEMO end-to-end encryption.

Monal and Airdrop screenshots

JabberFr, the French XMPP/Jabber community, has again translated the XMPP newsletter in French, merci beaucoup.

We have started a very minimalistic communication guide to help promoting a project through social networks and other means such as blog posts. Could be valuable for those involved in XMPP projects and would like to get ideas on how to reach out to different communities.

Events

As usual every year, that time has come: we proudly announce the XMPP Summit 24 and the XSF participation to FOSDEM. The 24th XMPP Summit will happen on Thursday 30th and Friday 31st of January, and FOSDEM will be held on Saturday 1st and Sunday 2nd of February. Prepare for the community gathering; now is a good time to start booking your flights!

FOSDEM 2020

Software releases

Servers

Réda Housni Alaoui is reviving the formerly dormant Vysper XMPP server (pronounced as "whisper").

Xabber Server v.0.9 alpha is released, with quick installation and management panel and interesting innovations.

MongooseIM 3.4.1 is out now with an important security upgrade, fixing a vulnerability that allowed any logged in user to crash the node with malicious stanza on certain (but popular) configurations. Read the whole thread on Twitter for more information.

The igniterealtime community has a lot of news:

  • Openfire 4.4.2: "This release should better support server to server (s2s) connections, fix a few admin console XSS-style issues, and improve client session stability."
  • The Fastpath Service plugin has been released in versions 4.4.4 and 4.4.5, bringing support for managed queued chat requests, such as a support team might use.
  • Search plugin 1.7.3: "This update adds protection against CSRF and XSS attacks."
  • Monitoring Service plugin 1.8.1: "This hotfix update adds protection against XSS attacks on Archiving Settings page."

Jérôme Sautret, from ProcessOne, has announced ejabberd 19.09, that brings improved automatic certificate management stack.

The Prosody team have just released a new update to their stable branch, Prosody 0.11.3 which includes performance and compatibility improvements among other fixes.

Clients and applications

Profanity, has been released in version 0.7.0 after five months of work, bringing OMEMO end-to-end encryption, followed by a 0.7.1 bug fix release.

Multiple vulnerabilities have been found in Dino, please update as soon as possible if you are a Dino user.

Converse has been released in versions 5.0.2 and 5.0.3 fixing security issues among others. Converse users may find useful the new plugin to verify HTTP Requests via XMPP from Agayon. For developers, there is a Converse Docker image by odajay.

Conversations has been released in versions 2.5.8, 2.5.9, 2.5.10 and 2.5.11.

Monal 4 with iOS 13 support and dark mode is out. Mac update is expected to be released for October.

Services

Christopher Muclumbus, a listing and search engine of public XMPP chats, has been updated with a visual redesign, group chat avatars, link to anonymous web chat and logs if available, and software version pie chart.

Extensions and specifications

This month, there were three proposed XEPs, and two updated. No XEPs in Last Call, New, or Obsoleted.

Proposed

Message Fastening

Abstract: This specification defines a way for payloads on a message to be marked as being logically fastened to a previous message.

URL: https://xmpp.org/extensions/inbox/fasten.html

XMPP Compliance Suites 2020

Abstract: This document defines XMPP protocol compliance levels.

URL: https://xmpp.org/extensions/inbox/cs-2020.html

Authorization Tokens

Abstract: This document defines an XMPP protocol extension for issuing authentication tokens to client applications and provides methods for managing сlient connections.

URL: https://xmpp.org/extensions/inbox/auth-tokens.html

Updated

  • Version 0.13.1 of XEP-0280 (Message Carbons) has been released. Changelog: Add clear example on problematic (spoofed) carbon messages and that they need to be handled. (gl). URL: https://xmpp.org/extensions/xep-0280.html
  • Version 1.16.0 of XEP-0060 (Publish-Subscribe) has been released. Changelog:Add a pubsub#rsm disco#info feature to clear confusion (edhelas). URL: https://xmpp.org/extensions/xep-0060.html

See you in November!

This XMPP Newsletter is a community collaborative effort. Thanks to Nÿco, Daniel, Jwi, and MDosch for aggregating the news. Thanks Nÿco, Seve, Jwi, and Matt for the copywriting. Thanks Guus, Seve, Jonas for the reviews.

Please follow and relay the XMPP news on our Twitter account @xmpp.

License

This newsletter is published under the CC by-sa license: https://creativecommons.org/licenses/by-sa/4.0/

Maxime Buquet: Posts


ProcessOne: The concepts behind SwiftUI: What is the keyword “some” doing?

$
0
0

When you want to start learning SwiftUI, you create a project and get a basic example view with just a Text field. That default view is pretty simple, but already introduces many new concepts. Let’s focus in this article on the some keyword.

Note: If you are new to SwiftUI, you may want to read the The concepts behind Swift UI: Introduction.

The default SwiftUI content view

Here is an empty default view:

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The ContentView is an implementation of the View protocol. The View protocol requires a body computed property of type some View. So, you may be wondering what is that some keyword doing?

Opaque Types

The some keyword was introduced in Swift 5.1 and is used to define an Opaque Type. An opaque type is a way to return a type without needing to provide details on the concrete type itself. It limits what callers need to know about the returned type, only exposing information about its protocol compliance. Using an opaque type is a way to let the compiler decide what would be the concrete type of a function return, based on the actual returned value, limiting the options to the types that comply to a given protocol.

So, in SwiftUI case, “some View” means that the body will always be implementing the View protocol, but the concrete implementation type does not need to be known by the caller.

As explained in Swift documentation on Opaque Types:

You can think of an opaque type like being the reverse of a generic type.

An opaque type lets the function implementation pick the type for the value it returns in a way that’s abstracted away from the code that calls the function.

In other words, standard generic placeholders are filled by the caller. When you call a generic function, you are constraining the generic types to the types passed to that function. You can think of opaque types as a kind of generic function where placeholder types are filled by the implementation return type.

Examples

In the following code using opaque type, the body will always be of type Text:

struct ContentView: View {
    var x: Bool = false

    var body: some View {
        if x {
            return Text("This is true")
        } else {
            return Text("This is false")
        }
    }
}

At compile time, the function is known to return a Text view. The code is valid because Text view is implementing the View protocol.

However, the following code does not contain a valid opaque type, as the two code branches return different concrete types. The first branch returns a Text view, the second branch returns a VStack view.

struct ContentView: View {
    var x: Bool = false

    var body: some View {
        if x {
            return Text("This is true")
        } else {
            return VStack { Text("This is false") }
        }
    }
}

The returned type cannot be known at compile time, so this is not a valid opaque type.

If we compare with just protocol constraints, the following code is valid because the computed value body is not an opaque type but a protocol. The type information are “dropped” by the compiler and the only method that can be called on that result are the one implemented by the protocol (in our case, none).

protocol P {}
struct S1: P {}
struct S2: P {}

var x: Bool = false

var body: P {
    if x {
        return S1()
    } else {
        return S2()
    }
}

Conclusion

If you are interested to dig deeper, you should read Opaque Types documentation, especially the section on the ‘Differences Between Opaque Types and Protocol Types’.

On the surface returning an opaque type or a protocol can look similar, but the opaque types solve many limitations encountered when using directly protocol return types. The main limitations is that functions returning a protocol are not nestable, because a value of a protocol type doesn’t conform to that protocol. Opaque types preserve the underlying type information through associated types and thus can be nested. That’s the reason why Opaques Types were needed to implement SwiftUI.

There is a lot more to learn on Opaque Types, as it is an advanced concept. You do not need to master opaque types to be using SwiftUI but, as always, it is still better to understand the fundamental idea to progress on your learning path.

Ignite Realtime Blog: inVerse Openfire plugin 5.0.4.1 released

$
0
0

@wroot wrote:

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

This update brings changes and fixes from Converse 5.0.2-5.0.4 versions. Among them a few security patches, a new option for message corrections and other bug fixes.

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

Erlang Solutions: MongooseIM: Designed with privacy in mind

$
0
0

Let’s face it. We are living in an age where all technology players gather and process huge piles of user data, starting from our behavioural patterns and finishing on our location data. Hence, we receive personalized emails from online retail stores we have visited for just a second or personalized ads of stores in our vicinity that are displayed in our social media streams.

Consequently, more and more people become aware of how their privacy could be at risk with all of that data collection. In turn, the European Union strived to fight for consumer rights by implementing privacy guidelines in the form of the General Data Protection Regulation (GDPR), which governs how consumer data can be handled by third parties. In fact, over 200,000 privacy violation cases have been filed during the course of the last year followed with over €56m in fines for data breaches. Therefore, the stakes are high for all messaging service providers out there.

You might wonder: “Why should this matter to me? After all, my company is not in Europe.” Well, if any of the users of your messaging service are located in the EU, you are affected by GDPR as if you would host your service right there. Feeling uneasy? Don’t worry, MongooseIM team has got you covered. Please welcome the new MongooseIM that brings us full GDPR compliance.

Privacy by Design

A new concept has been defined with the dawn of GDPR - privacy by default. It is assumed that the software solution being used follows the principles of minimising and limiting, hiding and protecting, separating, aggregating, as well as, providing privacy by default.

Minimise and limit

The minimise and limit principle regards the amount of personal data gathered by a service. The general principle here is to take only the bare minimum required for a service to run instead of saving unnecessary data just in case. If more data is taken out, the unnecessary part should be deleted. Luckily, MongooseIM is using only the bare minimum of personal data provided by the users and relies on the users themselves to provide more if they wish to - e.g. by filling out the roster information. Moreover, since it is implementing XMPP and is open source, everybody has an insight as to how the data is processed.

Hide and protect

The hide and protect principle refers to the fact that user data should not be made public and should be hidden from plain view disallowing third parties to identify users through personal data or its interrelation. We have tackled that by handling the creation of JIDs and having recommendations regarding log collection and archiving.

What is this all about? See, JIDs are the central and focal point of MongooseIM operation as they are user unique identifiers in the system. As long as the JID does not contain any personally identifiable information like a name or a telephone number, the JID is far more than pseudo-anonymous and cannot be linked to the individual it represents. This is why one should refrain from putting personally identifiable information in JIDs. For that reason, our release includes a mechanism that allows automatic user creation with random JIDs that you can invoke by typing ‘register’ in the console. Specific JIDs are created by intentionally invoking a different command (register_identified).

Still, it is possible that MongooseIM logs contain personally identifiable information such as IP addresses that could correlate to JIDs. Even though the JID is anonymous, an IP address next to a JID might lead to the person behind it through correlation. That is why we recommend that installations with privacy in mind have their log level set to at least ‘warning’ level in order to avoid breaches of privacy while still maintaining log usability.

Separate and aggregate

The separate principle boils down to partitioning user data into chunks rather than keeping them in a monolithic DB. Each chunk should contain only the necessary private data for its own functioning. Such a separation creates issues when trying to identify a person through correlation as the data is scattered and isolated - hence the popularity of microservices. Since MongooseIM is an XMPP server written in Erlang, it is naturally partitioned into modules that have their own storage backends. In this way, private data is separated by default in MongooseIM and can be also handled individually - e.g. by deleting all the private data relating to one function.

The aggregation principle refers to the fact that all data should be processed in an aggregated manner and not in one focused on detailed personal cases. For instance, behavioural patterns should be representative of a concrete, not identifiable cohort rather than of a certain Rick Sanchez or Morty Smith. All the usage data being processed by MongooseIM is devoid of any personally identifiable traits and instead tracks metrics relevant to the health of the server. The same can be said for WombatOAM if you pair it with MongooseIM. Therefore, aggregation is supported by default.

Privacy by default

It is assumed that the user should be offered the highest degree of privacy by default. This is highly dependant on your own implementation of the service running on top of MongooseIM. However, if you follow our recommendations laid out in this post, you can be sure you implement it well on the backend side, as we do not differentiate between the levels of privacy being offered.

The Right of Access

According to GDPR, each user has the right of access to their own data that’s being kept by a service provider. That data includes not only personal data provided by the user but also all the derivate data generated by MongooseIM on its basis. That includes data held in mod_vcard, mod_roster, mod_mam, mod_offline, mod_pubsub, mod_private, mod_inbox, and logs. If we add a range of PubSub backends and MAM backends to the fray, one can see it gets complicated.

With MongooseIM we have put a lot of effort in order to make the retrieval as painless as possible for system administrators that oversee the day to day operations. That is why we have developed a mechanism you can start by executing the retrieve_personal_data command in order to collect all the personal and derivative data belonging to a user behind a specific JID. The command will execute for all the modules no matter if they are enabled or disabled. Then, all the relevant data is extracted per module and is returned to the user in the form of an archive.

In order to facilitate the data collection, we have changed the schemas for all of our MAM backends. This has been done to allow a swift data extraction since up till now it was very inefficient and resource hungry to run such a query. Of course, we have prepared migration strategies for the affected backends.

The Right to be Forgotten

The right to be forgotten is another one that goes alongside the right of access. Each user has the right to remove their footprint from the service. Since we know retrieval from all the modules listed above is problematic, removal is even worse.

We have implemented a mechanism that removes the user account leaving behind only the JID. You can run it by executing the “unregister” command. All of the private data not shared with other users is deleted from the system. In contrast, all of the private data that is shared with other users - e.g. group chats messages or PubSub flat nodes - is left intact as the content is not only owned by one party.

Logs are not a part of this action. If the log levels are set at least to ‘warning’, there is no personal data that can be tied to the JIDs in the first place so there is no need for removal.

Final Words on GDPR

The elements above make MongooseIM fully compliant with the current GDPR. We have continued our commitment to making MongooseIM the most GPDR complaint instant messaging platform in our recent release, MongooseIM 3.5. You can read about the latest changes here. However, you have to remember that this is only a piece of the puzzle. Since MongooseIM is a backend to a service there are other considerations that have to be fulfilled in order for the entire service to be GDPR compliant. Some of these considerations include process-oriented requirements of informing, enforcing, controlling, and demonstrating that have to be taken into consideration during service design.

Changelog

Please feel free to read the detailed changelog. Here, you can find a full list of source code changes and useful links.

Test our work on MongooseIM and share your feedback

Help us improve MongooseIM:

  1. Star our repo: esl/MongooseIM
  2. Report issues: esl/MongooseIM/issues
  3. Share your thoughts via Twitter
  4. Download Docker image with new release.
  5. Sign up to our dedicated mailing list to stay up to date about MongooseIM, messaging innovations and industry news.
  6. Check out our MongooseIM product page for more information on the MongooseIM platform.

ProcessOne: SwiftNIO: Understanding Futures and Promises

$
0
0

SwiftNIO is Apple non-blocking networking library. It can be used to write either client libraries or server frameworks and works on macOS, iOS and Linux.

It is built by some of the Netty team members. It is a port of Netty, a high performance networking framework written in Java and adapted in Swift. SwiftNIO thus reuses years of experience designing a proven framework.

If you want to understand in depth how SwiftNIO works, you first have to understand underlying concept. I will start in this article by explaining the concept of futures and promises. The ‘future’ concept is available in many languages, including Javascript and C#, under the name async / await, or in Java and Scala, under the name ‘future’.

Futures and promises

Futures and promises are a set of programming abstractions to write asynchronous code. The principle is quite simple: Your asynchronous code will return a promise instead of the final result. The code calling your asynchronous function is not blocked and can do other operations before it finally decides to block and wait for the result, if / when it really needs to.

Even if the words ‘futures’ and ‘promises’ are often use interchangeably, there is a slight difference in meaning. They represent different points of view on the same value placeholder. As explained in Wikipedia page:

A future is a read-only placeholder view of a variable, while a promise is a writable, single assignment container which sets the value of the future.

In other words, the future is what the client code receives and can use as a handler to access a future value when it has been defined. The promise is the handler the asynchronous code will keep to write the value when it is ready and thus fulfill the promise by returning the future value.

Let’s see in practice how futures and promises work.

SwiftNIO comes with a built-in futures and promises library. The code lies in EventLoopFuture. Don’t be fooled by the name: It is a full-featured ‘future’ library that you can use in your code to handle asynchronous operations.

Let’s see how you can use it to write asynchronous code, without specific reference to SwiftNIO-oriented networking operations.

Note: The examples in this blog post should work both on macOS and Linux.

Anatomy of SwiftNIO future / promise implementation

Step 1: Create an EventLoopGroup

The basic skeleton for our example is as follow:

import NIO

let evGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)

// Do things

try evGroup.syncShutdownGracefully()

We create an EventLoopGroup and shut it down gracefully at the end. A graceful shutdown means it will properly terminate the asynchronous jobs being executed.

An EventLoopGroup can be seen as a provider of an execution context for your asynchronous code. You can ask the EventLoopGroup for an execution context: an EventLoop. Basically, each execution context, each EventLoop is a thread. EventLoops are used to provide an environment to run your your concurrent code.

In the previous example, we create as many threads as we have cores on our computer (System.coreCount), but the number of threads could be as low as 1.

Step 2: Getting an EventLoop to execute your promise

In SwiftNIO, you cannot model concurrent execution without at least an event loop. For more info on what I mean by concurrency, you can watch Rob Pike excellent talk: Concurrency is not parallelism.

To execute your asynchronous code, you need to ask the EventLoopGroup for an EventLoop. You can use the method next() to get a new EventLoop, in a round-robin fashion.

The following code gets 10 event loops, using the next() method and prints the event loops information.

import NIO

print("System cores: \(System.coreCount)\n")
let evGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)

for _ in 1...10 {
    let ev = evGroup.next()
    print(ev)
}

// Do things

try evGroup.syncShutdownGracefully()

On my system, with 8 cores, I get the following result:

System cores: 8

SelectableEventLoop { selector = Selector { descriptor = 3 }, scheduledTasks = PriorityQueue(count: 0): [] }
SelectableEventLoop { selector = Selector { descriptor = 4 }, scheduledTasks = PriorityQueue(count: 0): [] }
SelectableEventLoop { selector = Selector { descriptor = 5 }, scheduledTasks = PriorityQueue(count: 0): [] }
SelectableEventLoop { selector = Selector { descriptor = 6 }, scheduledTasks = PriorityQueue(count: 0): [] }
SelectableEventLoop { selector = Selector { descriptor = 7 }, scheduledTasks = PriorityQueue(count: 0): [] }
SelectableEventLoop { selector = Selector { descriptor = 8 }, scheduledTasks = PriorityQueue(count: 0): [] }
SelectableEventLoop { selector = Selector { descriptor = 9 }, scheduledTasks = PriorityQueue(count: 0): [] }
SelectableEventLoop { selector = Selector { descriptor = 10 }, scheduledTasks = PriorityQueue(count: 0): [] }
SelectableEventLoop { selector = Selector { descriptor = 3 }, scheduledTasks = PriorityQueue(count: 0): [] }
SelectableEventLoop { selector = Selector { descriptor = 4 }, scheduledTasks = PriorityQueue(count: 0): [] }

The description represents the id of the EventLoop. As you can see, you can use 8 different loops before being assigned again an existing EventLoop from the same group. As expected, this matches our number of cores.

Note: Under the hood, most EventLoops are designed using NIOThread, so that the implementation can be cross-platform: NIO threads are build using Posix Threads. However, some platform specific loops, like NIO Transport service, are free from multiplatform constrains and are using Apple Dispatch library. It means, if you are targeting only MacOS, you can thus use SwiftNIO futures and promises directly with Dispatch library. Libdispatch being shipped with Swift on Linux now, it could also work there, but I did not test it yet.

Step 3: Executing async code

If you just want to execute async code without needing to wait back for a result, you can just pass a function closure to the EventLoop.execute(_:):

import NIO

print("System cores: \(System.coreCount)\n")
let evGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)

let ev = evGroup.next()

ev.execute {
    print("Hello, ")
}
// sleep(1)
print("world!")

try evGroup.syncShutdownGracefully()

In the previous code, the order in which “Hello, ” and “world!” are displayed is undetermined.

Still, on my computer, it is clear that they are not executed in order. The print-out in the execute block is run asynchronously, after the execution of the print-out in the main thread:

System cores: 8

world!
Hello, 

You can uncomment the sleep(1) function call to insert one second of delay before the second print-out instruction. It will “force” the ordering by delaying the main thread print-out and have “Hello, world!” be displayed in sequence.

Step 4: Waiting for async code execution

Adding timers in your code to order code execution is a very bad practice. If you want to wait for the async code execution, that’s where ‘futures’ and ‘promises’ comes into play.

The following code will submit an async code to run on an EventLoop. The asyncPrint function will wait for a given delay in the EventLoop and then print the passed string.

When you call asyncPrint, you get a promise in return. With that promise, you can call the method wait() on it, to wait for the completion of the async code.

import NIO

// Async code
func asyncPrint(on ev: EventLoop, delayInSecond: Int, string: String) -> EventLoopFuture<Void> {
    // Do the async work
    let promise = ev.submit {
        sleepAndPrint(delayInSecond: 1, string: string)
        return
    }

    // Return the promise
    return promise
}

func sleepAndPrint(delayInSecond: UInt32, string: String) {
    sleep(delayInSecond)
    print(string)
}

// ===========================
// Main program

print("System cores: \(System.coreCount)\n")
let evGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)

let ev = evGroup.next()

let future = asyncPrint(on: ev, delayInSecond: 1, string: "Hello, ")

print("Waiting...")
try future.wait()

print("world!")

try evGroup.syncShutdownGracefully()

The print-out will pause for one second on the “Waiting…” message and then display the “Hello, ” and “world!” messages in order.

Step 5: Promises and futures result

When you need a result, you need to return a promise that will give you more than just a signaling letting you know the processing is done. Thus, it will not be a promise of a Void result, but can return a more complex promise.

First, let’s see a promise of a simple result that cannot fail. In your async code, you can return a promise that will return the result of factorial calculation asynchronously. Your code will promise to return a Double and then submit the job to the EventLoop.

import NIO

// Async code
func asyncFactorial(on ev: EventLoop, n: Double) -> EventLoopFuture<Double> {
    // Do the async work
    let promise = ev.submit { () -> Double in
        return factorial(n: n)
    }

    // Return the promise
    return promise
}

// I would use a BigInt library to go further small number factorial calculation
// but I do not want to introduce an external dependency.
func factorial(n: Double) -> Double {
    if n >= 0 {
        return n == 0 ? 1 : n * factorial(n: n - 1)
    } else {
        return 0 / 0
    }
}

// ===========================
// Main program

print("System cores: \(System.coreCount)\n")
let evGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)

let ev = evGroup.next()

let n: Double = 10
let future = asyncFactorial(on: ev, n: n)

print("Waiting...")

let result = try future.wait()

print("fact(\(n)) = \(result)")

try evGroup.syncShutdownGracefully()

The code will be executed asynchronously and the wait() method will return the result:

System cores: 8

Waiting...
fact(10.0) = 3628800.0

Step 6: Success and error processing

If you are doing network operations, like downloading a web page for example, the operation can fail. You can thus handle more complex result, that can be either success or error. SwiftNIO offers a ready made type call ResultType.

In the next example, we will show an async function performing an asynchronous network operation using callbacks and returning a future result of ResultType. The ResultType will wrap either the content of the downloaded page or a failure callback.

import NIO
import Foundation

// =============================================================================
// MARK: Helpers

struct CustomError: LocalizedError, CustomStringConvertible {
    var title: String
    var code: Int
    var description: String { errorDescription() }

    init(title: String?, code: Int) {
        self.title = title ?? "Error"
        self.code = code
    }

    func errorDescription() -> String {
        "\(title) (\(code))"
    }
}

// MARK: Async code
func asyncDownload(on ev: EventLoop, urlString: String) -> EventLoopFuture<String> {
    // Prepare the promise
    let promise = ev.makePromise(of: String.self)

    // Do the async work
    let url = URL(string: urlString)!

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        print("Task done")
        if let error = error {
            promise.fail(error)
            return
        }
        if let httpResponse = response as? HTTPURLResponse {
            if (200...299).contains(httpResponse.statusCode) {
                if let mimeType = httpResponse.mimeType, mimeType == "text/html",
                    let data = data,
                    let string = String(data: data, encoding: .utf8) {
                    promise.succeed(string)
                    return
                }
            } else {
                // TODO: Analyse response for better error handling
                let httpError = CustomError(title: "HTTP error", code: httpResponse.statusCode)
                promise.fail(httpError)
                return
            }
        }
        let err = CustomError(title: "no or invalid data returned", code: 0)
        promise.fail(err)
    }
    task.resume()

    // Return the promise of a future result
    return promise.futureResult
}

// =============================================================================
// MARK: Main

print("System cores: \(System.coreCount)\n")
let evGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)

let ev = evGroup.next()

print("Waiting...")

let future = asyncDownload(on: ev, urlString: "https://www.process-one.net/en/")
future.whenSuccess { page in
    print("Page received")
}
future.whenFailure { error in
    print("Error: \(error)")
}

// Timeout: As processing is async, we can handle timeout by just waiting in
// main thread before quitting.
// => Waiting 10 seconds for completion
sleep(10)

try evGroup.syncShutdownGracefully()

The previous code will either print “Page received” when the page is downloaded or print the error. As your success handler receives the page content itself, you could do something with it (print it, analyse it, etc.)

Step 7: Combining async work results

Where promises really shine is when you would like to chain several async calls that depend on each other. You can thus write a code that appear logically in a sequence, but that is actually asynchronous.

In the following code, we reuse the previous async download function and process several pages by counting the number of div elements in all pages.

By wrapping this processing in a reduce function, we can download all web pages in parallel. We receive the page data as they are downloaded and we keep track of a counter of the number of div per page. Finally, we return the total as the future result.

This is a more involved example that should give you a better taste of what developing with futures and promises looks like.

import NIO
import Foundation

// =============================================================================
// MARK: Helpers

struct CustomError: LocalizedError, CustomStringConvertible {
    var title: String
    var code: Int
    var description: String { errorDescription() }

    init(title: String?, code: Int) {
        self.title = title ?? "Error"
        self.code = code
    }

    func errorDescription() -> String {
        "\(title) (\(code))"
    }
}

// MARK: Async code
func asyncDownload(on ev: EventLoop, urlString: String) -> EventLoopFuture<String> {
    // Prepare the promise
    let promise = ev.makePromise(of: String.self)

    // Do the async work
    let url = URL(string: urlString)!

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        print("Loading \(url)")
        if let error = error {
            promise.fail(error)
            return
        }
        if let httpResponse = response as? HTTPURLResponse {
            if (200...299).contains(httpResponse.statusCode) {
                if let mimeType = httpResponse.mimeType, mimeType == "text/html",
                    let data = data,
                    let string = String(data: data, encoding: .utf8) {
                    promise.succeed(string)
                    return
                }
            } else {
                // TODO: Analyse response for better error handling
                let httpError = CustomError(title: "HTTP error", code: httpResponse.statusCode)
                promise.fail(httpError)
                return
            }
        }
        let err = CustomError(title: "no or invalid data returned", code: 0)
        promise.fail(err)
    }
    task.resume()

    // Return the promise of a future result
    return promise.futureResult
}

// =============================================================================
// MARK: Main

print("System cores: \(System.coreCount)\n")
let evGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)

var futures: [EventLoopFuture<String>] = []

for url in ["https://www.process-one.net/en/", "https://www.remond.im", "https://swift.org"] {
    let ev = evGroup.next()
    let future = asyncDownload(on: ev, urlString: url)
    futures.append(future)
}


let futureResult = EventLoopFuture.reduce(0, futures, on: evGroup.next()) { (count: Int, page: String) -> Int in
    let tok =  page.components(separatedBy:"<div")
    let p_count = tok.count-1
    return count + p_count
}

futureResult.whenSuccess { count in
    print("Result = \(count)")
}
futureResult.whenFailure { error in
    print("Error: \(error)")
}

// Timeout: As processing is async, we can handle timeout by just waiting in
// main thread before quitting.
// => Waiting 10 seconds for completion
sleep(10)

try evGroup.syncShutdownGracefully()

This code actually builds a pipeline as follows:

Conclusion

Futures and promises are at the heart of SwiftNIO design. To better understand SwiftNIO architecture, you need to understand the futures and promises mechanism.

However, there is more concepts that you need to master to fully understand SwiftNIO. Most notably, inbound and outbound channels allow you to structure your networking code into reusable components executed in a pipeline.

I will cover more SwiftNIO concepts in a next blog post. In the meantime, please send us your feedback :)

ProcessOne: FrenchKit Conference: Day 1 Highlights

$
0
0

FrenchKit is an iOS and macOS developer conference held in Paris. The fourth edition took place on October 7-8, 2019. I was attending this conference for the first time and really enjoyed the gathering. The conference is well organised, with a lot of excellent speakers. There is a true good vibe coming from the FrenchKit community, at least from a French perspective, but I feel this impression was shared by international visitors I spoke with.

Here are my highlights for the first day.

Swift Pills

Swift Pills were three small lightning talks presented at various time during the first day by Vincent Pradeilles.

The talks were nice, sharing small 5-minutes tips on various topics:
Encapsulating [weak self]
Let’s talk about @autoclosure
Optionals

I liked the talks, but more than that, I feel that spreading some lightning talks throughout the day is a very nice idea. It breaks the rhythm of long talks sequence and feels like a refreshing break. It felt better than to gather all lightning talks at once. I think other conferences should also adopt that idea.

Animations with SwiftUI

Chris Eidhof is famous for his live coding video tutorials on Objc.io. He demoed how to build a custom shake animation with SwiftUI, using a GeometryEffect view modifier. You can read more about the topic he presented on his blog: SwiftUI: Shake Animation

Understanding Combine

The second talk was presented by Daniel Steinberg, another prominent speaker in the Swift development community. He demonstrated how the reactive pattern differs from the delegation pattern, and did a great job explaining the use of Combine with UIKit (Yes, you can use @Published on UIKit View Controllers).

SwiftUI with Redux

Thomas Ricouard managed to pack a nice Redux introduction in a short time, with examples coming from its app MovieSwiftUI. You can check his Open Source app on GitHub: MovieSwiftUI.

As he says, Apple hinted at unidirectional data flow (a-la Redux) at WWDC, but did not precisely described how to leverage it. Thomas talks helped to fill the gap.

Showcase driven development

This talk by Jérôme Alves introduced the approach used at Heetch to help shorten each development iteration. The principle is simple: If you want to avoid large pull requests with complex merges, you need to focus on short-lived development branches changing only a few things. You thus split big features into sub-features and even split them by layers (model, view, etc) to have smaller branches to merge.

However, how do you demonstrate work-in-progress? The solution presented by Jérôme is to introduce a menu in the Debug build of their application dedicated to demonstrating your unfinished work. The showcase menu in the test app offers a showcase browser that can be used to show prototype views, workflows, animations, etc. It helps discussing the next steps and requirements with management, marketing or customers.

Finally, even if Jérôme recommends to roll your own code to set up that feature, Heetch released ShowcaseKit to give you an idea of what they did and how they are using the showcase approach.

Slide to unlock: Building Custom UI with UIKit and CoreAnimation

I also enjoyed this talk from Joel Kin. His talk is about showing that if you master the various layers of Apple UI frameworks (from UIKit to Core Animation), it often make more sense to develop a custom component based on Apple standard tooling than to introduce and adapt a dependency from GitHub.

He does a great job showing how he built a custom slide-to-unlock component, using less lines of code than in alternative open source components.

The example is quite convincing, starting from a UISlider and ending up with a great looking “slide-to-unlock” components, complete with even the shimmer effect.

You can checkout the code on GitHub: SlideToUnlock

Workshops!

To end the day, you had a choice of seven possible workshops. I attended “Exploring Combine”, hosted by Florent Pillet and Antoine Van Der Lee.

While I had already explored many concepts, it was a nice introduction to Apple Reactive programming framework.

The workshop is a nice way to meet people and share ideas through peer programming to solve the proposed exercices.

Conclusion for Day 1

Day 1 at FrenchKit was a blast. The day was packed with great talks. The atmosphere was very friendly and the venue was great.

Stay tuned for my highlights of Day 2!

ProcessOne: FrenchKit Conference: Day 2 Highlights

$
0
0

Yesterday, I shared my highlights on FrenchKit Conference 2019, Day 1. Today, I will talk about FrenchKit Day 2.

Swift Superpowers

Swift Superpowers were three lightning talks presented by David Bonnet, mostly focused on server-side Swift, and spread out during the day. He covered the following topics:

  • Vapor 3 code examples
  • Networking example with SwiftNIO and the new cross-platform URLSession client (you need to import FoundationNetworking on Linux)
  • Debug of Vapor apps in Xcode and cross-platform testing using XCTest and Docker

Like the day before, those lightning talks were great and refreshing.

Swift Generics: It isn’t supposed to hurt

Rob Napier explained how to incrementally refactor your code using generics to make it more flexible and avoid repeating the same patterns.

The talk was interesting and condensed, and was followed by an even more content-packed masterclass at the end of the day. The topic is fascinating, but the masterclass format does not do it justice. It is very hard to keep your focus for 90 minutes on hardcore generics code refactors.

The takeaway, as always, is to start with concrete code first and then work out the generics.

Note encryption: 10 lines for encryption, 1500 lines for key management

Next was a mind-blowing talk on cryptography by Anastasiia Voitova. She knows here topic and the story of how her company Cossack Labs helped implement end-to-end encryption in the Bear note taking app was very enlightening.

SwiftPM’s New Resolver: Can it Resolve the Conflicts in my Relationship?

I quite like Mert Buran talk. He managed to make a dry topic, Swift Package topic resolution, interesting.

And Mert talk was the most funny of the conference, with example of conflict resolution in his rock band.

Finally, my takeaway is that Swift Package Manager is a nice piece of Open Source code that you can read and learn from.

This is not rocket (data) science

It was another great talk. Hervé Beranger covered all the concrete use cases of AI that you can add today in your iOS applications:

  • Voice Interfaces
  • Translations
  • Semantic search
  • Sentiment analysis
  • Suggested related searches
  • Smart replies
  • Home-made text classifiers

He gave a lot of examples, with the Apple API you can use to implement them.

An introduction to property-based testing

I was happy to see a talk on property-based testing. At ProcessOne, we have worked with Quviq to use Erlang Quickcheck on our code base. I have been passionate about Property based testing.

Vincent Pradeilles did a good job presenting Property-based testing and explaining how you can use it as an addition to more traditional testing methods.

Swift has quite a nice implementation of Quickcheck, called SwiftCheck. You should give it a try.

Vincent also mentioned lightweight alternative to help testing with random data, such as using faker library to generate random data, using a fuzzy testing approach.

Shipping a Catalyst app: The Good, the Bad and the Ugly

This was a nice talk by Peter Steinberger sharing his feedback on Catalyst. Catalyst is a framework to port UIKit based iPadOS based apps to macOS. He shared the trick they had to use to make their PDFViewer app feel more native on macOS using Catalyst.

For example, Peter was forced to bridge to AppKit for some features like:

  • Toolbar with toolbar editor
  • NSSearch
  • NSCursor changes (MacSupport bundle)
  • Open Recent menu support (particularly painful to implement as Catalyst Apps are sandboxed, like all Mac AppStore apps)

And that’s a wrap

The final talk was from Olivier Halligon and was one of my favorite. He managed to explain why and how to use property wrappers in Swift. Property wrappers were added in Swift 5.1 and it is my favorite new feature. If you use it with a clear purpose, it can really help improve your code.

You can check his slides: And that’s a Wrap!

Make sure to check the last tip on how to use property wrappers to avoid implement JSON Codable manually for your struct just to handle properly the date format. This is a tip I expect to reuse often.

Conclusion

Overall, the FrenchKit Conference was really great. It was a well organised event packed with great talks.

If you could not attend, you can always catch up with the talks on video when they get released.

Debian XMPP Team: New Dino in Debian

$
0
0

Dino (dino-im in Debian), the modern and beautiful chat client for the desktop, has some nice, new features. Users of Debian testing (bullseye) might like to try them:

  • XEP-0391: Jingle Encrypted Transports (explained here)
  • XEP-0402: Bookmarks 2 (explained here)

Note, that users of Dino on Debian 10 (buster) should upgrade to version 0.0.git20181129-1+deb10u1, because of a number of security issues, that have been found (CVE-2019-16235, CVE-2019-16236, CVE-2019-16237).

There have been other XMPP related updates in Debian since release of buster, among them:

You might be interested in the Octobers XMPP newsletter, also available in German.


ProcessOne: Understanding ejabberd OAuth Support & Roadmap

$
0
0

Login and password authentication is still the most commonly used auth mechanism on XMPP services. However, it is causing security concerns because it requires to store the credentials on the client app in order to login again without asking for the password.

Mobile APIs on iOS and Android can let you encrypt the data at REST, but still, it is best not to rely on storing any password at all.

Fortunately, several solutions exist – all supported by ejabberd. You can either use OAuth or Certificate-based authentication. Client certificate management being still quite a tricky issue, I will focus in this post on explaining how to set up and use ejabberd OAuth support.

Understanding ejabberd OAuth Support

The principle of OAuth is simple: OAuth offers a mechanism to let your users generate a token to connect to your service. The client can just keep that token to authenticate and is not required to store the password for subsequent authentications.

Implicit grant

As of ejabberd 19.09, ejabberd supports only the OAuth implicit grant. Implicit grant is often used to let third-party clients — clients you do not control — connect to your server.

The implicit grant requires redirecting the client to a web page, so the client does not even see the login and password of the user. Indeed, as you cannot trust third-party clients, this is the sane thing to do to keep your users’ passwords for being typed directly in any third-party client. You can never be sure that the client will not store it (locally, or worse, in the cloud).

With the implicit grant, the client app directs the user to the sign-in page on your server to authenticate and get the token, often with login and password (but the mechanism can be different and could involve 2FA, for example). Your website then uses a redirect URL that will be passed back to the client, containing the token to use for logging in. The redirect happens usually using client-registered domain or custom URL scheme.

… and password grant

The implicit grant workflow is not ideal if your ejabberd service is only useable with your own client. Using web view redirects can feel cumbersome in your onboarding workflow. As you trust the client, you probably would like to be able to directly call an API with the login and password, get the OAuth token back, and forget about the password. The user experience will be more pleasant and feel more native.

This flow is known in OAuth as the OAuth password grant.

In the upcoming ejabberd version, you will be able to use OAuth password grant as an addition to the implicit grant. The beta feature is already in ejabberd master branch, so you have a good opportunity to try it and share your feedback.

Let’s use ejabberd OAuth Password grant in practice

Step 1: ejabberd configuration

To support OAuth2 in ejabberd, you can add the following directives in ejabberd config file:

# Default duration for generated tokens (in seconds)
# Here the default value is 30 days
oauth_expire: 2592000
# OAuth token generation is enabled for all server users
oauth_access: all
# Check that the client ID is registered
oauth_client_id_check: db

In your web HTTPS ejabberd handler, you also need to add the oauth request handler:

listen:
  # ...
  -
    port: 5443
    ip: "::"
    module: ejabberd_http
    tls: true
    request_handlers:
      # ...
      "/oauth": ejabberd_oauth

Note: I am using HTTPS, even for a demo, as it is mandatory to work on iOS. During the development phase, you should create your own CA to add a trusted development certificate to ejabberd. Read the following blog post if you need guidance on how to do that: Using a local development trusted CA on MacOS

You can download my full test config file here: ejabberd.yml

Step 2: Registering an OAuth client

If you produce a first party client, you can bypass the need for OAuth to redirect to your browser to get the token.

As you trust the application you are developing, you can let the user of your app directly enter the login and password inside your client. However, you should never store the password directly, only the OAuth tokens.

In ejabberd, I recommend you first configure an OAuth client, so that it can check that the client id is registered.

You can use the ejabberdctl command oauth_add_client_password, or use the Erlang command line.

Here is how to use ejabberdctl to register a first-party client:

ejabberdctl oauth_add_client_password <client_id> <client_name> <secret>

As the feature is still in development, you may find it easier to register your client directly using Erlang command-line. Parameters are client_id, client_name and a secret:

1> ejabberd_oauth:oauth_add_client_password(<<"client-id-Iegh7ooK">>, <<"Demo client">>, <<"3dc8b0885b3043c0e38aa2e1dc64">>).
{ok,[]}

Once you have registered a client, you can start generating OAuth tokens for your users from your client, using an HTTPS API.

Step 3: Generation a password grant token

You can use the standard OAuth2 password grant query to get a bearer token for a given user. You will need to pass the user JID and the password. You need to require the OAuth scope sasl_auth so that the token can be used to authentication directly in the XMPP flow.

Note: As you are passing the client secret as a parameter, you must use HTTPS in production for those queries.

Here is an example query to get a token using the password grant flow:

curl -i -POST 'https://localhost:5443/oauth/token' -d grant_type=password -d username=test@localhost -d password=test -d client_id=client-id-Iegh7ooK  -d client_secret=3dc8b0885b3043c0e38aa2e1dc64 -d scope=sasl_auth

HTTP/1.1 200 OK
Content-Length: 114
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{"access_token":"DGV4JFzW15iZFmsnvzT7IymupTAYvo6U","token_type":"bearer","scope":"sasl_auth","expires_in":2592000}

As you can see, the token is a JSON string. You can easily extract the access_token from it. That’s the part you will use to authenticate on XMPP.

Step 4: Connecting on XMPP using an OAuth token

To authenticate over XMPP, you need to use the X-OAUTH2 mechanism. X-OAUTH2 was defined by Google for Google Talk and reused later by Facebook chat. You can find Google description here: XMPP OAuth 2.0 Authorization.

Basically, it encodes the JID and token as in the SASL PLAIN authorisation, but instead of passing the PLAIN keyword as mechanism, it uses X-OAUTH2. ejabberd will thus know that it has to check the secret against the token table in the database, instead of checking the credentials against the password table.

Quick demo

Next, let’s demonstrate the connection using Fluux Go XMPP library, which is the only library I know that supports OAuth tokens today.

Here is an example client login on XMPP with an OAuth2 token:

package main

import (
    "fmt""log""os""gosrc.io/xmpp""gosrc.io/xmpp/stanza"
)

func main() {
    config := xmpp.Config{
        Address:      "localhost:5222",
        Jid:          "test@localhost",
        Credential:   xmpp.OAuthToken("DGV4JFzW15iZFmsnvzT7IymupTAYvo6U"),
        StreamLogger: os.Stdout,
    }

    router := xmpp.NewRouter()
    router.HandleFunc("message", handleMessage)

    client, err := xmpp.NewClient(config, router)
    if err != nil {
        log.Fatalf("%+v", err)
    }

    // If you pass the client to a connection manager, it will handle the reconnect policy
    // for you automatically.
    cm := xmpp.NewStreamManager(client, nil)
    log.Fatal(cm.Run())
}

func handleMessage(s xmpp.Sender, p stanza.Packet) {
    msg, ok := p.(stanza.Message)
    if !ok {
        _, _ = fmt.Fprintf(os.Stdout, "Ignoring packet: %T\n", p)
        return
    }

    _, _ = fmt.Fprintf(os.Stdout, "Body = %s - from = %s\n", msg.Body, msg.From)
}

The important part for OAuth is that you are telling the library to use an OAuth2 token with the following value in the xmpp.Config struct:

xmpp.Config{
    // ...
        Credential:   xmpp.OAuthToken("DGV4JFzW15iZFmsnvzT7IymupTAYvo6U"),
        }

You can check the example in Fluux XMPP example directory: xmpp_oauth2.go

There is more

As I said, ejabberd OAuth support is not limited to generating password grant. Since ejabberd 15.09, we support implicit grant generation and it is still available. You can find more information in ejabberd documentation: OAuth

Moreover, there is more than XMPP authentication with OAuth 2. In the current development version, you can authenticate your devices on ejabberd MQTT service using MQTT 5.0 Enhanced Authentication. The authentication method to use is the same as for XMPP: We reuse the X-OAUTH2 method name. When trying to use this method, the server will confirm you are allowed to use that method and you can pass your token in return.

Please, note that you will need to use an MQTT 5.0 client library to use OAuth2 authentication with MQTT.

Conclusion

ejabberd OAuth XMPP and MQTT authentication is using the informal auth mechanism that was introduced by Google Talk and reused by Facebook. It does the job and fills an important security need.

That said, I would love to see more standard support from the XMPP Standard Foundation regarding OAuth authentication. For example, getting a specification translating OAuth authentication to XMPP flow would be of great help.

Still, in the meantime, I hope more libraries support that informal OAuth specification, so that client developers have good alternative to local password storage for subsequent authentications.

Please, give it a try from master and send us feedback if you want to help us shape the evolution of OAuth support in ejabberd.

… And let’s end password-oriented client authentication :)

Isode: Isode at TechNet Europe

$
0
0

In a weeks time, TechNet Europe kicks off. This is AFCEA Europe’s second largest annual event, and we will be there.

We will be demonstrating our Military Messaging system, and showing off the capabilities that make it ideal for deployments requiring high levels of security, such as:

  • Conversion/equivalencies between label formats, using a Security Policy (SPIF) based approach to map between the various supported label formats on Email. This will be demonstrated using our web-based email client, Harrier.
  • XEP-0258 (Security Labels in XMPP) support as well as conversion/equivalencies between label formats in XMPP. Our Swift XMPP client will be used to to demonstrate 1-2-1 and MUC room chats using security labels.

We will also be demonstrating our IRC Gateway product, the M-Link IRC -Gateway enables connections between IRC and XMPP servers.

If you’re attending TechNet Europe, pop along to the Isode stand for a demonstration of our software and to say hi to Jeff Tillotson (our Defence business development manager) and Jon Purvis (one of our pre-sales engineers).

ProcessOne: ejabberd 19.09.1

$
0
0

We are announcing a supplemental bugfix release of ejabberd version 19.09.1. The main focus has been to fix the issue with webadmin returning 404 Not Found when Host header doesn’t match anything in configured hosts.

Bugfixes

Some people have reported still having issues when connecting to the web administration console. We solved that hopefully once and for all.

Technical changes

There is no change to perform on the database to move from ejabberd 19.09 to ejabberd 19.09.1. Still, as usual, please, make a backup before upgrading.

Download and install ejabberd 19.09.1

The source package and binary installers are available at ProcessOne. If you installed a previous version, there are no additional upgrade steps, but as a good practice, plase backup your data.

As usual, the release is tagged in the Git source code repository on Github. If you suspect that you’ve found a bug, please search or fill a bug report in Issues.


Full changelog
===========

* Bugfixes
– Fix issue with webadmin returning 404 when ‘Host’ header doesn’t match anything in configured hosts
– Change url to guide in webadmin to working one

Jérôme Poisson: SàT progress note 2019-W41

$
0
0

Hi everybody,

I've skipped the progress notes for a couples of weeks because I've been working on the same thing for a long time (writing documentation for jp + porting it to asyncio), it was making no sense to repeat this every week, and also because I've been seriously lacking time lastly due to events in my private life.

This week I've had absolutely no time to work on SàT, I could only update the instance running https://www.goffi.org and https://salut-a-toi.org to the dev version (i.e. on Python 3). One visible change is the performance boost when using tickets: retrieving tickets and building the page was previously taking ages, and the issue has been found and fixed.

Let's summarize some of the changes I've done while being silent on the blog:

  • jp, The CLI frontend of SàT is now fully documented. That means that all the commands (107 commands so far!) are explained with examples; you can check the rendered doc at https://salut-a-toi.org/documentation. This was the most time consuming task during the last weeks, really boring to do, but necessary. I hope it will help people to use the full potential of this tool.

  • also jp is now using asyncio, and some issues preventing to use an other bridge than D-Bus have been fixed. In other words, you can now use a bridge like pb (Twisted's Perspective Broker) instead of dbus. This has been requested by people using jp on servers where installing D-Bus was not desired, pb being natively supported by Twisted and then SàT, it's working without any extra dependencies (but D-Bus is still the default and recommanded bridge in most cases).

  • Many more or less small improvments have been done to jp and SàT during this process. Some commands options have been updated.

  • There are now 2 themes for jp, for light or dark background. You can specify the background using background option in [jp] section of sat.conf. Before that, jp was barely readable on light background. I still need to do something similar for Primitivus.

  • If not specified, jp will automatically detect the background colour of the terminal where it runs. I've discovered that it was possible to do that thanks to a ticket I've randomly found reported for vim and neovim: there is an ANSI escape code returning the RGB value of background colour on compatible terminal, this is really useful.

  • A long awaited feature: jp is now prompting for profile password if it is needed and not specified in arguments. This is better for security as the password is not visible anymore on the screen, in your shell history, or in your processes lists.

  • Thanks to TLS fixes in latest Twisted version (19.7.0), I could remove the code working around the issues. Twisted required minimal version is now 19.7.0.

  • last but not least, I've fixed an issues during launch of SàT backend, Libervia, and SàT Pubsub which could result in ReactorAlreadyInstalledError when they were all installed in the same Python environment.

That's it. With the new documentation and some usability fixes like background detection, I think jp is now very usable, and I hope people will give it a try. It's a tool particularly useful when working a lot in terminal (to send or upload a file for instance), or if you want to do scripting with XMPP. XMPP server admins could use it to, for instance, send service messages using ad-hoc command (see examples in documentation), and XMPP dev could use it to inspect PubSub services.

There are several options for next big features in SàT, I'm currently thinking about implementing video calls as I need it, but I may well also work on other tasks first (like improving file sharing).

Erlang Solutions: Build a complete iOS messaging app using XMPPFramework - Tutorial Part 1

$
0
0

YAXT??! Yet another XMPP tutorial?

 

Well, this is going to be another tutorial, but I’m going to try to make it a little bit different. This is an XMPP tutorial from an iOS developer’s perspective. I’ll try to answer all the questions I had when I started working in this area. This journey is going to go from no XMPP knowldege at all to having a fully functional instant messaging iOS app using this cool protocol. We are going to be using the super awesome (yet overwhelming at the beginning…) XMPPFramework library, and the idea is also to also mix in some iOS concepts that you are going to need for your app.

What’s XMPP?

 

From Wikipedia: Extensible Messaging and Presence Protocol (XMPP) is a communications protocol for message-oriented middleware based on XML.

This basically means XMPP is a protocol for exchanging stuff. What kind of stuff? Messages and presences. We all know what messages are, but what about presences? A presence is just a way of sharing a “status”, that’s it. You can be ‘online’, 'offline’, 'having lunch’, or whatever you want. Also there’s another important word: Extensible meaning it can grow. It started as an instant messaging protocol and it has grown into multiple fields for example IoT (Internet of Things). And last, but not least: every piece of information we are going to exchange under this protocol is going to be XML. I can heard you complaining but… Come on, it’s not that bad!

Why do we need XMPP? Why not just REST?

 

Well what other options do we have? On the one hand, a custom solution means building everything from scratch, that takes time. On the other hand, we have XMPP, a super tested technology broadly used by millions of people every day, so we can say that’s an advantage over a custom approach.

Everytime I talk about XMPP, someone asks me 'Why not just REST?’. Well, there is a misconception here. REST is not a protocol, it’s just a way of architecting a networked application; it’s just a standarized way of doing something (that I love btw). So let’s change the question to something that makes more sense: “Why not just build a custom REST chat application?”. The first thing that comes to my mind is what I already explained in the previous paragraph, but there is something else. How do I know when someone has sent me a message? For XMPP this is trivial: we have an open connection all the time so, as soon as a message arrives to the server, it will send us the message. We have a full-duplex. On the other hand, the only solution with REST is polling. We will need to ask the server for new messages from time to time to see if there is something new for us. That sucks. So, we will have to add a mechanism that allows us to receive the messages as soon as they are created, like SSE or WebSockets.

There is one more XMPP advantage over a custom REST chat application. REST uses HTTP, an application level protocol that is built on top of a transport level protocol: TCP. So everytime you want to use your REST solution, you will need HTTP, a protocol that is not always available everywhere (maybe you need to embed this in a cheap piece of hardware?). Besides, we have XMPP built on top of TCP that’s going to be always available.

What’s the basic stuff I need to know to get started?

 

Well, you know a lot already but let’s make a list. Lists are always good:

  • XMPP is built on top of TCP. It keeps an open connection all the time.
  • Client/Server architecture. Messages always go through a server.
  • Everything we send and receive is going to be XML and it’s called Stanza.
  • We have three different types of stanzas: iq, message and presence.
  • Every individual on the XMPP network is univocally identified by a JID (Jabber ID).
  • All the stanzas are cointained in a Stream. Let’s imagine the Stream as a white canvas where you and the server write the stanzas.
  • Stream, iq, message and presence are the core of XMPP. You can find everything perfectly detailed in RFC6120
  • XMPP can be extended to accomplish different stuff. Each extension is called XEP (XMPP Extension Protocol).

 

What’s a JID?

Jabber ID (JID) is how we univocally identify each individual in XMPP. It is the address to where we are going to send our stanzas.

This is how a JID looks like:

  • localpart: This is your username.
  • domainpart: Server name where the localpart resides.
  • resourcepart: This is optional, and it identifies a particular client for the user. For example: I can be logged in with andres@erlang-solutions.com on my iPhone, on my Android and on my mac at the same time… So all these will be the same localpart + domainpart but different resourcepart

I’m sure you have already noticed how similar the JID looks to a standard email address. This is because you can connect multiple servers together and the messages are rooted to the right user in the right server, just as email works. Pretty cool, right?

Sometimes you will see we have a JID with just the domain part. Why?! Because it’s also possible to send stanzas to a service instead of a user. A service? What’s a service?! Services are different pieces of an XMPP server that offer you some special functionality, but don’t worry about this right now, just remember: you can have JIDs without a localpart.

What’s a Stanza?

Stanza is the name of the XML pieces that we are going to be sending and receiving. The defined stanzas are: <message/><presence/> and <iq/>.

 

<message/>

This is a basic <message/> stanza. Everytime you want to send a message to someone (a JID), you will have to send this stanza:

<message from='andres@erlang-solutions.com/iphone' to='juana@erlang-solutions.com' type='chat'>
    <body>Hey there!</body>
</message>

 

<iq/>

It stands for Info/Query. It’s a query-action mechanism, you send an iq and you will get a response to that query. You can pair the iq-query with the iq-response using the stanza id.

For example, we send an iq to the server to do something (don’t pay attention to what we want to do… you just need to know there is an iq stanza and how the mechanism works):

<iq to='erlang-solutions.com' type='get' id='1'>
  <query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>

And we get back another iq with the same id with the result of the previous query:

<iq from='erlang-solutions.com' to='ramabit@erlang-solutions.com/Andress-MacBook-Air' id='1' type='result'>
    <query xmlns='http://jabber.org/protocol/disco#items'>
        <item jid='muc.erlang-solutions.com'/>
        <item jid='muclight.erlang-solutions.com'/>
        <item jid='pubsub.erlang-solutions.com'/>
    </query>
</iq>

 

<presence/>

Used to exchange presence information, as you could have imagined. Usually presences are sent from the client to the server and broadcasted by it. The most basic, yet valid presence, to indicate to the server that a user is avaiable is:

<presence/>

After a sucessfull connection, you are not going to receive any <message/> until you make yourself available sending the previous presence.

If you want to make yourself unavailable, you just have to send:

<presence type="unavailable"></presence>

If we want to make the presences more useful, we can send something like this:

<presence>
      <status>On vacation</status>
</presence>

 

What’s a Stream?

Before answering this, let’s refresh our mind. What’s a Unix socket? From Wikipedia: A socket is a special file used for inter-process communication. These allows communication between two processes. So a socket is a file that can be written by two processes (in the same computer or in different computers in the same network). So the client is going to write to this file and server too.

Ok, but how is a socket related to a Stream? Well, we are going to be connected to a server using a socket, therefore we are going to have a 'shared file’ between the client and the server. This shared file is a white canvas where we are going to start writting our XML stanzas. The first thing we are going to write to this file is an opening <stream> tag! And there you go… that’s our stream.

Perfect, I understand what a stream is, but I still don’t understand how to send a message to the server. Well, the only thing we need to do to send a message is writting a <message/> stanza in our shared file. But what happens when the server wants to send me a message? Simple: it will write the message in the 'shared file’.

Are we ok so far?

 

I’m sure at this point you have questions like:

  • “What?! An active TCP connection open all the time? I’m used to REST! How am I going to do that?!” 

​           Easy, you don’t have to care about that any more! That’s why we are going to use the library, and it will take care of that.

  • “You said nothing about how to connect to the server!”

           Believe me, you don’t have to care about this either. If we start adding all this info, we are going to get crazy. Trust me, I’ve been there.

  • “What about encrypted messages? We need security! How are we going to handle this?”

          Again, you don’t have to care about this at this point. Baby steps!

 

You just need to be able to answer: “What’s XMPP?”, “How do you send a message?”, “How do you change your status in XMPP?”, “How do you ask something to the server?”, “What’s a Stream?”. If you can answer all that, you are WAY better than me when I started.

All the concepts we described so far are the core of XMPP.  To find out how to get started with XMPPFramework, how to connect to the server and authenticate a user,go to PART 2!

Also, check out MongooseIM, our XMPP based open source mobile messaging platform.

We thought you might also be interested in:

XMPPFramework - build an iOS app Part 2

Our portfolio of Erlandg based products

Viewing all 3042 articles
Browse latest View live