LCOV - code coverage report
Current view: top level - src/domain/read - self.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 80.0 % 80 64
Test Date: 2026-04-20 19:54:24 Functions: 100.0 % 4 4

            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              : }
        

Generated by: LCOV version 2.0-1