Line data Source code
1 : /* SPDX-License-Identifier: GPL-3.0-or-later */
2 : /* Copyright 2026 Peter Csaszar */
3 :
4 : /**
5 : * @file domain/read/self.c
6 : * @brief Implementation of users.getUsers([inputUserSelf]).
7 : */
8 :
9 : #include "domain/read/self.h"
10 :
11 : #include "tl_serial.h"
12 : #include "tl_registry.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 : /* Method and input-type CRCs (stable across recent layers). */
21 : #define CRC_users_getUsers 0x0d91a548u
22 : #define CRC_inputUserSelf 0xf7c1b13fu
23 :
24 : /* Flag bits on user#... (layer 170+). The exact bit positions are part of
25 : * the schema; we read only the flags word to skip optional fields we don't
26 : * need. The three we care about are known by bit position:
27 : * flags.0 = has_access_hash
28 : * flags.1 = has_first_name
29 : * flags.2 = has_last_name
30 : * flags.3 = has_username
31 : * flags.4 = has_phone
32 : * Premium flag lives in flags2 (introduced in layer 144+): flags2.3.
33 : * We fall back to safe parsing and only fill what we find.
34 : */
35 :
36 3 : static int build_request(uint8_t *buf, size_t cap, size_t *out_len) {
37 : TlWriter w;
38 3 : tl_writer_init(&w);
39 3 : tl_write_uint32(&w, CRC_users_getUsers);
40 3 : tl_write_uint32(&w, TL_vector);
41 3 : tl_write_uint32(&w, 1); /* count */
42 3 : tl_write_uint32(&w, CRC_inputUserSelf); /* element */
43 :
44 3 : int rc = -1;
45 3 : if (w.len <= cap) {
46 3 : memcpy(buf, w.data, w.len);
47 3 : *out_len = w.len;
48 3 : rc = 0;
49 : }
50 3 : tl_writer_free(&w);
51 3 : return rc;
52 : }
53 :
54 2 : static void copy_str(char *dst, size_t dst_cap, const char *src) {
55 2 : if (!dst || dst_cap == 0) return;
56 2 : dst[0] = '\0';
57 2 : if (!src) return;
58 2 : size_t n = strlen(src);
59 2 : if (n >= dst_cap) n = dst_cap - 1;
60 2 : memcpy(dst, src, n);
61 2 : dst[n] = '\0';
62 : }
63 :
64 : /* Parse a single User (or Empty/Full). On success fills @out and returns 0. */
65 2 : static int parse_user(TlReader *r, SelfInfo *out) {
66 2 : if (!tl_reader_ok(r)) return -1;
67 2 : uint32_t crc = tl_read_uint32(r);
68 :
69 2 : if (crc == TL_userEmpty) {
70 1 : out->id = tl_read_int64(r);
71 1 : return 0;
72 : }
73 1 : if (crc != TL_user && crc != TL_userFull) {
74 0 : logger_log(LOG_WARN,
75 : "domain_get_self: unexpected user constructor 0x%08x", crc);
76 0 : return -1;
77 : }
78 :
79 1 : uint32_t flags = tl_read_uint32(r);
80 1 : uint32_t flags2 = tl_read_uint32(r); /* layer 144+: second flags word */
81 1 : out->id = tl_read_int64(r);
82 :
83 1 : if (flags & (1u << 0)) tl_read_int64(r); /* access_hash */
84 :
85 1 : if (flags & (1u << 1)) {
86 2 : RAII_STRING char *fn = tl_read_string(r);
87 1 : copy_str(out->first_name, sizeof(out->first_name), fn);
88 : }
89 1 : if (flags & (1u << 2)) {
90 0 : RAII_STRING char *ln = tl_read_string(r);
91 0 : copy_str(out->last_name, sizeof(out->last_name), ln);
92 : }
93 1 : if (flags & (1u << 3)) {
94 0 : RAII_STRING char *un = tl_read_string(r);
95 0 : copy_str(out->username, sizeof(out->username), un);
96 : }
97 1 : if (flags & (1u << 4)) {
98 2 : RAII_STRING char *ph = tl_read_string(r);
99 1 : copy_str(out->phone, sizeof(out->phone), ph);
100 : }
101 :
102 : /* Best-effort premium/bot detection from well-known flag bits:
103 : * flags.14 = bot (layer 144+)
104 : * flags2.3 = premium (layer 144+)
105 : * Trailing fields we do not need are left unread; Telegram tolerates a
106 : * short read on the client side for the purposes of this query because
107 : * the wire contains only the object we asked for. */
108 1 : out->is_bot = (flags & (1u << 14)) ? 1 : 0;
109 1 : out->is_premium = (flags2 & (1u << 3)) ? 1 : 0;
110 1 : return 0;
111 : }
112 :
113 3 : int domain_get_self(const ApiConfig *cfg,
114 : MtProtoSession *s, Transport *t,
115 : SelfInfo *out) {
116 3 : if (!cfg || !s || !t || !out) return -1;
117 3 : memset(out, 0, sizeof(*out));
118 :
119 : uint8_t query[64];
120 3 : size_t qlen = 0;
121 3 : if (build_request(query, sizeof(query), &qlen) != 0) {
122 0 : logger_log(LOG_ERROR, "domain_get_self: failed to build request");
123 0 : return -1;
124 : }
125 :
126 3 : RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
127 3 : if (!resp) return -1;
128 3 : size_t resp_len = 0;
129 3 : if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) {
130 0 : logger_log(LOG_ERROR, "domain_get_self: api_call failed");
131 0 : return -1;
132 : }
133 3 : if (resp_len < 8) {
134 0 : logger_log(LOG_ERROR, "domain_get_self: response too short");
135 0 : return -1;
136 : }
137 :
138 : /* RPC error? */
139 : uint32_t top;
140 3 : memcpy(&top, resp, 4);
141 3 : if (top == TL_rpc_error) {
142 : RpcError err;
143 1 : rpc_parse_error(resp, resp_len, &err);
144 1 : logger_log(LOG_ERROR, "domain_get_self: RPC error %d: %s",
145 : err.error_code, err.error_msg);
146 1 : return -1;
147 : }
148 :
149 : /* Expected: Vector<User> → [vector_crc, count, user0, ...] */
150 2 : if (top != TL_vector) {
151 0 : logger_log(LOG_ERROR,
152 : "domain_get_self: expected Vector, got 0x%08x", top);
153 0 : return -1;
154 : }
155 :
156 2 : TlReader r = tl_reader_init(resp, resp_len);
157 2 : tl_read_uint32(&r); /* vector */
158 2 : uint32_t count = tl_read_uint32(&r);
159 2 : if (count == 0) {
160 0 : logger_log(LOG_ERROR, "domain_get_self: empty Vector<User>");
161 0 : return -1;
162 : }
163 :
164 2 : return parse_user(&r, out);
165 : }
|