1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
|
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
// This is a fairly low-level client library for the tomsg protocol. No state
// is kept; the API just allows for sending commands and receiving responses
// and push messages from the server.
// Use tomsg_connect() to connect, then wait for messages by poll(2)'ing on the
// file descriptor returned by tomsg_poll_fd(). To send a command, use
// functions like tomsg_register(), tomsg_send(), etc.
struct tomsg_client;
enum tomsg_retval {
// Successful result
TOMSG_OK = 0,
// Error codes
TOMSG_ERR_CONNECT, // Server refused connection
TOMSG_ERR_UNTRUSTED, // Hostkey was rejected
TOMSG_ERR_CLOSED, // Server connection unexpectedly closed
TOMSG_ERR_VERSION, // Server protocol version incompatible
TOMSG_ERR_TRANSPORT, // Error in the underlying SSH transport
TOMSG_ERR_SPACE, // Argument contained space or LF, while it could not
TOMSG_ERR_AGAIN, // (tomsg_next_event) no events for now, poll(2) and try again
TOMSG_ERR_PARSE, // (tomsg_next_event) could not parse line from server, line ignored
TOMSG_ERR_MEMORY, // Error allocating memory
};
// Should return 'true' if the key is trusted, 'false' otherwise. The hash is
// sha256 in byte form, not yet encoded in hexadecimal or similar. The
// 'userdata' pointer comes from the 'tomsg_connect' invocation.
typedef bool (*tomsg_hostkey_checker_t)(
const unsigned char *hash, size_t length, void *userdata);
// Convenience function to convert a hash to a human-readable form. Returns a
// reference to an internal static buffer.
const char* tomsg_print_hash(const unsigned char *hash, size_t length);
// Returns reference to internal static buffer.
const char* tomsg_strerror(enum tomsg_retval code);
// If successful, stores a new connection structure in 'client' and returns
// TOMSG_OK. On error, stores NULL in 'client' and returns an error code.
// This function blocks until version negotiation is complete. For a
// non-blocking version, see tomsg_async_connect().
enum tomsg_retval tomsg_connect(
const char *hostname, int port,
tomsg_hostkey_checker_t checker,
void *userdata, // for checker
struct tomsg_client **client // output
);
struct tomsg_async_connect;
// If this returns an error code, it stores NULL in *client.
enum tomsg_retval tomsg_async_connect(
const char *hostname, int port,
struct tomsg_async_connect **client // output
);
enum tomsg_async_connect_event_type {
TOMSG_AC_HOSTKEY, // should check hostkey, see 'key' and tomsg_async_{accept,reject}()
TOMSG_AC_SUCCESS, // the tomsg_async_connect struct has been freed; connection is in 'client'
};
struct tomsg_async_connect_event {
enum tomsg_async_connect_event_type type;
union {
struct {
// Same as arguments to tomsg_hostkey_checker_t
unsigned char *hostkey;
size_t length;
} key; // TOMSG_AC_HOSTKEY
struct tomsg_client *client; // TOMSG_AC_SUCCESS
};
};
void tomsg_async_connect_event_nullify(struct tomsg_async_connect_event event);
// Will return TOMSG_ERR_AGAIN if no events are available at present. In that
// case, use poll(2) on the file descriptor from tomsg_async_connect_poll_fd().
// NOTE: If an error message is returned, the struct tomsg_async_connect will
// already be freed.
enum tomsg_retval tomsg_async_connect_next_event(
struct tomsg_async_connect *client,
struct tomsg_async_connect_event *event // output
);
// Accept or reject the hostkey obtained using a prior invocation of
// tomsg_async_connect_next_event(). Pass true to accept, false to reject. In
// case of rejection, as well as on error, the connection will be closed and
// the client structure freed. A successful rejection is indicated by
// TOMSG_ERR_UNTRUSTED.
enum tomsg_retval tomsg_async_connect_accept(struct tomsg_async_connect *client, bool accept);
// Returns a file descriptor that can be listened for read-ready events (using
// e.g. select(2) or poll(2)). Whenever it becomes ready for reading, you
// should call tomsg_async_connect_next_event().
int tomsg_async_connect_poll_fd(const struct tomsg_async_connect *client);
// Close the connection. This also frees the client structure. Note that even
// in case of an error, you must still call this function to prevent a memory
// leak.
void tomsg_close(struct tomsg_client *client);
// Returns a file descriptor that can be listened for read-ready events (using
// e.g. select(2) or poll(2)). Whenever it becomes ready for reading, you
// should call tomsg_next_event(). If the connection was already closed
// internally due to an error, returns -1.
int tomsg_poll_fd(const struct tomsg_client *client);
enum tomsg_event_type {
// Data in union fields indicated (but check 'error' first)
TOMSG_EV_REGISTER, // login
TOMSG_EV_LOGIN, // login
TOMSG_EV_LOGOUT, // -
TOMSG_EV_LIST_ROOMS, // list_rooms
TOMSG_EV_LIST_MEMBERS, // list_members
TOMSG_EV_CREATE_ROOM, // create_room
TOMSG_EV_LEAVE_ROOM, // leave_room
TOMSG_EV_INVITE, // join
TOMSG_EV_SEND, // send
TOMSG_EV_HISTORY, // history
TOMSG_EV_GET_MESSAGE, // get_message
TOMSG_EV_PING, // -
TOMSG_EV_IS_ONLINE, // is_online
TOMSG_EV_USER_ACTIVE, // user_active
TOMSG_EV_PUSH_ONLINE, // is_online
TOMSG_EV_PUSH_MESSAGE, // push_message
TOMSG_EV_PUSH_INVITE, // push_invite
TOMSG_EV_PUSH_JOIN, // join
TOMSG_EV_PUSH_LEAVE, // push_leave
};
struct history_message {
char *username;
int64_t timestamp;
int64_t msgid;
int64_t replyid; // -1 if normal message, the replied-to message id otherwise
char *message;
};
struct tomsg_event {
enum tomsg_event_type type;
// If the server returned an error, 'error' is non-NULL; otherwise it is
// NULL, and the union fields describe the server's response.
// Actually, if 'error' is non-NULL, the union fields that can be
// filled with request data will already be filled.
char *error;
union {
struct {
char *username;
} login;
struct {
int64_t count;
char **rooms;
} list_rooms;
struct {
char *room_name;
int64_t count;
char **members;
} list_members;
struct {
char *room_name;
} create_room;
struct {
char *room_name;
} leave_room;
struct {
char *room_name;
char *username;
} join;
struct {
int64_t tag; // the tag of the send request, returned by tomsg_send()
int64_t msgid;
} send;
struct {
char *room_name;
int64_t count;
struct history_message *messages;
} history;
struct {
char *room_name;
struct history_message message;
} get_message;
struct {
char *username;
int64_t online_count;
} is_online;
struct {
bool active;
} user_active;
struct {
char *room_name;
struct history_message message;
} push_message;
struct {
char *room_name;
char *inviter;
} push_invite;
struct {
char *room_name;
char *username;
} push_leave;
};
};
// Free the fields in the event structure corresponding to the event type set.
// The structure memory may be re-used for a different event afterwards. Note
// that, despite the name, the fields are not actually set to NULL -- they
// couldn't be, since the structure is passed by value (to indicate ownership
// transfer).
void tomsg_event_nullify(struct tomsg_event event);
// Will return TOMSG_ERR_AGAIN if no events are available at present. In that
// case, use poll(2) on the file descriptor from tomsg_poll_fd().
// NOTE: when calling this function, you must call it as long as it gives
// TOMSG_OK, because there might be multiple messages in one batch.
enum tomsg_retval tomsg_next_event(
struct tomsg_client *client,
struct tomsg_event *event // output
);
// Send a register command to the server.
enum tomsg_retval tomsg_register(
struct tomsg_client *client,
const char *username, const char *password
);
// Send a login command to the server.
enum tomsg_retval tomsg_login(
struct tomsg_client *client,
const char *username, const char *password
);
// Send a logout command to the server.
enum tomsg_retval tomsg_logout(struct tomsg_client *client);
// Send a list_rooms command to the server.
enum tomsg_retval tomsg_list_rooms(struct tomsg_client *client);
// Send a list_members command to the server.
enum tomsg_retval tomsg_list_members(struct tomsg_client *client, const char *room_name);
// Send a create_room command to the server.
enum tomsg_retval tomsg_create_room(struct tomsg_client *client);
// Send a leave_room command to the server.
enum tomsg_retval tomsg_leave_room(
struct tomsg_client *client, const char *room_name);
// Send an invite command to the server.
enum tomsg_retval tomsg_invite(
struct tomsg_client *client, const char *room_name, const char *username);
// Send a send command to the server.
// If 'tag' is not NULL, will write the message request tag to the referenced
// location. This tag will also be given in the TOMSG_EV_SEND response, so that
// the response can be linked to the original message.
// If 'replyid' is -1, will send a normal message. If not, it must be the id of
// an earlier message in the same room, and this will send a reply to the
// indicated message.
enum tomsg_retval tomsg_send(
struct tomsg_client *client, const char *room_name, const char *message,
int64_t replyid,
int64_t *tag // output
);
// Send a history command to the server.
// Pass -1 to 'before_msgid' to return the latest 'count' instead messages
// before that id.
enum tomsg_retval tomsg_history(
struct tomsg_client *client, const char *room_name,
int64_t count, int64_t before_msgid);
// Send a get_message command to the server.
enum tomsg_retval tomsg_get_message(struct tomsg_client *client, int64_t msgid);
// Send a ping command to the server.
enum tomsg_retval tomsg_ping(struct tomsg_client *client);
// Send an is_online command to the server.
enum tomsg_retval tomsg_is_online(struct tomsg_client *client, const char *username);
// Send a user_active command to the server.
enum tomsg_retval tomsg_user_active(struct tomsg_client *client, bool active);
|