summaryrefslogtreecommitdiff
path: root/toxcore/Messenger.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxcore/Messenger.c')
-rw-r--r--toxcore/Messenger.c469
1 files changed, 469 insertions, 0 deletions
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c
index 21cb2671..108159a3 100644
--- a/toxcore/Messenger.c
+++ b/toxcore/Messenger.c
@@ -42,6 +42,7 @@
42static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status); 42static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status);
43static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data, 43static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data,
44 uint32_t length, uint8_t congestion_control); 44 uint32_t length, uint8_t congestion_control);
45static int send_avatar_data_control(const Messenger *m, const uint32_t friendnumber, uint8_t op);
45 46
46// friend_not_valid determines if the friendnumber passed is valid in the Messenger object 47// friend_not_valid determines if the friendnumber passed is valid in the Messenger object
47static uint8_t friend_not_valid(const Messenger *m, int32_t friendnumber) 48static uint8_t friend_not_valid(const Messenger *m, int32_t friendnumber)
@@ -247,6 +248,10 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u
247 m->friendlist[i].statusmessage = calloc(1, 1); 248 m->friendlist[i].statusmessage = calloc(1, 1);
248 m->friendlist[i].statusmessage_length = 1; 249 m->friendlist[i].statusmessage_length = 1;
249 m->friendlist[i].userstatus = USERSTATUS_NONE; 250 m->friendlist[i].userstatus = USERSTATUS_NONE;
251 m->friendlist[i].avatar_info_sent = 0;
252 m->friendlist[i].avatar_recv_data = NULL;
253 m->friendlist[i].avatar_send_data.bytes_sent = 0;
254 m->friendlist[i].avatar_send_data.last_reset = 0;
250 m->friendlist[i].is_typing = 0; 255 m->friendlist[i].is_typing = 0;
251 memcpy(m->friendlist[i].info, data, length); 256 memcpy(m->friendlist[i].info, data, length);
252 m->friendlist[i].info_size = length; 257 m->friendlist[i].info_size = length;
@@ -330,6 +335,7 @@ int m_delfriend(Messenger *m, int32_t friendnumber)
330 onion_delfriend(m->onion_c, m->friendlist[friendnumber].onion_friendnum); 335 onion_delfriend(m->onion_c, m->friendlist[friendnumber].onion_friendnum);
331 crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id); 336 crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id);
332 free(m->friendlist[friendnumber].statusmessage); 337 free(m->friendlist[friendnumber].statusmessage);
338 free(m->friendlist[friendnumber].avatar_recv_data);
333 remove_request_received(&(m->fr), m->friendlist[friendnumber].client_id); 339 remove_request_received(&(m->fr), m->friendlist[friendnumber].client_id);
334 memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend)); 340 memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend));
335 uint32_t i; 341 uint32_t i;
@@ -564,6 +570,134 @@ int m_set_userstatus(Messenger *m, uint8_t status)
564 return 0; 570 return 0;
565} 571}
566 572
573int m_set_avatar(Messenger *m, uint8_t format, const uint8_t *data, uint32_t length)
574{
575 if (length > MAX_AVATAR_DATA_LENGTH)
576 return -1;
577
578 if (format == AVATARFORMAT_NONE) {
579 free(m->avatar_data);
580 m->avatar_data = NULL;
581 m->avatar_data_length = 0;
582 m->avatar_format = format;
583 memset(m->avatar_hash, 0, AVATAR_HASH_LENGTH);
584 } else {
585 if (length == 0 || data == NULL)
586 return -1;
587
588 uint8_t *tmp = realloc(m->avatar_data, length);
589
590 if (tmp == NULL)
591 return -1;
592
593 m->avatar_format = format;
594 m->avatar_data = tmp;
595 m->avatar_data_length = length;
596 memcpy(m->avatar_data, data, length);
597
598 m_avatar_hash(m->avatar_hash, m->avatar_data, m->avatar_data_length);
599 }
600
601 uint32_t i;
602
603 for (i = 0; i < m->numfriends; ++i)
604 m->friendlist[i].avatar_info_sent = 0;
605
606 return 0;
607}
608
609int m_get_self_avatar(const Messenger *m, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen,
610 uint8_t *hash)
611{
612 if (format)
613 *format = m->avatar_format;
614
615 if (length)
616 *length = m->avatar_data_length;
617
618 if (hash)
619 memcpy(hash, m->avatar_hash, AVATAR_HASH_LENGTH);
620
621 if (buf != NULL && maxlen > 0) {
622 if (m->avatar_data_length <= maxlen)
623 memcpy(buf, m->avatar_data, m->avatar_data_length);
624 else
625 return -1;
626 }
627
628 return 0;
629}
630
631int m_avatar_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen)
632{
633 if (hash == NULL)
634 return -1;
635
636 crypto_hash_sha256(hash, data, datalen);
637 return 0;
638}
639
640
641int m_request_avatar_info(const Messenger *m, const int32_t friendnumber)
642{
643 if (friend_not_valid(m, friendnumber))
644 return -1;
645
646 if (write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_INFO_REQ, 0, 0, 0) >= 0)
647 return 0;
648 else
649 return -1;
650}
651
652int m_send_avatar_info(const Messenger *m, const int32_t friendnumber)
653{
654 if (friend_not_valid(m, friendnumber))
655 return -1;
656
657 uint8_t data[sizeof(uint8_t) + AVATAR_HASH_LENGTH];
658 data[0] = m->avatar_format;
659 memcpy(data + 1, m->avatar_hash, AVATAR_HASH_LENGTH);
660
661 int ret = write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_INFO, data, sizeof(data), 0);
662
663 if (ret >= 0)
664 return 0;
665 else
666 return -1;
667}
668
669int m_request_avatar_data(const Messenger *m, const int32_t friendnumber)
670{
671 if (friend_not_valid(m, friendnumber))
672 return -1;
673
674 AVATARRECEIVEDATA *avrd = m->friendlist[friendnumber].avatar_recv_data;
675
676 if (avrd == NULL) {
677 avrd = malloc(sizeof(AVATARRECEIVEDATA));
678
679 if (avrd == NULL)
680 return -1;
681
682 memset(avrd, 0, sizeof(AVATARRECEIVEDATA));
683 avrd->started = 0;
684 m->friendlist[friendnumber].avatar_recv_data = avrd;
685 }
686
687 if (avrd->started) {
688 LOGGER_DEBUG("Resetting already started data request. "
689 "friendnumber == %u", friendnumber);
690 }
691
692 avrd->started = 0;
693 avrd->bytes_received = 0;
694 avrd->total_length = 0;
695 avrd->format = AVATARFORMAT_NONE;
696
697 return send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_REQ);
698}
699
700
567/* return the size of friendnumber's user status. 701/* return the size of friendnumber's user status.
568 * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH. 702 * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH.
569 */ 703 */
@@ -806,6 +940,20 @@ void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Mess
806 m->friend_connectionstatuschange_internal_userdata = userdata; 940 m->friend_connectionstatuschange_internal_userdata = userdata;
807} 941}
808 942
943void m_callback_avatar_info(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, void *),
944 void *userdata)
945{
946 m->avatar_info_recv = function;
947 m->avatar_info_recv_userdata = userdata;
948}
949
950void m_callback_avatar_data(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, uint8_t *,
951 uint32_t, void *), void *userdata)
952{
953 m->avatar_data_recv = function;
954 m->avatar_data_recv_userdata = userdata;
955}
956
809static void break_files(const Messenger *m, int32_t friendnumber); 957static void break_files(const Messenger *m, int32_t friendnumber);
810static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, uint8_t status) 958static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, uint8_t status)
811{ 959{
@@ -1857,6 +2005,9 @@ Messenger *new_messenger(Messenger_Options *options)
1857 m->net = new_networking(ip, TOX_PORT_DEFAULT); 2005 m->net = new_networking(ip, TOX_PORT_DEFAULT);
1858 } 2006 }
1859 2007
2008 m->avatar_format = AVATARFORMAT_NONE;
2009 m->avatar_data = NULL;
2010
1860 if (m->net == NULL) { 2011 if (m->net == NULL) {
1861 free(m); 2012 free(m);
1862 return NULL; 2013 return NULL;
@@ -1934,6 +2085,7 @@ void kill_messenger(Messenger *m)
1934 free(m->friendlist[i].statusmessage); 2085 free(m->friendlist[i].statusmessage);
1935 } 2086 }
1936 2087
2088 free(m->avatar_data);
1937 free(m->friendlist); 2089 free(m->friendlist);
1938 free(m); 2090 free(m);
1939} 2091}
@@ -1973,11 +2125,287 @@ static int handle_status(void *object, int i, uint8_t status)
1973 if (m->friendlist[i].status == FRIEND_ONLINE) { 2125 if (m->friendlist[i].status == FRIEND_ONLINE) {
1974 set_friend_status(m, i, FRIEND_CONFIRMED); 2126 set_friend_status(m, i, FRIEND_CONFIRMED);
1975 } 2127 }
2128
2129 /* Clear avatar transfer state */
2130 if (m->friendlist[i].avatar_recv_data) {
2131 free(m->friendlist[i].avatar_recv_data);
2132 m->friendlist[i].avatar_recv_data = NULL;
2133 }
2134 }
2135
2136 return 0;
2137}
2138
2139
2140/* Sends an avatar data control packet to the peer. Usually to return status
2141 * values or request data.
2142 */
2143static int send_avatar_data_control(const Messenger *m, const uint32_t friendnumber,
2144 uint8_t op)
2145{
2146 int ret = write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_DATA_CONTROL,
2147 &op, sizeof(op), 0);
2148 LOGGER_DEBUG("friendnumber = %u, op = %u, ret = %d",
2149 friendnumber, op, ret);
2150 return (ret >= 0) ? 0 : -1;
2151}
2152
2153
2154static int handle_avatar_data_control(Messenger *m, uint32_t friendnumber,
2155 uint8_t *data, uint32_t data_length)
2156{
2157 if (data_length != 1) {
2158 LOGGER_DEBUG("Error: PACKET_ID_AVATAR_DATA_CONTROL with bad "
2159 "data_length = %u, friendnumber = %u",
2160 data_length, friendnumber);
2161 send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR);
2162 return -1; /* Error */
2163 }
2164
2165 LOGGER_DEBUG("friendnumber = %u, op = %u", friendnumber, data[0]);
2166
2167 switch (data[0]) {
2168 case AVATARDATACONTROL_REQ: {
2169
2170 /* Check data transfer limits for this friend */
2171 AVATARSENDDATA *const avsd = &(m->friendlist[friendnumber].avatar_send_data);
2172
2173 if (avsd->bytes_sent >= AVATAR_DATA_TRANSFER_LIMIT) {
2174 /* User reached data limit. Check timeout */
2175 uint64_t now = unix_time();
2176
2177 if (avsd->last_reset > 0
2178 && (avsd->last_reset + AVATAR_DATA_TRANSFER_TIMEOUT < now)) {
2179 avsd->bytes_sent = 0;
2180 avsd->last_reset = now;
2181 } else {
2182 /* Friend still rate-limitted. Send an error and stops. */
2183 LOGGER_DEBUG("Avatar data transfer limit reached. "
2184 "friendnumber = %u", friendnumber);
2185 send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR);
2186 return 0;
2187 }
2188 }
2189
2190 /* Start the transmission with a DATA_START message. Format:
2191 * uint8_t format
2192 * uint8_t hash[AVATAR_HASH_LENGTH]
2193 * uint32_t total_length
2194 */
2195 LOGGER_DEBUG("Sending start msg to friend number %u. "
2196 "m->avatar_format = %u, m->avatar_data_length = %u",
2197 friendnumber, m->avatar_format, m->avatar_data_length);
2198 uint8_t start_data[1 + AVATAR_HASH_LENGTH + sizeof(uint32_t)];
2199 uint32_t avatar_len = htonl(m->avatar_data_length);
2200
2201 start_data[0] = m->avatar_format;
2202 memcpy(start_data + 1, m->avatar_hash, AVATAR_HASH_LENGTH);
2203 memcpy(start_data + 1 + AVATAR_HASH_LENGTH, &avatar_len, sizeof(uint32_t));
2204
2205 avsd->bytes_sent += sizeof(start_data); /* For rate limit */
2206
2207 int ret = write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_DATA_START,
2208 start_data, sizeof(start_data), 0);
2209
2210 if (ret < 0) {
2211 /* Something went wrong, try to signal the error so the friend
2212 * can clear up the state. */
2213 send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR);
2214 return 0;
2215 }
2216
2217 /* User have no avatar data, nothing more to do. */
2218 if (m->avatar_format == AVATARFORMAT_NONE)
2219 return 0;
2220
2221 /* Send the actual avatar data. */
2222 uint32_t offset = 0;
2223
2224 while (offset < m->avatar_data_length) {
2225 uint32_t chunk_len = m->avatar_data_length - offset;
2226
2227 if (chunk_len > AVATAR_DATA_MAX_CHUNK_SIZE)
2228 chunk_len = AVATAR_DATA_MAX_CHUNK_SIZE;
2229
2230 uint8_t chunk[AVATAR_DATA_MAX_CHUNK_SIZE];
2231 memcpy(chunk, m->avatar_data + offset, chunk_len);
2232 offset += chunk_len;
2233 avsd->bytes_sent += chunk_len; /* For rate limit */
2234
2235 int ret = write_cryptpacket_id(m, friendnumber,
2236 PACKET_ID_AVATAR_DATA_PUSH,
2237 chunk, chunk_len, 0);
2238
2239 if (ret < 0) {
2240 LOGGER_DEBUG("write_cryptpacket_id failed. ret = %d, "
2241 "friendnumber = %u, offset = %u",
2242 ret, friendnumber, offset);
2243 send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR);
2244 return -1;
2245 }
2246 }
2247
2248 return 0;
2249 }
2250
2251 case AVATARDATACONTROL_ERROR: {
2252 if (m->friendlist[friendnumber].avatar_recv_data) {
2253 /* We were receiving the data, sender detected an error
2254 (eg. changing avatar) and asked us to stop. */
2255 free(m->friendlist[friendnumber].avatar_recv_data);
2256 m->friendlist[friendnumber].avatar_recv_data = NULL;
2257 }
2258
2259 return 0;
2260 }
2261 }
2262
2263 return -1;
2264}
2265
2266
2267static int handle_avatar_data_start(Messenger *m, uint32_t friendnumber,
2268 uint8_t *data, uint32_t data_length)
2269{
2270 LOGGER_DEBUG("data_length = %u, friendnumber = %u", data_length, friendnumber);
2271
2272 if (data_length != 1 + AVATAR_HASH_LENGTH + sizeof(uint32_t)) {
2273 LOGGER_DEBUG("Invalid msg length = %u, friendnumber = %u",
2274 data_length, friendnumber);
2275 return -1;
2276 }
2277
2278 AVATARRECEIVEDATA *avrd = m->friendlist[friendnumber].avatar_recv_data;
2279
2280 if (avrd == NULL) {
2281 LOGGER_DEBUG("Received an unrequested DATA_START, friendnumber = %u",
2282 friendnumber);
2283 return -1;
2284 }
2285
2286 if (avrd->started) {
2287 /* Already receiving data from this friend. Must be an error
2288 * or an malicious request, because we zeroed the started bit
2289 * when we requested the data. */
2290 LOGGER_DEBUG("Received an unrequested duplicated DATA_START, "
2291 "friendnumber = %u", friendnumber);
2292 return -1;
2293 }
2294
2295 /* Copy data from message to our control structure */
2296 avrd->started = 1;
2297 avrd->format = data[0];
2298 memcpy(avrd->hash, data + 1, AVATAR_HASH_LENGTH);
2299 uint32_t tmp_len;
2300 memcpy(&tmp_len, data + 1 + AVATAR_HASH_LENGTH, sizeof(uint32_t));
2301 avrd->total_length = ntohl(tmp_len);
2302 avrd->bytes_received = 0;
2303
2304 LOGGER_DEBUG("friendnumber = %u, avrd->format = %u, "
2305 "avrd->total_length = %u, avrd->bytes_received = %u",
2306 friendnumber, avrd->format, avrd->total_length,
2307 avrd->bytes_received);
2308
2309 if (avrd->total_length > MAX_AVATAR_DATA_LENGTH) {
2310 /* Invalid data length. Stops. */
2311 LOGGER_DEBUG("Error: total_length > MAX_AVATAR_DATA_LENGTH, "
2312 "friendnumber = %u", friendnumber);
2313 free(avrd);
2314 avrd = NULL;
2315 m->friendlist[friendnumber].avatar_recv_data = NULL;
2316 return 0;
2317 }
2318
2319 if (avrd->format == AVATARFORMAT_NONE || avrd->total_length == 0) {
2320 /* No real data to receive. Run callback function and finish. */
2321 LOGGER_DEBUG("format == NONE, friendnumber = %u", friendnumber);
2322
2323 if (m->avatar_data_recv) {
2324 memset(avrd->hash, 0, AVATAR_HASH_LENGTH);
2325 (m->avatar_data_recv)(m, friendnumber, avrd->format, avrd->hash,
2326 NULL, 0, m->avatar_data_recv_userdata);
2327 }
2328
2329 free(avrd);
2330 avrd = NULL;
2331 m->friendlist[friendnumber].avatar_recv_data = NULL;
2332 return 0;
2333 }
2334
2335 /* Waits for more data to be received */
2336 return 0;
2337}
2338
2339
2340static int handle_avatar_data_push(Messenger *m, uint32_t friendnumber,
2341 uint8_t *data, uint32_t data_length)
2342{
2343 LOGGER_DEBUG("friendnumber = %u, data_length = %u", friendnumber, data_length);
2344
2345 AVATARRECEIVEDATA *avrd = m->friendlist[friendnumber].avatar_recv_data;
2346
2347 if (avrd == NULL) {
2348 /* No active transfer. It must be an error or a malicious request,
2349 * because we set the avatar_recv_data on the first DATA_START. */
2350 LOGGER_DEBUG("Error: avrd == NULL, friendnumber = %u", friendnumber);
2351 return -1; /* Error */
2352 }
2353
2354 if (avrd->started == 0) {
2355 /* Receiving data for a non-started request. Must be an error
2356 * or an malicious request. */
2357 LOGGER_DEBUG("Received an data push for a yet non started data "
2358 "request. friendnumber = %u", friendnumber);
2359 return -1; /* Error */
2360 }
2361
2362 uint32_t new_length = avrd->bytes_received + data_length;
2363
2364 if (new_length > avrd->total_length
2365 || new_length >= MAX_AVATAR_DATA_LENGTH) {
2366 /* Invalid data length due to error or malice. Stops. */
2367 LOGGER_DEBUG("Invalid data length. friendnumber = %u, "
2368 "new_length = %u, avrd->total_length = %u",
2369 friendnumber, new_length, avrd->total_length);
2370 free(avrd);
2371 m->friendlist[friendnumber].avatar_recv_data = NULL;
2372 return 0;
2373 }
2374
2375 memcpy(avrd->data + avrd->bytes_received, data, data_length);
2376 avrd->bytes_received += data_length;
2377
2378 if (avrd->bytes_received == avrd->total_length) {
2379 LOGGER_DEBUG("All data received. friendnumber = %u", friendnumber);
2380
2381 /* All data was received. Check if the hashes match. It the
2382 * requester's responsability to do this. The sender may have done
2383 * anything with its avatar data between the DATA_START and now.
2384 */
2385 uint8_t cur_hash[AVATAR_HASH_LENGTH];
2386 m_avatar_hash(cur_hash, avrd->data, avrd->bytes_received);
2387
2388 if (memcmp(cur_hash, avrd->hash, AVATAR_HASH_LENGTH) == 0) {
2389 /* Avatar successfuly received! */
2390 if (m->avatar_data_recv) {
2391 (m->avatar_data_recv)(m, friendnumber, avrd->format, cur_hash,
2392 avrd->data, avrd->bytes_received, m->avatar_data_recv_userdata);
2393 }
2394 } else {
2395 LOGGER_DEBUG("Avatar hash error. friendnumber = %u", friendnumber);
2396 }
2397
2398 free(avrd);
2399 m->friendlist[friendnumber].avatar_recv_data = NULL;
2400 return 0;
1976 } 2401 }
1977 2402
2403 /* Waits for more data to be received */
1978 return 0; 2404 return 0;
1979} 2405}
1980 2406
2407
2408
1981static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len) 2409static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
1982{ 2410{
1983 if (len == 0) 2411 if (len == 0)
@@ -2115,6 +2543,42 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
2115 break; 2543 break;
2116 } 2544 }
2117 2545
2546 case PACKET_ID_AVATAR_INFO_REQ: {
2547 /* Send our avatar information */
2548 m_send_avatar_info(m, i);
2549 break;
2550 }
2551
2552 case PACKET_ID_AVATAR_INFO: {
2553 if (m->avatar_info_recv) {
2554 /*
2555 * A malicious user may send an incomplete avatar info message.
2556 * Check if it have the correct size for the format:
2557 * [1 uint8_t: avatar format] [32 uint8_t: hash]
2558 */
2559 if (data_length == AVATAR_HASH_LENGTH + 1) {
2560 (m->avatar_info_recv)(m, i, data[0], data + 1, m->avatar_info_recv_userdata);
2561 }
2562 }
2563
2564 break;
2565 }
2566
2567 case PACKET_ID_AVATAR_DATA_CONTROL: {
2568 handle_avatar_data_control(m, i, data, data_length);
2569 break;
2570 }
2571
2572 case PACKET_ID_AVATAR_DATA_START: {
2573 handle_avatar_data_start(m, i, data, data_length);
2574 break;
2575 }
2576
2577 case PACKET_ID_AVATAR_DATA_PUSH: {
2578 handle_avatar_data_push(m, i, data, data_length);
2579 break;
2580 }
2581
2118 case PACKET_ID_RECEIPT: { 2582 case PACKET_ID_RECEIPT: {
2119 uint32_t msgid; 2583 uint32_t msgid;
2120 2584
@@ -2343,6 +2807,11 @@ void do_friends(Messenger *m)
2343 m->friendlist[i].userstatus_sent = 1; 2807 m->friendlist[i].userstatus_sent = 1;
2344 } 2808 }
2345 2809
2810 if (m->friendlist[i].avatar_info_sent == 0) {
2811 if (m_send_avatar_info(m, i) == 0)
2812 m->friendlist[i].avatar_info_sent = 1;
2813 }
2814
2346 if (m->friendlist[i].user_istyping_sent == 0) { 2815 if (m->friendlist[i].user_istyping_sent == 0) {
2347 if (send_user_istyping(m, i, m->friendlist[i].user_istyping)) 2816 if (send_user_istyping(m, i, m->friendlist[i].user_istyping))
2348 m->friendlist[i].user_istyping_sent = 1; 2817 m->friendlist[i].user_istyping_sent = 1;