> ## Content Index
> Fetch the complete content index at: https://www.process-one.net/llms.txt
> Use this file to discover other available public pages before exploring further.

# go-xmpp v0.4.0
- URL: https://www.process-one.net/blog/go-xmpp-v0-4-0/
- Published: 2020-01-14T17:25:00.000Z
- Updated: 2024-09-16T16:26:16.000Z
- Author: ProcessOne

A new version of the go-xmpp library, which can be used to write XMPP clients or components in Go, has been released. It’s available on [GitHub](https://github.com/FluuxIO/go-xmpp?ref=process-one.net).

You can find the full changelog here: [CHANGELOG](https://github.com/FluuxIO/go-xmpp/blob/master/CHANGELOG.md?ref=process-one.net)  
Some noteworthy features are the support of three new extensions:

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

for both component and client.

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

## New Extensions

### PubSub

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

This example assumes that you have a locally running jabber server listening on port 5222, like [ejabberd](https://www.ejabberd.im/?ref=process-one.net).

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

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

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

```

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

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

```

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

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

```

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

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

```

We can connect:

```go
err := c.Connect()

```

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

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

```

Now we just have to wait for our response:

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

```

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

```go
// Create a subscribe request
rqSubscribe, err := stanza.NewSubRq(serviceName, stanza.SubInfo{        
  Node: nodeName,
  Jid:  userJID,
})
// Send it
pubRespCh, _ := client.SendIQ(ctx, rqSubscribe)

```

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

Let’s say we want to publish this item:

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

```

This is how we would need to build the request:

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

```

Then we can send it !

```go
client.SendIQ(ctx, pub)

```

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

You can find another use of this extension in our [xmpp\_jukebox example](https://github.com/FluuxIO/go-xmpp/tree/master/%5Fexamples/xmpp%5Fjukebox?ref=process-one.net).

### Full example

The full program that:

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

is available in our [library repository](https://github.com/FluuxIO/go-xmpp/tree/master/%5Fexamples/xmpp%5Fpubsub%5Fclient?ref=process-one.net), with a few extras.

## XEP-0050 : Ad-Hoc Commands

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

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

```

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

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

## XEP-0004: Data Forms

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

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

```

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

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

```

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

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

```

Edit fields to your liking:

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

```

and send them back to the service :

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

```

## Error callbacks

### Overview

The “NewClient” function changed signature from :

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

```

to :

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

```

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

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

### Simple example

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

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

```

That’s it !