Line data Source code
1 : /* SPDX-License-Identifier: GPL-3.0-or-later */
2 : /* Copyright 2026 Peter Csaszar */
3 :
4 : /**
5 : * @file domain/read/contacts.c
6 : */
7 :
8 : #include "domain/read/contacts.h"
9 :
10 : #include "tl_serial.h"
11 : #include "tl_registry.h"
12 : #include "tl_skip.h"
13 : #include "mtproto_rpc.h"
14 : #include "logger.h"
15 : #include "raii.h"
16 :
17 : #include <stdlib.h>
18 : #include <string.h>
19 :
20 : #define CRC_contacts_getContacts 0x5dd69e12u
21 : #define CRC_contact 0x145ade0bu
22 :
23 8 : int domain_get_contacts(const ApiConfig *cfg,
24 : MtProtoSession *s, Transport *t,
25 : ContactEntry *out, int max_entries, int *out_count) {
26 8 : if (!cfg || !s || !t || !out || !out_count || max_entries <= 0) return -1;
27 6 : *out_count = 0;
28 6 : if (max_entries > CONTACTS_MAX) max_entries = CONTACTS_MAX;
29 :
30 6 : TlWriter w; tl_writer_init(&w);
31 6 : tl_write_uint32(&w, CRC_contacts_getContacts);
32 6 : tl_write_int64 (&w, 0); /* hash = 0 → server always returns full list */
33 : uint8_t query[32];
34 6 : if (w.len > sizeof(query)) { tl_writer_free(&w); return -1; }
35 6 : memcpy(query, w.data, w.len);
36 6 : size_t qlen = w.len;
37 6 : tl_writer_free(&w);
38 :
39 6 : RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
40 6 : if (!resp) return -1;
41 6 : size_t resp_len = 0;
42 6 : if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) return -1;
43 6 : if (resp_len < 4) return -1;
44 :
45 : uint32_t top;
46 6 : memcpy(&top, resp, 4);
47 6 : if (top == TL_rpc_error) {
48 1 : RpcError err; rpc_parse_error(resp, resp_len, &err);
49 1 : logger_log(LOG_ERROR, "contacts: RPC error %d: %s",
50 : err.error_code, err.error_msg);
51 1 : return -1;
52 : }
53 5 : if (top == TL_contacts_contactsNotModified) {
54 0 : return 0;
55 : }
56 5 : if (top != TL_contacts_contacts) {
57 0 : logger_log(LOG_ERROR, "contacts: unexpected top 0x%08x", top);
58 0 : return -1;
59 : }
60 :
61 5 : TlReader r = tl_reader_init(resp, resp_len);
62 5 : tl_read_uint32(&r); /* top constructor */
63 :
64 : /* contacts:Vector<Contact> */
65 5 : uint32_t vec = tl_read_uint32(&r);
66 5 : if (vec != TL_vector) return -1;
67 5 : uint32_t count = tl_read_uint32(&r);
68 5 : int written = 0;
69 12 : for (uint32_t i = 0; i < count && written < max_entries; i++) {
70 7 : if (!tl_reader_ok(&r)) break;
71 7 : uint32_t ccrc = tl_read_uint32(&r);
72 7 : if (ccrc != CRC_contact) {
73 0 : logger_log(LOG_WARN,
74 : "contacts: unknown Contact 0x%08x — stopping", ccrc);
75 0 : break;
76 : }
77 : ContactEntry e;
78 7 : memset(&e, 0, sizeof(e));
79 7 : e.user_id = tl_read_int64(&r);
80 7 : int b = tl_read_bool(&r);
81 7 : e.mutual = (b == 1) ? 1 : 0;
82 7 : out[written++] = e;
83 : }
84 5 : *out_count = written;
85 :
86 : /* saved_count:int */
87 5 : tl_read_int32(&r);
88 :
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. */
91 5 : uint32_t uvec = tl_read_uint32(&r);
92 5 : if (uvec == TL_vector) {
93 5 : uint32_t ucount = tl_read_uint32(&r);
94 5 : for (uint32_t i = 0; i < ucount; i++) {
95 0 : if (!tl_reader_ok(&r)) break;
96 0 : UserSummary us = {0};
97 0 : if (tl_extract_user(&r, &us) != 0) {
98 0 : logger_log(LOG_WARN, "contacts: user parse failed at index %u", i);
99 0 : break;
100 : }
101 0 : for (int j = 0; j < written; j++) {
102 0 : if (out[j].user_id == us.id) {
103 0 : out[j].access_hash = us.access_hash;
104 0 : size_t n = strlen(us.name);
105 0 : if (n >= sizeof(out[j].name)) n = sizeof(out[j].name) - 1;
106 0 : memcpy(out[j].name, us.name, n);
107 0 : out[j].name[n] = '\0';
108 0 : n = strlen(us.username);
109 0 : if (n >= sizeof(out[j].username)) n = sizeof(out[j].username) - 1;
110 0 : memcpy(out[j].username, us.username, n);
111 0 : out[j].username[n] = '\0';
112 0 : break;
113 : }
114 : }
115 : }
116 : }
117 :
118 5 : return 0;
119 : }
|