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 2 : int domain_get_contacts(const ApiConfig *cfg,
23 : MtProtoSession *s, Transport *t,
24 : ContactEntry *out, int max_entries, int *out_count) {
25 2 : if (!cfg || !s || !t || !out || !out_count || max_entries <= 0) return -1;
26 2 : *out_count = 0;
27 2 : if (max_entries > CONTACTS_MAX) max_entries = CONTACTS_MAX;
28 :
29 2 : TlWriter w; tl_writer_init(&w);
30 2 : tl_write_uint32(&w, CRC_contacts_getContacts);
31 2 : tl_write_int64 (&w, 0); /* hash = 0 → server always returns full list */
32 : uint8_t query[32];
33 2 : if (w.len > sizeof(query)) { tl_writer_free(&w); return -1; }
34 2 : memcpy(query, w.data, w.len);
35 2 : size_t qlen = w.len;
36 2 : tl_writer_free(&w);
37 :
38 2 : RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
39 2 : if (!resp) return -1;
40 2 : size_t resp_len = 0;
41 2 : if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) return -1;
42 2 : if (resp_len < 4) return -1;
43 :
44 : uint32_t top;
45 2 : memcpy(&top, resp, 4);
46 2 : if (top == TL_rpc_error) {
47 0 : RpcError err; rpc_parse_error(resp, resp_len, &err);
48 0 : logger_log(LOG_ERROR, "contacts: RPC error %d: %s",
49 : err.error_code, err.error_msg);
50 0 : return -1;
51 : }
52 2 : 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 2 : if (top != TL_contacts_contacts) {
58 0 : logger_log(LOG_ERROR, "contacts: unexpected top 0x%08x", top);
59 0 : return -1;
60 : }
61 :
62 2 : TlReader r = tl_reader_init(resp, resp_len);
63 2 : tl_read_uint32(&r); /* top constructor */
64 :
65 2 : uint32_t vec = tl_read_uint32(&r);
66 2 : if (vec != TL_vector) return -1;
67 2 : uint32_t count = tl_read_uint32(&r);
68 2 : int written = 0;
69 4 : for (uint32_t i = 0; i < count && written < max_entries; i++) {
70 2 : if (!tl_reader_ok(&r)) break;
71 2 : uint32_t ccrc = tl_read_uint32(&r);
72 2 : if (ccrc != CRC_contact) {
73 0 : logger_log(LOG_WARN,
74 : "contacts: unknown Contact 0x%08x — stopping", ccrc);
75 0 : break;
76 : }
77 2 : ContactEntry e = { .user_id = tl_read_int64(&r) };
78 2 : int b = tl_read_bool(&r);
79 2 : e.mutual = (b == 1) ? 1 : 0;
80 2 : out[written++] = e;
81 : }
82 2 : *out_count = written;
83 2 : return 0;
84 : }
|