From dc07e4bf7f4628852eee26b114348498caad0182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Fri, 25 Feb 2022 00:54:36 +0100 Subject: netestirano, se prevede --- .gitignore | 4 +- Makefile | 2 +- README.md | 1 + src/api.c | 152 ++++++++++++++++++++++++++++++++++--------------- src/h.c | 189 +++++++++++++++++++++++++++++++++++-------------------------- src/json.c | 1 + src/main.c | 12 +++- 7 files changed, 231 insertions(+), 130 deletions(-) diff --git a/.gitignore b/.gitignore index 9b05aed..06a0a6d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,10 @@ a.out # debugging log files valgrind-out.txt .gdb_history -out.txt +out*.txt core +gmon.out +analysis.txt # files I like to keep in my WD src.old/ src/ui.glade~ diff --git a/Makefile b/Makefile index 19d2c60..a10bd45 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,11 @@ DESTDIR=/ SRCFILE=src/main.c BINFILE=discord.c O=0 +CC=cc CFLAGS += -Wextra -Wall -pedantic -g -O$O -Itmp -Isrc -I. -odiscord.c -Wno-unused-parameter -rdynamic -finput-charset=UTF-8 -fextended-identifiers LIBS += -lm CFLAGS += $(shell pkg-config --cflags libwebsockets) $(shell pkg-config --cflags gtk+-3.0) $(shell pkg-config --cflags gmodule-export-2.0) LIBS += $(shell pkg-config --libs libwebsockets) $(shell pkg-config --libs gtk+-3.0) $(shell pkg-config --libs gmodule-export-2.0) -lcjson -CC=cc VGARGS += --leak-check=full --track-origins=yes --verbose --log-file=valgrind-out.txt --suppressions=/usr/share/glib-2.0/valgrind/glib.supp --suppressions=tmp/gtk.supp --suppressions=/usr/lib/i386-linux-gnu/valgrind/default.supp --suppressions=tmp/gnome.supp --show-leak-kinds=definite --suppressions=misc/misc.supp --suppressions=/usr/share/gtk-3.0/valgrind/gtk.supp --leak-resolution=low # notparallel ker morajo biti ukazi izvedeni po vrsti, mkdir se mora zgoditi pred xxd recimo. diff --git a/README.md b/README.md index 91d681e..3d4d023 100644 --- a/README.md +++ b/README.md @@ -93,3 +93,4 @@ it would be useful to have an android port, and luckily this is possible with li * `make -e CC=gcc` to choose a compiler instead of `cc`. * `make -e CC="clang -fsanitize=address" && ASAN_OPTIONS=detect_leaks=1` to use `clang` leak checker * `scan-build make` to staticly analyze build with `clang` (`clang-tools` package required). `gcc`'s `-fanalyze` can't be used without many false positives, because the code is not written so pedantically (without `__attribute__`s on function arguments, return values, ...). +* `make -e CC="cc -pg"` can be used to generate a gprof binary (gcc/clang only, tcc has no support). After running the produced executable, run `gprof discord.c gmon.out > analysis.txt` for report. diff --git a/src/api.c b/src/api.c index fbd9495..32a2058 100644 --- a/src/api.c +++ b/src/api.c @@ -24,6 +24,17 @@ cJSON_GetObjectItem(cJSON_GetObjectItem3(ro, na1, na2, na3), na4) : NULL) #define cJSON_GetObjectItem5(r, n1, n2, n3, n4, n5) (cJSON_GetObjectItem4(r, n1, n2, n3, n4) ? \ cJSON_GetObjectItem(cJSON_GetObjectItem4(r, n1, n2, n3, n4), n5) : NULL) +#define cJSON_GSV cJSON_GetStringValue +#define cJSON_GNV cJSON_GetNumberValue +#define cJSON_GAS cJSON_GetArraySize +#define cJSON_GOI cJSON_GetObjectItem +#define cJSON_GOI2 cJSON_GetObjectItem2 +#define cJSON_GOI3 cJSON_GetObjectItem3 +#define cJSON_GOI4 cJSON_GetObjectItem4 +#define cJSON_GOI5 cJSON_GetObjectItem5 +#define cJSON_GSV cJSON_GetStringValue +#define cJSON_IN cJSON_IsNumber +#define cJSON_AFE cJSON_ArrayForEach unsigned char dc_api_identify_u[] = { #include }; @@ -37,19 +48,26 @@ void dc_api_stack (struct dc_api_io i) { /* stack output struct to be delivered } unsigned long long int dc_calculate_permissions (struct dc_user * u, struct dc_channel * c) { unsigned long long int p = 0; /* note: this is NOT according to server's implementation of */ - struct dc_role ** role = &c->guild->role; /* permission parsing, but should suffice for most */ - while (*role) { /* cases. */ - if ((*role)->status & DC_EVERYONE || dc_find_user((*role)->users, (*role)->users_length, u->id)) + struct dc_role ** role = &c->guild->role; /* perm parsing, but should suffice 4 most cases */ + if (!*role) /* see struct dc_guild: if NULL then assume all permissions - a DM guild */ + return DC_ALL_PERMISSIONS; + while (*role) { + if (DC_ROLE_EVERYONE(*role) + || dc_find_user((*role)->users, (*role)->users_length, u->id)) p |= (*role)->permissions; role = &(*role)->next; } if (p & DC_ADMIN) return DC_ALL_PERMISSIONS; for (size_t i = 0; i < c->permissions_length; i++) - if (c->permissions[i]->user == u || dc_find_user(c->permissions[i]->role->users, c->permissions[i]->role->users_length, u->id) || c->permissions[i]->role->status & DC_EVERYONE) { + if (c->permissions[i]->user + ? c->permissions[i]->user == u + : dc_find_user(c->permissions[i]->role->users, + c->permissions[i]->role->users_length, u->id) + || DC_ROLE_EVERYONE(c->permissions[i]->role)) { p &= ~c->permissions[i]->deny; p |= c->permissions[i]->allow; - if ((*role)->permissions & DC_ADMIN) + if (p & DC_ADMIN) return DC_ALL_PERMISSIONS; } return p; @@ -57,15 +75,17 @@ unsigned long long int dc_calculate_permissions (struct dc_user * u, struct dc_c struct dc_user * dc_parse_user (struct dc_user * dst, const cJSON * src) { char * cp; if (!dst) - dst = dc_user_init(); - if ((cp = cJSON_GetStringValue(cJSON_GetObjectItem(src, "username")))) + dst = dc_user_init(); + if ((cp = cJSON_GSV(cJSON_GOI(src, "username")))) dst->username = strdup(cp); - if ((cp = cJSON_GetStringValue(cJSON_GetObjectItem(src, "discriminator")))) + if ((cp = cJSON_GSV(cJSON_GOI(src, "discriminator")))) dst->discriminator = atoi(cp); - else - dst->discriminator = -1; - if ((cp = cJSON_GetStringValue(cJSON_GetObjectItem(src, "id")))) + if ((cp = cJSON_GSV(cJSON_GOI(src, "id")))) dst->id = strtoull(cp, NULL, 10); + if (!dst->username || dst->discriminator == -1) { /* it's quite useless to store only ids */ + dc_user_free(dst, DC_UNSET); + return NULL; + } return dst; } static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, void * in, size_t len) { @@ -207,60 +227,100 @@ static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, fprintf(stdout, "%.*s", len, (const unsigned char *) in); char * serialized = dc_json(pass->json, in, len); while (serialized) { - cJSON * json = cJSON_Parse(serialized); - cJSON * obj; + cJSON * obj, * obje, * json = cJSON_Parse(serialized); char * st; if (getenv("DC_J")) { st = cJSON_Print(json); - if (st) { - printf("%s\n", st); - cJSON_free(st); - } else - fprintf(stderr, "_RECEIVE: _Print NULL: %s\n", serialized); + printf("%s\n", st); + cJSON_free(st); + } +#define DC_PTYP(t) ((st = cJSON_GSV(cJSON_GOI(json, "t"))) && !strcmp(st, t)) + if (DC_PTYP("READY") && getenv("DC_R")) { + printf("%s\n", (st = cJSON_Print(json))); + cJSON_free(st); } - pass->api_io.client->last_packet - = cJSON_GetNumberValue(cJSON_GetObjectItem(json, "s")); - if (cJSON_GetNumberValue(cJSON_GetObjectItem(json, "op")) - == DC_PING) { + if ((st = cJSON_GSV(cJSON_GOI(json, "t")))) + fprintf(stderr, "t: %s\n", st); + pass->api_io.client->last_packet = cJSON_GNV(cJSON_GOI(json, "s")); + if (cJSON_GNV(cJSON_GOI(json, "op")) == DC_PING) { pass->api_io.client->last_ping = 0; dc_handle_ping(pass->api_io, NULL); } - if (cJSON_IsNumber((obj - = cJSON_GetObjectItem2(json, "d", "heartbeat_interval")))) { - pass->api_io.client->ping_interval - = cJSON_GetNumberValue(obj)/1000-1; + if (cJSON_IN((obj = cJSON_GOI2(json, "d", "heartbeat_interval")))) { + pass->api_io.client->ping_interval = cJSON_GNV(obj)/1000-1; pass->api_io.client->last_ping = 1; } #define DC_PARSEOBJ(w, object, also) if ((obj = object)) { \ struct dc_##w * w; \ - w = dc_parse_##w(NULL, obj); \ - w = dc_addr_##w(pass->api_io.program, \ + if ((w = dc_parse_##w(NULL, obj))) \ + w = dc_addr_##w(pass->api_io.program, \ DC_ISAE(pass->api_io.program->w##s), w, \ - DC_MAY_FREE | DC_REPLACE);\ + DC_MAY_FREE | DC_REPLACE | DC_INCOMPLETE);\ also \ } - DC_PARSEOBJ(user, cJSON_GetObjectItem2(json, "d", "user"), - if (!pass->api_io.client->user) pass->api_io.client->user = user;) - DC_PARSEOBJ(user, cJSON_GetObjectItem3(json, "d", "member", "user"),) - DC_PARSEOBJ(user, cJSON_GetObjectItem2(json, "d", "author"), ) - DC_PARSEOBJ(user, cJSON_GetObjectItem3(json, "d", - "referenced_message", "author"), ) -#define DC_PARSEARR(what, arr) cJSON_ArrayForEach(obj, arr) { \ + DC_PARSEOBJ(user, cJSON_GOI2(json, "d", "user"), + if (!pass->api_io.client->user) + pass->api_io.client->user = user; + ) + DC_PARSEOBJ(user, cJSON_GOI3(json, "d", "member", "user"),) + DC_PARSEOBJ(user, cJSON_GOI2(json, "d", "author"),) + DC_PARSEOBJ(user, cJSON_GOI3(json, + "d", "referenced_message", "author"),) +#define DC_PARSEARR(what, arr, also) cJSON_AFE(obj, arr) { \ struct dc_##what * what = dc_parse_##what(NULL, obj); \ if (!what) \ continue; \ dc_addr_##what(pass->api_io.program, \ DC_ISAE(pass->api_io.program->what##s), \ - what, DC_MAY_FREE | DC_REPLACE); \ + what, DC_MAY_FREE|DC_REPLACE|DC_INCOMPLETE);\ + also \ } - DC_PARSEARR(user, cJSON_GetObjectItem2(json, "d", "users")); - DC_PARSEARR(user, cJSON_GetObjectItem2(json, "d", "mentions")); - DC_PARSEARR(user, cJSON_GetObjectItem3(json, "d", - "referenced_message", "mentions")); -#define DC_PTYP(t) ((st = cJSON_GetStringValue(cJSON_GetObjectItem(json, "t"))) && !strcmp(st, t)) - if (DC_PTYP("MESSAGE_CREATE")) { - + DC_PARSEARR(user, cJSON_GOI2(json, "d", "users"),) + DC_PARSEARR(user, cJSON_GOI2(json, "d", "mentions"),) + DC_PARSEARR(user, cJSON_GOI3(json, + "d", "referenced_message", "mentions"),) + cJSON_AFE(obj, cJSON_GOI2(json, "d", "private_channels")) { + if (/* !cJSON_GAS(cJSON_GOI(obj, "recipient_ids")) || */ + /* commented. DMs with 0 members're shown /\ */ !cJSON_GSV(cJSON_GOI(obj, "id")) || + !DC_CHANNEL_SUPPORTED( + /* cJSON is called many times here but I don't care */ cJSON_GNV(cJSON_GOI(obj, "type")))) + continue; + struct dc_channel * ch = dc_channel_init(); + if ((st = cJSON_GSV(cJSON_GOI(obj, "name")))) + ch->name = strdup(st); + ch->type = cJSON_GNV(cJSON_GOI(obj, "type")); + ch->id = strtoull(cJSON_GSV(cJSON_GOI(obj, "id")), NULL, 10); + cJSON_AFE(obje, cJSON_GOI(obj, "recipient_ids")) { + struct dc_user * part = dc_user_init(); + part->username = strdup("Private channel recipient"); + part = dc_add_user( + DC_ISAE(pass->api_io.program->users), + /* no replace here. stored user can be better. */ part, DC_MAY_FREE); + DC_MR(ch->users); /* needn't dc_add here coz start */ + ch->users[ch->users_length++] = part; /* empty ch. */ + } + ch = dc_addr_channel(pass->api_io.program, + DC_ISAE(pass->api_io.program->channels), ch, + /* replace here. ours is better - fresher. */ DC_MAY_FREE | DC_REPLACE | DC_INCOMPLETE); + ch->guild = pass->api_io.client->guilds[0]; } + cJSON_AFE(obj, cJSON_GOI2(json, "d", "merged_members")) + cJSON_AFE(obje, cJSON_GOI(obj, "roles")) { + if (!(st = cJSON_GSV(obje))) + continue; + struct dc_role * role = dc_role_init(); + role->id = strtoull(st, NULL, 10); + role = dc_add_role( + DC_ISAE(pass->api_io.program->roles), + role, DC_MAY_FREE); + struct dc_user * user = dc_user_init(); + user->id = strtoull(cJSON_GSV(cJSON_GOI( + obj, "user_id")), NULL, 10); + user = dc_add_user( + DC_ISAE(pass->api_io.program->users), + /* again, no replace here, because we only have ID */ user, DC_MAY_FREE); + /* role may have users */ dc_add_user(DC_ISAE(role->users), user, DC_UNSET); + } cJSON_Delete(json); json = NULL; serialized = dc_json(pass->json, NULL, 0); @@ -292,7 +352,7 @@ void dc_api_i (struct dc_api_io i) { /* this function does not call attached fun struct dc_lws_pass * pass; assert(i.program); if (i.program->lws_context && !(i.status & DC_FROM_LWS)) - lws_service(i.program->lws_context, 0); /* DO NOT CALL THIS FROM _cb! */ + lws_service(i.program->lws_context, -1); /* DO NOT CALL THIS FROM _cb! */ switch (i.type) { case DC_API_MESSAGE: break; @@ -440,7 +500,7 @@ struct dc_api_io dc_api_o (struct dc_api_io i /* for ->program */) { return i; } if (i.program->lws_context) - lws_service(i.program->lws_context, 0); + lws_service(i.program->lws_context, -1); /* with -1 this takes ~0 time */ for (size_t x = 0; x < i.program->clients_length; x++) { if (i.program->clients[x]->status & DC_WS_ACTIVE && !i.program->clients[x]->pass && i.program->clients[x]->disconnect_time+DC_RECONNECT_DELAYdata) { \ + n->data = o->data; \ + o->data = NULL; \ + } /* ... and what happens with this data with _transfer_ */ #define DC_LWS_BUF 65535 /* 2^16 SMALL FUCKING HINT: ^ je XOR operator v C (in povsod drugje) */ #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 have open. if unset, LWS acquires all unused */ @@ -24,11 +28,11 @@ #endif /* 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 */ - DC_INCOMPLETE = 1 << 0, /* struct SHALL NOT be used by the ui, it is yet to be filled by api */ + 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: tough luck ur IP flagd */ + 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 */ @@ -41,17 +45,16 @@ enum dc_status { /* theese are flags and should be and-checked */ 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, /* whether _CREATE _cb shall set client->passs. api sets on _WS create */ + 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, /* whether dc_*_add() shall free passed in pointer if found one in ISA */ - DC_WS_ACTIVE = 1 << 20, /* set at WSI_CREATE so api_o will reconnect WS if connection dropps */ + 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, /* whether _CREATE _cb shall set client->status =| DC_WS_ACTIVE */ + 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 will NULL this member (PROGRES?aftr free) (& clear bit) */ - DC_EVERYONE = 1 << 25, /* role applies to all guild users */ - DC_USER_ERROR = 1 << 26, /* lib user made an error, passed NULL pointer to _o for example */ + 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 */ @@ -60,14 +63,16 @@ enum dc_status { /* theese are flags and should be and-checked */ #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) /* admins get this@parsing, UI need not check admin separatly */ +#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) +#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) */ @@ -398,13 +403,13 @@ void dc_payload_free (struct dc_payload * s, enum dc_status t) { free(s); return; } -#define DC_ISASQ(shortname) DC_ISA(struct dc_##shortname, shortname##s) /* in struct array of structs quick */ -#define DC_ISAS_INIT(type/* w/o struct */, name) do { name##_sizeof = DC_ALLOC_CHUNK; /* structs ISA */ \ - name = calloc(name##_sizeof, sizeof(struct type *)); } while (0) /* prep arr, NO INIT membrs */ +#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++) /* hmm, I used to fre */ \ - dc_##shortname##_free(s->shortname##s[i], DC_UNSET); /* till _sizeof, but now I fixd */ \ - free(s->shortname##s); /* to only free till _length. is this problematic in any ways? */ +#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 */ @@ -416,7 +421,7 @@ struct dc_client { 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 reconnection is delayed for some seconds */ + 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 */ @@ -426,13 +431,12 @@ struct dc_guild { char * name; /* yesfree */ unsigned long long int id; /* 0 for virtual DMs guild */ struct dc_channel * channel; /* nofree - first channel */ - struct dc_role * role; /* nofree - first role. NOTE: role->id == guild->id => @everyone role */ + 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 */ #ifdef DC_UI_GTK - GtkTreeIter iter; /* NOTE: only works when GtkTreeModel has a flag GTK_TREE_MODEL_ITERS_PERSIST; see paragraph 8 of description of file:///usr/share/doc/libgtk-3-doc/gtk3/GtkTreeModel.html */ - gboolean is_iter; - /* GtkTreeRowReference * row; */ /* yesfree - indicating the row */ /* not used: IRC message: "00:51:29 @Company | also: Don't create too many tree row references, those things are slow" */ -#endif + 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)); @@ -489,8 +493,8 @@ struct dc_channel { struct dc_guild * guild; /* nofree */ struct dc_channel * next; /* nofree - next channel (linked list of all channel of dc_guild) */ struct dc_message * message; /* nofree - first message (ordered by time) */ - DC_ISASQ(permission); /* yesfree array and members - ch permissions for users/roles */ 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 */ @@ -514,39 +518,31 @@ void dc_channel_free (struct dc_channel * s, enum dc_status t) { if (!(t & DC_REPLACE)) free(s); } -struct dc_message { +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 is in ISO string format¬ implemt */ + 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 to the client that should send */ - DC_ISASQ(user); /* yesfree pointer array only - mentions */ - DC_ISASQ(role); /* yesfree pointer array only - mentions */ - DC_ISASQ(channel); /* yesfree pointer array only - mentions */ + 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)); - DC_ISASIQ(user); - DC_ISASIQ(role); - DC_ISASIQ(channel); return s; } void dc_message_free (struct dc_message * s, enum dc_status t) { if (!s) return; free(s->message); - free(s->users); - free(s->roles); - free(s->channels); 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 */ @@ -554,8 +550,8 @@ struct dc_role { 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) */ - DC_ISASQ(user); /* yesfree pointer array only - users with this role */ 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)); @@ -575,7 +571,6 @@ struct dc_user { char * username; /* yesfree */ unsigned long long int id; short int discriminator; - struct dc_program * program; /* nofree - for dc_calculate_permissions */ enum dc_status status; }; struct dc_user * dc_user_init () { @@ -760,15 +755,16 @@ void dc_api_stack (struct dc_api_io); 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) { \ + if (s & DC_MAY_FREE) \ dc_##x##_free(u, DC_UNSET); \ - } \ return us; \ } \ if (*so <= *l) { \ @@ -797,53 +793,84 @@ void dc_api_stack (struct dc_api_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) -DC_GEN_X(user, USER) -DC_GEN_X(channel, CHANNEL) -DC_FIND_LL_X(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) #define DC_ISAE(a) &(a), &(a##_sizeof), &(a##_length) /* ISA Expand */ #define DC_ISAN NULL, NULL, NULL /* ISA NULL */ -void dc_transfer_channel(struct dc_channel * n, struct dc_channel * o) { /* n is going to _REPLACE o */ \ - n->message = o->message; /* don't transfer perms because we get them in the new */ \ - n->next = o->next; /* new channel object */ \ +#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(next) + 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; ) - if (!n->permissions_length) { - free(n->permissions); - n->permissions = o->permissions; - n->permissions_sizeof = o->permissions_sizeof; - n->permissions_length = o->permissions_length; - o->permissions = NULL; - o->permissions_sizeof = 0; - o->permissions_length = 0; - } } -#define DC_TRANSFER_GUILD(n, o) do { \ - DC_IF_UI_GTK( \ - memmove(&(n)->iter, &(o)->iter, sizeof(GtkTreeIter)); \ - (n)->is_iter = (o)->is_iter; \ - ) \ - } while (0) +void dc_transfer_guild (struct dc_guild * n, struct dc_guild * o) { + DC_TRANSFER_PREFIX + DC_TRANSFER_MEMBER(name) + DC_TRANSFER_MEMBER(id) + DC_TRANSFER_MEMBER(channel); + DC_TRANSFER_MEMBER(role); + DC_TRANSFER_MEMBER(status); + 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) { - n->next = o->next; - if (!n->users_length) { /* if we didn't update users array in the new role object */ - free(n->users); /* thanks, clang, we have to free pointer array before overwriting it! */ - n->users = o->users; - n->users_sizeof = o->users_sizeof; - n->users_length = o->users_sizeof; - o->users = NULL; /* so that free does not free array of pointers */ - o->users_sizeof = 0; - o->users_length = 0; - } + 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) { - n->next = o->next; - if (!n->reply) - n->reply = o->reply; + 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) + DC_TRANSFER_MEMBER(discriminator) + DC_TRANSFER_MEMBER(status) +} +DC_GEN_X(user, USER) +DC_GEN_X(channel, CHANNEL) +DC_FIND_LL_X(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) diff --git a/src/json.c b/src/json.c index aae6f5d..bcf0d10 100644 --- a/src/json.c +++ b/src/json.c @@ -18,6 +18,7 @@ char * dc_json (struct dc_json * j, const char * in, int ln) { /* detects start/ if (j->ready > 0) memmove(j->buf, j->buf+j->ready, strlen(j->buf+j->ready)+1); j->buf[0] = j->backup; + j->ready = 0; } size_t bufstrlen = strlen(j->buf); /* could optimize and cache it into the struct */ if (ln == -1) diff --git a/src/main.c b/src/main.c index 7c3f3b9..1d9daaa 100644 --- a/src/main.c +++ b/src/main.c @@ -45,7 +45,17 @@ int main (int argc, char * argv[]) { i = dc_api_o(i); switch (i.type) { case DC_API_USER: - fprintf(stderr, "DC_API_USER: %s#%04d %llu\n", i.user->username, i.user->discriminator, i.user->id); + fprintf(stderr, "DC_API_USER: %s#%04d %llu\n", i.user->username, + i.user->discriminator, i.user->id); + break; + case DC_API_CHANNEL: + fprintf(stderr, "DC_API_CHANNEL participants: %u, type: %s\n", + i.channel->users_length, + dc_channel_type_str[i.channel->type]); + break; + case DC_API_LOGIN: + if (i.status & DC_CAPTCHA_NEEDED) + fprintf(stderr, "DC_CAPTCHA_NEEDED\n"); break; default: break; -- cgit v1.2.3