1 /* SPDX-License-Identifier: GPL-3.0-or-later
2  * Copyright © 2016-2018 The TokTok team.
3  * Copyright © 2013 Tox project.
4  */
5 
6 /*
7  * Handle friend requests.
8  */
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12 
13 #include "friend_requests.h"
14 
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include "util.h"
19 
20 /* NOTE: The following is just a temporary fix for the multiple friend requests received at the same time problem.
21  * TODO(irungentoo): Make this better (This will most likely tie in with the way we will handle spam.)
22  */
23 #define MAX_RECEIVED_STORED 32
24 
25 struct Received_Requests {
26     uint8_t requests[MAX_RECEIVED_STORED][CRYPTO_PUBLIC_KEY_SIZE];
27     uint16_t requests_index;
28 };
29 
30 struct Friend_Requests {
31     uint32_t nospam;
32     fr_friend_request_cb *handle_friendrequest;
33     uint8_t handle_friendrequest_isset;
34     void *handle_friendrequest_object;
35 
36     filter_function_cb *filter_function;
37     void *filter_function_userdata;
38 
39     struct Received_Requests received;
40 };
41 
42 /* Set and get the nospam variable used to prevent one type of friend request spam. */
set_nospam(Friend_Requests * fr,uint32_t num)43 void set_nospam(Friend_Requests *fr, uint32_t num)
44 {
45     fr->nospam = num;
46 }
47 
get_nospam(const Friend_Requests * fr)48 uint32_t get_nospam(const Friend_Requests *fr)
49 {
50     return fr->nospam;
51 }
52 
53 
54 /* Set the function that will be executed when a friend request is received. */
callback_friendrequest(Friend_Requests * fr,fr_friend_request_cb * function,void * object)55 void callback_friendrequest(Friend_Requests *fr, fr_friend_request_cb *function, void *object)
56 {
57     fr->handle_friendrequest = function;
58     fr->handle_friendrequest_isset = 1;
59     fr->handle_friendrequest_object = object;
60 }
61 
62 /* Set the function used to check if a friend request should be displayed to the user or not. */
set_filter_function(Friend_Requests * fr,filter_function_cb * function,void * userdata)63 void set_filter_function(Friend_Requests *fr, filter_function_cb *function, void *userdata)
64 {
65     fr->filter_function = function;
66     fr->filter_function_userdata = userdata;
67 }
68 
69 /* Add to list of received friend requests. */
addto_receivedlist(Friend_Requests * fr,const uint8_t * real_pk)70 static void addto_receivedlist(Friend_Requests *fr, const uint8_t *real_pk)
71 {
72     if (fr->received.requests_index >= MAX_RECEIVED_STORED) {
73         fr->received.requests_index = 0;
74     }
75 
76     id_copy(fr->received.requests[fr->received.requests_index], real_pk);
77     ++fr->received.requests_index;
78 }
79 
80 /* Check if a friend request was already received.
81  *
82  *  return false if it did not.
83  *  return true if it did.
84  */
request_received(const Friend_Requests * fr,const uint8_t * real_pk)85 static bool request_received(const Friend_Requests *fr, const uint8_t *real_pk)
86 {
87     for (uint32_t i = 0; i < MAX_RECEIVED_STORED; ++i) {
88         if (id_equal(fr->received.requests[i], real_pk)) {
89             return true;
90         }
91     }
92 
93     return false;
94 }
95 
96 /* Remove real pk from received.requests list.
97  *
98  *  return 0 if it removed it successfully.
99  *  return -1 if it didn't find it.
100  */
remove_request_received(Friend_Requests * fr,const uint8_t * real_pk)101 int remove_request_received(Friend_Requests *fr, const uint8_t *real_pk)
102 {
103     for (uint32_t i = 0; i < MAX_RECEIVED_STORED; ++i) {
104         if (id_equal(fr->received.requests[i], real_pk)) {
105             crypto_memzero(fr->received.requests[i], CRYPTO_PUBLIC_KEY_SIZE);
106             return 0;
107         }
108     }
109 
110     return -1;
111 }
112 
113 
friendreq_handlepacket(void * object,const uint8_t * source_pubkey,const uint8_t * packet,uint16_t length,void * userdata)114 static int friendreq_handlepacket(void *object, const uint8_t *source_pubkey, const uint8_t *packet, uint16_t length,
115                                   void *userdata)
116 {
117     Friend_Requests *const fr = (Friend_Requests *)object;
118 
119     if (length <= 1 + sizeof(fr->nospam) || length > ONION_CLIENT_MAX_DATA_SIZE) {
120         return 1;
121     }
122 
123     ++packet;
124     --length;
125 
126     if (fr->handle_friendrequest_isset == 0) {
127         return 1;
128     }
129 
130     if (request_received(fr, source_pubkey)) {
131         return 1;
132     }
133 
134     if (memcmp(packet, &fr->nospam, sizeof(fr->nospam)) != 0) {
135         return 1;
136     }
137 
138     if (fr->filter_function) {
139         if (fr->filter_function(source_pubkey, fr->filter_function_userdata) != 0) {
140             return 1;
141         }
142     }
143 
144     addto_receivedlist(fr, source_pubkey);
145 
146     const uint32_t message_len = length - sizeof(fr->nospam);
147     VLA(uint8_t, message, message_len + 1);
148     memcpy(message, packet + sizeof(fr->nospam), message_len);
149     message[SIZEOF_VLA(message) - 1] = 0; /* Be sure the message is null terminated. */
150 
151     fr->handle_friendrequest(fr->handle_friendrequest_object, source_pubkey, message, message_len, userdata);
152     return 0;
153 }
154 
friendreq_init(Friend_Requests * fr,Friend_Connections * fr_c)155 void friendreq_init(Friend_Requests *fr, Friend_Connections *fr_c)
156 {
157     set_friend_request_callback(fr_c, &friendreq_handlepacket, fr);
158 }
159 
friendreq_new(void)160 Friend_Requests *friendreq_new(void)
161 {
162     return (Friend_Requests *)calloc(1, sizeof(Friend_Requests));
163 }
164 
friendreq_kill(Friend_Requests * fr)165 void friendreq_kill(Friend_Requests *fr)
166 {
167     free(fr);
168 }
169