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:
- Connect() method or automatic reconnect (see ReconnectTimeout)
:
- Disconnect(), ForceDisconnect() or Delete() method, or the connection failed
:
- Connection was successful
:
- ForceDisconnect() or Delete() method, or a fatal network error
:
- Disconnect() method, or a protocol error
:
- 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.