LCOV - code coverage report
Current view: top level - src/domain/write - send.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 72.7 % 77 56
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 3 3

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file domain/write/send.c
       6              :  * @brief messages.sendMessage — first write-capable domain.
       7              :  */
       8              : 
       9              : #include "domain/write/send.h"
      10              : 
      11              : #include "tl_serial.h"
      12              : #include "tl_registry.h"
      13              : #include "mtproto_rpc.h"
      14              : #include "crypto.h"
      15              : #include "logger.h"
      16              : 
      17              : #include <stdlib.h>
      18              : #include <string.h>
      19              : 
      20              : #define CRC_messages_sendMessage 0x0d9d75a4u
      21              : 
      22              : /* messages.Updates constructors we may see in the response. We only need
      23              :  * to recognise them enough to return success + optionally extract the
      24              :  * outgoing message id from updateShortSentMessage. */
      25              : #define CRC_updateShortSentMessage  0x9015e101u
      26              : #define CRC_updates                 TL_updates
      27              : #define CRC_updatesCombined         TL_updatesCombined
      28              : #define CRC_updateShort             TL_updateShort
      29              : 
      30           19 : static int write_input_peer(TlWriter *w, const HistoryPeer *p) {
      31           19 :     switch (p->kind) {
      32           16 :     case HISTORY_PEER_SELF:
      33           16 :         tl_write_uint32(w, TL_inputPeerSelf);
      34           16 :         return 0;
      35            3 :     case HISTORY_PEER_USER:
      36            3 :         tl_write_uint32(w, TL_inputPeerUser);
      37            3 :         tl_write_int64 (w, p->peer_id);
      38            3 :         tl_write_int64 (w, p->access_hash);
      39            3 :         return 0;
      40            0 :     case HISTORY_PEER_CHAT:
      41            0 :         tl_write_uint32(w, TL_inputPeerChat);
      42            0 :         tl_write_int64 (w, p->peer_id);
      43            0 :         return 0;
      44            0 :     case HISTORY_PEER_CHANNEL:
      45            0 :         tl_write_uint32(w, TL_inputPeerChannel);
      46            0 :         tl_write_int64 (w, p->peer_id);
      47            0 :         tl_write_int64 (w, p->access_hash);
      48            0 :         return 0;
      49            0 :     default:
      50            0 :         return -1;
      51              :     }
      52              : }
      53              : 
      54              : #define CRC_inputReplyToMessage 0x22c0f6d5u
      55              : 
      56           26 : int domain_send_message_reply(const ApiConfig *cfg,
      57              :                                MtProtoSession *s, Transport *t,
      58              :                                const HistoryPeer *peer,
      59              :                                const char *message,
      60              :                                int32_t reply_to_msg_id,
      61              :                                int32_t *msg_id_out,
      62              :                                RpcError *err) {
      63           26 :     if (!cfg || !s || !t || !peer || !message) return -1;
      64           25 :     size_t mlen = strlen(message);
      65           25 :     if (mlen == 0 || mlen > 4096) {
      66            6 :         logger_log(LOG_ERROR, "send: message length %zu out of bounds", mlen);
      67            6 :         return -1;
      68              :     }
      69              : 
      70              :     /* random_id — must be uniformly random 64-bit. */
      71           19 :     uint8_t rand_buf[8] = {0};
      72           19 :     int64_t random_id = 0;
      73           19 :     if (crypto_rand_bytes(rand_buf, sizeof(rand_buf)) == 0) {
      74           19 :         memcpy(&random_id, rand_buf, 8);
      75              :     } else {
      76              :         /* Fall back to non-crypto randomness; not security-critical. */
      77            0 :         random_id = (int64_t)rand() << 32 | (int64_t)rand();
      78              :     }
      79              : 
      80           19 :     TlWriter w; tl_writer_init(&w);
      81           19 :     tl_write_uint32(&w, CRC_messages_sendMessage);
      82           19 :     uint32_t flags = (reply_to_msg_id > 0) ? 1u : 0u; /* flags.0 = reply_to */
      83           19 :     tl_write_uint32(&w, flags);
      84           19 :     if (write_input_peer(&w, peer) != 0) {
      85            0 :         tl_writer_free(&w);
      86            0 :         return -1;
      87              :     }
      88           19 :     if (reply_to_msg_id > 0) {
      89              :         /* inputReplyToMessage#22c0f6d5 flags:# reply_to_msg_id:int
      90              :          *                              top_msg_id:flags.0?int
      91              :          *                              reply_to_peer_id:flags.1?InputPeer
      92              :          *                              quote_text:flags.2?string ... */
      93            3 :         tl_write_uint32(&w, CRC_inputReplyToMessage);
      94            3 :         tl_write_uint32(&w, 0);                    /* inner flags */
      95            3 :         tl_write_int32 (&w, reply_to_msg_id);
      96              :     }
      97           19 :     tl_write_string(&w, message);
      98           19 :     tl_write_int64 (&w, random_id);
      99              : 
     100              :     uint8_t query[8192];
     101           19 :     if (w.len > sizeof(query)) {
     102            0 :         tl_writer_free(&w);
     103            0 :         logger_log(LOG_ERROR, "send: query too large (%zu)", w.len);
     104            0 :         return -1;
     105              :     }
     106           19 :     memcpy(query, w.data, w.len);
     107           19 :     size_t qlen = w.len;
     108           19 :     tl_writer_free(&w);
     109              : 
     110           19 :     uint8_t resp[8192]; size_t resp_len = 0;
     111           19 :     if (api_call(cfg, s, t, query, qlen, resp, sizeof(resp), &resp_len) != 0) {
     112            0 :         logger_log(LOG_ERROR, "send: api_call failed");
     113            0 :         return -1;
     114              :     }
     115           19 :     if (resp_len < 4) return -1;
     116              : 
     117              :     uint32_t top;
     118           19 :     memcpy(&top, resp, 4);
     119           19 :     if (top == TL_rpc_error) {
     120            5 :         if (err) rpc_parse_error(resp, resp_len, err);
     121            5 :         return -1;
     122              :     }
     123              : 
     124           14 :     if (msg_id_out) *msg_id_out = 0;
     125              : 
     126              :     /* updateShortSentMessage#9015e101 flags:# out:flags.1?true id:int
     127              :      *   pts:int pts_count:int date:int media:flags.9?MessageMedia
     128              :      *   entities:flags.7?Vector<MessageEntity> ttl_period:flags.25?int */
     129           14 :     if (top == CRC_updateShortSentMessage) {
     130           13 :         TlReader r = tl_reader_init(resp, resp_len);
     131           13 :         tl_read_uint32(&r);                 /* crc */
     132           13 :         tl_read_uint32(&r);                 /* flags */
     133           13 :         if (tl_reader_ok(&r) && r.len - r.pos >= 4) {
     134           13 :             int32_t id = tl_read_int32(&r);
     135           13 :             if (msg_id_out) *msg_id_out = id;
     136              :         }
     137           13 :         return 0;
     138              :     }
     139              : 
     140              :     /* For updates / updatesCombined / updateShort we just accept success.
     141              :      * Message-id extraction from the full Updates envelope is future work. */
     142            1 :     if (top == CRC_updates || top == CRC_updatesCombined
     143            1 :         || top == CRC_updateShort) {
     144            1 :         return 0;
     145              :     }
     146              : 
     147            0 :     logger_log(LOG_WARN, "send: unexpected top 0x%08x — assuming success", top);
     148            0 :     return 0;
     149              : }
     150              : 
     151           23 : int domain_send_message(const ApiConfig *cfg,
     152              :                          MtProtoSession *s, Transport *t,
     153              :                          const HistoryPeer *peer,
     154              :                          const char *message,
     155              :                          int32_t *msg_id_out,
     156              :                          RpcError *err) {
     157           23 :     return domain_send_message_reply(cfg, s, t, peer, message, 0,
     158              :                                        msg_id_out, err);
     159              : }
        

Generated by: LCOV version 2.0-1