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 "mtproto_rpc.h"
13 : #include "logger.h"
14 : #include "raii.h"
15 :
16 : #include <stdlib.h>
17 : #include <string.h>
18 :
19 : #define CRC_contacts_getContacts 0x5dd69e12u
20 : #define CRC_contact 0x145ade0bu
21 :
22 8 : int domain_get_contacts(const ApiConfig *cfg,
23 : MtProtoSession *s, Transport *t,
24 : ContactEntry *out, int max_entries, int *out_count) {
25 8 : if (!cfg || !s || !t || !out || !out_count || max_entries <= 0) return -1;
26 6 : *out_count = 0;
27 6 : if (max_entries > CONTACTS_MAX) max_entries = CONTACTS_MAX;
28 :
29 6 : TlWriter w; tl_writer_init(&w);
30 6 : tl_write_uint32(&w, CRC_contacts_getContacts);
31 6 : tl_write_int64 (&w, 0); /* hash = 0 → server always returns full list */
32 : uint8_t query[32];
33 6 : if (w.len > sizeof(query)) { tl_writer_free(&w); return -1; }
34 6 : memcpy(query, w.data, w.len);
35 6 : size_t qlen = w.len;
36 6 : tl_writer_free(&w);
37 :
38 6 : RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
39 6 : if (!resp) return -1;
40 6 : size_t resp_len = 0;
41 6 : if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) return -1;
42 6 : if (resp_len < 4) return -1;
43 :
44 : uint32_t top;
45 6 : memcpy(&top, resp, 4);
46 6 : if (top == TL_rpc_error) {
47 1 : RpcError err; rpc_parse_error(resp, resp_len, &err);
48 1 : logger_log(LOG_ERROR, "contacts: RPC error %d: %s",
49 : err.error_code, err.error_msg);
50 1 : return -1;
51 : }
52 5 : if (top == TL_contacts_contactsNotModified) {
53 : /* Server thinks the client-provided hash matches its state.
54 : * We passed hash=0, so this shouldn't happen — treat as empty. */
55 0 : return 0;
56 : }
57 5 : if (top != TL_contacts_contacts) {
58 0 : logger_log(LOG_ERROR, "contacts: unexpected top 0x%08x", top);
59 0 : return -1;
60 : }
61 :
62 5 : TlReader r = tl_reader_init(resp, resp_len);
63 5 : tl_read_uint32(&r); /* top constructor */
64 :
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 7 : ContactEntry e = { .user_id = tl_read_int64(&r) };
78 7 : int b = tl_read_bool(&r);
79 7 : e.mutual = (b == 1) ? 1 : 0;
80 7 : out[written++] = e;
81 : }
82 5 : *out_count = written;
83 5 : return 0;
84 : }
|