File: biblesync.hh

package info (click to toggle)
biblesync 1.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 308 kB
  • sloc: cpp: 1,050; ansic: 81; sh: 30; makefile: 20
file content (435 lines) | stat: -rw-r--r-- 14,225 bytes parent folder | download
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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/*
 * BibleSync library
 * biblesync.hh
 *
 * Karl Kleinpaste, May 2014
 *
 * All files related to implementation of BibleSync, including program
 * source, READMEs, manual pages, and related similar documents, are in
 * the public domain.  As a matter of simple decency, your social
 * obligations are to credit the source and to coordinate any changes you
 * make back to the origin repository.  These obligations are non-
 * binding for public domain software, but they are to be seriously
 * handled nonetheless.
 */

#ifndef __BIBLESYNC_HH__
#define __BIBLESYNC_HH__

#include <libintl.h>
#ifndef _
#define	_(x)	gettext(x)
#endif

#include "biblesync-version.hh"

//
// Bible Sync Protocol.
// http://biblesyncprotocol.wikispaces.com/
//
// BSP provides a classroom type of arrangement, where one person
// (speaker) is in charge of inducing others' (audience) Bible
// software to navigate as speaker requires.  The speaker only
// xmits and the audience only recvs.
// BSP also provides a personal mode which both xmits and recvs, where
// one user works with multiple programs across several devices,
// also suitable for small teams working together, such as translators.
// BSP is implemented using multicast UDP with small packets in a
// simple format employing a few bytes of packet control followed by a
// series of newline-terminated "name=value" pairs.
//
// * Application interface *
//
// - object creation.
//	BibleSync *YourBibleSyncObjectPtr = new BibleSync(app, version, user);
//		create exactly one.
//		identify the application, its version, and the user.
//
// - mode selection.
//	setMode(BSP_MODE_xyz, your_void_nav_func, "passphrase");
//		invoke a mode, including net.setup as needed.
//		xyz = { DISABLE, PERSONAL, SPEAKER, AUDIENCE }.
//		   DISABLE kills it, shuts off network access.
//		   PERSONAL is bidirectional.
//		   SPEAKER xmits only.
//		   AUDIENCE recvs only.
//	=> empty passphrase ("") means re-use existing passphrase.
//	=> for any active mode, the application must then start polling using
//	   the receiver, BibleSync::Receive(), and stop polling when mode
//	   goes to DISABLE.  if Receive() is called while disabled, it will
//	   return FALSE to indicate its polled use should stop, otherwise TRUE.
//	=> interface for your_void_nav_func:
//		(char cmd, string speakerkey,
//		 string bible, string ref, string alt,
//		 string group, string domain,
//		 string info,  string dump)
//		there are 6 your_void_nav_func() use cases, identified in cmd.
//		non-error cases provide valid speakerkey (UUID), else it is "".
//		1. 'A' (announce)
//		   presence message in alt.  dump available.
//		2. 'N' (navigation)
//		   bible, ref, alt, group, domain as arrived.
//		   info + dump available.
//		3. 'M' (mismatch) against passphrase or mode or listen status.
//		   info == "announce" or "sync" or "beacon" (+ user @ [ipaddr])
//			   sync:     bible, ref, alt, group, domain as arrived.
//			   announce: presence message in alt.
//			      also, individual elements are also available:
//			      overload: bible   ref       group    domain
//					user    [ipaddr]  app+ver  device
//		   dump available.
//		4. 'S' (new speaker)
//		   param overload as above. alt unused. see listenToSpeaker().
//		5. 'D' (dead speaker)
//		   opposite of new speaker. only param is speakerkey.
//		6. 'E' (error) for network errors & malformed packets.
//		   only info + dump are useful.
//
// - get current mode.
//	BibleSync_mode getMode().
//
// - get current passphrase, for default use when setting a new one.
//	string getPassphrase().
//
// - receive navigation.
//	BibleSync::Receive(YourBibleSyncObjPtr);	// *-* poll often *-*
//		see note below; calls your_void_nav_func().
//
// - send navigation.
//	BibleSync_xmit_status retval =
//		Transmit("NASB", "John.3.16", "some alt ref", "1", "BIBLE-VERSE");
//	  params: bible, ref, alt-ref, group, domain.
//	  all params have defaults.
//	=> it is the application's responsibility to send well-formed verse
//	   references.
//
// - set self as private
//	bool setPrivate(boolean);
//	  sets outgoing TTL to zero so no one hears you off-machine.
//	  applicable only to BSP_PERSONAL mode.
//
// - set beacon count based on frequency of calling Receive()
//      void setBeaconCount(int);
//        how often will your app call Receive()?
//        divide 10 by that, call setBeaconCount(result).
//
// - set new username
//      void setUser(string);
//        for apps (such as Bishop) which allow the user to set the friendly name
//        and which may be changed at some point whle BibleSync runs.
//
// - allow another speaker to drive us
//	void listenToSpeaker(bool listen, string speakerkey)
//		say yes/no to listening.
//
// Receive() USAGE NOTE:
// the application must call BibleSync::Receive(YourBibleSyncObjPtr)
// frequently.  For example:
//    g_timeout_add(1000,	// 1sec in msec.
//		    (GSourceFunc)BibleSync::Receive,
//		    biblesyncobj);	// of type (BibleSync *).
// g_timeout_add is a glib function for polled function calls.
// this will induce timed-interval calls to the function, as long
// as the function returns TRUE; upon returning FALSE, calls stop.
// other than with glib, accomplish the same thing somehow else.
// Receive is a static method accessible from C or C++.  it must be
// called with the pointer to your BibleSync object in order that
// object context be re-entered.  the internal receive routine
// is private.
//
// Note on speaker beacons:
// Protocol operates using periodic (10sec) beacons of speaker availability.
// By default, PERSONAL & AUDIENCE accepts listening to 1st announced speaker,
// thereafter ignores any more, but includes them in the list of available
// speakers and notifies the app of their availability (see above, 'S'/'D').
// Override this behavior choice however desired, using listenToSpeaker() in
// reaction to 'S' events or on user request.
// Speakers who stop xmitting beacons will timeout, be declared dead, and
// removed after 30sec beacon silence, with app notification ('D').
// Observe that pure Speaker clears the speaker list and by default ignores
// all newly-identified claimants to speaker status.  Again, this is default
// behavior, but it makes no sense to try to listen to one as the mode is
// a mismatch.
// Note also that Personal is both speaker and audience.

#include <map>
#include <string>

#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <sys/types.h>
#include <unistd.h>
#include <time.h>

#ifndef WIN32
#include <sys/utsname.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <uuid/uuid.h>
#else
#define	uuid_t	UUID
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#endif

using namespace std;

typedef enum _BibleSync_mode {
    BSP_MODE_DISABLE,
    BSP_MODE_PERSONAL,
    BSP_MODE_SPEAKER,
    BSP_MODE_AUDIENCE,
    N_BSP_MODE
} BibleSync_mode;

typedef enum _BibleSync_xmit_status {
    BSP_XMIT_OK,
    BSP_XMIT_FAILED,
    BSP_XMIT_NO_SOCKET,
    BSP_XMIT_BAD_TYPE,
    BSP_XMIT_NO_AUDIENCE_XMIT,
    BSP_XMIT_RECEIVING,
    N_BSP_XMIT
} BibleSync_xmit_status;

// args: cmd, speakerkey, bible, verse, alt, group, domain, info, dump.
typedef void (*BibleSync_navigate)(char,   string,
				   string, string, string,
				   string, string,
				   string, string);

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

#ifndef min
#define	min(a,b)	(((a) < (b)) ? (a) : (b))
#endif

// message structure constants
#define	BSP_MULTICAST	"239.225.27.227"
#define	BSP_PORT	22272
#define	BSP_MAX_SIZE	1280	// in spec, it's 512.  experimenting.
#define	BSP_RES_SIZE	6	// unused bytes, fill out header to 32.
#define	BSP_HEADER_SIZE	32
#define	BSP_MAX_PAYLOAD	(BSP_MAX_SIZE - BSP_HEADER_SIZE)

// message indications
#define	BSP_MAGIC	htonl(0x409CAF11)
#define	BSP_PROTOCOL	2

// message types
#define	BSP_ANNOUNCE	1	// presence announcement.
#define	BSP_SYNC	2	// navigation synchronization.
#define	BSP_BEACON	3	// speaker availability beacon.
// beacon packet is identical to presence announcement except for type.

// beacon constants
#define	BSP_BEACON_COUNT	10	// xmit every N calls of Receive().
#define	BSP_BEACON_MULTIPLIER	3	// multiplier for aging to death.

// message content names.
#define BSP_APP_NAME			"app.name"		// req'd
#define BSP_APP_VERSION			"app.version"		// opt
#define BSP_APP_INSTANCE_UUID		"app.inst.uuid"		// req'd
#define BSP_APP_OS			"app.os"		// opt
#define BSP_APP_DEVICE			"app.device"		// opt
#define BSP_APP_USER			"app.user"		// req'd
#define BSP_MSG_SYNC_DOMAIN		"msg.sync.domain"	// req'd
#define BSP_MSG_SYNC_VERSE		"msg.sync.verse"	// req'd
#define BSP_MSG_SYNC_ALTVERSE		"msg.sync.altVerse"	// opt
#define BSP_MSG_SYNC_BIBLEABBREV	"msg.sync.bibleAbbrev"	// req'd
#define BSP_MSG_SYNC_GROUP		"msg.sync.group"	// req'd
#define BSP_MSG_PASSPHRASE		"msg.sync.passPhrase"	// req'd

// required number of fields to send (out) or verify (in).
#define	BSP_FIELDS_RECV_ANNOUNCE	4
#define	BSP_FIELDS_RECV_SYNC		8
#define	BSP_FIELDS_XMIT_ANNOUNCE	7
#define	BSP_FIELDS_XMIT_SYNC		12

#ifdef linux
# define	BSP_OS	"Linux"
#else
# ifdef WIN32
#  define	BSP_OS	"Windows"
# else
#  define	BSP_OS	"UNIX"
# endif
#endif

#define	BSP_UUID_PRINT_LENGTH		37	// actually 36, plus '\0'.

class BibleSync {

private:

    // simple name/value pairs.
    typedef std::map < string, string > BibleSyncContent;

    typedef struct _BibleSyncMessage {
	uint32_t  magic;
	uint8_t   version;
	uint8_t   msg_type;
	uint16_t  num_packets;
	uint16_t  index_packet;
	uint8_t   reserved[BSP_RES_SIZE];
	uuid_t    uuid;
	char      body[BSP_MAX_PAYLOAD+1];	// +1 for stuffing '\0'.
    } BibleSyncMessage;

    typedef struct _BibleSyncSpeaker {
	bool      listen;			// nav for this guy?
	uint8_t   countdown;			// lifetime aging.
	string    addr;				// for spoof check.
    } BibleSyncSpeaker;

    // key string is origin uuid.
    typedef std::map < string, BibleSyncSpeaker > BibleSyncSpeakerMap;
    typedef BibleSyncSpeakerMap::iterator BibleSyncSpeakerMapIterator;

    // self identification.
    string BibleSync_version;

    // application identifiers.
    string application;
    string version;
    string user;
    string device;

    // currently processing received navigation.
    // prevents use of Transmit.
    bool receiving;

    // when xmit-capable, we xmit BSP_BEACON every N calls of Receive().
    uint8_t beacon_countdown;	// progress toward our next beacon xmit
    uint8_t beacon_count;	// how many Receive() calls between beacon xmits

    // track currently-known speaker set.
    BibleSyncSpeakerMap speakers;

    // what operational mode we're in.
    BibleSync_mode mode;

    // callback by which Receive induces navigation.
    BibleSync_navigate nav_func;

    // privacy
    string passphrase;

    // network access
    struct sockaddr_in server, client;
    int server_fd, client_fd;
    struct ip_mreq multicast_req;

    // default address discoverer, for multicast configuration.
    void InterfaceAddress();
    struct in_addr interface_addr;

    // unique identification.
    uuid_t uuid;
    char uuid_string[BSP_UUID_PRINT_LENGTH];	// printable

    // socket init and listener start, called from setMode().
    string Setup();

    // dispose of network access.
    void Shutdown();

    // real receiver.
    int ReceiveInternal();		// C++ object context.
    int InitSelectRead(char *, struct sockaddr_in *, BibleSyncMessage *);

    // real transmitter.
    BibleSync_xmit_status
    TransmitInternal(char message_type = BSP_SYNC,
		     string bible  = "KJV",
		     string ref    = "Gen.1.1",
		     string alt    = "",
		     string group  = "1",
		     string domain = "BIBLE-VERSE");

    // speaker list management.
    void ageSpeakers();
    void clearSpeakers();

    // uuid dumper;
    void uuid_dump(uuid_t &u, char *destination);
    char uuid_dump_string[BSP_UUID_PRINT_LENGTH];
    void uuid_gen(uuid_t &u);		// differentiates linux/win32.

#ifdef linux
    // network self-analysis, borrowed from the net.
    int get_default_if_name(char *name, socklen_t size);
    int parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo);
    int readNlSock(int sockFd, char *bufPtr, size_t buf_size,
		   unsigned int seqNum, unsigned int pId);
#else
    // no other support routines needed for Windows/Solaris/BSD.
#endif /* linux */

public:
    BibleSync(string a, string v, string u);
    ~BibleSync();

    // operation.
    BibleSync_mode setMode(BibleSync_mode m,
			   BibleSync_navigate n = NULL,
			   string p = "");
    inline BibleSync_mode getMode(void) { return mode; };

    // library identification.
    inline string getVersion(void) { return BibleSync_version; };

    // obtain passphrase, for default choice.
    inline string getPassphrase(void) { return passphrase; };

    // audience receiver
    static int Receive(void *myself); // assume C context: poll from timeout.

    // speaker transmitter
    // public interface permits only BSP_SYNC transmission.
    // there is no reason for an app to send presence or beacon on its own.
    inline BibleSync_xmit_status Transmit(string bible  = "KJV",
					  string ref    = "Gen.1.1",
					  string alt    = "",
					  string group  = "1",
					  string domain = "BIBLE-VERSE")
    {
	return TransmitInternal(BSP_SYNC, bible, ref, alt, group, domain);
    }

    // set privacy using TTL 0 in personal mode.
    bool setPrivate(bool privacy);

    // say whether you want to hear from this speaker.
    void listenToSpeaker(bool listen, string speakerkey);

    // Speaker beacon must go out roughly every 10 seconds,
    // set count to approx divisor. how often does your app call Receive()?
    // every second, default 10.
    // every 2 seconds, use 5.
    // every 3 seconds, use 3 (9 seconds, fine).
    // value is force-bounded [3..10].
    inline void setBeaconCount(uint8_t count)
    {
	if (count > 10) count = 10;
	if (count < 3)  count = 3;
	beacon_count = count;
    }

    // set new user name
    // useful for apps that can change the name on the fly (e.g. Bishop).
    inline void setUser(string u)
    {
	user = u;
    }
};

#endif // __BIBLESYNC_HH__