= WeeChat Relay protocol
:author: Sébastien Helleu
:email: flashcode@flashtux.org
:lang: en
:toc:
:toclevels: 4


This document is the specification of WeeChat Relay protocol: the protocol used
to relay WeeChat data to clients, which are mostly remote interfaces.


[[introduction]]
== Introduction

[[terminology]]
=== Terminology

The following terms are used in this document:

* 'relay': this is the WeeChat with relay plugin, which acts as "server" and
  allows 'clients' to connect
* 'client': this is another software, connected to 'relay' via a network
  connection; in most cases, this 'client' is a remote interface.

[[network_diagram]]
=== Network diagram

The 'clients' are connected to 'relay' like shown in this diagram:

....
                                              ┌──────────┐ Workstation
 ┌────────┐                               ┌───┤ client 1 │ (Linux, Windows,
 │  irc   │◄──┐  ╔═══════════╤═══════╗    │   └──────────┘ BSD, Mac OS X ...)
 └────────┘   └──╢           │       ║◄───┘   ┌──────────┐
   ......        ║  WeeChat  │ Relay ║◄───────┤ client 2 │ Mobile device
 ┌────────┐   ┌──╢           │       ║◄───┐   └──────────┘ (Android, iPhone ...)
 │ jabber │◄──┘  ╚═══════════╧═══════╝    │      ......
 └────────┘                               │   ┌──────────┐
   ......                                 └───┤ client N │ Other devices
                                              └──────────┘


└────────────┘   └───────────────────┘╘══════╛└────────────────────────────────┘
network servers    ncurses interface    relay         remote interfaces
                                      protocol
....

[NOTE]
All clients here are clients using 'weechat' protocol in 'relay' plugin. The
'relay' plugin also allows IRC clients, then 'relay' plugin acts as an
'IRC proxy' (not described in this document).

[[protocol_generalities]]
== Protocol generalities

* Connections from 'client' to 'relay' are made using TCP sockets on IP/port
  used by 'relay' plugin to listen to new connections.
* Number of 'clients' is limited by the option 'relay.network.max_clients'.
* Each 'client' is independent from other clients.
* Messages from 'client' to 'relay' are called 'commands', they are sent as text
  (a string).
* Messages from 'relay' to 'client' are called 'messages', they are sent as
  binary data.

[[commands]]
== Commands (client → relay)

Commands have format: "(id) command arguments\n".

Fields are:

* 'id': optional message identifier that will be sent in answer from 'relay';
  it must be enclosed in parentheses, and must not start with an underscore
  ("_") (ids starting with underscore are reserved for WeeChat 'event' messages)
* 'command': a command (see table below)
* 'arguments': optional arguments for command (many arguments are separated by
  spaces).

List of available commands (detail in next chapters):

[width="80%",cols="^3m,14",options="header"]
|===
| Command  | Description
| init     | Initialize connection with 'relay'
| hdata    | Request a 'hdata'
| info     | Request an 'info'
| infolist | Request an 'infolist'
| nicklist | Request a 'nicklist'
| input    | Send data to a buffer (text or command)
| sync     | Synchronize buffer(s) (get updates for buffer(s))
| desync   | Desynchronize buffer(s) (stop updates for buffer(s))
| quit     | Disconnect from 'relay'
|===

[[command_init]]
=== init

Initialize connection with 'relay'. This must be first command sent to 'relay'.
If not sent, 'relay' will close connection on first command received, without
warning.

Syntax:

----
init [<option>=<value>,[<option>=<value>,...]]
----

Arguments:

* 'option': one of following options:
** 'password': password used to authenticate on 'relay' (option
   'relay.network.password' in WeeChat)
** 'compression': compression type:
*** 'zlib': enable 'zlib' compression for messages sent by 'relay'
*** 'off': disable compression

[NOTE]
Compression 'zlib' is enabled by default if 'relay' supports 'zlib' compression.

Examples:

----
# initialize and use zlib compression by default (if WeeChat supports it)
init password=mypass

# initialize and disable compression
init password=mypass,compression=off
----

[[command_hdata]]
=== hdata

Request a 'hdata'.

Syntax:

----
(id) hdata <path> [<keys>]
----

Arguments:

* 'path': path to a hdata, with format: "hdata:pointer/var/var/.../var", the
  last var is the hdata returned:
** 'hdata': name of hdata
** 'pointer': pointer ("0x12345") or list name (for example: "gui_buffers")
   (count allowed, see below)
** 'var': a variable name in parent hdata (previous name in path)
   (count allowed, see below)
* 'keys': comma-separated list of keys to return in hdata (if not specified, all
  keys are returned, which is not recommended on large hdata structures)

A count is allowed after pointer and variables, with format "(N)". Possible
values are:

* positive number: iterate using next element, N times
* negative number: iterate using previous element, N times
* '*': iterate using next element, until end of list

Examples:

----
# request all buffers, hdata of type "buffer" is returned
# keys "number" and "name" are returned for each buffer
hdata buffer:gui_buffers(*) number,name

# request all lines of all buffers, hdata of type "line_data" is returned
# all keys are returned
hdata buffer:gui_buffers(*)/lines/first_line(*)/data

# request full name of first buffer
hdata buffer:gui_buffers full_name
----

[[command_info]]
=== info

Request an 'info'.

Syntax:

----
(id) info <name>
----

Arguments:

* 'name': name of info to retrieve

Example:

----
info version
----

[[command_infolist]]
=== infolist

Request an 'infolist'.

[IMPORTANT]
Content of infolist is a duplication of actual data. Wherever possible, use
command <<command_hdata,hdata>>, which is direct access to data (it is
faster, uses less memory and returns smaller objects in message).

Syntax:

----
(id) infolist <name> [<pointer> [<arguments>]]
----

Arguments:

* 'name': name of infolist to retrieve
* 'pointer': pointer (optional)
* 'arguments': arguments (optional)

Example:

----
infolist buffer
----

[[command_nicklist]]
=== nicklist

Request a 'nicklist', for one or all buffers.

Syntax:

----
(id) nicklist [<buffer>]
----

Arguments:

* 'buffer': pointer ('0x12345') or full name of buffer (for example:
  'core.weechat' or 'irc.freenode.#weechat')

Examples:

----
# request nicklist for all buffers
nicklist

# request nicklist for irc.freenode.#weechat
nicklist irc.freenode.#weechat
----

[[command_input]]
=== input

Send data to a buffer.

Syntax:

----
input <buffer> <data>
----

Arguments:

* 'buffer': pointer ('0x12345') or full name of buffer (for example:
  'core.weechat' or 'irc.freenode.#weechat')
* 'data': data to send to buffer: if beginning by '/', this will be executed as
   a command on buffer, otherwise text is sent as input of buffer

Examples:

----
input core.weechat /help filter
input irc.freenode.#weechat hello!
----

[[command_sync]]
=== sync

_Updated in version 0.4.1._

Synchronize one or more buffers, to get updates.

[IMPORTANT]
It is recommended to send this command immediately after you asked
data for buffers (lines, ...). It can be send in same message (after a new
line char: "\n").

Syntax:

----
sync [<buffer>[,<buffer>...] <option>[,<option>...]]
----

Arguments:

* 'buffer': pointer ('0x12345') or full name of buffer (for example:
  'core.weechat' or 'irc.freenode.#weechat'); name "*" can be used to
  specify all buffers
* 'options': one of following keywords, separated by commas (default is
  'buffers,upgrade,buffer,nicklist' for "*" and 'buffer,nicklist' for a buffer):
** 'buffers': receive signals about buffers (opened/closed, moved, renamed,
   merged/unmerged, hidden/unhidden); this can be used only with name "*"
   _(WeeChat ≥ 0.4.1)_
** 'upgrade': receive signals about WeeChat upgrade (upgrade, upgrade ended);
   this can be used only with name "*"
   _(WeeChat ≥ 0.4.1)_
** 'buffer': receive signals about buffer (new lines, type changed, title
   changed, local variable added/removed, and same signals as 'buffers' for the
   buffer) _(updated in version 0.4.1)_
** 'nicklist': receive nicklist after changes

Examples:

----
# synchronize all buffers with nicklist
# (the 3 commands are equivalent, but the first one is recommended
# for compatibility with future versions)
sync
sync *
sync * buffers,upgrade,buffer,nicklist

# synchronize core buffer
sync core.buffer

# synchronize #weechat channel, without nicklist
sync irc.freenode.#weechat buffer

# get general signals + all signals for #weechat channel
sync * buffers,upgrade
sync irc.freenode.#weechat
----

[[command_desync]]
=== desync

_Updated in version 0.4.1._

Desynchronize one or more buffers, to stop updates.

[NOTE]
This will remove 'options' for buffers. If some options are still active for
buffers, the client will still receive updates for these buffers.

Syntax:

----
desync [<buffer>[,<buffer>...] <option>[,<option>...]]
----

Arguments:

* 'buffer': pointer ('0x12345') or full name of buffer (for example:
  'core.weechat' or 'irc.freenode.#weechat'); name "*" can be used to
  specify all buffers
* 'options': one of following keywords, separated by commas (default is
  'buffers,upgrade,buffer,nicklist' for "*" and 'buffer,nicklist' for a buffer);
  see <<command_sync,command sync>> for values

[NOTE]
When using buffer "*", the other buffers synchronized (using a name) are kept. +
So if you send: "sync *", then "sync irc.freenode.#weechat", then "desync *",
the updates on #weechat channel will still be sent by WeeChat (you must remove
it explicitly to stop updates).

Examples:

----
# desynchronize all buffers
# (the 3 commands are equivalent, but the first one is recommended
# for compatibility with future versions)
desync
desync *
desync * buffers,upgrade,buffer,nicklist

# desynchronize nicklist for #weechat channel (keep buffer updates)
desync irc.freenode.#weechat nicklist

# desynchronize #weechat channel
desync irc.freenode.#weechat
----

[[command_test]]
=== test

Test command: WeeChat will reply with various different objects.

This command is useful to test the decoding of binary objects returned by
WeeChat.

[IMPORTANT]
You must not use the pointer values returned by this command, they are not
valid. This command must be used only to test decoding of a message sent by
WeeChat.

Syntax:

----
test
----

Example:

----
test
----

Returned objects (in this order):

[width="80%",cols="^3,3m,5m",options="header"]
|===
| Type              | Type (in message) | Value
| char              | chr               | 65 ("A")
| integer           | int               | 123456
| integer           | int               | -123456
| long              | lon               | 1234567890
| long              | lon               | -1234567890
| string            | str               | "a string"
| string            | str               | ""
| string            | str               | NULL
| buffer            | buf               | "buffer"
| buffer            | buf               | NULL
| pointer           | ptr               | 0x1234abcd
| pointer           | ptr               | NULL
| time              | tim               | 1321993456
| array of strings  | arr str           | [ "abc", "de" ]
| array of integers | arr int           | [ 123, 456, 789 ]
|===

[[command_ping]]
=== ping

_WeeChat ≥ 0.4.2._

Send a ping to WeeChat which will reply with a message "_pong" and same arguments.

This command is useful to test that connection with WeeChat is still alive and
measure the response time.

Syntax:

----
ping [<arguments>]
----

Example:

----
ping 1370802127000
----

[[command_quit]]
=== quit

Disconnect from 'relay'.

Syntax:

----
quit
----

Example:

----
quit
----

[[messages]]
== Messages (relay → client)

Messages are sent as binary data, using following format (with size in bytes):

....
┌────────╥─────────────╥────╥────────┬──────────╥───────╥────────┬──────────┐
│ length ║ compression ║ id ║ type 1 │ object 1 ║  ...  ║ type N │ object N │
└────────╨─────────────╨────╨────────┴──────────╨───────╨────────┴──────────┘
 └──────┘ └───────────┘ └──┘ └──────┘ └────────┘         └──────┘ └────────┘
     4          1        ??      3        ??                 3        ??
 └────────────────────┘ └──────────────────────────────────────────────────┘
       header (5)                        compressed data (??)
 └─────────────────────────────────────────────────────────────────────────┘
                               'length' bytes
....

* 'length' (unsigned integer): number of bytes of whole message (including
  this field)
* 'compression' (byte): flag:
** '0x00': following data is not compressed
** '0x01': following data is compressed with 'zlib'
* 'id' (string): identifier sent by client (before command name); it can be
  empty (string with zero length and no content) if no identifier was given in
  command
* 'type' (3 chars): a type: 3 letters (see table below)
* 'object': an object (see table below)

[[message_compression]]
=== Compression

If flag 'compression' is equal to 0x01, then *all* data after is compressed
with 'zlib', and therefore must be uncompressed before being processed.

[[message_identifier]]
=== Identifier

There are two types of identifiers ('id'):

* 'id' sent by 'client': 'relay' will answer with same 'id' in its answer
* 'id' of an event: on some events, 'relay' will send message to 'client' using
  a specific 'id', beginning with underscore (see table below)

WeeChat reserved identifiers:

[width="100%",cols="5,5,3,4,7",options="header"]
|===
| Identifier | Received with 'sync' | Data sent |
  Description | Recommended action in client

| _buffer_opened | buffers / buffer | hdata: buffer |
  Buffer opened | Open buffer

| _buffer_moved | buffers / buffer | hdata: buffer |
  Buffer moved | Move buffer

| _buffer_merged | buffers / buffer | hdata: buffer |
  Buffer merged | Merge buffer

| _buffer_unmerged | buffers / buffer | hdata: buffer |
  Buffer unmerged | Unmerge buffer

| _buffer_hidden | buffers / buffer | hdata: buffer |
  Buffer hidden | Hide buffer

| _buffer_unhidden | buffers / buffer | hdata: buffer |
  Buffer unhidden | Unhide buffer

| _buffer_renamed | buffers / buffer | hdata: buffer |
  Buffer renamed | Rename buffer

| _buffer_title_changed | buffers / buffer | hdata: buffer |
  Title of buffer changed | Change title of buffer

| _buffer_cleared | buffer | hdata: buffer |
  Buffer cleared | Clear buffer

| _buffer_type_changed | buffer | hdata: buffer |
  Type of buffer changed | Change type of buffer

| _buffer_localvar_added | buffer | hdata: buffer |
  Local variable added | Add local variable in buffer

| _buffer_localvar_changed | buffer | hdata: buffer |
  Local variable changed | Change local variable in buffer

| _buffer_localvar_removed | buffer | hdata: buffer |
  Local variable removed | Remove local variable from buffer

| _buffer_line_added | buffer | hdata: line |
  Line added in buffer | Display line in buffer

| _buffer_closing | buffers / buffer | hdata: buffer |
  Buffer closing | Close buffer

| _nicklist | nicklist | hdata: nicklist_item |
  Nicklist for a buffer | Replace nicklist

| _nicklist_diff | nicklist | hdata: nicklist_item |
  Nicklist diffs for a buffer  | Update nicklist

| _pong | (always) | string: ping arguments |
  Answer to a "ping" | Measure response time

| _upgrade | upgrade | (empty) |
  WeeChat is upgrading | Desync from WeeChat (or disconnect)

| _upgrade_ended | upgrade | (empty) |
  Upgrade of WeeChat done | Sync/resync with WeeChat
|===

[[message_buffer_opened]]
==== _buffer_opened

This message is sent to the client when the signal "buffer_opened" is sent by
WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name            | Type      | Description
| number          | integer   | Buffer number (≥ 1)
| full_name       | string    | Full name (example: 'irc.freenode.#weechat')
| short_name      | string    | Short name (example: '#weechat')
| nicklist        | integer   | 1 if buffer has a nicklist, otherwise 0
| title           | string    | Buffer title
| local_variables | hashtable | Local variables
| prev_buffer     | pointer   | Pointer to previous buffer
| next_buffer     | pointer   | Pointer to next buffer
|===

Example: channel '#weechat' joined on freenode, new buffer
'irc.freenode.#weechat':

[source,python]
----
id: '_buffer_opened'
hda:
  keys: {'number': 'int', 'full_name': 'str', 'short_name': 'str', 'nicklist': 'int',
         'title': 'str', 'local_variables': 'htb', 'prev_buffer': 'ptr', 'next_buffer': 'ptr'}
  path: ['buffer']
  item 1:
    __path: ['0x35a8a60']
    number: 3
    full_name: 'irc.freenode.#weechat'
    short_name: None
    nicklist: 0
    title: None
    local_variables: {'plugin': 'irc', 'name': 'freenode.#weechat'}
    prev_buffer: '0x34e7400'
    next_buffer: '0x0'
----

[[message_buffer_moved]]
==== _buffer_moved

This message is sent to the client when the signal "buffer_moved" is sent by
WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name        | Type    | Description
| number      | integer | Buffer number (≥ 1)
| full_name   | string  | Full name (example: 'irc.freenode.#weechat')
| prev_buffer | pointer | Pointer to previous buffer
| next_buffer | pointer | Pointer to next buffer
|===

Example: buffer 'irc.freenode.#weechat' moved to number 2:

[source,python]
----
id: '_buffer_moved'
hda:
  keys: {'number': 'int', 'full_name': 'str', 'prev_buffer': 'ptr', 'next_buffer': 'ptr'}
  path: ['buffer']
  item 1:
    __path: ['0x34588c0']
    number: 2
    full_name: 'irc.freenode.#weechat'
    prev_buffer: '0x347b9f0'
    next_buffer: '0x3471bc0'
----

[[message_buffer_merged]]
==== _buffer_merged

This message is sent to the client when the signal "buffer_merged" is sent by
WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name        | Type    | Description
| number      | integer | Buffer number (≥ 1)
| full_name   | string  | Full name (example: 'irc.freenode.#weechat')
| prev_buffer | pointer | Pointer to previous buffer
| next_buffer | pointer | Pointer to next buffer
|===

Example: buffer 'irc.freenode.#weechat' merged with buffer #2:

[source,python]
----
id: '_buffer_merged'
hda:
  keys: {'number': 'int', 'full_name': 'str', 'prev_buffer': 'ptr', 'next_buffer': 'ptr'}
  path: ['buffer']
  item 1:
    __path: ['0x4db4c00']
    number: 2
    full_name: 'irc.freenode.#weechat'
    prev_buffer: '0x4cef9b0'
    next_buffer: '0x0'
----

[[message_buffer_unmerged]]
==== _buffer_unmerged

This message is sent to the client when the signal "buffer_unmerged" is sent by
WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name        | Type    | Description
| number      | integer | Buffer number (≥ 1)
| full_name   | string  | Full name (example: 'irc.freenode.#weechat')
| prev_buffer | pointer | Pointer to previous buffer
| next_buffer | pointer | Pointer to next buffer
|===

Example: buffer 'irc.freenode.#weechat' unmerged:

[source,python]
----
id: '_buffer_unmerged'
hda:
  keys: {'number': 'int', 'full_name': 'str', 'prev_buffer': 'ptr', 'next_buffer': 'ptr'}
  path: ['buffer']
  item 1:
    __path: ['0x4db4c00']
    number: 3
    full_name: 'irc.freenode.#weechat'
    prev_buffer: '0x4cef9b0'
    next_buffer: '0x0'
----

[[message_buffer_hidden]]
==== _buffer_hidden

_WeeChat ≥ 1.0._

This message is sent to the client when the signal "buffer_hidden" is sent by
WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name        | Type    | Description
| number      | integer | Buffer number (≥ 1)
| full_name   | string  | Full name (example: 'irc.freenode.#weechat')
| prev_buffer | pointer | Pointer to previous buffer
| next_buffer | pointer | Pointer to next buffer
|===

Example: buffer 'irc.freenode.#weechat' hidden:

[source,python]
----
id: '_buffer_hidden'
hda:
  keys: {'number': 'int', 'full_name': 'str', 'prev_buffer': 'ptr', 'next_buffer': 'ptr'}
  path: ['buffer']
  item 1:
    __path: ['0x4db4c00']
    number: 2
    full_name: 'irc.freenode.#weechat'
    prev_buffer: '0x4cef9b0'
    next_buffer: '0x0'
----

[[message_buffer_unhidden]]
==== _buffer_unhidden

_WeeChat ≥ 1.0._

This message is sent to the client when the signal "buffer_unhidden" is sent by
WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name        | Type    | Description
| number      | integer | Buffer number (≥ 1)
| full_name   | string  | Full name (example: 'irc.freenode.#weechat')
| prev_buffer | pointer | Pointer to previous buffer
| next_buffer | pointer | Pointer to next buffer
|===

Example: buffer 'irc.freenode.#weechat' unhidden:

[source,python]
----
id: '_buffer_unhidden'
hda:
  keys: {'number': 'int', 'full_name': 'str', 'prev_buffer': 'ptr', 'next_buffer': 'ptr'}
  path: ['buffer']
  item 1:
    __path: ['0x4db4c00']
    number: 3
    full_name: 'irc.freenode.#weechat'
    prev_buffer: '0x4cef9b0'
    next_buffer: '0x0'
----

[[message_buffer_renamed]]
==== _buffer_renamed

This message is sent to the client when the signal "buffer_renamed" is sent by
WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name            | Type      | Description
| number          | integer   | Buffer number (≥ 1)
| full_name       | string    | Full name (example: 'irc.freenode.#weechat')
| short_name      | string    | Short name (example: '#weechat')
| local_variables | hashtable | Local variables
|===

Example: private buffer renamed from 'FlashCode' to 'Flash2':

[source,python]
----
id: '_buffer_renamed'
hda:
  keys: {'number': 'int', 'full_name': 'str', 'short_name': 'str', 'local_variables': 'htb'}
  path: ['buffer']
  item 1:
    __path: ['0x4df7b80']
    number: 5
    full_name: 'irc.freenode.Flash2'
    short_name: 'Flash2'
    local_variables: {'server': 'freenode', 'plugin': 'irc', 'type': 'private',
                      'channel': 'FlashCode', 'nick': 'test', 'name': 'local.Flash2'}
----

[[message_buffer_title_changed]]
==== _buffer_title_changed

This message is sent to the client when the signal "buffer_title_changed" is
sent by WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name      | Type    | Description
| number    | integer | Buffer number (≥ 1)
| full_name | string  | Full name (example: 'irc.freenode.#weechat')
| title     | string  | Buffer title
|===

Example: topic changed on channel '#weechat':

[source,python]
----
id: '_buffer_title_changed'
hda:
  keys: {'number': 'int', 'full_name': 'str', 'title': 'str'}
  path: ['buffer']
  item 1:
    __path: ['0x4a715d0']
    number: 3
    full_name: 'irc.freenode.#weechat'
    title: 'Welcome on #weechat!  http://weechat.org/'
----

[[message_buffer_cleared]]
==== _buffer_cleared

_WeeChat ≥ 1.0._

This message is sent to the client when the signal "buffer_cleared" is sent by
WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name      | Type    | Description
| number    | integer | Buffer number (≥ 1)
| full_name | string  | Full name (example: 'irc.freenode.#weechat')
|===

Example: buffer 'irc.freenode.#weechat' has been cleared:

[source,python]
----
id: '_buffer_cleared'
hda:
  keys: {'number': 'int', 'full_name': 'str'}
  path: ['buffer']
  item 1:
    __path: ['0x4a715d0']
    number: 3
    full_name: 'irc.freenode.#weechat'
----

[[message_buffer_type_changed]]
==== _buffer_type_changed

This message is sent to the client when the signal "buffer_type_changed" is sent
by WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name      | Type    | Description
| number    | integer | Buffer number (≥ 1)
| full_name | string  | Full name (example: 'irc.freenode.#weechat')
| type      | integer | Buffer type: 0 = formatted (default), 1 = free content
|===

Example: type of buffer 'script.scripts' changed from formatted (0) to free
content (1):

[source,python]
----
id: '_buffer_type_changed'
hda:
  keys: {'number': 'int', 'full_name': 'str', 'type': 'int'}
  path: ['buffer']
  item 1:
    __path: ['0x27c9a70']
    number: 4
    full_name: 'script.scripts'
    type: 1
----

[[message_buffer_localvar_added]]
==== _buffer_localvar_added

This message is sent to the client when the signal "buffer_localvar_added" is
sent by WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name            | Type      | Description
| number          | integer   | Buffer number (≥ 1)
| full_name       | string    | Full name (example: 'irc.freenode.#weechat')
| local_variables | hashtable | Local variables
|===

Example: local variable 'test' added in buffer 'irc.freenode.#weechat':

[source,python]
----
id='_buffer_localvar_added', objects:
hda:
  keys: {'number': 'int', 'full_name': 'str', 'local_variables': 'htb'}
  path: ['buffer']
  item 1:
    __path: ['0x4a73de0']
    number: 3
    full_name: 'irc.freenode.#weechat'
    local_variables: {'server': 'freenode', 'test': 'value', 'plugin': 'irc',
                      'type': 'channel', 'channel': '#weechat', 'nick': 'test',
                      'name': 'freenode.#weechat'}
----

[[message_buffer_localvar_changed]]
==== _buffer_localvar_changed

This message is sent to the client when the signal "buffer_localvar_changed" is
sent by WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name            | Type      | Description
| number          | integer   | Buffer number (≥ 1)
| full_name       | string    | Full name (example: 'irc.freenode.#weechat')
| local_variables | hashtable | Local variables
|===

Example: local variable 'test' updated in buffer 'irc.freenode.#weechat':

[source,python]
----
id='_buffer_localvar_changed', objects:
hda:
  keys: {'number': 'int', 'full_name': 'str', 'local_variables': 'htb'}
  path: ['buffer']
  item 1:
    __path: ['0x4a73de0']
    number: 3
    full_name: 'irc.freenode.#weechat'
    local_variables: {'server': 'local', 'test': 'value2', 'plugin': 'irc',
                      'type': 'channel', 'channel': '#weechat', 'nick': 'test',
                      'name': 'freenode.#weechat'}
----

[[message_buffer_localvar_removed]]
==== _buffer_localvar_removed

This message is sent to the client when the signal "buffer_localvar_removed" is
sent by WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name            | Type      | Description
| number          | integer   | Buffer number (≥ 1)
| full_name       | string    | Full name (example: 'irc.freenode.#weechat')
| local_variables | hashtable | Local variables
|===

Example: local variable 'test' removed from buffer 'irc.freenode.#weechat':

[source,python]
----
id: '_buffer_localvar_removed'
hda:
  keys: {'number': 'int', 'full_name': 'str', 'local_variables': 'htb'}
  path: ['buffer']
  item 1:
    __path: ['0x4a73de0']
    number: 3
    full_name: 'irc.freenode.#prout'
    local_variables: {'server': 'local', 'plugin': 'irc', 'type': 'channel',
                      'channel': '#weechat', 'nick': 'test', 'name': 'freenode.#weechat'}
----

[[message_buffer_line_added]]
==== _buffer_line_added

This message is sent to the client when the signal "buffer_line_added" is sent
by WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name         | Type             | Description
| buffer       | pointer          | Buffer pointer
| date         | time             | Date of message
| date_printed | time             | Date when WeeChat displayed message
| displayed    | char             | 1 if message is displayed, 0 if message is filtered (hidden)
| highlight    | char             | 1 if line has a highlight, otherwise 0
| tags_array   | array of strings | List of tags for line
| prefix       | string           | Prefix
| message      | string           | Message
|===

Example: new message 'hello!' from nick 'FlashCode' on buffer 'irc.freenode.#weechat':

[source,python]
----
id: '_buffer_line_added'
hda:
  keys: {'buffer': 'ptr', 'date': 'tim', 'date_printed': 'tim', 'displayed': 'chr',
         'highlight': 'chr', 'tags_array': 'arr', 'prefix': 'str', 'message': 'str'}
  path: ['line_data']
  item 1:
    __path: ['0x4a49600']
    buffer: '0x4a715d0'
    date: 1362728993
    date_printed: 1362728993
    displayed: 1
    highlight: 0
    tags_array: ['irc_privmsg', 'notify_message', 'prefix_nick_142', 'nick_FlashCode', 'log1']
    prefix: 'F06@F@00142FlashCode'
    message: 'hello!'
----

[[message_buffer_closing]]
==== _buffer_closing

This message is sent to the client when the signal "buffer_closing" is sent by
WeeChat.

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name      | Type    | Description
| number    | integer | Buffer number (≥ 1)
| full_name | string  | Full name (example: 'irc.freenode.#weechat')
|===

Example: buffer 'irc.freenode.#weechat' is being closed by WeeChat:

[source,python]
----
id: '_buffer_closing'
hda:
  keys: {'number': 'int', 'full_name': 'str'}
  path: ['buffer']
  item 1:
    __path: ['0x4a715d0']
    number: 3
    full_name: 'irc.freenode.#weechat'
----

[[message_nicklist]]
==== _nicklist

This message is sent to the client when large updates are made on a nicklist
(groups/nicks added/removed/changed). The message contains full nicklist.

When small updates are made on a nicklist (for example just add one nick),
another message with identifier '_nicklist_diff' is sent (see below).

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name         | Type    | Description
| group        | char    | 1 for a group, 0 for a nick
| visible      | char    | 1 if group/nick is displayed, otherwise 0
| level        | integer | Level of group (0 for a nick)
| name         | string  | Name of group/nick
| color        | string  | Name color
| prefix       | string  | Prefix (only for a nick)
| prefix_color | string  | Prefix color (only for a nick)
|===

Example: nicklist for buffer 'irc.freenode.#weechat':

[source,python]
----
id: '_nicklist'
hda:
  keys: {'group': 'chr', 'visible': 'chr', 'level': 'int', 'name': 'str', 'color': 'str',
         'prefix': 'str', 'prefix_color': 'str'}
  path: ['buffer', 'nicklist_item']
  item 1:
    __path: ['0x4a75cd0', '0x31e95d0']
    group: 1
    visible: 0
    level: 0
    name: 'root'
    color: None
    prefix: None
    prefix_color: None
  item 2:
    __path: ['0x4a75cd0', '0x41247b0']
    group: 1
    visible: 1
    level: 1
    name: '000|o'
    color: 'weechat.color.nicklist_group'
    prefix: None
    prefix_color: None
  item 3:
    __path: ['0x4a75cd0', '0x4a60d20']
    group: 0
    visible: 1
    level: 0
    name: 'FlashCode'
    color: '142'
    prefix: '@'
    prefix_color: 'lightgreen'
  item 4:
    __path: ['0x4a75cd0', '0x4aafaf0']
    group: 1
    visible: 1
    level: 1
    name: '001|v'
    color: 'weechat.color.nicklist_group'
    prefix: None
    prefix_color: None
  item 5:
    __path: ['0x4a75cd0', '0x4a48d80']
    group: 1
    visible: 1
    level: 1
    name: '999|...'
    color: 'weechat.color.nicklist_group'
    prefix: None
    prefix_color: None
  item 6:
    __path: ['0x4a75cd0', '0x4a5f560']
    group: 0
    visible: 1
    level: 0
    name: 'test'
    color: 'weechat.color.chat_nick_self'
    prefix: ' '
    prefix_color: ''
----

[[message_nicklist_diff]]
==== _nicklist_diff

_WeeChat ≥ 0.4.1._

This message is sent to the client when small updates are made on a nicklist
(groups/nicks added/removed/changed). The message contains nicklist differences
(between old nicklist and current one).

Data sent as hdata:

[width="100%",cols="3m,2,10",options="header"]
|===
| Name         | Type    | Description
| _diff        | char    | Type of diff (see below)
| group        | char    | 1 for a group, 0 for a nick
| visible      | char    | 1 if group/nick is displayed, otherwise 0
| level        | integer | Level of group (0 for a nick)
| name         | string  | Name of group/nick
| color        | string  | Name color
| prefix       | string  | Prefix (only for a nick)
| prefix_color | string  | Prefix color (only for a nick)
|===

The value of '_diff' can be:

* `^`: the parent group: group(s) or nick(s) after this one are related to this
  group
* `+`: group/nick added in the parent group
* `-`: group/nick removed from the parent group
* `*`: group/nick updated in the parent group

Example: nick 'master' added in group '000|o' (channel ops on an IRC channel),
nicks 'nick1' and 'nick2' added in group '999|...' (standard users on an IRC
channel):

[source,python]
----
id: '_nicklist_diff'
hda:
  keys: {'_diff': 'chr', 'group': 'chr', 'visible': 'chr', 'level': 'int', 'name': 'str',
         'color': 'str', 'prefix': 'str', 'prefix_color': 'str'}
  path: ['buffer', 'nicklist_item']
  item 1:
    __path: ['0x46f2ee0', '0x343c9b0']
    _diff: 94 ('^')
    group: 1
    visible: 1
    level: 1
    name: '000|o'
    color: 'weechat.color.nicklist_group'
    prefix: None
    prefix_color: None
  item 2:
    __path: ['0x46f2ee0', '0x47e7f60']
    _diff: 43 ('+')
    group: 0
    visible: 1
    level: 0
    name: 'master'
    color: 'magenta'
    prefix: '@'
    prefix_color: 'lightgreen'
  item 3:
    __path: ['0x46f2ee0', '0x46b8e70']
    _diff: 94 ('^')
    group: 1
    visible: 1
    level: 1
    name: '999|...'
    color: 'weechat.color.nicklist_group'
    prefix: None
    prefix_color: None
  item 4:
    __path: ['0x46f2ee0', '0x3dba240']
    _diff: 43 ('+')
    group: 0
    visible: 1
    level: 0
    name: 'nick1'
    color: 'green'
    prefix: ' '
    prefix_color: ''
  item 5:
    __path: ['0x46f2ee0', '0x3c379d0']
    _diff: 43 ('+')
    group: 0
    visible: 1
    level: 0
    name: 'nick2'
    color: 'lightblue'
    prefix: ' '
    prefix_color: ''
----

[[message_pong]]
==== _pong

_WeeChat ≥ 0.4.2._

This message is sent to the client when 'relay' receives a "ping" message.

Data sent as string: arguments received in the "ping" message.

The recommended action in client is to measure the response time and disconnect
if it is high.

[[message_upgrade]]
==== _upgrade

_WeeChat ≥ 0.3.8._

This message is sent to the client when WeeChat is starting upgrade process.

There is no data in the message.

The recommended action in client is to desynchronize from WeeChat (send command
'desync'), or to disconnect from WeeChat (because after upgrade, all pointers
will change).

[NOTE]
During WeeChat upgrade, the socket remains opened (except if connection uses
SSL).

[[message_upgrade_ended]]
==== _upgrade_ended

_WeeChat ≥ 0.3.8._

This message is sent to the client when WeeChat has finished the upgrade
process.

There is no data in the message.

The recommended action in client is to resynchronize with WeeChat: resend all
commands sent on startup after the 'init'.

[[objects]]
=== Objects

Objects are identified by 3 letters, called 'type'. Following types are used:

[width="100%",cols="^2m,5,10",options="header"]
|===
| Type | Value                | Length
| chr  | Signed char          | 1 byte
| int  | Signed integer       | 4 bytes
| lon  | Signed long integer  | 1 byte + length of integer as string
| str  | String               | 4 bytes + length of string (without final '\0')
| buf  | Buffer of bytes      | 4 bytes + length of data
| ptr  | Pointer              | 1 byte + length of pointer as string
| tim  | Time                 | 1 byte + length of time as string
| htb  | Hashtable            | Variable
| hda  | Hdata content        | Variable
| inf  | Info: name + content | Variable
| inl  | Infolist content     | Variable
| arr  | Array of objects     | 3 bytes (type) + number of objects + data
|===

[[object_char]]
==== Char

A signed char is stored as 1 byte.

Example:

....
┌────┐
│ 41 │ ────► 65 (0x41: "A")
└────┘
....

[[object_integer]]
==== Integer

A signed integer is stored as 4 bytes, encoded as big-endian format (most
significant byte first).

Range: -2147483648 to 2147483647.

Examples:

....
┌────┬────┬────┬────┐
│ 00 │ 01 │ E2 │ 40 │ ────► 123456
└────┴────┴────┴────┘

┌────┬────┬────┬────┐
│ FF │ FE │ 1D │ C0 │ ────► -123456
└────┴────┴────┴────┘
....

[[object_long_integer]]
==== Long integer

A signed long integer is encoded as a string, with length on one byte.

Range: -9223372036854775808 to 9223372036854775807.

Examples:

....
┌────╥────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0A ║ 31 │ 32 │ 33 │ 34 │ 35 │ 36 │ 37 │ 38 │ 39 │ 30 │ ────► 1234567890
└────╨────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
 └──┘ └───────────────────────────────────────────────┘
length '1'  '2'  '3'  '4'  '5'  '6'  '7'  '8'  '9'  '0'

┌────╥────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0B ║ 2D │ 31 │ 32 │ 33 │ 34 │ 35 │ 36 │ 37 │ 38 │ 39 │ 30 │ ────► -1234567890
└────╨────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
 └──┘ └────────────────────────────────────────────────────┘
length '-'  '1'  '2'  '3'  '4'  '5'  '6'  '7'  '8'  '9'  '0'
....

[[object_string]]
==== String

A string is a length (integer on 4 bytes) + content of string (without final '\0').

Example:

....
┌────┬────┬────┬────╥────┬────┬────┬────┬────┐
│ 00 │ 00 │ 00 │ 05 ║ 68 │ 65 │ 6C │ 6C │ 6F │ ────► "hello"
└────┴────┴────┴────╨────┴────┴────┴────┴────┘
 └─────────────────┘ └──────────────────────┘
       length         'h'  'e'  'l'  'l'  'o'
....

An empty string has a length of zero:

....
┌────┬────┬────┬────┐
│ 00 │ 00 │ 00 │ 00 │ ────► ""
└────┴────┴────┴────┘
 └─────────────────┘
       length
....

A 'NULL' string (NULL pointer in C) has a length of -1:

....
┌────┬────┬────┬────┐
│ FF │ FF │ FF │ FF │ ────► NULL
└────┴────┴────┴────┘
 └─────────────────┘
       length
....

[[object_buffer]]
==== Buffer

Same format as <<object_string,string>>; content is just an array of bytes.

[[object_pointer]]
==== Pointer

A pointer is encoded as string (hex), with length on one byte.

Example:

....
┌────╥────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 09 ║ 31 │ 61 │ 32 │ 62 │ 33 │ 63 │ 34 │ 64 │ 35 │ ────► 0x1a2b3c4d5
└────╨────┴────┴────┴────┴────┴────┴────┴────┴────┘
 └──┘ └──────────────────────────────────────────┘
length '1'  'a'  '2'  'b'  '3'  'c'  '4'  'd'  '5'
....

A 'NULL' pointer has a length of 1 with value 0:

....
┌────╥────┐
│ 01 ║ 00 │ ────► NULL (0x0)
└────╨────┘
 └──┘ └──┘
length  0
....

[[object_time]]
==== Time

A time (number of seconds) is encoded as a string, with length on one byte.

Example:

....
┌────╥────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0A ║ 31 │ 33 │ 32 │ 31 │ 39 │ 39 │ 33 │ 34 │ 35 │ 36 │ ────► 1321993456
└────╨────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
 └──┘ └───────────────────────────────────────────────┘
length '1'  '3'  '2'  '1'  '9'  '9'  '3'  '4'  '5'  '6'
....

[[object_hashtable]]
==== Hashtable

A hashtable contains type for keys, type for values, number of items in
hashtable (integer on 4 bytes), and then keys and values of items.

....
┌───────────┬─────────────┬───────╥───────┬─────────╥─────╥───────┬─────────┐
│ type_keys │ type_values │ count ║ key 1 │ value 1 ║ ... ║ key N │ value N │
└───────────┴─────────────┴───────╨───────┴─────────╨─────╨───────┴─────────┘
....

Example:

....
┌─────┬─────┬───╥──────┬─────╥──────┬─────┐
│ str │ str │ 2 ║ key1 │ abc ║ key2 │ def │ ────► { 'key1' => 'abc',
└─────┴─────┴───╨──────┴─────╨──────┴─────┘         'key2' => 'def' }
 └───┘ └───┘ └─┘ └──────────┘ └──────────┘
 type  type count   item 1       item 2
 keys values
....

[[object_hdata]]
==== Hdata

A 'hdata' contains a path with hdata names, list of keys, number of set of
objects, and then set of objects (path with pointers, then objects).

....
┌────────┬──────┬───────╥────────┬─────────────────────╥──
│ h-path │ keys │ count ║ p-path │ value 1 ... value N ║ ...
└────────┴──────┴───────╨────────┴─────────────────────╨──

   ──╥────────┬─────────────────────╥─────┐
 ... ║ p-path │ value 1 ... value N ║ ... │
   ──╨────────┴─────────────────────╨─────┘
....

* 'h-path' (string): path used to reach hdata (example:
  'buffer/lines/line/line_data'); the last element in path is the hdata returned
* 'keys' (string): string with list of 'key:type' (separated by commas),
  example: 'number:int,name:str'
* 'count' (integer): number of set of objects
* 'p-path': path with pointers to objects (number of pointers here is number of
  elements in path)
* 'values': list of values (number of values is number of keys returned for
  hdata)

Example of hdata with two buffers (weechat core and freenode server) and two
keys ('number' and 'full_name'):

....
# command
hdata buffer:gui_buffers(*) number,full_name

# response
┌────────┬──────────────────────────┬───╥──
│ buffer │ number:int,full_name:str │ 2 ║ ...
└────────┴──────────────────────────┴───╨──
 └──────┘ └────────────────────────┘ └─┘
  h-path          keys              count

   ──╥─────────┬───┬──────────────╥─────────┬───┬────────────────────┐
 ... ║ 0x12345 │ 1 │ core.weechat ║ 0x6789a │ 2 │irc.server.freenode │
   ──╨─────────┴───┴──────────────╨─────────┴───┴────────────────────┘
      └──────────────────────────┘ └────────────────────────────────┘
               buffer 1                        buffer 2
....

Example of hdata with lines of core buffer:

....
# command
hdata buffer:gui_buffers(*)/lines/first_line(*)/data

# response
┌─────────────────────────────┬─────┬────╥──
│ buffer/lines/line/line_data │ ... │ 50 ║ ...
└─────────────────────────────┴─────┴────╨──
 └───────────────────────────┘ └───┘ └──┘
      h-path (hdata names)     keys  count

   ──╥───────────┬───────────┬───────────┬───────╥──
 ... ║ 0x23cf970 │ 0x23cfb60 │ 0x23d5f40 │ ..... ║ ...
   ──╨───────────┴───────────┴───────────┴───────╨──
      └─────────────────────────────────┘ └─────┘
               p-path (pointers)          objects
      └─────────────────────────────────────────┘
                         line 1

   ──╥───────────┬───────────┬───────────┬───────╥──────────────┐
 ... ║ 0x23cf970 │ 0x23cfb60 │ 0x23d6110 │ ..... ║ ............ │
   ──╨───────────┴───────────┴───────────┴───────╨──────────────┘
      └─────────────────────────────────┘ └─────┘
               p-path (pointers)          objects
      └─────────────────────────────────────────┘ └────────────┘
                         line 2                     lines 3-50
....

Example of hdata with nicklist:

....
# command
nicklist

# response
┌───────────────────┬──
│ buffer/nick_group │ ...
└───────────────────┴──
 └─────────────────┘
        h-path

   ──╥───────────────────────────────────────────────────────────┬────╥──
 ... ║ group:chr,visible:chr,name:str,color:str,prefix:str,(...) │ 12 ║ ...
   ──╨───────────────────────────────────────────────────────────┴────╨──
      └─────────────────────────────────────────────────────────┘ └──┘
                                 keys                             count

   ──╥─────────┬─────────┬───┬───┬──────┬─┬─┬─┬───╥──
 ... ║ 0x12345 │ 0x6789a │ 1 │ 0 │ root │ │ │ │ 0 ║ ...
   ──╨─────────┴─────────┴───┴───┴──────┴─┴─┴─┴───╨──
      └─────────────────┘ └──────────────────────┘
             p-path               objects
      └──────────────────────────────────────────┘
                  group (nicklist root)

   ──╥─────────┬─────────┬───┬───┬───────┬─┬─┬─┬───╥──
 ... ║ 0x123cf │ 0x678d4 │ 1 │ 0 │ 000|o │ │ │ │ 1 ║ ...
   ──╨─────────┴─────────┴───┴───┴───────┴─┴─┴─┴───╨──
      └─────────────────┘ └───────────────────────┘
             p-path                objects
      └───────────────────────────────────────────┘
                    group (channel ops)

   ──╥─────────┬─────────┬───┬───┬──────────┬──────┬───┬────────────┬───╥──
 ... ║ 0x128a7 │ 0x67ab2 │ 0 │ 1 │ ChanServ │ blue │ @ │ lightgreen │ 0 ║ ...
   ──╨─────────┴─────────┴───┴───┴──────────┴──────┴───┴────────────┴───╨──
      └─────────────────┘ └────────────────────────────────────────────┘
             p-path                          objects
      └────────────────────────────────────────────────────────────────┘
                               nick (@ChanServ)
....

[[object_info]]
==== Info

A 'info' contains a name and a value (both are strings).

....
┌──────┬───────┐
│ name │ value │
└──────┴───────┘
....

* 'name' (string): name of info
* 'value' (string): value

Example of info 'version':

....
┌─────────┬───────────────────┐
│ version │ WeeChat 0.3.7-dev │
└─────────┴───────────────────┘
....

[[object_infolist]]
==== Infolist

A 'infolist' contains a name, number of items, and then items (set of
variables).

....
┌──────┬───────╥────────╥─────╥────────┐
│ name │ count ║ item 1 ║ ... ║ item N │
└──────┴───────╨────────╨─────╨────────┘
....

An item is:

....
┌───────╥────────┬────────┬─────────╥─────╥────────┬────────┬─────────┐
│ count ║ name 1 │ type 1 │ value 1 ║ ... ║ name N │ type N │ value N │
└───────╨────────┴────────┴─────────╨─────╨────────┴────────┴─────────┘
....

* 'name' (string): name of infolist ('buffer', 'window', 'bar', ...)
* 'count' (integer): number of items
* 'item':
** 'count': number of variables in item
** 'name': name of variable
** 'type': type of variable ('int', 'str', ...)
** 'value': value of variable

Example of infolist with two buffers (weechat core and freenode server):

....
# command
infolist buffer

# response
┌────────┬───╥────┬─────────┬─────┬─────────┬─────╥──
│ buffer │ 2 ║ 42 │ pointer │ ptr │ 0x12345 │ ... ║ ...
└────────┴───╨────┴─────────┴─────┴─────────┴─────╨──
 └──────┘ └─┘ └──────────────────────────────────┘
   name  count              item 1

   ──╥────┬─────────┬─────┬─────────┬─────┐
 ... ║ 42 │ pointer │ ptr │ 0x6789a │ ... │
   ──╨────┴─────────┴─────┴─────────┴─────┘
      └──────────────────────────────────┘
                    item 2
....

[[object_array]]
==== Array

An array is a type (3 bytes) + number of objects (integer on 4 bytes) + data.

Example of array with two strings:

....
┌─────╥────┬────┬────┬────╥────┬────┬────┬────╥──
│ str ║ 00 │ 00 │ 00 │ 02 ║ 00 │ 00 │ 00 │ 03 ║ ...
└─────╨────┴────┴────┴────╨────┴────┴────┴────╨──
 └───┘ └─────────────────┘ └─────────────────┘
 type   number of strings        length

   ──╥────┬────┬────╥────┬────┬────┬────╥────┬────┐
 ... ║ 61 │ 62 │ 63 ║ 00 │ 00 │ 00 │ 02 ║ 64 │ 65 │ ────► [ "abc", "de" ]
   ──╨────┴────┴────╨────┴────┴────┴────╨────┴────┘
      └────────────┘ └─────────────────┘ └───────┘
       'a'  'b'  'c'       length         'd'  'e'
....

Example of array with three integers:

....
┌─────╥────┬────┬────┬────╥────┬────┬────┬────╥──
│ int ║ 00 │ 00 │ 00 │ 03 ║ 00 │ 00 │ 00 │ 7B ║ ...
└─────╨────┴────┴────┴────╨────┴────┴────┴────╨──
 └───┘ └─────────────────┘ └─────────────────┘
 type   number of integers      123 (0x7B)

   ──╥────┬────┬────┬────╥────┬────┬────┬────┐
 ... ║ 00 │ 00 │ 01 │ C8 ║ 00 │ 00 │ 03 │ 15 │ ────► [ 123, 456, 789 ]
   ──╨────┴────┴────┴────╨────┴────┴────┴────┘
      └─────────────────┘ └─────────────────┘
          456 (0x1C8)         789 (0x315)
....

A 'NULL' array:

....
┌─────╥────┬────┬────┬────┐
│ str ║ 00 │ 00 │ 00 │ 00 │ ────► NULL
└─────╨────┴────┴────┴────┘
 └───┘ └─────────────────┘
 type   number of strings
....

[[typical_session]]
== Typical session

....
    ┌────────┐                         ┌───────┐                ┌─────────┐
    │ Client ├ ─ ─ ─ ─(network)─ ─ ─ ─ ┤ Relay ├────────────────┤ WeeChat │
    └────────┘                         └───────┘                └─────────┘
         ║                                 ║                         ║
         ╟───────────────────────────────► ║                         ║
         ║ open socket                     ║ add client              ║
         ║                                 ║                         ║
         ╟───────────────────────────────► ║                         ║
         ║ cmd: init password=xxx,...      ║ init/allow client       ║
         ║                                 ║                         ║
         ╟───────────────────────────────► ║                         ║
         ║ cmd: hdata buffer ...           ╟───────────────────────► ║
         ║      sync ...                   ║ request hdata           ║ read hdata
         ║                                 ║                         ║ values
         ║                                 ║ ◄───────────────────────╢
         ║ ◄───────────────────────────────╢                   hdata ║
  create ║                 msg: hda buffer ║                         ║
 buffers ║                                 ║                         ║
         ║            ........             ║         ........        ║
         ║                                 ║                         ║
         ╟───────────────────────────────► ║                         ║
         ║ cmd: input ...                  ╟───────────────────────► ║
         ║                                 ║ send data to buffer     ║ send data
         ║                                 ║                         ║ to buffer
         ║            ........             ║         ........        ║
         ║                                 ║                         ║ signal
         ║                                 ║ ◄───────────────────────╢ received
         ║ ◄───────────────────────────────╢              signal XXX ║ (hooked by
 update  ║          msg: id: "_buffer_..." ║                         ║ relay)
 buffers ║                                 ║                         ║
         ║            ........             ║         ........        ║
         ║                                 ║                         ║
         ╟───────────────────────────────► ║                         ║
         ║ cmd: ping ...                   ║                         ║
         ║                                 ║                         ║
         ║ ◄───────────────────────────────╢                         ║
 measure ║            msg: id: "_pong" ... ║                         ║
response ║                                 ║                         ║
    time ║            ........             ║         ........        ║
         ║                                 ║                         ║
         ╟───────────────────────────────► ║                         ║
         ║ cmd: quit                       ║ disconnect client       ║
         ║                                 ║                         ║
....
