summaryrefslogtreecommitdiffstats
path: root/src/h.c
blob: 8067c5b4257a7a07d38d8767e5e3a2bacec7206a (plain) (blame)
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
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
/* ISAs: _sizeof means size of array, _length means usable/initialized members in array */
#define DC_ISA(type, name) type ** name; size_t name##_sizeof; size_t name##_length /* struct arr */
#define DC_ALLOC_CHUNK 1
#define DC_REALLOC_K 1.5
#define DC_BIGGER_ARRAY(name) do { /* unlike in previous programs, _BIGGER_ARRAY */ \
		name = realloc(name, sizeof(name[0])*ceil(name##_sizeof*DC_REALLOC_K)); \
		for (int DC_BA = name##_sizeof; DC_BA < ceil(name##_sizeof*DC_REALLOC_K); DC_BA++) \
			name[DC_BA] = NULL; \
		name##_sizeof = ceil(name##_sizeof*DC_REALLOC_K); /* no longer initializes */ \
	} while (0) /* note that sizeof(name[0]) does not dereferencec name */
#define DC_MR(n) if (n##_sizeof <= n##_length) /* make room */ \
	DC_BIGGER_ARRAY(n)
#define DC_STRUCT_PREFIX void * data;	/* some user data to be kind to api library users ... */
#define DC_TRANSFER_PREFIX	if (!n->data) {			\
				n->data = o->data;	\
/* |	          	   | */	o->data = NULL;		\
/* V KEEP LOW! is on stack V */	}	/* ... and what happens with this data with _transfer_ */
#define DC_LWS_BUF 65536 /* don't worry. larger payloads still work and are joined by dc_json */
#define DC_LWS_MAX_RX DC_LWS_BUF /* max bytes a websocket may handle in a single receive */
#define DC_LWS_MAX_FD 64 /* max file descriptors LWS will open. unset: LWS acquires all unused */
#define DC_LWS_MAX_HEADER_LENGTH 64 /* _MAX_HEADER_LENGTH*_HEADERS_LENGTH is allocated on request */
#define DC_WS_PING_FORMAT "{\"op\":1,\"d\":%lld}"
#define DC_ID2TIME(x) ((x >> 22 /* this gives us ms since 2015 */)/1000 + 1420070400 /* UNIX 2015 */)
#ifdef DC_UI_GTK
#define DC_IF_UI_GTK(...) __VA_ARGS__
#else
#define DC_IF_UI_GTK(...)
#endif
#define DC_PAIP (pass->api_io.program)
/* it's strongly recommended to calloc structs during initialization. */
enum dc_status { /* theese are flags and should be and-checked */
	DC_UNSET = 0, /* default value when enum is calloced */		/* \/ USEFUL FOR ->next!!! */
	DC_INCOMPLETE = 1 << 0, /* _add_ w/ DC_REPLACE: new gets empty data from old w/ _transfer_ */
	DC_OK = 1 << 1, /* success status and also ws established*/
	DC_BAD_LOGIN = 1 << 2, /* login failed because of wrong credentials */
	DC_VERIFICATION_NEEDED = 1 << 3, /* login: check email, click link/reg: ur IP flagd */
	DC_CAPTCHA_NEEDED = 1 << 4, /* must solve captcha, tough luck, not impl, use browser login */
	DC_BAD_USERNAME = 1 << 5, /* provided username can't be registered */
	DC_BAD_EMAIL = 1 << 6, /* provided email can't be registered */
	DC_NOT_FOUND = 1 << 7, /* when querying for roles/users/... received a 404 */
	DC_BREAK = 1 << 8, /* attached handlers return this to stop processing this output */
	DC_RETRY = 1 << 9, /* attached timeout handlers return this so that ->last is not updated */
	DC_FROM_LWS = 1 << 10, /* LWS cb is the caller, so do not attempt to do lws_service (loop) */
	DC_FROM_API = 1 << 11, /* call originates from API function */
	DC_REQUEST_FAILED = 1 << 12, /* http request failed, reported to ui */
	DC_ERROR = 1 << 13, /* unknown error, non implemented non expected response */
	DC_NET_ERROR = 1 << 14, /* network failed or ws closed */
	DC_LEJP_CONSTRUCTED = 1 << 15, /* json parser was constructed */
	DC_SET_PASS = 1 << 16, /* if _CREATE _cb shall set client->passs. api sets on _WS create */
	DC_IN_PROGRESS = 1 << 17, /* object is in parsing (by json_cb) */
	DC_DESTROY_CB = 1 << 18, /* wether cb shall call api on DESTROY (used for http responses) */
	DC_MAY_FREE = 1 << 19, /* if dc_*_add() shall free passed in pointer if found one in ISA */
	DC_WS_ACTIVE = 1 << 20, /* set at WSI_CREATE so api_o'll reconnect WS if connection dropps */
	DC_NO_WRITE = 1 << 21, /* signaling dc_ws_stack not to call lws_callback_on_writeable */
	DC_SET_WS_ACTIVE = 1 << 22, /* if _CREATE _cb shall set client->status =| DC_WS_ACTIVE */
	DC_REPLACE = 1 << 23, /* dc_add_x replace old with new on found, _free: only free members */
	DC_EXPLICIT_NULL = 1 << 24, /* MEMB_GC'll NULL this memb (PROGRES?aftr free) (& clear bit) */
	DC_USER_ERROR = 1 << 25, /* lib user made an error, passed NULL pointer to _o for example */
	DC_INTERNAL = DC_FROM_LWS | DC_FROM_API, /* call originates from an internal function */
}; /* note: when checking status, first check for DC_OK, if it's set then disregard errors! */
#define DC_ADMIN (1 << 3) /* not all enum fields are implemented/understood */
#define DC_CHANNEL_VIEW (1 << 10) /* all enum fields here have values same as the server values */
#define DC_MESSAGE_SEND (1 << 11) /* this is not an enum as there are over 40 permissions */
#define DC_MESSAGE_READ (1 << 16) /* na tistem vegova serverju sem lahko pošiljal ne pa bral sporočil */
#define DC_VOICE_LISTEN (1 << 20) /* ISO C enums are at most int-wide */
#define DC_VOICE_SPEAK (1 << 21)
#define DC_ALL_PERMISSIONS (DC_ADMIN | DC_CHANNEL_VIEW | DC_MESSAGE_SEND | DC_MESSAGE_READ \
		| DC_VOICE_LISTEN | DC_VOICE_SPEAK) /* UI need not check ADMIN for WRITE 4example */
enum dc_channel_type { /* other types exist, but are not implemented/understood */
	DC_GC, /* guild channel */
	DC_DM, /* direct messages channel */
	DC_VOICE, /* all enum fields here have values same as the values that the server sends */
	DC_GROUP_DM
};
#define DC_CHANNEL_SUPPORTED(x) ((x) == DC_GC || (x) == DC_DM || (x) == DC_VOICE || (x)==DC_GROUP_DM)
char * dc_channel_type_str[] = { "DC_GC", "DC_DM", "DC_VOICE", "DC_GROUP_DM" };
enum dc_ws_packet { /* op numbers of websocket packets or json objects in other words */
	DC_PING = 1,
	DC_STRPKTOFF = 100, /* here follow string types (t) */
	DC_NONE = DC_STRPKTOFF + 0, /* unknown packet or packet type not yet defermined */
	DC_MESSAGE_CREATE,
}; /* intents enum was removed - intents're for bots, clients have capabilities - are not understood */
char * dc_ws_packet[] = {
	"",
	"MESSAGE_CREATE"
};
enum dc_message_type { /* other types exist, but are not implemented/understood, same values as server */
	DC_MESSAGE,
	DC_REPLY = 19,
};
#define DC_MESSAGE_SUPPORTED(x) (x == DC_MESSAGE || x == DC_REPLY)
enum dc_permission_type { /* same values as server */
	DC_ROLE,
	DC_USER
};
enum dc_api_io_type {
	/* all output structs have important structs set when an output is broadcast, for example if we get a channel from server but we don't possess it's guild (by id), api will wait for the guild and hold the channel in it's cache. for messages, tags in message are also parsed and queried. roles in premissions in channels are also fetched but not users in permissions, because permissions are currently only used for checking our permissions, not others' */
	/* none of the outputs transfer memory ownership. tr0 for inputs means transfer none, trp means pointer to struct owned by library is to be passed, tr1 means transfer of ownership to library */
	/* network requests are sent to the event-based network library immediatley on input. network library is polled for incoming network traffic on each _event call. not totally filled structs in cache are checked and if one is/becomes filled, it will be output. if a role is requested when this role already exists and is searched for (not filled), for example because a channel needs the role to be output or the user created another search for this role before, this search will be dropped and the passed struct will be freed. */
	/* this library has internally attached functions for handling many io types so that the library nicely detects when for example a new role is received. the on-role-received function for example checks cache and updates structs that need this role */
	/* struct dc_program * serves as a handle for the API. create it with dc_program_init (may return NULL which indicates a LWS init failure) and destroy it with dc_program_free. pass it to every call inside struct dc_api_io.program */
	DC_API_NONE,	/* i: do nothing */
			/* o: nothing to output */
	DC_API_MESSAGE,	/* i: send a message-tr0 or edit a message that was already sent-trp */
			/* o: message notification: GUI spawn or respawn if edited. */
	DC_API_CHANNEL,	/* i: TODO: create a channel-tr0 */
			/* o: new channel created, GUI spawn it. */
	DC_API_GUILD,	/* i: TODO: create a guild-tr0 */
			/* o: TODO: new guild created, GUI spawn it */
	DC_API_LOGIN,	/* i: pass a dc_client-tr1, to relogin FIX prev retd cl not create new */
			/* o: the previously passed dc_client with set status, do not use yet! \/ */
	DC_API_LOGIN_CB,/* i: used internally for passing response from http client to api, see source */
			/* o: to tell user that ->client is now fully filled and ready for use */
	DC_API_WS,	/* i: internal, LOGIN_CB calls it to start websocket setup */
			/* o: ->status of websocket setup (DC_OK indicates success) */
	DC_API_WS_CB,	/* i: internal, for passing response from ws lib to api, see source */
			/* o: N/A */
	DC_API_REGISTER,/* i: pass a dc_client, to relogin FIX pr rt cl&cl->user not creat new */
			/* o: the previously passed dc_client with set status */
	DC_API_STATUS,	/* i: N/A */
			/* o: check ->status */
	DC_API_USER,	/* i: query for user by id, pass dc_user-tr1 */
			/* o: prev passed dc_user but filled (or not: ->status may indicate error) */
	DC_API_ROLE,	/* i: query for role by id, pass dc_role-tr1 */
			/* o: prev passed dc_role but filled (or not: ->status may indicate error) */
	DC_API_ATTACH,	/* i: attaches function to handle output types */
			/* o: N/A */
}; /* do not confuse yourself, when for example login response is checked for errors, check client->status and not struct dc_api_io's member named status. that member is mostly only used internally. */
#define DC_API_TIMEOUT DC_API_NONE /* attached functions with type DC_API_TIMEOUT are called ->every seconds */
/* enum dc_status (* dc_api_attached_func) (struct dc_api_io, void * data); */ /* i tried simplifying but didn't work */
struct dc_api_io { /* output struct does NOT contain void * data (user pointer) from the input struct! */
	DC_STRUCT_PREFIX /* mostly useless here but it's only a couple of bytes so wth */
	enum dc_api_io_type type; /* NOTE: ALL POINTERS HERE ARE NOFREE */
	enum dc_status status;
	void * body;
	char * string;
	struct dc_message * message; /* for safety and to mininize casting, each type has a pointer */
	struct dc_channel * channel;
	struct dc_guild * guild;
	struct dc_client * client; /* must be set in pass->api_io.client when creating ws for client! */
	struct dc_user * user;
	struct dc_role * role;
	struct dc_program * program; /* this is library handle */
	struct dc_permission * permission;
	struct dc_attached_function * attached_function; /* ptr2 heap alloc struct when DC_API_ATTAH */
	struct dc_lws_pass * pass;
	unsigned long long int id;
};
#define DC_API_IO_MEMB_GC(m, t) do { \
		if (m && m->status & DC_IN_PROGRESS) { \
			t##_free(m, DC_UNSET); \
			m = NULL; \
		} \
		if (m && m->status & DC_EXPLICIT_NULL) { \
			m->status &= ~DC_EXPLICIT_NULL; \
			m = NULL; \
		} \
	} while (0)
#define DC_API_IO_GC(s) do { /* this is called if: */ \
		if (s.message) \
			DC_API_IO_MEMB_GC(s.message->reply, dc_message); \
		DC_API_IO_MEMB_GC(s.message, dc_message); /* WS connection closed, meaning json_cb */ \
		DC_API_IO_MEMB_GC(s.channel, dc_channel); /* will never be called again */ \
		DC_API_IO_MEMB_GC(s.guild, dc_guild); \
		DC_API_IO_MEMB_GC(s.user, dc_user); \
		DC_API_IO_MEMB_GC(s.role, dc_role); \
		DC_API_IO_MEMB_GC(s.permission, dc_permission); \
	} while (0)
#define DC_API_IO_EVERY(i, b, a) do { /* not sure if this works */ \
	 	if (i.message) b i.message a \
		if (i.channel) b i.channel a \
		if (i.guild) b i.guild a \
		if (i.user) b i.user a \
		if (i.role) b i.role a \
		if (i.permission) b i.permission a \
	} while (0)
void dc_api_io_free (struct dc_api_io * s, enum dc_status t) {
	if (!s)
		return;
	if (!(t & DC_REPLACE))
		free(s);
	return;
}
void dc_api_i (struct dc_api_io);
enum dc_lws_headers {
	DC_LWS_AUTHORIZATION,
	DC_LWS_CONTENT_TYPE,
	DC_LWS_HEADERS_LENGTH
};
char * dc_lws_headers[] = {
	"Authorization:",
	"Content-Type:"
};
enum dc_json_paths { /* lws reduces the following char array to uint8_t, so we can match easier */
	DC_JSON_OP, /* packet type */
	DC_JSON_S, /* packet sequence number */
	DC_JSON_T, /* type of packet for op=0 */
	DC_JSON_PING, /* interval */
	DC_JSON_ME,
	DC_JSON_ME_USERNAME, /* note: _ME is client->user ONLY for first d.user object. subsequent */
	DC_JSON_ME_ID, /* packets may contain user objects as well, they are other users */
	DC_JSON_ME_DISCRIMINATOR,
	DC_JSON_FRIEND, /* this is the id */
	DC_JSON_USER, /* list of users at first connect */
	DC_JSON_USER_ID,
	DC_JSON_USER_NAME,
	DC_JSON_USER_DISCRIMINATOR,
	DC_JSON_DM,
	DC_JSON_DM_TYPE,
	DC_JSON_DM_ID,
	DC_JSON_DM_NAME, /* yes, some do have names. */
	DC_JSON_DM_USER, /* this is the ID */
	DC_JSON_GUILD,
	DC_JSON_GUILD_NAME,
	DC_JSON_GUILD_ID,
	DC_JSON_GUILD_ROLE,
	DC_JSON_GUILD_ROLE_PERMISSION,
	DC_JSON_GUILD_ROLE_NAME,
	DC_JSON_GUILD_ROLE_ID,
	DC_JSON_GUILD_CHANNEL,
	DC_JSON_GUILD_CHANNEL_TYPE,
	DC_JSON_GUILD_CHANNEL_NAME,
	DC_JSON_GUILD_CHANNEL_ID,
	DC_JSON_GUILD_CHANNEL_TOPIC,
	DC_JSON_GUILD_CHANNEL_PERMISSION,
	DC_JSON_GUILD_CHANNEL_PERMISSION_TYPE,
	DC_JSON_GUILD_CHANNEL_PERMISSION_ID,
	DC_JSON_GUILD_CHANNEL_PERMISSION_DENY,
	DC_JSON_GUILD_CHANNEL_PERMISSION_ALLOW,
	DC_JSON_MEMBERSHIP, /* this object unfortionately only tells us our roles, */
	DC_JSON_MEMBERSHIP_USER, /* id */ /* though this is enough to calculate permissions */
	DC_JSON_MEMBERSHIP_ROLE, /* id */
	DC_JSON_MEMBER, /* guild member object */
	DC_JSON_MEMBER_USERNAME,
	DC_JSON_MEMBER_ID,
	DC_JSON_MEMBER_DISCRIMINATOR,
	DC_JSON_MEMBER_ROLE, /* id, array */
	DC_JSON_MESSAGE, /* also check if pass->packet == DC_MESSAGE_CREATE */
	DC_JSON_MESSAGE_ATTACHMENTS, /* not implemented yet */
	DC_JSON_MESSAGE_ID,
	DC_JSON_MESSAGE_TYPE,
	DC_JSON_MESSAGE_CHANNEL, /* id */
	DC_JSON_MESSAGE_CONTENT,
	DC_JSON_MESSAGE_AUTHOR,
	DC_JSON_MESSAGE_AUTHOR_USERNAME,
	DC_JSON_MESSAGE_AUTHOR_DISCRIMINATOR,
	DC_JSON_MESSAGE_AUTHOR_ID,
	DC_JSON_MESSAGE_REFERENCE, /* id, not implemented, only DC_JSON_MESSAGE_REFOBJ is used */
	DC_JSON_MESSAGE_MENTION_CHANNEL,
	DC_JSON_MESSAGE_MENTION_CHANNEL_ID,
	DC_JSON_MESSAGE_MENTION_CHANNEL_GUILD, /* id */
	DC_JSON_MESSAGE_MENTION_CHANNEL_NAME,
	DC_JSON_MESSAGE_MENTION_CHANNEL_TYPE,
	DC_JSON_MESSAGE_MENTION_USER,
	DC_JSON_MESSAGE_MENTION_USER_NAME,
	DC_JSON_MESSAGE_MENTION_USER_ROLE,
	DC_JSON_MESSAGE_MENTION_USER_ID,
	DC_JSON_MESSAGE_MENTION_USER_DISCRIMINATOR,
	DC_JSON_MESSAGE_MENTION_ROLE, /* id, array */
	DC_JSON_MESSAGE_REFOBJ, /* server only sends reference object one level deep */
	DC_JSON_MESSAGE_REFOBJ_ATTACHMENTS,
	DC_JSON_MESSAGE_REFOBJ_ID,
	DC_JSON_MESSAGE_REFOBJ_TYPE,
	DC_JSON_MESSAGE_REFOBJ_CHANNEL, /* id */
	DC_JSON_MESSAGE_REFOBJ_CONTENT,
	DC_JSON_MESSAGE_REFOBJ_AUTHOR,
	DC_JSON_MESSAGE_REFOBJ_AUTHOR_USERNAME,
	DC_JSON_MESSAGE_REFOBJ_AUTHOR_DISCRIMINATOR,
	DC_JSON_MESSAGE_REFOBJ_AUTHOR_ID,
	DC_JSON_MESSAGE_REFOBJ_AUTHOR_ROLES,
	DC_JSON_MESSAGE_REFOBJ_REFERENCE, /* id, this _is_ sent for second nesting level */
	DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL, /* TODO: implement role and user mentions fetching */
	DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_ID,
	DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_GUILD, /* id */
	DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_NAME,
	DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_TYPE,
	DC_JSON_MESSAGE_REFOBJ_MENTION_USER,
	DC_JSON_MESSAGE_REFOBJ_MENTION_USER_NAME,
	DC_JSON_MESSAGE_REFOBJ_MENTION_USER_ROLE,
	DC_JSON_MESSAGE_REFOBJ_MENTION_USER_ID,
	DC_JSON_MESSAGE_REFOBJ_MENTION_USER_DISCRIMINATOR,
	DC_JSON_MESSAGE_REFOBJ_MENTION_ROLE, /* id */
	DC_JSON_PATHS_LENGTH /* we have 256 max length, because that's what library supports */
};
char * dc_json_paths[] = { /* array of paths we are interested in */
	"op",
	"s",
	"t",
	"d.heartbeat_interval",
	"d.user",
	"d.user.username",
	"d.user.id",
	"d.user.discriminator",
	"d.relationships[].id",
	"d.users[]",
	"d.users[].id",
	"d.users[].username",
	"d.users[].discriminator",
	"d.private_channels[]",
	"d.private_channels[].type",
	"d.private_channels[].id",
	"d.private_channels[].name",
	"d.private_channels[].recipient_ids",
	"d.guilds[]",
	"d.guilds[].name",
	"d.guilds[].id",
	"d.guilds[].roles[]",
	"d.guilds[].roles[].permissions",
	"d.guilds[].roles[].name",
	"d.guilds[].roles[].id",
	"d.guilds[].channels[]",
	"d.guilds[].channels[].type",
	"d.guilds[].channels[].name",
	"d.guilds[].channels[].id",
	"d.guilds[].channels[].topic",
	"d.guilds[].channels[].permission_overwrites",
	"d.guilds[].channels[].permission_overwrites.type",
	"d.guilds[].channels[].permission_overwrites.id",
	"d.guilds[].channels[].permission_overwrites.deny",
	"d.guilds[].channels[].permission_overwrites.allow",
	"d.merged_members",
	"d.merged_members[][].user_id",
	"d.merged_members[][].roles",
	"d.member",
	"d.member.user.username",
	"d.member.user.id",
	"d.member.user.discriminator",
	"d.member.roles",
	"d",
	"d.attachments[]",
	"d.id",
	"d.type",
	"d.channel_id",
	"d.content",
	"d.author",
	"d.author.username",
	"d.author.discriminator",
	"d.author.id",
	"d.message_reference", /* this is ID */
	"d.mention_channels[]",
	"d.mention_channels[].id",
	"d.mention_channels[].guild_id",
	"d.mention_channels[].name",
	"d.mention_channels[].type",
	"d.mentions[]",
	"d.mentions[].username",
	"d.mentions[].member.roles",
	"d.mentions[].id",
	"d.mentions[].discriminator",
	"d.mentions[].mention_roles",
	"d.referenced_message",
	"d.referenced_message.attachments",
	"d.referenced_message.id",
	"d.referenced_message.type",
	"d.referenced_message.channel_id",
	"d.referenced_message.content",
	"d.referenced_message.author",
	"d.referenced_message.author.username",
	"d.referenced_message.author.discriminator",
	"d.referenced_message.author.id",
	"d.referenced_message.member.roles",
	"d.referenced_message.message_reference", /* this is ID */
	"d.referenced_message.mention_channels[]",
	"d.referenced_message.mention_channels[].id",
	"d.referenced_message.mention_channels[].guild_id",
	"d.referenced_message.mention_channels[].name",
	"d.referenced_message.mention_channels[].type",
	"d.referenced_message.mentions[]",
	"d.referenced_message.mentions[].username",
	"d.referenced_message.mentions[].member.roles",
	"d.referenced_message.mentions[].id",
	"d.referenced_message.mentions[].discriminator",
	"d.referenced_message.mention_roles"
};
struct dc_lws_pass { /* struct that is allocated for in dc_lws_cb unique per connection in void * us */
	DC_STRUCT_PREFIX
	char * body; /* this contains post body and when _CB is called, it contains response, HTTP*/
	size_t body_length; /* body is NULL terminated or NULL in case of failure. WS doesn't use this */
	char headers[DC_LWS_HEADERS_LENGTH][DC_LWS_MAX_HEADER_LENGTH]; /* nofree, a static 2d array */
	int status; /* HTTP response code		/\ headers contain request headers, then resp. */
	struct dc_api_io api_io; /* so dc_api_io can decide what shall be passed into _CB */
	struct lws * wsi; /* set on ESTABLISHED and NULLed on CLOSED or ERROR */
	uint16_t reason; /* reason for closing connection */
	struct dc_json * json; /* dc_json for detecting start/end */
	/* temporary debug things */
	char * cp;
	int len;
};
struct dc_payload {
	DC_STRUCT_PREFIX
	size_t length;
	char * body; /* yesfree - null terminated heap alocated string of strlen length, may contain 0 */
};
struct dc_payload * dc_payload_init () {
	struct dc_payload * s = calloc(1, sizeof(struct dc_payload));
	return s;
}
void dc_payload_free (struct dc_payload * s, enum dc_status t) {
	if (!s)
		return;
	if (s->body)
		free(s->body-LWS_PRE);
	if (!(t & DC_REPLACE))
		free(s);
	return;
}
#define DC_ISASQ(shortname) DC_ISA(struct dc_##shortname, shortname##s) /* ISA of structs quick */
#define DC_ISAS_INIT(type/* w/o struct */, name) do { name##_sizeof = DC_ALLOC_CHUNK; \
	name = calloc(name##_sizeof, sizeof(struct type *)); } while (0) /* prep arr, NO INIT mmbs */
#define DC_ISASIQ(shortname) DC_ISAS_INIT(dc_##shortname, s->shortname##s) /* ISAS init quick */
#define DC_ISAF(shortname) for (size_t i = 0; i < s->shortname##s_length; i++) \
		dc_##shortname##_free(s->shortname##s[i], DC_UNSET); \
	free(s->shortname##s); /* only free till _length */
struct dc_client {
	DC_STRUCT_PREFIX
	char * authorization; /* yesfree - authorization header value */
	char * email; /* yesfree */
	char * password; /* yesfree */
	struct dc_user * user; /* nofree - logged in user */
	enum dc_status status;
	struct dc_lws_pass * pass; /* pass of ws. _cb sets it on CREATE and NULLs on DESTROY */
	unsigned long long int last_packet; /* last packet sequence number for ping */
	time_t ping_interval;
	time_t last_ping;
	time_t disconnect_time; /* set at disconnect, so that reconn is delayed for some secs */
	DC_ISASQ(payload); /* yesfree - array of payloads we must send over ws */
	DC_ISASQ(guild); /* yesfree array of pointers only - guilds of this user */
	DC_ISASQ(user); /* yesfree array of pointers only - friends of this user */
};
struct dc_guild {
	DC_STRUCT_PREFIX
	char * name; /* yesfree */
	char * description; /* yesfree */
	unsigned long long int id; /* 0 for virtual DMs guild */
	struct dc_role * role; /* nofree - first role. NOTE: role->id==guild->id => @everyone */
	enum dc_status status; /* /\ if NULL then assume all permissions - a DM guild */
	DC_ISASQ(channel); /* yesfree array only - can't be LL because of DM channels in many g[0] */
#ifdef DC_UI_GTK
	GtkTreeIter iter; /* GtkTreeModel needs GTK_TREE_MODEL_ITERS_PERSIST for this to work */
	gboolean is_iter; /* see parag 8 file:///usr/share/doc/libgtk-3-doc/gtk3/GtkTreeModel.html */
#endif /* GtkTreeRowReference is not used, because it's slow (notice trimmed, see older commits) */
};
struct dc_guild * dc_guild_init () {
	struct dc_guild * s = calloc(1, sizeof(*s));
	return s;
}
void dc_guild_free (struct dc_guild * s, enum dc_status t) {
	if (!s)
		return;
	free(s->name);
	free(s->description);
	free(s->channels);
	if (!(t & DC_REPLACE)) /* we do this because we want to keep the pointer intact sometimes and */
		free(s); /* reused; for example when replacing/updating structs */
}
struct dc_client * dc_client_init () {
	struct dc_client * s = calloc(1, sizeof(*s));
	DC_ISASIQ(payload);
	DC_ISASIQ(guild);
	DC_ISASIQ(user);
	DC_MR(s->guilds); /* direct messages virtual guild is not in program, to prevent confusion */
	s->guilds_length = 1; /* because of duplicated 0 id value; freeing is therefore done from */
	s->guilds[0] = dc_guild_init(); /* dc_client_free */
	s->guilds[0]->name = strdup("Direct messages"); /* TODO: use gettext or similar for t9ns */
	return s;
}
void dc_client_free (struct dc_client * s, enum dc_status t) {
	if (!s)
		return;
	free(s->authorization);
	free(s->email);
	free(s->password);
	DC_ISAF(payload);
	dc_guild_free(s->guilds[0], DC_UNSET); /* see dc_client_init for explanation */
	free(s->guilds);
	free(s->users);
	if (!(t & DC_REPLACE))
		free(s);
}
void dc_ws_stack(struct dc_client * c, struct dc_payload * p, enum dc_status s) {
	fprintf(stderr, "dc_ws_stack: stacking for write: %.*s\n", p->length, p->body);
	DC_MR(c->payloads); /* the server closes if we send two objects in one */
	c->payloads[c->payloads_length++] = p; /* packet, like so: {"packet":1}{"packet":2}, so this. */
	p->body = realloc(p->body, p->length+LWS_PRE+1); /* when freeing, free p->body-LWS_PRE */
	memmove(p->body+LWS_PRE, p->body, p->length+1); /* body is no longer pointer we have to free */
	p->body += LWS_PRE;
	if (c->pass && c->pass->wsi && !(s & DC_NO_WRITE))
		lws_callback_on_writable(c->pass->wsi);
	return;
}
struct dc_channel {
	DC_STRUCT_PREFIX
	char * name; /* yesfree - name */
	char * topic; /* yesfree - topic */
	unsigned long long int id;		/* send message functions must therefore take	*/
	enum dc_channel_type type;		/*	vvv	client as parameter to get g[0]	*/
	struct dc_guild * guild; /* nofree - in case of DM channel this is NULL, many g[0] same ch */
	struct dc_message * message; /* nofree - first message (ordered by time) */
	enum dc_status status;
	DC_ISASQ(permission); /* yesfree array and members - ch permissions for users/roles */
	DC_ISASQ(user); /* yesfree array only - participants in DM channels */
#ifdef DC_UI_GTK
	GtkTreeIter iter; /* see notes of struct dc_guild */
	gboolean is_iter; /* XXX use this with caution: only if it's possible for treemodels to contains orphans, this works. otherwise, parents will be removed first and then channels will not het their is_iters invalidated. guild's is_iter will always get invalidated. file:///usr/share/doc/libgtk-3-doc/gtk3/GtkTreeStore.html#gtk-tree-store-iter-is-valid could be a solution but it's said to be slow. */
#endif
};
struct dc_channel * dc_channel_init () {
	struct dc_channel * s = calloc(1, sizeof(*s));
	DC_ISASIQ(user);
	DC_ISASIQ(permission);
	return s;
}
void dc_permission_free (struct dc_permission *, enum dc_status);
void dc_channel_free (struct dc_channel * s, enum dc_status t) {
	if (!s)
		return;
	free(s->name);
	free(s->topic);
	free(s->users);
	DC_ISAF(permission);
	if (!(t & DC_REPLACE))
		free(s);
}
struct dc_message { /* mentions are not "cached" or parsed by API. UI should extract them. */
	DC_STRUCT_PREFIX
	char * message; /* yesfree */
	struct dc_channel * channel; /* nofree */
	struct dc_user * user; /* nofree */
	time_t time; /* obtained with DC_ID2TIME, edited timestamp in ISO string format is N/I */
	unsigned long long int id;
	struct dc_message * next; /* next message (linked list of all messages of dc_channel) */
	struct dc_message * reply; /* nofree - this message replies to another message or NULL */
	enum dc_status status;
	enum dc_message_type type;
	struct dc_client * client; /* nofree - set when sending message - which client should send */
};
struct dc_message * dc_message_init () {
	struct dc_message * s = calloc(1, sizeof(*s));
	return s;
}
void dc_message_free (struct dc_message * s, enum dc_status t) {
	if (!s)
		return;
	free(s->message);
	if (!(t & DC_REPLACE))
		free(s);
}
#define DC_ROLE_EVERYONE(role) ((role)->guild && (role)->id == (role)->guild->id) /* triple eval!1 */
struct dc_role {
	DC_STRUCT_PREFIX
	char * name; /* yesfree */
	unsigned long long int id;
	unsigned long long int permissions; /* this are guild permission */
	struct dc_guild * guild; /* nofree - owner of the role */
	struct dc_role * next; /* nofree - next role (linked list of all roles of dc_guild) */
	enum dc_status status;
	DC_ISASQ(user); /* yesfree pointer array only - users with this role */
};
struct dc_role * dc_role_init () {
	struct dc_role * s = calloc(1, sizeof(*s));
	DC_ISASIQ(user);
	return s;
}
void dc_role_free (struct dc_role * s, enum dc_status t) {
	if (!s)
		return;
	free(s->name);
	free(s->users);
	if (!(t & DC_REPLACE))
		free(s);
}
struct dc_user {
	DC_STRUCT_PREFIX
	char * username; /* yesfree */
	unsigned long long int id;
	short int discriminator;
	enum dc_status status;
};
struct dc_user * dc_user_init () {
	struct dc_user * s = calloc(1, sizeof(*s));
	return s;
}
void dc_user_free (struct dc_user * s, enum dc_status t) {
	if (!s)
		return;
	free(s->username);
	if (!(t & DC_REPLACE))
		free(s);
}
struct dc_permission { /* permissions can be individual on a per-channel basis */
	DC_STRUCT_PREFIX /* assume all permissions */
	unsigned long long int allow;
	unsigned long long int deny;
	enum dc_permission_type type;
	struct dc_channel * channel; /* nofree - on which channel does it apply */
	struct dc_user * user; /* nofree - non-null if permission applies to a user */
	struct dc_role * role; /* nofree - non-null if it applies to a role */
	enum dc_status status;
}; /* permissions are only useful for checking OUR permissions, not others'. keep that in mind. */
struct dc_permission * dc_permission_init () {
	struct dc_permission * s = calloc(1, sizeof(*s));
	return s;
}
void dc_permission_free (struct dc_permission * s, enum dc_status t) {
	if (!s)
		return;
	if (!(t & DC_REPLACE))
		free(s);
}
struct dc_attached_function {
	DC_STRUCT_PREFIX
	enum dc_api_io_type type;
	void * user_data; /* I hope I don't confuse this with void * data in DC_STRUCT_PREFIX */
	enum dc_status (* function) (struct dc_api_io, void *);
	time_t every; /* no meaning if type != DC_API_TIMEOUT */
	time_t last; /* no meaning if type != DC_API_TIMEOUT */
};
struct dc_attached_function * dc_attached_function_init () {
	struct dc_attached_function * s = calloc(1, sizeof(*s));
	return s;
}
void dc_attached_function_free (struct dc_attached_function * s, enum dc_status t) {
	if (!s)
		return;
	if (!(t & DC_REPLACE))
		free(s);
}
struct dc_lws_pass * dc_lws_pass_init () {
	struct dc_lws_pass * s = calloc(1, sizeof(*s));
	s->json = dc_json_init();
	return s;
}
void dc_lws_pass_free (struct dc_lws_pass * s, enum dc_status t) {
	if (!s)
		return;
	free(s->body);
	dc_json_free(s->json); /* only frees contents */
	DC_API_IO_GC(s->api_io);
	if (!(t & DC_REPLACE))
		free(s);
}
static int dc_lws_cb (struct lws *, enum lws_callback_reasons, void *, void *, size_t);
static const struct lws_protocols dc_lws_protocols[] = {
	{"dc", dc_lws_cb, /* sizeof(struct dc_lws_pass) */ 0 /* lws naj NE ALOCIRA */, DC_LWS_MAX_RX, 0, NULL, 0},
	{NULL, NULL, 0, 0, 0, NULL, 0}
};
struct dc_program { /* data storage and token used for communication with the library */
	DC_STRUCT_PREFIX /* this is the only struct that contains DC_ISAs */
	DC_ISASQ(client); /* yesfree */
	DC_ISASQ(guild); /* yesfree */
	DC_ISASQ(channel); /* yesfree */
	DC_ISASQ(message); /* yesfree */
	DC_ISASQ(role); /* yesfree */
	DC_ISASQ(user); /* yesfree */
	DC_ISASQ(attached_function); /* yesfree */
	DC_ISASQ(api_io); /* yesfree */
	struct lws_context * lws_context; /* yesfree */
}; /* ui functions MUST check if !(->status & DC_INCOMPLETE) before using array members, incompletes are yet to be filled by the api */
enum dc_status dc_handle_ping (struct dc_api_io io, void * userdata) { /* tries to ping ws of every cl */
	if (io.status & DC_INTERNAL)
		return DC_RETRY;
	for (size_t i = 0; i < io.program->clients_length; i++) {
		struct dc_client * client = io.program->clients[i];
		struct dc_lws_pass * pass = client->pass;
		if (!pass)
			continue;
		if (!pass->wsi)
			continue;
		fprintf(stderr, "dc_handle_ping: interval %lu last %lu\n",
				client->ping_interval, client->last_ping);
		if (!userdata) { /* so packet parser iow server may request an immediate ping */
			if (client->ping_interval && client->last_ping + client->ping_interval < time(NULL))
				client->last_ping = time(NULL);
			else
				continue;
		}
		fprintf(stderr, "dc_handle_ping: handling ping for client %d\n", i);
		struct dc_payload * payload = calloc(1, sizeof(struct dc_payload));
		payload->length = asprintf(&payload->body, DC_WS_PING_FORMAT, client->last_packet);
		dc_ws_stack(client, payload, DC_UNSET); /* send a payload */
	}
	return DC_OK;
}
struct dc_program * dc_program_init () {
	struct dc_program * s = calloc(1, sizeof(struct dc_program));
	/* lws init, it may fail so we first do this to return NULL without memleak (thanks, clang) */
	struct lws_context_creation_info info;
	memset(&info, 0, sizeof(info));
	info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
	info.port = CONTEXT_PORT_NO_LISTEN; /* we are not listening - we are a client */
	info.protocols = dc_lws_protocols;
	info.fd_limit_per_thread = DC_LWS_MAX_FD;
	if (!(s->lws_context = lws_create_context(&info))) {
		lwsl_err("lws init failed\n");
		free(s);
		return NULL; /* of course our UI does not check for failure of init function, so the */
	} /* program will just crash, but I assume lws init will never fail (correct me if I'm wrong) */
	DC_ISASIQ(client);
	DC_ISASIQ(guild);
	DC_ISASIQ(channel);
	DC_ISASIQ(message);
	DC_ISASIQ(role);
	DC_ISASIQ(user);
	DC_ISASIQ(attached_function);
	DC_ISASIQ(api_io);
	struct dc_api_io io; /* attach a function for pinging wss of every client */
	memset(&io, 0, sizeof(io));
	io.type = DC_API_ATTACH;
	io.attached_function = dc_attached_function_init();
	io.attached_function->type = DC_API_TIMEOUT;
	io.attached_function->every = 0; /* repediately call, handle_ping handles timing */
	io.attached_function->function = dc_handle_ping;
	io.program = s;
	dc_api_i(io);
	return s;
}
void dc_program_free (struct dc_program * s, enum dc_status t) {
	if (!s)
		return; /* \/ call first because _cb is called several times during destroy ,,, */
	lws_context_destroy(s->lws_context); /* closes all connections and destroys all wsis */
	DC_ISAF(client); /* /\ ,,, and callback handlers (_CLOSED, _DESTROY) may do stuff with memory. */
	DC_ISAF(guild);
	DC_ISAF(channel);
	DC_ISAF(message);
	DC_ISAF(role);
	DC_ISAF(user);
	DC_ISAF(attached_function);
	DC_ISAF(api_io);
	if (!(t & DC_REPLACE))
		free(s);
}
void dc_api_stack (struct dc_api_io);
#define DC_FIND_X(x) struct dc_##x * dc_find_##x(struct dc_##x ** p, size_t l, unsigned long long int id) { \
	for (size_t i = 0; i < l; i++) \
		if (p[i]->id == id) { \
			/* fprintf(stderr, "id %llu was found!\n", id); */ /* too much */ \
			return p[i]; \
		} \
	return NULL; \
}
#define DC_FIND_LL_X(x) struct dc_##x * dc_find_ll_##x(struct dc_##x * u, unsigned long long int id) { \
	struct dc_##x ** z = &u; \
	if (!u) \
		return NULL; \
	while (*z) { \
		if ((*z)->id == id) \
			return *z; \
		z = &(*z)->next; \
	} \
	return NULL; \
} /* untested, not sure if works!!! */
#define DC_ADD_X(x) struct dc_##x * dc_add_##x (struct dc_##x *** p, size_t * so, size_t * l, struct dc_##x * u, enum dc_status s) { \
	struct dc_##x * us; \
	assert(u); \
	if ((us = dc_find_##x(*p, *l, u->id))) { \
		if (getenv("DC_I")) \
			fprintf(stderr, "debug: %s found existing. id = %llu\n", __func__, u->id); \
		if (us == u) \
			return us; \
		if (s & DC_REPLACE) { \
			if (s & DC_INCOMPLETE) \
				dc_transfer_##x(u /* this is then memmoved */, us); \
			dc_##x##_free(us, s); \
			memmove(us, u, sizeof(*us)); \
			if (s & DC_MAY_FREE) \
				free(u); /* we don't free members, they've been copied */ \
			return us; \
		} \
		if (s & DC_MAY_FREE) \
			dc_##x##_free(u, DC_UNSET); \
		return us; \
	} \
	if (*so <= *l) { \
		*so = ceil(*so*DC_REALLOC_K); \
		*p = realloc(*p, sizeof(**p) * *so); \
	} \
	if (getenv("DC_I")) \
		fprintf(stderr, "debug: %s inserted into ISA. id = %llu\n", __func__, u->id); \
	(*p)[(*l)++] = u; \
	return u; \
}
#define DC_ADDR_X(x, y) struct dc_##x * dc_addr_##x (struct dc_program * p, struct dc_##x *** a, size_t * so, size_t * l, struct dc_##x * u, enum dc_status s) { \
	struct dc_##x * us; \
	if (!a) \
		us = dc_add_##x(&p->x##s, &p->x##s_sizeof, &p->x##s_length, u, s); \
	else \
		us = dc_add_##x(a, so, l, u, s); \
	if (us != u) \
		return us; \
	struct dc_api_io io; \
	memset(&io, 0, sizeof(io)); \
	io.type = DC_API_##y; \
	io.program = p; \
	io.x = u; \
	dc_api_stack(io); \
	return u; \
} /* add with report */
#define DC_GEN_X(x, y) DC_FIND_X(x) DC_ADD_X(x) DC_ADDR_X(x, y)
#define DC_ISAE(a) &(a), &(a##_sizeof), &(a##_length) /* ISA Expand */
#define DC_ISAN NULL, NULL, NULL /* ISA NULL */
#define DC_TRANSFER_MEMBER(member) if (!n->member) {	\
		n->member = o->member;			\
		o->member = 0;				\
	}
#define DC_TRANSFER_ISA(what) if (!n->what##s_length) {	\
		free(n->what##s);			\
		n->what##s = o->what##s;		\
		n->what##s_sizeof = o->what##s_sizeof;	\
		n->what##s_length = o->what##s_sizeof;	\
		o->what##s = NULL;			\
		o->what##s_sizeof = 0;			\
		o->what##s_length = 0;			\
	}
void dc_transfer_channel (struct dc_channel * n, struct dc_channel * o) { /* n will _REPLACE o */ \
	DC_TRANSFER_PREFIX
	DC_TRANSFER_MEMBER(name)	/* all _transfer_ functions assume old will be discarded */
	DC_TRANSFER_MEMBER(topic)	/* and passed to _free_ after use. */
	DC_TRANSFER_MEMBER(id)
	DC_TRANSFER_MEMBER(type)
	DC_TRANSFER_MEMBER(guild)
	DC_TRANSFER_MEMBER(message)
	DC_TRANSFER_ISA(permission)
	DC_TRANSFER_ISA(user)
	DC_IF_UI_GTK(
		memmove(&n->iter, &o->iter, sizeof(GtkTreeIter));
		n->is_iter = o->is_iter;
	)
}
void dc_transfer_guild (struct dc_guild * n, struct dc_guild * o) {
	DC_TRANSFER_PREFIX
	DC_TRANSFER_MEMBER(name)
	DC_TRANSFER_MEMBER(description)
	DC_TRANSFER_MEMBER(id)
	DC_TRANSFER_MEMBER(role)
	DC_TRANSFER_MEMBER(status)
	DC_TRANSFER_ISA(channel)
	DC_IF_UI_GTK(
		memmove(&n->iter, &o->iter, sizeof(GtkTreeIter));
		n->is_iter = o->is_iter;
		)
}
void dc_transfer_role (struct dc_role * n, struct dc_role * o) {
	DC_TRANSFER_PREFIX
	DC_TRANSFER_MEMBER(name)
	DC_TRANSFER_MEMBER(id)
	DC_TRANSFER_MEMBER(permissions)
	DC_TRANSFER_MEMBER(guild)
	DC_TRANSFER_MEMBER(next)
	DC_TRANSFER_MEMBER(status)
	DC_TRANSFER_ISA(user)
}
void dc_transfer_message (struct dc_message * n, struct dc_message * o) {
	DC_TRANSFER_PREFIX
	DC_TRANSFER_MEMBER(message)
	DC_TRANSFER_MEMBER(channel)
	DC_TRANSFER_MEMBER(user)
	DC_TRANSFER_MEMBER(time)
	DC_TRANSFER_MEMBER(id)
	DC_TRANSFER_MEMBER(next)
	DC_TRANSFER_MEMBER(reply)
	DC_TRANSFER_MEMBER(status)
	DC_TRANSFER_MEMBER(type)
	DC_TRANSFER_MEMBER(client)
}
void dc_transfer_user (struct dc_user * n, struct dc_user * o) {
	DC_TRANSFER_PREFIX
	DC_TRANSFER_MEMBER(username)
	DC_TRANSFER_MEMBER(id)
	if (n->discriminator == -1) { /* zero is a valid discriminator - AFAIK - it seems like it */
		n->discriminator = o->discriminator;
		o->discriminator = -1;
	}
}
DC_GEN_X(user, USER)
DC_GEN_X(channel, CHANNEL)
DC_GEN_X(guild, GUILD)
DC_GEN_X(role, ROLE)
DC_FIND_LL_X(role)
DC_GEN_X(message, MESSAGE)
DC_FIND_LL_X(message)