Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
developers:client_examples:echo_bot [2015/11/03 06:02] nurupodevelopers:client_examples:echo_bot [2020/03/23 22:08] (current) – Minor code fixes nurupo
Line 6: Line 6:
 It's assumed that the reader is familiar with the C programming language. It's assumed that the reader is familiar with the C programming language.
  
-Before jumping to coding, we need to install the toxcore library, which can be done by following the [[https://github.com/irungentoo/toxcore/blob/master/INSTALL.md|installation instructions from toxcore's git repository]].+Before jumping to coding, we need to install the toxcore library, which can be done by following the [[https://github.com/TokTok/c-toxcore/blob/master/INSTALL.md|installation instructions from toxcore's git repository]].
  
 For the sake of simplicity, we will use a couple of helper functions from the sodium library, which is a dependency of toxcore, so it should be installed along with it. For the sake of simplicity, we will use a couple of helper functions from the sodium library, which is a dependency of toxcore, so it should be installed along with it.
  
-Although not required for this example, it's also a good idea to glance over [[https://github.com/irungentoo/toxcore/blob/master/toxcore/tox.h|tox.h file]], which is the Tox public header file, to familiarize yourself with the API and concepts used in it. It's very well documented, so you can figure out how to use toxcore by simply reading through it.+Although not required for this example, it's also a good idea to glance over [[https://github.com/TokTok/c-toxcore/blob/master/toxcore/tox.h|tox.h file]], which is the Tox public header file, to familiarize yourself with the API and concepts used in it. It's very well documented, so you can figure out how to use toxcore by simply reading through it.
  
 ===== Coding ===== ===== Coding =====
Line 24: Line 24:
 It takes a structure of options as the first argument and an error enum as the second argument. It takes a structure of options as the first argument and an error enum as the second argument.
  
-The ''Tox_Options'' struct can be used to specify such options as whether toxcore should be using a proxy for routing the network traffic, whether to use ipv4 or ipv6, which ports should it be using, and several other options.+The ''Tox_Options'' struct can be used to specify such options as whether toxcore should be using a proxy for routing the network traffic, whether to use ipv4 or ipv4 and ipv6, which ports should it be using, and several other options.
 Toxcore provides a way to get and store internal data of the Tox instance, which includes the asymmetric key pair (our identity), name, status message, friend list, list of sent friend requests and some other data. Loading off the stored internal Tox instance data is also one of many options available through ''Tox_Options'' struct. Toxcore provides a way to get and store internal data of the Tox instance, which includes the asymmetric key pair (our identity), name, status message, friend list, list of sent friend requests and some other data. Loading off the stored internal Tox instance data is also one of many options available through ''Tox_Options'' struct.
 ''Tox_Options'' can be set to ''NULL'', in which case default options will be used. ''Tox_Options'' can be set to ''NULL'', in which case default options will be used.
Line 40: Line 40:
 ==== Setting name and status message ==== ==== Setting name and status message ====
  
-When creating a new Tox instance, without loading data from the previous saved instance, by default the name and status message are empty and user status is set to none. Let's update the name and status message fields.+When creating a new Tox instance, unless you are creating it off data saved from the previous Tox instance, by default the name and status message are empty and user status is set to none. Let's update the name and status message fields.
  
 We will use "Echo Bot" for the name and "Echoing your messages" for the status message. We will leave user status set to none. We will use "Echo Bot" for the name and "Echoing your messages" for the status message. We will leave user status set to none.
Line 62: Line 62:
 It's a bit early for us to go online, as there are still a few things we need to do, but let's set up network connection first as it will make things a bit easier to explain later. Note that we are not going online just yet; we are just setting up a network connection. It's a bit early for us to go online, as there are still a few things we need to do, but let's set up network connection first as it will make things a bit easier to explain later. Note that we are not going online just yet; we are just setting up a network connection.
  
-Let's try to think about what it means in the context of peer-to-peer application to be online. In a centralized server model it's easy: there is a central server to which everyone connects. However, in a distributed peer-to-peer network such as the one Tox uses, peers connect to one another without using centralized servers. Essentially, for a peer-to-peer software the notion of being "online" means to be connected to at least one other peer. The typical method for connecting to someone is to know their IP address and port, but that's neither convenient nor practical. Entering the IP address and port of every person we want to communicate with through Tox is cumbersome. Humans are bad at remembering long sequences of numbers, and the IP of whoever we want to communicate with can change at any given time. In the centralized server model, the IP and port can be stored on the central server and associated with some unique identifier, such as username or email, so that you could query the central server of the IP and port of user under username X and the server would give you the most recent IP and port for user X. However in our case there is no central server where such information could be stored, as centralization goes against the Tox philosophy. Luckily, there is a solution that allows the store of IP and port of a peer associated with peer's unique identifier in a decentralized fashion -- DHT (Distributed Hash Table). DHT is essentially a table of keyvalue pairs that is distributed among all the peers in the network. So, to be able to connect to Tox users that we want to communicate with, we need first to connect to DHT network and then look up these Tox users' IP and port using their unique identifiers.+Let's try to think about what it means in the context of peer-to-peer application to be online. In a centralized server model it's easy: there is a central server to which everyone connects. However, in a distributed peer-to-peer network such as the one Tox uses, peers connect to one another without using centralized servers. Essentially, for a peer-to-peer software the notion of being "online" means to be connected to at least one other peer. The typical method for connecting to someone is to know their IP address and port, but that's neither convenient nor practical. Entering the IP address and port of every person we want to communicate with through Tox is cumbersome, and the IP of whoever we want to communicate with can change at any given time. In the centralized server model, the IP and port can be stored on the central server and associated with some unique identifier, such as username or email, so that you could query the central server of the IP and port of user under username X and the server would give you the most recent IP and port for user X. However in our case there is no central server where such information could be stored, as centralization goes against the Tox philosophy. Luckily, there is a solution that allows the store of IP and port of a peer associated with peer's unique identifier in a decentralized fashion -- DHT (Distributed Hash Table). DHT is essentially a table of key-value pairs that is distributed among all the peers in the network. So, to be able to connect to Tox users that we want to communicate with, we need first to connect to DHT network and then look up these Tox users' IP and port using their unique identifiers.
  
 Tox uses a 32-byte random number as a user unique identifier, and not just any random number, but a public key of the user's permanent asymmetric key pair. The key pair is generated using a cryptographic random function, so the probability of two people generating the random number is extremely low, making it a good unique identifier. The 32-byte public key is textually represented as 64 upper case hex characters, each 2 characters encoding a single byte, e.g. ''F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67''. A new key pair is generated every time ''tox_new'' function is called without loading a previous Tox instance's data. Tox uses a 32-byte random number as a user unique identifier, and not just any random number, but a public key of the user's permanent asymmetric key pair. The key pair is generated using a cryptographic random function, so the probability of two people generating the random number is extremely low, making it a good unique identifier. The 32-byte public key is textually represented as 64 upper case hex characters, each 2 characters encoding a single byte, e.g. ''F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67''. A new key pair is generated every time ''tox_new'' function is called without loading a previous Tox instance's data.
  
-Now we know that to be online in the context of Tox it means to connect to the DHT, so let's do that. First we need to know IP and port of a DHT node, as well as its public key, to be able to protect the communication. However, in contrast to regular Tox peers, users using Tox to communicate with their friends, DHT nodes can be dedicated, i.e. have a more permanent IP address and be online most of the time, which means that knowing IP, port and public key of just few nodes is generally enough to be able to always connect to the DHT network. In Tox, every peer who has UDP connections enabled is a DHT node, even our Echo Bot. This means that it is possible for someone to connect to the DHT (bootstrap) through our bot. However using dedicated DHT bootstrap nodes is more reliable, since they are more likely to be online when we want bootstrap into the DHT, thus that's what we will be bootstrapping off of. Tox's wiki contains [[https://wiki.tox.chat/users/nodes|a list of DHT bootstrap nodes]] ran by Tox'community ([[https://wiki.tox.chat/users/runningnodes#daemonized_version|you can run one and be on that list too!]]). To connect to the DHT network we will use the following nodes taken from the wiki page:+Now we know that to be online in the context of Tox it means to connect to the DHT, so let's do that. First we need to know IP and port of a DHT node, as well as its public key, to be able to protect the communication. In Tox, every peer who has UDP connections enabled is a DHT node, even our Echo Bot. This means that it is possible for someone to connect to the DHT (i.e. to bootstrap) through our bot. However, in contrast to regular Tox peers, users using Tox to communicate with their friends, DHT nodes can be dedicated, i.e. have a more permanent IP address and be online most of the time, which means that knowing IP, port and public key of just few nodes is generally enough to be able to always connect to the DHT network. Because using dedicated DHT nodes to connect to the DHT is more reliable, since they are more likely to be online when we want to bootstrap into the DHT, that's what we will be using. Tox's wiki contains [[https://wiki.tox.chat/users/nodes|a list of DHT bootstrap nodes]] ran by Tox community ([[https://wiki.tox.chat/users/runningnodes#daemonized_version|you can run one and be on that list too!]]). To connect to the DHT network we will use the following nodes taken from the wiki page:
  
- +^ IP                                  ^ Port   ^ Public Key                                                            
-^ IP ^ Port ^ Public Key ^ +85.143.221.42                       | 33445  | ''DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43''  
-144.76.60.215 | 33445 | ''04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F''+2a04:ac00:1:9f00:5054:ff:fe01:becd  | 33445  | ''DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43'' 
-23.226.230.47 | 33445 | ''A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074''+| 78.46.73.141                        | 33445  | ''02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46''  
-178.21.112.187 | 33445 | ''4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057''+2a01:4f8:120:4091::               | 33445  | ''02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46'' 
-195.154.119.113 | 33445 | ''E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354''+| tox.initramfs.io                    | 33445  | ''3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25''  
-192.210.149.121 | 33445 | ''F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67'' |+tox2.abilinski.com                  | 33445  | ''7A6098B590BDC73F9723FC59F82B3F9085A64D1B213AAF8E610FD351930D052D''  
 +205.185.115.131                     | 53     | ''3091C6BEB2A993F1C6300C16549FABA67098FF3D62C6D253828B531470B53D68'' 
 +| tox.kurnevsky.net                   | 33445  | ''82EF82BA33445A1F91A7DB27189ECFC0C013E06E3DA71F588ED692BED625EC23''  |
  
 We will use ''tox_boostrap'' function for bootstraping into DHT network. The function has following signature: We will use ''tox_boostrap'' function for bootstraping into DHT network. The function has following signature:
Line 104: Line 106:
     uint16_t port;     uint16_t port;
     const char key_hex[TOX_PUBLIC_KEY_SIZE*2 + 1]; // 1 for null terminator     const char key_hex[TOX_PUBLIC_KEY_SIZE*2 + 1]; // 1 for null terminator
-    unsigned char key_bin[TOX_PUBLIC_KEY_SIZE]; 
 } DHT_node; } DHT_node;
  
Line 111: Line 112:
 DHT_node nodes[] = DHT_node nodes[] =
 { {
-    {"144.76.60.215",   33445, "04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F", {0}}, +    {"85.143.221.42",                      33445, "DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43"}, 
-    {"23.226.230.47",   33445, "A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074", {0}}, +    {"2a04:ac00:1:9f00:5054:ff:fe01:becd", 33445, "DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43"}, 
-    {"178.21.112.187",  33445, "4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057", {0}}, +    {"78.46.73.141",                       33445, "02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46"}, 
-    {"195.154.119.113", 33445, "E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354", {0}}, +    {"2a01:4f8:120:4091::3",               33445, "02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46"}, 
-    {"192.210.149.121", 33445, "F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67", {0}}+    {"tox.initramfs.io",                   33445, "3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25"}, 
 +    {"tox2.abilinski.com",                 33445, "7A6098B590BDC73F9723FC59F82B3F9085A64D1B213AAF8E610FD351930D052D"}, 
 +    {"205.185.115.131",                       53, "3091C6BEB2A993F1C6300C16549FABA67098FF3D62C6D253828B531470B53D68"}, 
 +    {"tox.kurnevsky.net",                  33445, "82EF82BA33445A1F91A7DB27189ECFC0C013E06E3DA71F588ED692BED625EC23"}
 }; };
    
 for (size_t i = 0; i < sizeof(nodes)/sizeof(DHT_node); i ++) { for (size_t i = 0; i < sizeof(nodes)/sizeof(DHT_node); i ++) {
-    sodium_hex2bin(nodes[i].key_bin, sizeof(nodes[i].key_bin), +    unsigned char key_bin[TOX_PUBLIC_KEY_SIZE]; 
-                   nodes[i].key_hex, sizeof(nodes[i].key_hex)-1, NULL, NULL, NULL); +    sodium_hex2bin(key_bin, sizeof(key_bin), nodes[i].key_hex, sizeof(nodes[i].key_hex)-1, 
-    tox_bootstrap(tox, nodes[i].ip, nodes[i].port, nodes[i].key_bin, NULL);+                   NULL, NULL, NULL); 
 +    tox_bootstrap(tox, nodes[i].ip, nodes[i].port, key_bin, NULL);
 } }
 </code> </code>
Line 180: Line 185:
                                    void *user_data);                                    void *user_data);
  
-void tox_callback_friend_request(Tox *tox, tox_friend_request_cb *callback, void *user_data);+void tox_callback_friend_request(Tox *tox, tox_friend_request_cb *callback);
 </code> </code>
  
Line 189: Line 194:
                                    size_t length, void *user_data);                                    size_t length, void *user_data);
  
-void tox_callback_friend_message(Tox *tox, tox_friend_message_cb *callback, void *user_data);+void tox_callback_friend_message(Tox *tox, tox_friend_message_cb *callback);
 </code> </code>
  
-Functions for registering callbacks have the ''tox_callback_<event>'' name pattern, taking a pointer to a callback function, which are typedef'd as ''tox_<event>_cb'', as the first argument, and any user-specified pointer as the second argument. The callback function handling the event can be changed by registering for the same event with a different callback function specified. It's also possible to unregister by passing ''NULL'' for the function pointer argument. The second argument, ''user_data'', is any pointer that we want to be passed back to us in the callback function.+Functions for registering callbacks have the ''tox_callback_<event>'' name pattern, taking a pointer to a callback function, which are typedef'd as ''tox_<event>_cb''. The callback function handling the event can be changed by registering for the same event with a different callback function specified. It's also possible to unregister by passing ''NULL'' for the function pointer argument.
  
-The arguments of callback functions have pretty self-explanatory names. ''message'' is a UTF-8 encoded string, which is **not** null-terminated and, in fact, can contain nulls in the middle of it, as this is valid UTF-8. ''length'' is the length of the message. We already know what ''public_key'' is. ''friend_number'' is just a number used for referring to an existing friend, which toxcore might reuse if a friend is deleted and another one is added. ''TOX_MESSAGE_TYPE'' does what its name says -- tells us what type of message we are dealing with.+The arguments of callback functions have pretty self-explanatory names. ''message'' is a UTF-8 encoded string. ''length'' is the length of the message in bytes. We already know what ''public_key'' is. ''friend_number'' is just a number used for referring to an existing friend, which toxcore might reuse if a friend is deleted and another one is added. ''TOX_MESSAGE_TYPE'' does what its name says -- tells us what type of message we are dealing with. ''user_data'' is the pointer that we will later ask toxcore to pass back into our callbacks, it's generally used for passing context data.
  
 Let's register for these events, accepting all friend requests and replying to a message with exactly the same message. Let's register for these events, accepting all friend requests and replying to a message with exactly the same message.
Line 224: Line 229:
 ... ...
  
-    tox_callback_friend_request(tox, friend_request_cb, NULL); +    tox_callback_friend_request(tox, friend_request_cb); 
-    tox_callback_friend_message(tox, friend_message_cb, NULL);+    tox_callback_friend_message(tox, friend_message_cb);
 </code> </code>
  
Line 233: Line 238:
 typedef void tox_self_connection_status_cb(Tox *tox, TOX_CONNECTION connection_status, void *user_data); typedef void tox_self_connection_status_cb(Tox *tox, TOX_CONNECTION connection_status, void *user_data);
  
-void tox_callback_self_connection_status(Tox *tox, tox_self_connection_status_cb *callback, void *user_data);+void tox_callback_self_connection_status(Tox *tox, tox_self_connection_status_cb *callback);
 </code> </code>
  
Line 256: Line 261:
 ... ...
  
-    tox_callback_self_connection_status(tox, self_connection_status_cb, NULL);+    tox_callback_self_connection_status(tox, self_connection_status_cb);
 </code> </code>
  
Line 264: Line 269:
  
 <code C> <code C>
-void tox_iterate(Tox *tox);+void tox_iterate(Tox *tox, void *user_data);
  
 uint32_t tox_iteration_interval(const Tox *tox); uint32_t tox_iteration_interval(const Tox *tox);
 </code> </code>
  
-''tox_iterate'' is the function that handles all the networking and calls our callbacks. It has to be called periodically for toxcore to keep doing the networking. If we don't call it often enough, we will go offline. ''tox_iteration_interval'' function tells us how often ''tox_iterate'' should be called -- how much time in milliseconds should pass from previous ''tox_iterate'' call to the next ''tox_iterate'' call, for optional performance.+''tox_iterate'' is the function that handles all the networking and calls our callbacks with the ''user_data'' pointer we specify here. It has to be called periodically for toxcore to keep doing the networking. If we don't call it often enough, we will go offline. ''tox_iteration_interval'' function tells us how often ''tox_iterate'' should be called -- how much time in milliseconds should pass from previous ''tox_iterate'' call to the next ''tox_iterate'' call, for optional performance.
  
 So, all we need to do is to call ''tox_interval'' every ''tox_iteration_interval'' milliseconds. We use ''usleep'' function for sleeping, assuming that we are on POSIX-compliant system: So, all we need to do is to call ''tox_interval'' every ''tox_iteration_interval'' milliseconds. We use ''usleep'' function for sleeping, assuming that we are on POSIX-compliant system:
Line 275: Line 280:
 <code C> <code C>
 while (1) { while (1) {
-    tox_iterate(tox);+    tox_iterate(tox, NULL);
     usleep(tox_iteration_interval(tox) * 1000);     usleep(tox_iteration_interval(tox) * 1000);
 } }
Line 282: Line 287:
 ==== Deinitializing Tox instance ==== ==== Deinitializing Tox instance ====
  
-Our tox event loop is an infinite while loop, so anything we put after it won't get executed. But just for the sake of the example we do a ''tox_kill'' call, which is the counter part of ''tox_new'' -- it de-initialized the Tox instance, releasing resources and disconnecting us from the network:+Our Tox event loop is an infinite while loop, so anything we put after it won't get executed. But just for the sake of the example we do a ''tox_kill'' call, which is the counter part of ''tox_new'' -- it de-initialized the Tox instance, releasing resources and disconnecting us from the network:
  
 <code C> <code C>
Line 314: Line 319:
     uint16_t port;     uint16_t port;
     const char key_hex[TOX_PUBLIC_KEY_SIZE*2 + 1];     const char key_hex[TOX_PUBLIC_KEY_SIZE*2 + 1];
-    unsigned char key_bin[TOX_PUBLIC_KEY_SIZE]; 
 } DHT_node; } DHT_node;
  
 void friend_request_cb(Tox *tox, const uint8_t *public_key, const uint8_t *message, size_t length, void friend_request_cb(Tox *tox, const uint8_t *public_key, const uint8_t *message, size_t length,
-                                   void *user_data)+                       void *user_data)
 { {
     tox_friend_add_norequest(tox, public_key, NULL);     tox_friend_add_norequest(tox, public_key, NULL);
Line 324: Line 328:
  
 void friend_message_cb(Tox *tox, uint32_t friend_number, TOX_MESSAGE_TYPE type, const uint8_t *message, void friend_message_cb(Tox *tox, uint32_t friend_number, TOX_MESSAGE_TYPE type, const uint8_t *message,
-                                   size_t length, void *user_data)+                       size_t length, void *user_data)
 { {
     tox_friend_send_message(tox, friend_number, type, message, length, NULL);     tox_friend_send_message(tox, friend_number, type, message, length, NULL);
Line 356: Line 360:
     DHT_node nodes[] =     DHT_node nodes[] =
     {     {
-        {"144.76.60.215",   33445, "04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F", {0}}, +        {"85.143.221.42",                      33445, "DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43"}, 
-        {"23.226.230.47",   33445, "A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074", {0}}, +        {"2a04:ac00:1:9f00:5054:ff:fe01:becd", 33445, "DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43"}, 
-        {"178.21.112.187",  33445, "4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057", {0}}, +        {"78.46.73.141",                       33445, "02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46"}, 
-        {"195.154.119.113", 33445, "E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354", {0}}, +        {"2a01:4f8:120:4091::3",               33445, "02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46"}, 
-        {"192.210.149.121", 33445, "F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67", {0}}+        {"tox.initramfs.io",                   33445, "3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25"}, 
 +        {"tox2.abilinski.com",                 33445, "7A6098B590BDC73F9723FC59F82B3F9085A64D1B213AAF8E610FD351930D052D"}, 
 +        {"205.185.115.131",                       53, "3091C6BEB2A993F1C6300C16549FABA67098FF3D62C6D253828B531470B53D68"}, 
 +        {"tox.kurnevsky.net",                  33445, "82EF82BA33445A1F91A7DB27189ECFC0C013E06E3DA71F588ED692BED625EC23"}
     };     };
  
     for (size_t i = 0; i < sizeof(nodes)/sizeof(DHT_node); i ++) {     for (size_t i = 0; i < sizeof(nodes)/sizeof(DHT_node); i ++) {
-        sodium_hex2bin(nodes[i].key_bin, sizeof(nodes[i].key_bin), +        unsigned char key_bin[TOX_PUBLIC_KEY_SIZE]; 
-                       nodes[i].key_hex, sizeof(nodes[i].key_hex)-1, NULL, NULL, NULL); +        sodium_hex2bin(key_bin, sizeof(key_bin), nodes[i].key_hex, sizeof(nodes[i].key_hex)-1, 
-        tox_bootstrap(tox, nodes[i].ip, nodes[i].port, nodes[i].key_bin, NULL);+                       NULL, NULL, NULL); 
 +        tox_bootstrap(tox, nodes[i].ip, nodes[i].port, key_bin, NULL);
     }     }
  
Line 381: Line 389:
     printf("Tox ID: %s\n", tox_id_hex);     printf("Tox ID: %s\n", tox_id_hex);
  
-    tox_callback_friend_request(tox, friend_request_cb, NULL); +    tox_callback_friend_request(tox, friend_request_cb); 
-    tox_callback_friend_message(tox, friend_message_cb, NULL);+    tox_callback_friend_message(tox, friend_message_cb);
  
-    tox_callback_self_connection_status(tox, self_connection_status_cb, NULL);+    tox_callback_self_connection_status(tox, self_connection_status_cb); 
 + 
 +    printf("Connecting...\n");
  
     while (1) {     while (1) {
-        tox_iterate(tox);+        tox_iterate(tox, NULL); 
         usleep(tox_iteration_interval(tox) * 1000);         usleep(tox_iteration_interval(tox) * 1000);
     }     }
Line 408: Line 419:
 $ ./echo_bot $ ./echo_bot
 Tox ID: 4F8E7814B40B22F7DBB8B18B8518EBB369F45DE6B40309F43F39AFECF340FD7624FC706CE668 Tox ID: 4F8E7814B40B22F7DBB8B18B8518EBB369F45DE6B40309F43F39AFECF340FD7624FC706CE668
 +Connecting...
 Online, using UDP Online, using UDP
 </code> </code>
Line 445: Line 457:
 Since we will be storing the data to disk, we want to make sure that it won't get corrupted by a system restart, power outage etc. On Linux, file corruption can be prevented by writing data to a temporary file, followed by renaming the temporary file to the file we originally wanted to store this data as. Note that this behaviour varies from system to system, so if you are not using Linux this might not be necessarily true. Since we will be storing the data to disk, we want to make sure that it won't get corrupted by a system restart, power outage etc. On Linux, file corruption can be prevented by writing data to a temporary file, followed by renaming the temporary file to the file we originally wanted to store this data as. Note that this behaviour varies from system to system, so if you are not using Linux this might not be necessarily true.
  
-Applying all mentioned, we add two new functions to our code: ''create_tox'' and ''update_savedata_file''. ''create_tox'' creates Tox instance using the previously stored internal data, if available, or plain new Tox instance otherwise. ''update_savedata_file'' just writes Tox internal data to a file. We also refactored DHT boostrapping and Tox ID printing into separate functions, ''bootstrap'' and ''print_tox_it''.+Applying all mentioned, we add two new functions to our code: ''create_tox'' and ''update_savedata_file''. ''create_tox'' creates Tox instance using the previously stored internal data, if available, or plain new Tox instance otherwise. ''update_savedata_file'' just writes Tox internal data to a file. We also refactored DHT boostrapping and Tox ID printing into separate functions, ''bootstrap'' and ''print_tox_id''.
  
 <code C echo_bot.c> <code C echo_bot.c>
Line 463: Line 475:
     uint16_t port;     uint16_t port;
     const char key_hex[TOX_PUBLIC_KEY_SIZE*2 + 1];     const char key_hex[TOX_PUBLIC_KEY_SIZE*2 + 1];
-    unsigned char key_bin[TOX_PUBLIC_KEY_SIZE]; 
 } DHT_node; } DHT_node;
  
Line 483: Line 494:
         fseek(f, 0, SEEK_SET);         fseek(f, 0, SEEK_SET);
  
-        char *savedata = malloc(fsize);+        uint8_t *savedata = malloc(fsize);
  
         fread(savedata, fsize, 1, f);         fread(savedata, fsize, 1, f);
Line 505: Line 516:
 { {
     size_t size = tox_get_savedata_size(tox);     size_t size = tox_get_savedata_size(tox);
-    char *savedata = malloc(size);+    uint8_t *savedata = malloc(size);
     tox_get_savedata(tox, savedata);     tox_get_savedata(tox, savedata);
  
Line 521: Line 532:
     DHT_node nodes[] =     DHT_node nodes[] =
     {     {
-        {"144.76.60.215",   33445, "04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F", {0}}, +        {"85.143.221.42",                      33445, "DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43"}, 
-        {"23.226.230.47",   33445, "A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074", {0}}, +        {"2a04:ac00:1:9f00:5054:ff:fe01:becd", 33445, "DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43"}, 
-        {"178.21.112.187",  33445, "4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057", {0}}, +        {"78.46.73.141",                       33445, "02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46"}, 
-        {"195.154.119.113", 33445, "E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354", {0}}, +        {"2a01:4f8:120:4091::3",               33445, "02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46"}, 
-        {"192.210.149.121", 33445, "F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67", {0}}+        {"tox.initramfs.io",                   33445, "3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25"}, 
 +        {"tox2.abilinski.com",                 33445, "7A6098B590BDC73F9723FC59F82B3F9085A64D1B213AAF8E610FD351930D052D"}, 
 +        {"205.185.115.131",                       53, "3091C6BEB2A993F1C6300C16549FABA67098FF3D62C6D253828B531470B53D68"}, 
 +        {"tox.kurnevsky.net",                  33445, "82EF82BA33445A1F91A7DB27189ECFC0C013E06E3DA71F588ED692BED625EC23"}
     };     };
  
     for (size_t i = 0; i < sizeof(nodes)/sizeof(DHT_node); i ++) {     for (size_t i = 0; i < sizeof(nodes)/sizeof(DHT_node); i ++) {
-        sodium_hex2bin(nodes[i].key_bin, sizeof(nodes[i].key_bin), +        unsigned char key_bin[TOX_PUBLIC_KEY_SIZE]; 
-                       nodes[i].key_hex, sizeof(nodes[i].key_hex)-1, NULL, NULL, NULL); +        sodium_hex2bin(key_bin, sizeof(key_bin), nodes[i].key_hex, sizeof(nodes[i].key_hex)-1, 
-        tox_bootstrap(tox, nodes[i].ip, nodes[i].port, nodes[i].key_bin, NULL);+                       NULL, NULL, NULL); 
 +        tox_bootstrap(tox, nodes[i].ip, nodes[i].port, key_bin, NULL);
     }     }
 } }
Line 551: Line 566:
  
 void friend_request_cb(Tox *tox, const uint8_t *public_key, const uint8_t *message, size_t length, void friend_request_cb(Tox *tox, const uint8_t *public_key, const uint8_t *message, size_t length,
-                                   void *user_data)+                       void *user_data)
 { {
     tox_friend_add_norequest(tox, public_key, NULL);     tox_friend_add_norequest(tox, public_key, NULL);
Line 559: Line 574:
  
 void friend_message_cb(Tox *tox, uint32_t friend_number, TOX_MESSAGE_TYPE type, const uint8_t *message, void friend_message_cb(Tox *tox, uint32_t friend_number, TOX_MESSAGE_TYPE type, const uint8_t *message,
-                                   size_t length, void *user_data)+                       size_t length, void *user_data)
 { {
     tox_friend_send_message(tox, friend_number, type, message, length, NULL);     tox_friend_send_message(tox, friend_number, type, message, length, NULL);
Line 593: Line 608:
     print_tox_id(tox);     print_tox_id(tox);
  
-    tox_callback_friend_request(tox, friend_request_cb, NULL); +    tox_callback_friend_request(tox, friend_request_cb); 
-    tox_callback_friend_message(tox, friend_message_cb, NULL);+    tox_callback_friend_message(tox, friend_message_cb);
  
-    tox_callback_self_connection_status(tox, self_connection_status_cb, NULL);+    tox_callback_self_connection_status(tox, self_connection_status_cb);
  
     update_savedata_file(tox);     update_savedata_file(tox);
 +
 +    printf("Connecting...\n");
  
     while (1) {     while (1) {
-        tox_iterate(tox);+        tox_iterate(tox, NULL);
         usleep(tox_iteration_interval(tox) * 1000);         usleep(tox_iteration_interval(tox) * 1000);
     }     }
Line 610: Line 627:
 } }
 </code> </code>
 +
  
 Now Echo Bot's Tox ID and friend list should be persistent across restarts. Now Echo Bot's Tox ID and friend list should be persistent across restarts.
  
 +Compile it using the same command as the previous code snippet:
 +
 +<code bash>
 +gcc -o echo_bot echo_bot.c -std=gnu99 -lsodium -I /usr/local/include/ -ltoxcore
 +</code>
Print/export