Skip to content

Commit 5b6f9f8

Browse files
csjpeterclaude
andcommitted
fix: contacts now show names via tl_extract_user
The custom parse_user_into helper stopped reading after username, leaving the TL cursor inside user1's data so every subsequent User CRC was read as garbage. Replace it with tl_extract_user, which advances past the full User object (phone, photo, status, etc.). ContactEntry struct simplified: first_name/last_name merged into name[128] matching UserSummary.name. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent a768c8d commit 5b6f9f8

5 files changed

Lines changed: 39 additions & 80 deletions

File tree

src/domain/read/contacts.c

Lines changed: 22 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "tl_serial.h"
1111
#include "tl_registry.h"
12+
#include "tl_skip.h"
1213
#include "mtproto_rpc.h"
1314
#include "logger.h"
1415
#include "raii.h"
@@ -19,61 +20,6 @@
1920
#define CRC_contacts_getContacts 0x5dd69e12u
2021
#define CRC_contact 0x145ade0bu
2122

22-
/* Copy at most dst_cap-1 bytes from src (NUL-terminates). */
23-
static void copy_str(char *dst, size_t dst_cap, const char *src) {
24-
if (!dst || dst_cap == 0) return;
25-
dst[0] = '\0';
26-
if (!src) return;
27-
size_t n = strlen(src);
28-
if (n >= dst_cap) n = dst_cap - 1;
29-
memcpy(dst, src, n);
30-
dst[n] = '\0';
31-
}
32-
33-
/* Parse one User/UserEmpty from @r; fill id + name fields on @e.
34-
* Unknown constructors are skipped via tl_skip_object. */
35-
static void parse_user_into(TlReader *r, ContactEntry *entries, int count) {
36-
if (!tl_reader_ok(r)) return;
37-
uint32_t crc = tl_read_uint32(r);
38-
39-
if (crc == TL_userEmpty) {
40-
/* userEmpty#d3bc4b7a id:long */
41-
int64_t id = tl_read_int64(r);
42-
(void)id;
43-
return;
44-
}
45-
if (crc != TL_user && crc != TL_user2) {
46-
logger_log(LOG_WARN, "contacts: unknown User 0x%08x — skipping", crc);
47-
return;
48-
}
49-
50-
uint32_t flags = tl_read_uint32(r);
51-
(void)tl_read_uint32(r); /* flags2 */
52-
int64_t id = tl_read_int64(r);
53-
54-
int64_t access_hash = 0;
55-
if (flags & (1u << 0)) access_hash = tl_read_int64(r);
56-
57-
char first_name[64] = {0};
58-
char last_name[64] = {0};
59-
char username[64] = {0};
60-
61-
if (flags & (1u << 1)) { RAII_STRING char *s = tl_read_string(r); copy_str(first_name, sizeof(first_name), s); }
62-
if (flags & (1u << 2)) { RAII_STRING char *s = tl_read_string(r); copy_str(last_name, sizeof(last_name), s); }
63-
if (flags & (1u << 3)) { RAII_STRING char *s = tl_read_string(r); copy_str(username, sizeof(username), s); }
64-
65-
/* Match by user_id and fill name fields. */
66-
for (int i = 0; i < count; i++) {
67-
if (entries[i].user_id == id) {
68-
entries[i].access_hash = access_hash;
69-
copy_str(entries[i].first_name, sizeof(entries[i].first_name), first_name);
70-
copy_str(entries[i].last_name, sizeof(entries[i].last_name), last_name);
71-
copy_str(entries[i].username, sizeof(entries[i].username), username);
72-
break;
73-
}
74-
}
75-
}
76-
7723
int domain_get_contacts(const ApiConfig *cfg,
7824
MtProtoSession *s, Transport *t,
7925
ContactEntry *out, int max_entries, int *out_count) {
@@ -140,13 +86,32 @@ int domain_get_contacts(const ApiConfig *cfg,
14086
/* saved_count:int */
14187
tl_read_int32(&r);
14288

143-
/* users:Vector<User> — enriches the entries already written */
89+
/* users:Vector<User> — tl_extract_user advances past the FULL object
90+
* so the cursor is correctly positioned for the next user each iteration. */
14491
uint32_t uvec = tl_read_uint32(&r);
14592
if (uvec == TL_vector) {
14693
uint32_t ucount = tl_read_uint32(&r);
14794
for (uint32_t i = 0; i < ucount; i++) {
14895
if (!tl_reader_ok(&r)) break;
149-
parse_user_into(&r, out, written);
96+
UserSummary us = {0};
97+
if (tl_extract_user(&r, &us) != 0) {
98+
logger_log(LOG_WARN, "contacts: user parse failed at index %u", i);
99+
break;
100+
}
101+
for (int j = 0; j < written; j++) {
102+
if (out[j].user_id == us.id) {
103+
out[j].access_hash = us.access_hash;
104+
size_t n = strlen(us.name);
105+
if (n >= sizeof(out[j].name)) n = sizeof(out[j].name) - 1;
106+
memcpy(out[j].name, us.name, n);
107+
out[j].name[n] = '\0';
108+
n = strlen(us.username);
109+
if (n >= sizeof(out[j].username)) n = sizeof(out[j].username) - 1;
110+
memcpy(out[j].username, us.username, n);
111+
out[j].username[n] = '\0';
112+
break;
113+
}
114+
}
150115
}
151116
}
152117

src/domain/read/contacts.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ typedef struct {
2121
int64_t user_id;
2222
int64_t access_hash;
2323
int mutual;
24-
char first_name[64];
25-
char last_name[64];
24+
char name[128]; /**< "First Last" joined; empty if server withholds. */
2625
char username[64];
2726
} ContactEntry;
2827

src/main/tg_cli.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -478,22 +478,20 @@ static int cmd_contacts(const ArgResult *args) {
478478
for (int i = 0; i < count; i++) {
479479
if (i) printf(",");
480480
printf("{\"user_id\":%lld,\"mutual\":%s"
481-
",\"first_name\":\"%s\",\"last_name\":\"%s\",\"username\":\"%s\"}",
481+
",\"name\":\"%s\",\"username\":\"%s\"}",
482482
(long long)entries[i].user_id,
483483
entries[i].mutual ? "true" : "false",
484-
entries[i].first_name,
485-
entries[i].last_name,
484+
entries[i].name,
486485
entries[i].username);
487486
}
488487
printf("]\n");
489488
} else {
490-
printf("%-12s %-20s %-20s %-20s %s\n",
491-
"user_id", "first_name", "last_name", "username", "mutual");
489+
printf("%-12s %-30s %-20s %s\n",
490+
"user_id", "name", "username", "mutual");
492491
for (int i = 0; i < count; i++)
493-
printf("%-12lld %-20s %-20s %-20s %s\n",
492+
printf("%-12lld %-30s %-20s %s\n",
494493
(long long)entries[i].user_id,
495-
entries[i].first_name,
496-
entries[i].last_name,
494+
entries[i].name,
497495
entries[i].username,
498496
entries[i].mutual ? "yes" : "no");
499497
if (count == 0) printf("(no contacts)\n");

src/main/tg_cli_ro.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -468,22 +468,20 @@ static int cmd_contacts(const ArgResult *args) {
468468
for (int i = 0; i < count; i++) {
469469
if (i) printf(",");
470470
printf("{\"user_id\":%lld,\"mutual\":%s"
471-
",\"first_name\":\"%s\",\"last_name\":\"%s\",\"username\":\"%s\"}",
471+
",\"name\":\"%s\",\"username\":\"%s\"}",
472472
(long long)entries[i].user_id,
473473
entries[i].mutual ? "true" : "false",
474-
entries[i].first_name,
475-
entries[i].last_name,
474+
entries[i].name,
476475
entries[i].username);
477476
}
478477
printf("]\n");
479478
} else {
480-
printf("%-12s %-20s %-20s %-20s %s\n",
481-
"user_id", "first_name", "last_name", "username", "mutual");
479+
printf("%-12s %-30s %-20s %s\n",
480+
"user_id", "name", "username", "mutual");
482481
for (int i = 0; i < count; i++) {
483-
printf("%-12lld %-20s %-20s %-20s %s\n",
482+
printf("%-12lld %-30s %-20s %s\n",
484483
(long long)entries[i].user_id,
485-
entries[i].first_name,
486-
entries[i].last_name,
484+
entries[i].name,
487485
entries[i].username,
488486
entries[i].mutual ? "yes" : "no");
489487
}

src/main/tg_tui.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,12 @@ static void do_contacts(const ApiConfig *cfg, MtProtoSession *s, Transport *t) {
235235
puts("contacts: request failed");
236236
return;
237237
}
238-
printf("%-12s %-20s %-20s %-20s %s\n",
239-
"user_id", "first_name", "last_name", "username", "mutual");
238+
printf("%-12s %-30s %-20s %s\n",
239+
"user_id", "name", "username", "mutual");
240240
for (int i = 0; i < count; i++) {
241-
printf("%-12lld %-20s %-20s %-20s %s\n",
241+
printf("%-12lld %-30s %-20s %s\n",
242242
(long long)entries[i].user_id,
243-
entries[i].first_name,
244-
entries[i].last_name,
243+
entries[i].name,
245244
entries[i].username,
246245
entries[i].mutual ? "yes" : "no");
247246
}

0 commit comments

Comments
 (0)