projects@yorhel.nl
home - git - @ayo
= pgp = only used for releases
key - mit
7446 0D32 B808 10EB A9AF A2E9 6239 4C69 8C27 39FA

Cute decorative scissors, cutting through your code.

The Globster D-Bus API

Introduction

This document describes the D-Bus API of the Globster daemon. Familiarity with D-Bus is assumed; There's a good overview available if you're new to D-Bus, and there's also the (surprisingly readable) official specification.

Globster is still in an early stage of its life, so the objects and interfaces described here are subject to change. I do have every intention on stabilizing the interfaces in the future. As Globster gets more mature, I intent to add new features and improvements in an incremental and backwards compatible manner.

Overview

As ugly as they are, Globster follows the D-Bus naming conventions. All object names are prefixed with /net/blicky/Globster and all interface names with net.blicky.Globster. The Globster daemon registers itself to D-Bus with the well-known bus name net.blicky.Globster.

The hierarchy of all exported objects is as follows:

net
└─ blicky
   └─ Globster
      ├─ HubManager
      └─ Hub
         ├─ 1
         ├─ 2
         └─ $n

And here's a listing of all exported objects with the names of the interfaces they implement.

/net/blicky/Globster
  net.blicky.Globster

/net/blicky/Globster/HubManager
  net.blicky.Globster.HubManager

/net/blicky/Globster/Hub/$n
  net.blicky.Globster.Hub
  net.blicky.Globster.HubConnection
  net.blicky.Globster.HubUsers
  net.blicky.Globster.HubChat

Each /net/blicky/Globster/Hub/$n object represents a connection to a hub. These objects are dynamically created and destroyed through the /net/blicky/Globster/HubManager object.

Interfaces

The interfaces are described in detail below. Note that this entire document actually serves two different purposes. The first obviously being documentation, but this document also happens to function as the input for a code generator within Globster. The interface names and method, signal and property signatures are automatically extracted from this document and used to generate code to ease the implementation of these services. As such, the interface descriptions follow a common format.

Every interface heading has the full name of the interface followed by a short name in parenthesis. E.g. net.blicky.Globster (app). The short name is used internally by Globster, feel free to ignore it. Methods, signals and properties are defined in code blocks and use the following notation.

method <name>(<args>)
method <name>(<args>) -> (<args>)
signal <name>(<args>)
property <type> <name> <access>

Where <args> is a possibly empty list of arguments. Each argument follows C-style notation, i.e. <type> <name>. Multiple arguments are separated by a comma. As part of D-Bus, a method can have multiple return values (out arguments). If a method doesn't have any out arguments, the second argument list is omitted.

A <type> refers to a single complete type in the short D-Bus notation. E.g. u is an unsigned 32-bit integer and ay is an array of bytes. Refer to the D-Bus specification for the complete list. The <access> of a property is either r, w or rw for read-only, write-only or read-write properties, respectively.

Unless otherwise noted, changes to a property always emit a org.freedesktop.DBus.Properties.PropertiesChanged signal with the new value.

net.blicky.Globster (app)

This interface is used for application-global stuff. Accessed through the /net/blicky/Globster object.

method Shutdown()

Calling this method will cause the Globster daemon to shut itself down.

property s VersionString r
property u VersionNumber r

These properties provide the version of the Globster daemon. The VersionString follows the following format (stuff between square brackets is optional):

<major>[.<minor>[.<patch>]][-<commit>-g<hash>[-d]]

The VersionNumber provides a numeric version of the above for the purpose of easy comparison. In the decimal representation, this number is formatted as aaiiippccc, where a=major, i=minor, p=patch, c=commit. For example, 0.0-180 becomes 180, and 1.5.12-10 is 100512010.

property s LogLevel rw

The currently active log level. The format is equivalent to the --log-level argument of globster(1). This setting can be changed on the fly.

property s TigerPID rw
property s TigerCID r

These properties provide the base32-encoded PID and CID of the client. It is possible to change the PID, the CID will be updated automatically in that case. A modification to the PID/CID pair requires reconnecting to each hub to be effective.

net.blicky.Globster.HubManager (hubm)

Three methods to manage hubs:

method Create() -> (u id)
method Delete(u id)
method List() -> (au list)

These should be self-explanatory. Create() creates a new hub object that can be accessed with /net/blicky/Globster/Hub/$id, with $id replaced with the decimal number returned by Create(). Hubs and their associated configuration are stored in the database and remembered after a restart of the Globster daemon.

signal Created(u id)
signal Deleted(u id)

property q NormalHubs r
property q RegHubs r
property q OpHubs r

net.blicky.Globster.Hub (hub)

Generic hub interface, holds miscellaneous settings and information.

property s MyNick rw
property s MyDescription rw
property s MyEmail rw
property u MyConnection rw

These properties specify the public information of the user. MyConnection should indicate the upload speed of the user in bytes/s. All fields can be left empty, except MyNick. If no nick is set, a randomly generated one is used.

property s MyPassword rw

Should be set prior to connecting if the hub requires a password. (TODO: Allow more dynamic login, e.g. by providing a "hub wants your password" signal and a "here's my password" method.)

property s HubEncoding rw

Configures the encoding used for messages sent to and received from the hub. Defaults to UTF-8. This setting is only used for NMDC hubs, ADC always uses UTF-8. Modifying the encoding while being connected to an NMDC hub may cause odd issues with some chat messages and user nicks using the old encoding while others use the new. A reconnect is thus recommended.

All of the above settings, with the exception of MyPassword, are stored in the database and remembered across restarts. (TODO: Figure something out for MyPassword, too).

property s HubName r
property s HubDescription r
property s HubVersion r

Information provided by the hub while logging in. HubVersion is (currently) always empty for NMDC hubs. These properties are reset to an empty string when the hub is disconnected.

property s HubError r
property s HubErrorMessage r
property s HubRedirect r

These properties are set when the hub indicates an error of any kind, and can be checked when the hub is Disconnected() with an net.blicky.Globster.Error.Hub error. These properties are reset to an empty string after a reconnect. HubError takes any of the following values:

VALUE             MEANING
 Full              Hub is full
 InvalidNick       Invalid nick name, or nick already taken
 NeedPassword      Hub requires a password, but MyPassword is empty
 InvalidPassword   Invalid password
 Redirect          Hub wants you to redirect to another address
 Disconnect        Hub has disconnected you (kick/ban/whatever - only on ADC)
 Protocol          Any other error

The HubErrorMessage property will have a human-readable error message, which often originates from the hub. If HubError is Redirect, then HubRedirect will hold the address to redirect to. Redirects are currently not automatically followed.

net.blicky.Globster.HubConnection (hubc)

property q ConnectState r

Holds the connection state of the hub. Possible values are 0:IDLE (not connected), 1:BUSY (connecting), 2:CONN (connected), 3:DISC (disconnecting), 4:TIMER (IDLE, but reconnect timeout is active). The connection follows the following state machine:

   /--------------\  1   /------\  3   /------\  5   /------\
-> | IDLE / TIMER | ---> | BUSY | ---> | CONN | ---> | DISC |
   \--------------/      \------/      \------/      \------/
          ^                  |             |             |
          |                2 |           4 |           6 |
          |                  |             |             |
          '----------------------------------------------`

The numbered state transitions happen on the following events:

1. Connect() method or automatic reconnect (see ReconnectTimeout)
2. Disconnect(), ForceDisconnect() or Delete() method, or the connection failed
3. Connection was successful
4. ForceDisconnect() or Delete() method, or a fatal network error
5. Disconnect() method, or a protocol error
6. ForceDisconnect() or Delete() method, network error, or successful disconnect

The Delete() method refers to the one in the net.blicky.Globster.HubManager interface. The connection always transitions to the IDLE state right before the hub object is deleted. The other methods are described below.

method Connect(s addr, q timeout_sec)

Can only be called in the IDLE or TIMER state. Moves to the BUSY state, resolves the address and tries to open a connection. Only returns after the connection has been established - at which point we're in the CONN state - or moves back to IDLE or TIMER on error.

If addr takes the form of adc://host:port/ or adcs://.., then the hub is assumed to understand the ADC protocol. Otherwise NMDC is assumed. If addr starts with adcs:// or nmdcs://, the hub is assumed to use TLS. If addr is an empty string, the address given to the last call to Connect() is re-used, or an error is thrown if Connect() hasn't been called before. The given address is saved in the database and can be re-used across Globster restarts.

timeout_sec must be smaller than the D-Bus method reply timeout, otherwise you will receive a generic timeout error and not the actual result of the Connect() method. With most D-Bus bindings, you should be able to specify this timeout. The default timeout is 25 seconds in libdbus (1.6.4). Perl's Net::DBus (1.0.0) unfortunately uses a hardcoded timeout of 60 seconds.

Connect() may throw the following errors:

net.blicky.Globster.Disconnected
  Operation cancelled by a Disconnect().

org.freedesktop.DBus.Error.InvalidArgs
  Invalid address format.

net.blicky.Globster.Error.DNS
  DNS lookup failed

net.blicky.Globster.Error.ConnectionRefused
  Remote server didn't accept the connection

org.freedesktop.DBus.Error.Timeout
  The timeout exceeded.

org.freedesktop.DBus.Error.IOError
  Other network error while connecting

Note that the TLS handshake and hub login is done in the CONN state rather than the BUSY state. This means that Connect() may succeed without errors even if, shortly afterwards, the TLS handshake fails or if you couldn't login to the hub.

If ReconnectTimeout > 0, automatic reconnect will be attempted if connecting fails with any error other than net.blicky.Globster.Disconnected or org.freedesktop.DBus.Error.InvalidArgs.

method Disconnect()
method ForceDisconnect()

Disconnect() aborts a connection attempt in the BUSY state, performs a graceful disconnect in the CONN state, or disables the reconnect timeout in the TIMER state. It throws an error when used in any other state. ForceDisconnect() is a stronger version of Disconnect() that can be called from any state. It moves directly into the IDLE state, forcefully aborting a connection attempt and forcing a hub disconnect when connected.

property b AutoConnect rw

When set, a Connect() with an empty string as address and a 60 second connect timeout is automatically done when the Globster daemon is (re)started. This functionality can be globally disabled with the -n option to globster(1).

property s ConnectAddress r
property s ResolvedAddress r

ConnectAddress holds the address as last given to Connect(), or an empty string if Connect() hasn't been called yet. ResolvedAddress holds the resolved IP address and port, and is updated on each connection attempt.

property q ReconnectTimeout rw

Configured timeout before automatically reconnecting to the hub after being disconnected, in seconds. This timeout will start when the Disconnected signal has been emitted with a network error or non-fatal hub error. Setting this to 0 will disable automatic reconnect. Default is 60 seconds. If this setting is changed while in the TIMER state, the timer will be restarted with the new value or the IDLE state is activated if the new timeout is 0.

property s KeyprintSHA256 r

If TLS is enabled, this property will hold the base32-encoded SHA-256 keyprint of the hub certificate. This property is set to an empty string whenever a new connection is attempted (transition 1 in the state diagram), and updated to reflect the actual keyprint of the hub as soon as the TLS handshake has been completed in the CONN state.

signal Disconnected(s reason, s message)

This signal is emitted whenever the hub moves from the BUSY or CONN states to the IDLE, TIMER or DISC state (transitions 2, 4 and 5 in the state diagram). When moving out of the BUSY state, reason takes the same value as the error code of Connect(). The following values are used for reason when moving out of the CONN state:

net.blicky.Globster.Disconnected
  User-initiated disconnect, either through Disconnect(), ForceDisconnect()
  or Delete() on the hub object.

net.blicky.Globster.Error.ConnectionReset
  Hub closed the connection.

net.blicky.Globster.Error.TLSHandshake
  TLS handshake failed.

net.blicky.Globster.Error.LoginTimeout
  TLS handshake or hub login took too long.

org.freedesktop.DBus.Error.IOError
  Other network error.

net.blicky.Globster.Error.Hub
  ADC or NMDC hub error. See the HubError properties in the
  net.blicky.Globster.Hub interface for more detailed information.

The message argument may have more detailed human-readable information.

net.blicky.Globster.HubUsers (hubu)

This interface provides access to the user list of the connected hub.

A user is internally identified with a signed 64-bit positive number. This number uniquely identifies a (hub, user) pair within the entire application: The same user on two different hubs will have a different ID. The number is stable in the sense that it remains the same for a certain user even after reconnect or restart. On ADC, this identifies a (hub, CID) tuple, on NMDC a (hub, nick) tuple. The special value 0 always identifies this client (and violates the "unique within the entire application" property, since it's 0 on every hub). Negative identifiers may have a special meaning depending on the context where the ID is used.

User information is stored in the following fields,

NO TYPE    DEFAULT   DESCRIPTION
 1 s       -         Nick (not guaranteed to be unique on an
                     NMDC hub, due to conversion issues)
 2 s       empty     Description
 3 s       empty     Client
 4 s       empty     E-Mail
 5 u       0         Slot count
 6 u       0         Number of shared files
 7 t       0         Share size, in bytes
 8 u       0         Upload speed, in bytes/s
 9 (uuu)   0         Number hubs this user in on
                     (normal/registered/operator)
10 s       empty     IPv4 address
11 s       empty     IPv6 address
12 u       0         Auto-opens slots if total upload is smaller
                     than this number in bytes/s
13 ay      empty     CID
14 u       0         Flags

NO is the field number, referenced by the methods and signals described below. DEFAULT is its value if the field is not known or empty. Note that in many cases these default values are also valid, so it's not always possible to determine the difference between "we simply don't know the value of this field" and "this field has intentionally been left empty".

The Flags field is a bit mask of the following numbers:

 1: Set if the user is currently online
 2: Set if the user is 'away'
 4: Set if the user is an operator
 8: Set if the users' client supports C-C TLS
16: Set if the user is in active mode (May not always be known on NMDC hubs)

Note that the user list does not only contain users that are currently online, but may also include a few users that have already left the hub. The above online flag is used to indicates this status.

More fields and flags may be added in future versions.

method UserInfo(x user, aq reqfields) -> (aq fields, a{xv} list)

The UserInfo() method is used to obtain the full user list (if user is negative) or fetch information of only a single user (if user is non-negative). The list includes users that don't have their online flag set. An empty list is returned if the user list if empty or if the requested user ID does not exist.

list is a dictionary with the user ID as key, and a (variable) struct as value. The struct contains the fields in the same order as they appear in the returned fields array. For example, if fields is [13, 3, 7], then the type of the variant will be (ayst). If the fields array is empty, then the type of the variant will simply be a bool with value true.

The fields array will contain all the requested fields in reqfields known to the Globster daemon. Unknown or duplicate fields are removed and the order in which the fields appear may be different.

property b UserListComplete r

This property is true when the user list has been successfully synchronised with the hub. If this is false, then we're either not logged in to the hub or we're still receiving the user list. Note that on some NMDC hubs, this signal may be delayed for a few seconds after receiving the user list. This is because it's not always possible to detect when we've received the full list. In rare cases, it's also possible that this signal is emitted before the list has been fully received.

signal UserChanged(x user, aq fields, v values)
signal UserJoined(x user, aq fields, v values)
signal UserLeft(x user, x initiator, s message)

These signals are emitted when a users' information has changed, a user has joined the hub or quit the hub, respectively.

The fields and values arguments to UserChanged and UserJoined have the same meaning as described for the UserInfo() method. The UserJoined signal only mentions fields that have a non-empty value for the new user. Similarly, the UserChanged signal only mentions modified fields. Keep in mind that new fields may be added in later versions, so your program has to deal with (ignore) unknown fields. The type of each field, including those not known to your program, can be inferred by the type of the struct included in the D-Bus message. For most D-Bus bindings you should just be able to skip over an unknown value without caring about its type.

The initiator argument to UserLeft indicates user who kicked/banned the user out of the hub. The special value -1 is used for the hub (i.e. no specific user), and the special value -2 indicates that the user has not been kicked out, but just left the hub. Many hubs do not actually send this information, so -2 can mean anything. message indicates the reason for leaving the hub or the kick/ban message. Again, many hubs don't send this information, so it's often just an empty string.

After a UserLeft signal, the user can still be queried for with UserInfo() for at least 30 seconds. That is, it will remain in the list for a while with it's online flag reset.

The above signals are only sent when the UserListComplete property is true, and only for users with the online flag set. In order to obtain the full user list, first wait for UserListComplete property to become true, then use UserInfo() to fetch everything, and then check these signals for changes.

property u UserCount r
property t UserShareSize r

These properties hold the number of users currently online and the total share size that they provide. As with the above signals, changes to these properties are only emitted once UserListComplete is true.

net.blicky.Globster.HubChat (hubch)

This interface provides access to the chatting and private messaging facilities of the hub. All chat messages are assigned to a group. For private messages, this is the ID of the user. For chat rooms, this is the ID of the chat room user (on Direct Connect, "chat rooms" appear as "users"). The main chat has the special group '-1'.

method SendChat(x group, s message, b me)

Send a message to a group. Setting me to true will send it in first person, as in /me. Since NMDC does not support first-person speak, the message will simple be prefixed with "/me" instead. Throws a org.freedesktop.DBus.Error.InvalidArgs error if the specified group does not exist or we're not logged in to the hub.

Messages sent with SendChat() will be echoed back in the ReceiveChat signal and ChatLog() method. Normally, this echoing happens through the hub (i.e. we receive our own message back from the hub), but on NMDC hubs this echo is emulated for private messages. This is noticeable only by the fact that the message is echoed immediately rather than after a round-trip delay through the hub.

signal ReceiveChat(x group, x from, s message, b me)

This signal is emitted when a new chat message has been received. from can have the special value -1 to indicate that the message came directly from the hub. The MOTD and hub-related status messages are currently sent in a ReceiveChat as well, but this is subject to change.

property u ChatLogSize rw

This property determines how many messages to keep in memory for each group. It defaults to 0 in order to preserve memory, so applications that makes use of the ChatLog() and ChatGroups() methods below should change this property to a sane value. There will never be more messages in the log than specified by this property, so decreasing this value will behave as if the CleanLog() method is called for every item in ChatGroups().

method ChatLog(x group, u number, d maxage) -> (a(dxsb) log)

Returns the contents of the in-memory message log for the particular group. At most number messages are returned, and only those that are newer than maxage (in seconds). Each log message is represented as a struct (in pseudo-C):

struct {
  double  age;
  int64_t from;
  string  message;
  bool    me;
};

age is the time since the arrival of the message, in seconds. The other fields are equivalent to the respective arguments of the ReceiverChat signal. An empty list is returned if there are no messages in the log for the requested group.

If group or from refer to an actual user (i.e. not -1), then they are guaranteed to still be available in the user list and their information can be queried for with a UserInfo(), even if the user hasn't been online in the past few days. Note, however, that the user information may have changed after a message has been received. In particular, on ADC it is possible for users to change their nick, so a message may have been sent from a different nick. Similarly, this log only handles chat messages, information about where in the chat history a user has joined or quit is not available.

method ChatGroups() -> (ax groups)

Returns a list of groups for which we have at least one message logged. I.e. the groups for which ChatLog() will return a non-empty list.

method ClearLog(x group, u keep)

Remove old messages from the in-memory log for a particular group. At most keep messages will be kept.