Skip to content

PluginDocumentStream

Armin Burgmeier edited this page Aug 9, 2014 · 1 revision

The document-stream plugin

The document-stream plugin provides a read-only real-time interface to the documents hosted on the server via unix domain sockets. This can be used by third-party applications that need to access documents on the infinote server and be notified about all changes in real-time. While it is possible to use libinfinity for this task to write a full-fledged infinote client, this can be a considerate amount of work, and also imposes a higher load on the client computer. Furthermore, when the third-party software cannot be written in C or C++, the libinfinity library cannot be used due to lack of appropriate language bindings.

This plugin is not available on Microsoft Windows.

Plugin options

This plugin does not have any configurable options. The listening address of the server should be made configurable, but at the moment it is not.

Binary Protocol

The protocol used to communicate the information is a simple binary protocol. The server is listening on the address "\0org.infinote.infinoted" in the abstract namespace (see unix(7)). After a connection has been established, commands are exchanged between the plugin and the client application. Each command starts with a 32 bit identifier, plus command-specific extra data. The following commands are defined:

Client To Plugin

  • HELO 0(4)|USERLEN(2)|USERNAME(USERLEN)|PATHLEN(2)|PATHNAME(PATHLEN): Client Hello. This should be the first message sent by the client. The 32 bit command number is 0x00000000. After that follows a 16 bit number indicating the length of a user name and the user name in UTF-8 encoding. After that, a 16 bit number indicating the length of the document path and the document path follow. The document path specifies which document on the infinote server the client wants to access.

Plugin To Client

  • ERRO 0(4)|ERRLEN(2)|ERRMSG(ERRLEN): Server error. The 32 bit command number is 0x00000000. After that follows a 16 bit number indicating the length of the error message, plus the text of the error message in UTF-8 format. The error message is typically a human-readable string. This message is sent by the plugin if an error occurred.
  • SYNC 1(4)|SYNCLEN(4)|SYNCDATA(SYNCLEN): Document Synchronization. The 32 bit command number is 0x00000001. This command is sent by the plugin after it received the Client HELO, and it contains the initial document text. There may be many SYNC messages in sequence. The command identifier is followed by a 32-bit number indicating the length of the text (in bytes, not in characters), followed by the actual text.
  • SYNE 2(4): Document Synchronization End. The 32 bit command number is 0x00000002. This command is sent when the document synchronization has completed.
  • INSE 3(4)|INSPOS(4)|INSLEN(4)|INSDATA(INSLEN): Insert Text. The 32 bit command number is 0x00000003. This command is sent by the plugin after synchronization has ended when a user has inserted text into the document. The command number is followed by two 32-bit numbers indicating the position (in characters) at which an insertion is made, and the length of text that was inserted (in bytes, not in characters). The actual text that was inserted follows the two numbers.
  • ERAS 4(4)|DELPOS(4)|DELLEN(4): Erase Text. The 32 bit command number is 0x00000004. This command is sent by the plugin after synchronization has ended when a user has deleted text from the document. The command number is followed by two 32-bit numbers indicating the position (in characters) and the length (in characters) of the text that has been removed from the document.

Python Example

The following is a small example written in Python demonstrating how the document-stream interface can be used. The python script queries the document called /test on the infinote server and prints its content to stdout.

import socket
import struct
import sys

address = '\000org.infinote.infinoted'
address += '\000' * (108 - len(address))
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(address)

user = 'ck'
path = '/test'

command = struct.pack('IH%dsH%ds' % (len(user), len(path)), 0, len(user), user, len(path), path)
sock.send(command)

def parse(buf):
    if len(buf) < 4: return None

    (command,) = struct.unpack('I', buf[0:4])

    if command == 0:
        return parse_error(buf[4:])
    elif command == 1:
        return parse_sync(buf[4:])
    elif command == 2:
        return parse_sync_end(buf[4:])
    else:
        raise Exception('Unexpected command: %u' % command)

def parse_error(buf):
    if len(buf) < 2: return None
    (length,) = struct.unpack('H', buf[0:2])

    if len(buf) < 2 + length: return None
    (message,) = struct.unpack('%us' % length, buf[2:2+length])

    raise Exception('Server error: %s' % message)

def parse_sync(buf):
    if len(buf) < 4: return None
    (length,) = struct.unpack('I', buf[0:4])

    if len(buf) < 4 + length: return None
    (content,) = struct.unpack('%us' % length, buf[4:4+length])

    utf = unicode(content, 'UTF-8')
    sys.stdout.write(utf)
    return (buf[4+length:])

def parse_sync_end(buf):
    # Done
    sys.stdout.write('\n')
    sys.exit(0)

buf = ""
while True:
    data = sock.recv(4096)
    buf += data
    while True:
        rebuf = parse(buf)
        if rebuf == None: break
        buf = rebuf

Other Plugins

Back to the list of infinoted plugins.