diff --git a/docs/Group_chats.txt b/docs/Group_chats.txt
new file mode 100644
index 00000000..0da6da04
--- /dev/null
+++ b/docs/Group_chats.txt
@@ -0,0 +1,71 @@
+Massive public group chats.
+
+Everyone generates a short term public private key pair right before joining
+the chat.
+
+Note that for public group chats it is impossible to protect the chat from
+being spied on by a very dedicated attacker, encryption is therefor used as a
+form of spam/access control.
+
+## Joining the chats
+
+
+## Protocol
+
+
+Node format:
+```
+[char array (node_id), length=32 bytes][ip (in network byte order), length=4 bytes][port (in network byte order), length=2 bytes][Padding , length=2 bytes]
+```
+
+Get nodes (Request):
+Packet contents:
+```
+[char with a value of 48][Bob's (The reciever's) Public key (client_id) (32 bytes))][Alice's (The sender's) Public key (client_id) (32 bytes)][Random nonce (24 bytes)][Encrypted with the nonce and private key of the sender:[char with a value of 48][random 8 byte (ping_id)]
+```
+Valid replies: a send_nodes packet
+
+Send_nodes (response):
+```
+[char with a value of 48][Bob's (The reciever's) Public key (client_id) (32 bytes))][Alice's (The sender's) Public key (client_id) (32 bytes)][Random nonce (24 bytes)][Encrypted with the nonce and private key of the sender:[char with a value of 49][random 8 byte (ping_id)][Nodes in node format, length=40 * (number of nodes (maximum of 6 nodes)) bytes]]
+```
+
+Broadcast packet:
+```
+[char with a value of 48][Bob's (The reciever's) Public key (client_id) (32 bytes))][Alice's (The sender's) Public key (client_id) (32 bytes)][nonce][Encrypted with the nonce and private key of the sender:[char with a value of 50][Data to send to everyone]]
+```
+
+
+Data to send to everyone:
+TODO: signing and spam control + permissions.
+[client_id of sender][uint32_t message number][char with a value representing id of message][data]
+
+Note: the message number is increased by 1 for each sent message.
+
+message ids:
+0 - ping
+sent every ~60 seconds by every peer.
+No data.
+
+16 - new_peer
+Tell everyone about a new peer in the chat.
+[uint8_t public_key[public_key_len]]
+
+17 - ban_peer
+Ban a peer
+[uint8_t public_key[public_key_len]]
+
+18 - topic change
+[uint8_t topic[topiclen]]
+
+48 - name change
+[uint8_t name[namelen]]
+
+49 - status change
+[uint8_t (status id)]
+
+64 - chat message
+[uint8_t message[messagelen]]
+
+65 - action (/me)
+[uint8_t message[messagelen]]
\ No newline at end of file
diff --git a/testing/experiment/group_chats.c b/testing/experiment/group_chats.c
new file mode 100644
index 00000000..faf274d5
--- /dev/null
+++ b/testing/experiment/group_chats.c
@@ -0,0 +1,535 @@
+/* group_chats.c
+ *
+ * An implementation of massive text only group chats.
+ *
+ *
+ * Copyright (C) 2013 Tox project All Rights Reserved.
+ *
+ * This file is part of Tox.
+ *
+ * Tox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Tox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Tox. If not, see .
+ *
+ */
+
+#include "group_chats.h"
+
+
+#define GROUPCHAT_MAXDATA_LENGTH (MAX_DATA_SIZE - (1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES))
+#define GROUPCHAT_MAXPLAINDATA_LENGTH (GROUPCHAT_MAXDATA_LENGTH - crypto_box_MACBYTES)
+
+#define GROUP_MAX_SENDNODES (GROUP_CLOSE_CONNECTIONS * 2)
+
+typedef struct {
+ uint64_t pingid;
+ //uint8_t client_id[crypto_box_PUBLICKEYBYTES];
+
+} getnodes_data;
+
+typedef struct {
+ uint8_t client_id[crypto_box_PUBLICKEYBYTES];
+ IP_Port ip_port;
+
+} groupchat_nodes;
+
+typedef struct {
+ uint64_t pingid;
+ groupchat_nodes nodes[GROUP_CLOSE_CONNECTIONS];
+ //uint8_t client_id[crypto_box_PUBLICKEYBYTES];
+
+} sendnodes_data;
+
+/* Compares client_id1 and client_id2 with client_id
+ * return 0 if both are same distance
+ * return 1 if client_id1 is closer
+ * return 2 if client_id2 is closer
+ */
+static int id_closest(uint8_t *id, uint8_t *id1, uint8_t *id2)
+{
+ size_t i;
+ uint8_t distance1, distance2;
+
+ for (i = 0; i < CLIENT_ID_SIZE; ++i) {
+
+ distance1 = abs(((int8_t *)id)[i] ^ ((int8_t *)id1)[i]);
+ distance2 = abs(((int8_t *)id)[i] ^ ((int8_t *)id2)[i]);
+
+ if (distance1 < distance2)
+ return 1;
+
+ if (distance1 > distance2)
+ return 2;
+ }
+
+ return 0;
+}
+
+
+/*
+ * check if peer with client_id is in peer array.
+ *
+ * return peer number if peer is in chat.
+ * return -1 if peer is not in chat.
+ *
+ * TODO: make this more efficient.
+ */
+
+static int peer_in_chat(Group_Chat *chat, uint8_t *client_id)
+{
+ uint32_t i;
+
+ for (i = 0; i < chat->numpeers; ++i) {
+ /* Equal */
+ if (memcmp(chat->group[i].client_id, client_id, crypto_box_PUBLICKEYBYTES) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+#define BAD_NODE_TIMEOUT 30
+
+/*
+ * Check if peer is closer to us that the other peers in the list and if the peer is in the list.
+ * Return the number of peers it is closer to if it is not in the closelist.
+ * Return -1 if the peer is in the closelist.
+ */
+
+static int peer_okping(Group_Chat *chat, uint8_t *client_id)
+{
+ uint32_t i, j = 0;
+ uint64_t temp_time = unix_time();
+
+ for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
+ if (chat->close[i].last_recv + BAD_NODE_TIMEOUT < temp_time) {
+ ++j;
+ continue;
+ }
+
+ /* Equal */
+ if (memcmp(chat->close[i].client_id, client_id, crypto_box_PUBLICKEYBYTES) == 0)
+ return -1;
+
+ if (id_closest(chat->self_public_key, chat->close[i].client_id, client_id) == 2)
+ ++j;
+ }
+
+ return j;
+}
+
+
+
+/* Attempt to add a peer to the close list.
+ * Update last_recv if it is in list.
+ * Attempt to add it to list if it is not.
+ *
+ * Return 0 if success.
+ * Return -1 if peer was not put in list/updated.
+ */
+static int add_closepeer(Group_Chat *chat, uint8_t *client_id, IP_Port ip_port)
+{
+ uint32_t i;
+ uint64_t temp_time = unix_time();
+
+ for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Check if node is already in list, if it is update its last_recv */
+ if (memcmp(chat->close[i].client_id, client_id, crypto_box_PUBLICKEYBYTES) == 0) {
+ chat->close[i].last_recv = temp_time;
+ return 0;
+ }
+ }
+
+ for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Try replacing bad nodes first */
+ if (chat->close[i].last_recv + BAD_NODE_TIMEOUT < temp_time) {
+ memcpy(chat->close[i].client_id, client_id, crypto_box_PUBLICKEYBYTES);
+ chat->close[i].ip_port = ip_port;
+ chat->close[i].last_recv = temp_time;
+ return 0;
+ }
+ }
+
+ for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Replace nodes if given one is closer. */
+ if (id_closest(chat->self_public_key, chat->close[i].client_id, client_id) == 2) {
+ memcpy(chat->close[i].client_id, client_id, crypto_box_PUBLICKEYBYTES);
+ chat->close[i].ip_port = ip_port;
+ chat->close[i].last_recv = temp_time;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int send_groupchatpacket(Group_Chat *chat, IP_Port ip_port, uint8_t *public_key, uint8_t *data, uint32_t length,
+ uint8_t request_id)
+{
+ if (memcmp(chat->self_public_key, public_key, crypto_box_PUBLICKEYBYTES) == 0)
+ return -1;
+
+ uint8_t packet[MAX_DATA_SIZE];
+ int len = create_request(chat->self_public_key, chat->self_secret_key, packet, public_key, data, length, request_id);
+ packet[0] = 48;
+
+ if (len == -1)
+ return -1;
+
+ if (sendpacket(chat->net->sock, ip_port, packet, len) == len)
+ return 0;
+
+ return -1;
+
+}
+
+/*
+ * Send data to all peers in close peer list.
+ *
+ * return the number of peers the packet was sent to.
+ */
+static uint8_t sendto_allpeers(Group_Chat *chat, uint8_t *data, uint16_t length, uint8_t request_id)
+{
+ uint16_t sent = 0;
+ uint32_t i;
+ uint64_t temp_time = unix_time();
+
+ for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
+ if (chat->close[i].ip_port.ip.uint32 != 0 && chat->close[i].last_recv > temp_time + BAD_NODE_TIMEOUT) {
+ if (send_groupchatpacket(chat, chat->close[i].ip_port, chat->close[i].client_id, data, length, request_id) == 0)
+ ++sent;
+ }
+ }
+
+ return sent;
+}
+
+
+/*
+ * Add a peer to the group chat.
+ *
+ * return peernum if success or peer already in chat.
+ * return -1 if error.
+ */
+static int addpeer(Group_Chat *chat, uint8_t *client_id)
+{
+ int peernum = peer_in_chat(chat, client_id);
+
+ if (peernum != -1)
+ return peernum;
+
+ Group_Peer *temp;
+ temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers + 1));
+ memset(&(temp[chat->numpeers]), 0, sizeof(Group_Peer));
+
+ if (temp == NULL)
+ return -1;
+
+ chat->group = temp;
+ memcpy(chat->group[chat->numpeers].client_id, client_id, crypto_box_PUBLICKEYBYTES);
+ ++chat->numpeers;
+ return (chat->numpeers - 1);
+}
+
+/*
+ * Delete a peer to the group chat.
+ *
+ * return 0 if success
+ * return -1 if error.
+ */
+static int delpeer(Group_Chat *chat, uint8_t *client_id)
+{
+ uint32_t i;
+ Group_Peer *temp;
+
+ for (i = 0; i < chat->numpeers; ++i) {
+ /* Equal */
+ if (memcmp(chat->group[i].client_id, client_id, crypto_box_PUBLICKEYBYTES) == 0) {
+ --chat->numpeers;
+
+ if (chat->numpeers != i) {
+ memcpy( chat->group[i].client_id,
+ chat->group[chat->numpeers].client_id,
+ crypto_box_PUBLICKEYBYTES );
+ }
+
+ temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers));
+
+ if (temp == NULL)
+ return -1;
+
+ chat->group = temp;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+/* min time between pings sent to one peer in seconds */
+#define PING_TIMEOUT 5
+static int send_getnodes(Group_Chat *chat, IP_Port ip_port, int peernum)
+{
+ if (peernum < 0 || peernum >= chat->numpeers)
+ return -1;
+
+ uint64_t temp_time = unix_time();
+
+ getnodes_data contents;
+
+ if (chat->group[peernum].last_pinged + PING_TIMEOUT > temp_time)
+ return -1;
+
+ contents.pingid = ((uint64_t)random_int() << 32) + random_int();
+ chat->group[peernum].last_pinged = temp_time;
+ chat->group[peernum].pingid = contents.pingid;
+ return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents, sizeof(contents), 48);
+}
+
+static int send_sendnodes(Group_Chat *chat, IP_Port ip_port, int peernum, uint64_t pingid)
+{
+ if (peernum < 0 || peernum >= chat->numpeers)
+ return -1;
+
+ sendnodes_data contents;
+ contents.pingid = pingid;
+ uint32_t i, j = 0;
+ uint64_t temp_time = unix_time();
+
+ for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
+ if (chat->close[i].last_recv + BAD_NODE_TIMEOUT > temp_time) {
+ memcpy(contents.nodes[j].client_id, chat->close[i].client_id, crypto_box_PUBLICKEYBYTES);
+ contents.nodes[j].ip_port = chat->close[i].ip_port;
+ ++j;
+ }
+ }
+
+ return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents,
+ sizeof(contents.pingid) + sizeof(groupchat_nodes) * j, 49);
+}
+
+static int handle_getnodes(Group_Chat *chat, IP_Port source, int peernum, uint8_t *data, uint32_t len)
+{
+ if (len != sizeof(getnodes_data))
+ return 1;
+
+ if (peernum < 0 || peernum >= chat->numpeers)
+ return 1;
+
+ getnodes_data contents;
+ memcpy(&contents, data, sizeof(contents));
+ send_sendnodes(chat, source, peernum, contents.pingid);
+
+ if (peer_okping(chat, chat->group[peernum].client_id) > 0)
+ send_getnodes(chat, source, peernum);
+
+ return 0;
+}
+
+static int handle_sendnodes(Group_Chat *chat, IP_Port source, int peernum, uint8_t *data, uint32_t len)
+{
+ if (peernum < 0 || peernum >= chat->numpeers)
+ return 1;
+
+ if (len > sizeof(sendnodes_data) || len < sizeof(uint64_t))
+ return 1;
+
+ if ((len - sizeof(uint64_t)) % sizeof(groupchat_nodes) != 0)
+ return 1;
+
+ if (chat->group[peernum].last_pinged + PING_TIMEOUT < unix_time())
+ return 1;
+
+ sendnodes_data contents;
+ memcpy(&contents, data, len);
+
+ if (contents.pingid != chat->group[peernum].pingid)
+ return 1;
+
+ uint16_t numnodes = (len - sizeof(contents.pingid)) / sizeof(groupchat_nodes);
+ uint32_t i;
+
+ for (i = 0; i < numnodes; ++i) {
+ if (peer_okping(chat, contents.nodes[i].client_id) > 0) {
+ int peern = peer_in_chat(chat, contents.nodes[i].client_id);
+
+ if (peern == -1) { /*NOTE: This is just for testing and will be removed later.*/
+ peern = addpeer(chat, contents.nodes[i].client_id);
+ }
+
+ if (peern == -1)
+ continue;
+
+ send_getnodes(chat, contents.nodes[i].ip_port, peern);
+ }
+ }
+
+ add_closepeer(chat, chat->group[peernum].client_id, source);
+ return 0;
+}
+#define GROUP_DATA_MIN_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + 1)
+static int handle_data(Group_Chat *chat, uint8_t *data, uint32_t len)
+{
+ if (len < GROUP_DATA_MIN_SIZE)
+ return 1;
+
+//TODO:
+ int peernum = peer_in_chat(chat, data);
+
+ if (peernum == -1) { /*NOTE: This is just for testing and will be removed later.*/
+ peernum = addpeer(chat, data);
+ }
+
+ if (peernum == -1)
+ return 1;
+
+ uint32_t message_num;
+ memcpy(&message_num, data + crypto_box_PUBLICKEYBYTES, sizeof(uint32_t));
+ message_num = ntohl(message_num);
+
+ if (message_num - chat->group[peernum].last_message_number > 64 ||
+ message_num == chat->group[peernum].last_message_number)
+ return 1;
+
+ chat->group[peernum].last_message_number = message_num;
+
+ int handled = 0;
+
+ if (data[crypto_box_PUBLICKEYBYTES + sizeof(message_num)] == 64
+ && chat->group_message != NULL) { /* If message is chat message */
+ (*chat->group_message)(chat, peernum, data + GROUP_DATA_MIN_SIZE, len - 1, chat->group_message_userdata);
+ handled = 1;
+ }
+
+ if (handled == 1) {
+ sendto_allpeers(chat, data, len, 50);
+ return 0;
+ }
+
+ return 1;
+}
+
+static uint8_t send_data(Group_Chat *chat, uint8_t *data, uint32_t len, uint8_t message_id)
+{
+ if (len + GROUP_DATA_MIN_SIZE > MAX_DATA_SIZE) /*NOTE: not the real maximum len.*/
+ return 1;
+
+ uint8_t packet[MAX_DATA_SIZE];
+ uint32_t message_num = htonl(chat->message_number);
+//TODO
+ memcpy(packet, chat->self_public_key, crypto_box_PUBLICKEYBYTES);
+ memcpy(packet + crypto_box_PUBLICKEYBYTES, &message_num, sizeof(message_num));
+ packet[crypto_box_PUBLICKEYBYTES + sizeof(message_num)] = message_id;
+ return sendto_allpeers(chat, packet, len + GROUP_DATA_MIN_SIZE, 50);
+}
+/*
+ * Handle get nodes group packet.
+ *
+ * return 0 if handled correctly.
+ * return 1 if error.
+ */
+
+int handle_groupchatpacket(Group_Chat *chat, IP_Port source, uint8_t *packet, uint32_t length)
+{
+ if (length > MAX_DATA_SIZE)
+ return 1;
+
+ uint8_t public_key[crypto_box_PUBLICKEYBYTES];
+ uint8_t data[MAX_DATA_SIZE];
+ uint8_t number;
+ int len = handle_request(chat->self_public_key, chat->self_secret_key, public_key, data, &number, packet, length);
+
+ if (len <= 0)
+ return 1;
+
+ if (memcmp(chat->self_public_key, public_key, crypto_box_PUBLICKEYBYTES) == 0)
+ return 1;
+
+ int peernum = peer_in_chat(chat, public_key);
+
+ if (peernum == -1)/*NOTE: This is just for testing and will be removed later.*/
+ peernum = addpeer(chat, public_key);
+
+ if (peernum == -1)
+ return 1;
+
+ switch (number) {
+ case 48:
+ return handle_getnodes(chat, source, peernum, data, len);
+
+ case 49:
+ return handle_sendnodes(chat, source, peernum, data, len);
+
+ case 50:
+ return handle_data(chat, data, len);
+
+ default:
+ return 1;
+ }
+
+ return 1;
+}
+
+uint32_t group_sendmessage(Group_Chat *chat, uint8_t *message, uint32_t length)
+{
+ return send_data(chat, message, length, 64); //TODO: better return values?
+}
+
+void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat, int, uint8_t *, uint16_t, void *),
+ void *userdata)
+{
+ chat->group_message = function;
+ chat->group_message_userdata = userdata;
+}
+
+Group_Chat *new_groupchat(Networking_Core *net)
+{
+ if (net == 0)
+ return 0;
+
+ Group_Chat *chat = calloc(1, sizeof(Group_Chat));
+ chat->net = net;
+ crypto_box_keypair(chat->self_public_key, chat->self_secret_key);
+ return chat;
+}
+
+#define NODE_PING_INTERVAL 10
+
+static void ping_close(Group_Chat *chat)
+{
+ uint32_t i;
+ uint64_t temp_time = unix_time();
+
+ for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
+ if (chat->close[i].last_recv < temp_time + BAD_NODE_TIMEOUT) {
+ int peernum = peer_in_chat(chat, chat->close[i].client_id);
+
+ if (peernum == -1)
+ continue;
+
+ if (chat->group[peernum].last_pinged + NODE_PING_INTERVAL < temp_time)
+ send_getnodes(chat, chat->close[i].ip_port, peernum);
+ }
+ }
+}
+
+void do_groupchat(Group_Chat *chat)
+{
+ ping_close(chat);
+}
+
+void kill_groupchat(Group_Chat *chat)
+{
+ free(chat->group);
+ free(chat);
+}
+
+void chat_bootstrap(Group_Chat *chat, IP_Port ip_port, uint8_t *client_id)
+{
+ send_getnodes(chat, ip_port, addpeer(chat, client_id));
+}
diff --git a/testing/experiment/group_chats.h b/testing/experiment/group_chats.h
new file mode 100644
index 00000000..42ea3e08
--- /dev/null
+++ b/testing/experiment/group_chats.h
@@ -0,0 +1,113 @@
+/* group_chats.h
+ *
+ * An implementation of massive text only group chats.
+ *
+ *
+ * Copyright (C) 2013 Tox project All Rights Reserved.
+ *
+ * This file is part of Tox.
+ *
+ * Tox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Tox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Tox. If not, see .
+ *
+ */
+
+#ifndef GROUP_CHATS_H
+#define GROUP_CHATS_H
+
+#include "../../toxcore/net_crypto.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ uint8_t client_id[crypto_box_PUBLICKEYBYTES];
+ uint64_t last_recv;
+ uint64_t pingid;
+ uint64_t last_pinged;
+ uint32_t last_message_number;
+} Group_Peer;
+
+typedef struct {
+ uint8_t client_id[crypto_box_PUBLICKEYBYTES];
+ IP_Port ip_port;
+ uint64_t last_recv;
+
+} Group_Close;
+
+#define GROUP_CLOSE_CONNECTIONS 6
+
+typedef struct Group_Chat {
+ Networking_Core *net;
+ uint8_t self_public_key[crypto_box_PUBLICKEYBYTES];
+ uint8_t self_secret_key[crypto_box_SECRETKEYBYTES];
+
+ Group_Peer *group;
+ Group_Close close[GROUP_CLOSE_CONNECTIONS];
+ uint32_t numpeers;
+
+ uint32_t message_number;
+ void (*group_message)(struct Group_Chat *m, int, uint8_t *, uint16_t, void *);
+ void *group_message_userdata;
+
+} Group_Chat;
+
+/*
+ * Set callback function for chat messages.
+ *
+ * format of function is: function(Group_Chat *chat, peer number, message, message length, userdata)
+ */
+
+void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat, int, uint8_t *, uint16_t, void *),
+ void *userdata);
+
+/*
+ * Send a message to the group.
+ *
+ */
+uint32_t group_sendmessage(Group_Chat *chat, uint8_t *message, uint32_t length);
+
+/* Create a new group chat.
+ *
+ * Returns a new group chat instance if success.
+ *
+ * Returns a NULL pointer if fail.
+ */
+Group_Chat *new_groupchat(Networking_Core *net);
+
+
+/* Kill a group chat
+ *
+ * Frees the memory and everything.
+ */
+void kill_groupchat(Group_Chat *chat);
+
+/*
+ * This is the main loop.
+ */
+void do_groupchat(Group_Chat *chat);
+
+/* if we receive a group chat packet we call this function so it can be handled.
+ return 0 if packet is handled correctly.
+ return 1 if it didn't handle the packet or if the packet was shit. */
+int handle_groupchatpacket(Group_Chat *chat, IP_Port source, uint8_t *packet, uint32_t length);
+
+
+void chat_bootstrap(Group_Chat *chat, IP_Port ip_port, uint8_t *client_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/testing/experiment/group_chats_test.c b/testing/experiment/group_chats_test.c
new file mode 100644
index 00000000..8ef5b10e
--- /dev/null
+++ b/testing/experiment/group_chats_test.c
@@ -0,0 +1,98 @@
+#include "group_chats.h"
+#define NUM_CHATS 8
+
+#ifdef WIN32
+#define c_sleep(x) Sleep(1*x)
+#else
+#define c_sleep(x) usleep(1000*x)
+#endif
+Group_Chat *chats[NUM_CHATS];
+
+void print_close(Group_Close *close)
+{
+ uint32_t i, j;
+ IP_Port p_ip;
+ printf("___________________CLOSE________________________________\n");
+
+ for (i = 0; i < GROUP_CLOSE_CONNECTIONS; i++) {
+ printf("ClientID: ");
+
+ for (j = 0; j < CLIENT_ID_SIZE; j++) {
+ printf("%02hhX", close[i].client_id[j]);
+ }
+
+ p_ip = close[i].ip_port;
+ printf("\nIP: %u.%u.%u.%u Port: %u", p_ip.ip.uint8[0], p_ip.ip.uint8[1], p_ip.ip.uint8[2], p_ip.ip.uint8[3],
+ ntohs(p_ip.port));
+ printf("\nTimestamp: %llu", (long long unsigned int) close[i].last_recv);
+ printf("\n");
+ }
+}
+
+void print_group(Group_Chat *chat)
+{
+ uint32_t i, j;
+ printf("-----------------\nClientID: ");
+
+ for (j = 0; j < CLIENT_ID_SIZE; j++) {
+ printf("%02hhX", chat->self_public_key[j]);
+ }
+
+ printf("\n___________________GROUP________________________________\n");
+
+ for (i = 0; i < chat->numpeers; i++) {
+ printf("ClientID: ");
+
+ for (j = 0; j < CLIENT_ID_SIZE; j++) {
+ printf("%02hhX", chat->group[i].client_id[j]);
+ }
+
+ printf("\nTimestamp: %llu", (long long unsigned int) chat->group[i].last_recv);
+ printf("\nlast_pinged: %llu", (long long unsigned int) chat->group[i].last_pinged);
+ printf("\npingid: %llu", (long long unsigned int) chat->group[i].pingid);
+ printf("\n");
+ }
+}
+
+int main()
+{
+ IP ip;
+ ip.uint32 = 0;
+ uint32_t i;
+
+
+ for (i = 0; i < NUM_CHATS; ++i) {
+ chats[i] = new_groupchat(new_networking(ip, 12745));
+
+ if (chats[i] == 0)
+ exit(1);
+
+ networking_registerhandler(chats[i]->net, 48, &handle_groupchatpacket, chats[i]);
+ }
+
+ printf("ok\n");
+ IP_Port ip_port;
+ ip_port.ip.uint32 = 0;
+ ip_port.ip.uint8[0] = 127;
+ ip_port.ip.uint8[3] = 1;
+ ip_port.port = htons(12745);
+
+ for (i = 0; i < NUM_CHATS; ++i) {
+ chat_bootstrap(chats[i], ip_port, chats[0]->self_public_key);
+ printf("%u\n", i);
+ }
+
+ while (1) {
+ for (i = 0; i < NUM_CHATS; ++i) {
+ networking_poll(chats[i]->net);
+ do_groupchat(chats[i]);
+ printf("%u\n", chats[i]->numpeers);
+ print_close(chats[i]->close);
+ print_group(chats[i]);
+ }
+
+ c_sleep(100);
+ }
+
+ return 0;
+}
diff --git a/toxcore/DHT.c b/toxcore/DHT.c
index 749eb78c..4807c369 100644
--- a/toxcore/DHT.c
+++ b/toxcore/DHT.c
@@ -483,12 +483,12 @@ static int getnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *cli
{
/* Check if packet is going to be sent to ourself. */
if (id_equal(public_key, dht->c->self_public_key) || is_gettingnodes(dht, ip_port, 0))
- return 1;
+ return -1;
uint64_t ping_id = add_gettingnodes(dht, ip_port);
if (ping_id == 0)
- return 1;
+ return -1;
uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING];
uint8_t plain[sizeof(ping_id) + CLIENT_ID_SIZE];
@@ -522,7 +522,7 @@ static int sendnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *cl
{
/* Check if packet is going to be sent to ourself. */
if (id_equal(public_key, dht->c->self_public_key))
- return 1;
+ return -1;
uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id)
+ sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING];
diff --git a/toxcore/net_crypto.c b/toxcore/net_crypto.c
index 8c179eb3..b20917cf 100644
--- a/toxcore/net_crypto.c
+++ b/toxcore/net_crypto.c
@@ -254,30 +254,31 @@ int create_request(uint8_t *send_public_key, uint8_t *send_secret_key, uint8_t *
*
* return -1 if not valid request.
*/
-static int handle_request(Net_Crypto *c, uint8_t *public_key, uint8_t *data, uint8_t *request_id, uint8_t *packet,
- uint16_t length)
+int handle_request(uint8_t *self_public_key, uint8_t *self_secret_key, uint8_t *public_key, uint8_t *data,
+ uint8_t *request_id, uint8_t *packet, uint16_t length)
{
-
if (length > crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING &&
- length <= MAX_DATA_SIZE + ENCRYPTION_PADDING &&
- memcmp(packet + 1, c->self_public_key, crypto_box_PUBLICKEYBYTES) == 0) {
- memcpy(public_key, packet + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
- uint8_t nonce[crypto_box_NONCEBYTES];
- uint8_t temp[MAX_DATA_SIZE];
- memcpy(nonce, packet + 1 + crypto_box_PUBLICKEYBYTES * 2, crypto_box_NONCEBYTES);
- int len1 = decrypt_data(public_key, c->self_secret_key, nonce,
- packet + 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES,
- length - (crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1), temp);
+ length <= MAX_DATA_SIZE) {
+ if (memcmp(packet + 1, self_public_key, crypto_box_PUBLICKEYBYTES) == 0) {
+ memcpy(public_key, packet + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
+ uint8_t nonce[crypto_box_NONCEBYTES];
+ uint8_t temp[MAX_DATA_SIZE];
+ memcpy(nonce, packet + 1 + crypto_box_PUBLICKEYBYTES * 2, crypto_box_NONCEBYTES);
+ int len1 = decrypt_data(public_key, self_secret_key, nonce,
+ packet + 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES,
+ length - (crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1), temp);
- if (len1 == -1 || len1 == 0)
- return -1;
+ if (len1 == -1 || len1 == 0)
+ return -1;
- request_id[0] = temp[0];
- --len1;
- memcpy(data, temp + 1, len1);
- return len1;
- } else
- return -1;
+ request_id[0] = temp[0];
+ --len1;
+ memcpy(data, temp + 1, len1);
+ return len1;
+ }
+ }
+
+ return -1;
}
void cryptopacket_registerhandler(Net_Crypto *c, uint8_t byte, cryptopacket_handler_callback cb, void *object)
@@ -299,7 +300,7 @@ static int cryptopacket_handle(void *object, IP_Port source, uint8_t *packet, ui
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
uint8_t data[MAX_DATA_SIZE];
uint8_t number;
- int len = handle_request(dht->c, public_key, data, &number, packet, length);
+ int len = handle_request(dht->c->self_public_key, dht->c->self_secret_key, public_key, data, &number, packet, length);
if (len == -1 || len == 0)
return 1;
diff --git a/toxcore/net_crypto.h b/toxcore/net_crypto.h
index 030cc678..e5dfcae0 100644
--- a/toxcore/net_crypto.h
+++ b/toxcore/net_crypto.h
@@ -139,6 +139,12 @@ int write_cryptpacket(Net_Crypto *c, int crypt_connection_id, uint8_t *data, uin
int create_request(uint8_t *send_public_key, uint8_t *send_secret_key, uint8_t *packet, uint8_t *recv_public_key,
uint8_t *data, uint32_t length, uint8_t request_id);
+/* puts the senders public key in the request in public_key, the data from the request
+ in data if a friend or ping request was sent to us and returns the length of the data.
+ packet is the request packet and length is its length
+ return -1 if not valid request. */
+int handle_request(uint8_t *self_public_key, uint8_t *self_secret_key, uint8_t *public_key, uint8_t *data,
+ uint8_t *request_id, uint8_t *packet, uint16_t length);
/* Function to call when request beginning with byte is received. */
void cryptopacket_registerhandler(Net_Crypto *c, uint8_t byte, cryptopacket_handler_callback cb, void *object);