toxcore/testing/nTox.c

1449 lines
44 KiB
C
Raw Normal View History

/* nTox.c
*
* Textual frontend for Tox.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
2013-08-27 19:06:19 +08:00
#ifdef HAVE_CONFIG_H
#include "config.h"
2013-08-27 19:06:19 +08:00
#endif
2014-01-20 07:18:25 +08:00
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
#define _WIN32_WINNT 0x501
#include <winsock2.h>
#include <ws2tcpip.h>
2013-08-27 19:06:19 +08:00
#else
#include <arpa/inet.h>
#include <netdb.h>
2016-09-01 07:33:20 +08:00
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
2013-08-27 19:06:19 +08:00
#endif
2014-06-08 22:08:52 +08:00
#include <sys/select.h>
2013-08-27 19:06:19 +08:00
#include "misc_tools.c"
2016-09-01 07:33:20 +08:00
#include "nTox.h"
2013-07-28 00:17:31 +08:00
2016-09-01 07:33:20 +08:00
#include <locale.h>
#include <stdio.h>
#include <time.h>
2013-07-31 14:15:01 +08:00
2014-01-20 07:18:25 +08:00
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
2013-07-17 07:02:44 +08:00
#define c_sleep(x) Sleep(1*x)
#else
#include <unistd.h>
#define c_sleep(x) usleep(1000*x)
#endif
static void new_lines(char const *line);
static void do_refresh(void);
static char lines[HISTORY][STRING_LENGTH];
static uint8_t flag[HISTORY];
static char input_line[STRING_LENGTH];
/* wrap: continuation mark */
enum { wrap_cont_len = 3 };
static const char wrap_cont_str[] = "\n+ ";
#define STRING_LENGTH_WRAPPED (STRING_LENGTH + 16 * (wrap_cont_len + 1))
/* documented: fdmnlsahxgiztq(c[rfg]) */
/* undocumented: d (tox_do()) */
/* 251+1 characters */
static const char *help_main =
"[i] Available main commands:\n+ "
"/x (to print one's own id)|"
"/s status (to change status, e.g. AFK)|"
"/n nick (to change your nickname)|"
"/q (to quit)|"
"/cr (to reset conversation)|"
"/h friend (for friend related commands)|"
"/h group (for group related commands)";
/* 190+1 characters */
static const char *help_friend1 =
"[i] Available friend commands (1/2):\n+ "
"/l list (to list friends)|"
"/r friend no. (to remove from the friend list)|"
"/f ID (to send a friend request)|"
"/a request no. (to accept a friend request)";
/* 187+1 characters */
static const char *help_friend2 =
"[i] Available friend commands (2/2):\n+ "
"/m friend no. message (to send a message)|"
"/t friend no. filename (to send a file to a friend)|"
"/cf friend no. (to talk to that friend per default)";
/* 253+1 characters */
static const char *help_group =
"[i] Available group commands:\n+ "
"/g (to create a group)|"
"/i friend no. group no. (to invite a friend to a group)|"
"/z group no. message (to send a message to a group)|"
"/p group no. (to list a group's peers)|"
"/cg group no. (to talk to that group per default)";
static int x, y;
static int conversation_default = 0;
typedef struct {
uint8_t id[TOX_PUBLIC_KEY_SIZE];
uint8_t accepted;
} Friend_request;
static Friend_request pending_requests[256];
static uint8_t num_requests = 0;
2015-03-11 06:49:06 +08:00
#define NUM_FILE_SENDERS 64
2013-10-01 01:13:49 +08:00
typedef struct {
FILE *file;
2015-03-11 06:49:06 +08:00
uint32_t friendnum;
uint32_t filenumber;
2013-10-01 01:13:49 +08:00
} File_Sender;
static File_Sender file_senders[NUM_FILE_SENDERS];
static uint8_t numfilesenders;
2013-10-01 01:13:49 +08:00
static void tox_file_chunk_request(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position,
size_t length,
void *user_data)
2013-10-01 01:13:49 +08:00
{
2015-03-11 06:49:06 +08:00
unsigned int i;
2013-10-01 01:13:49 +08:00
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
2015-03-11 06:49:06 +08:00
/* This is slow */
if (file_senders[i].file && file_senders[i].friendnum == friend_number && file_senders[i].filenumber == file_number) {
if (length == 0) {
2013-10-01 01:13:49 +08:00
fclose(file_senders[i].file);
file_senders[i].file = 0;
char msg[512];
sprintf(msg, "[t] %u file transfer: %u completed", file_senders[i].friendnum, file_senders[i].filenumber);
new_lines(msg);
2013-10-01 01:13:49 +08:00
break;
}
2015-03-11 06:49:06 +08:00
fseek(file_senders[i].file, position, SEEK_SET);
uint8_t data[length];
int len = fread(data, 1, length, file_senders[i].file);
tox_file_send_chunk(tox, friend_number, file_number, position, data, len, 0);
2015-03-11 06:49:06 +08:00
break;
2013-10-01 01:13:49 +08:00
}
}
}
2015-03-11 06:49:06 +08:00
static uint32_t add_filesender(Tox *m, uint16_t friendnum, char *filename)
2013-10-01 01:13:49 +08:00
{
2015-03-11 06:49:06 +08:00
FILE *tempfile = fopen(filename, "rb");
2013-10-01 01:13:49 +08:00
2016-09-01 02:12:19 +08:00
if (tempfile == 0) {
2013-10-01 01:13:49 +08:00
return -1;
2016-09-01 02:12:19 +08:00
}
2013-10-01 01:13:49 +08:00
fseek(tempfile, 0, SEEK_END);
uint64_t filesize = ftell(tempfile);
fseek(tempfile, 0, SEEK_SET);
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, filesize, 0, (uint8_t *)filename,
strlen(filename), 0);
2016-09-01 02:12:19 +08:00
if (filenum == -1) {
return -1;
2016-09-01 02:12:19 +08:00
}
file_senders[numfilesenders].file = tempfile;
2013-10-01 01:13:49 +08:00
file_senders[numfilesenders].friendnum = friendnum;
file_senders[numfilesenders].filenumber = filenum;
2013-10-01 01:13:49 +08:00
++numfilesenders;
return filenum;
2013-10-01 01:13:49 +08:00
}
2013-08-27 19:06:19 +08:00
#define FRADDR_TOSTR_CHUNK_LEN 8
#define FRADDR_TOSTR_BUFSIZE (TOX_ADDRESS_SIZE * 2 + TOX_ADDRESS_SIZE / FRADDR_TOSTR_CHUNK_LEN + 1)
static void fraddr_to_str(uint8_t *id_bin, char *id_str)
{
uint32_t i, delta = 0, pos_extra = 0, sum_extra = 0;
for (i = 0; i < TOX_ADDRESS_SIZE; i++) {
sprintf(&id_str[2 * i + delta], "%02hhX", id_bin[i]);
2016-09-01 02:12:19 +08:00
if ((i + 1) == TOX_PUBLIC_KEY_SIZE) {
pos_extra = 2 * (i + 1) + delta;
2016-09-01 02:12:19 +08:00
}
2016-09-01 02:12:19 +08:00
if (i >= TOX_PUBLIC_KEY_SIZE) {
sum_extra |= id_bin[i];
2016-09-01 02:12:19 +08:00
}
if (!((i + 1) % FRADDR_TOSTR_CHUNK_LEN)) {
id_str[2 * (i + 1) + delta] = ' ';
delta++;
}
}
id_str[2 * i + delta] = 0;
2016-09-01 02:12:19 +08:00
if (!sum_extra) {
id_str[pos_extra] = 0;
2016-09-01 02:12:19 +08:00
}
}
static void get_id(Tox *m, char *data)
2013-07-14 01:31:16 +08:00
{
2013-08-14 16:44:25 +08:00
sprintf(data, "[i] ID: ");
int offset = strlen(data);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(m, address);
fraddr_to_str(address, data + offset);
}
static int getfriendname_terminated(Tox *m, int friendnum, char *namebuf)
{
tox_friend_get_name(m, friendnum, (uint8_t *)namebuf, NULL);
int res = tox_friend_get_name_size(m, friendnum, NULL);
2016-09-01 02:12:19 +08:00
if (res >= 0) {
namebuf[res] = 0;
2016-09-01 02:12:19 +08:00
} else {
namebuf[0] = 0;
2016-09-01 02:12:19 +08:00
}
return res;
}
static void new_lines_mark(char const *line, uint8_t special)
{
int i = 0;
2013-08-17 01:11:09 +08:00
for (i = HISTORY - 1; i > 0; i--) {
2013-08-17 01:11:09 +08:00
strncpy(lines[i], lines[i - 1], STRING_LENGTH - 1);
flag[i] = flag[i - 1];
}
strncpy(lines[0], line, STRING_LENGTH - 1);
flag[i] = special;
2013-07-14 09:57:09 +08:00
do_refresh();
2013-07-14 01:31:16 +08:00
}
static void new_lines(char const *line)
{
new_lines_mark(line, 0);
}
static const char ptrn_friend[] = "[i] Friend %i: %s\n+ id: %s";
static const int id_str_len = TOX_ADDRESS_SIZE * 2 + 3;
static void print_friendlist(Tox *m)
{
new_lines("[i] Friend List:");
2013-08-17 01:11:09 +08:00
char name[TOX_MAX_NAME_LENGTH + 1];
uint8_t fraddr_bin[TOX_ADDRESS_SIZE];
char fraddr_str[FRADDR_TOSTR_BUFSIZE];
/* account for the longest name and the longest "base" string and number (int) and id_str */
char fstring[TOX_MAX_NAME_LENGTH + strlen(ptrn_friend) + 21 + id_str_len];
2013-12-20 01:36:51 +08:00
uint32_t i = 0;
while (getfriendname_terminated(m, i, name) != -1) {
2016-09-01 02:12:19 +08:00
if (tox_friend_get_public_key(m, i, fraddr_bin, NULL)) {
fraddr_to_str(fraddr_bin, fraddr_str);
2016-09-01 02:12:19 +08:00
} else {
sprintf(fraddr_str, "???");
2016-09-01 02:12:19 +08:00
}
if (strlen(name) <= 0) {
sprintf(fstring, ptrn_friend, i, "No name?", fraddr_str);
} else {
sprintf(fstring, ptrn_friend, i, (uint8_t *)name, fraddr_str);
}
2013-08-17 01:11:09 +08:00
i++;
new_lines(fstring);
}
2016-09-01 02:12:19 +08:00
if (i == 0) {
new_lines("+ no friends! D:");
2016-09-01 02:12:19 +08:00
}
}
static int fmtmsg_tm_mday = -1;
2013-08-17 01:11:09 +08:00
static void print_formatted_message(Tox *m, char *message, int friendnum, uint8_t outgoing)
{
char name[TOX_MAX_NAME_LENGTH + 1];
getfriendname_terminated(m, friendnum, name);
2013-08-17 01:11:09 +08:00
char msg[100 + strlen(message) + strlen(name) + 1];
2013-07-30 23:32:17 +08:00
time_t rawtime;
2013-08-17 01:11:09 +08:00
struct tm *timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
2013-08-17 01:11:09 +08:00
/* assume that printing the date once a day is enough */
if (fmtmsg_tm_mday != timeinfo->tm_mday) {
fmtmsg_tm_mday = timeinfo->tm_mday;
/* strftime(msg, 100, "Today is %a %b %d %Y.", timeinfo); */
/* %x is the locale's preferred date format */
strftime(msg, 100, "Today is %x.", timeinfo);
new_lines(msg);
}
char time[64];
/* strftime(time, 64, "%I:%M:%S %p", timeinfo); */
/* %X is the locale's preferred time format */
strftime(time, 64, "%X", timeinfo);
if (outgoing) {
/* tgt: friend */
sprintf(msg, "[%d] %s =>{%s} %s", friendnum, time, name, message);
} else {
/* src: friend */
sprintf(msg, "[%d] %s <%s>: %s", friendnum, time, name, message);
}
2013-08-17 01:11:09 +08:00
new_lines(msg);
2013-07-30 23:32:17 +08:00
}
/* forward declarations */
static int save_data(Tox *m);
static void print_groupchatpeers(Tox *m, int groupnumber);
static void line_eval(Tox *m, char *line)
2013-07-14 01:31:16 +08:00
{
if (line[0] == '/') {
2013-07-31 14:15:01 +08:00
char inpt_command = line[1];
2013-08-17 01:11:09 +08:00
char prompt[STRING_LENGTH + 2] = "> ";
int prompt_offset = 3;
2013-07-31 14:15:01 +08:00
strcat(prompt, line);
new_lines(prompt);
2013-08-17 01:11:09 +08:00
2013-07-31 14:15:01 +08:00
if (inpt_command == 'f') { // add friend command: /f ID
int i, delta = 0;
2013-07-14 01:31:16 +08:00
char temp_id[128];
2013-08-17 01:11:09 +08:00
for (i = 0; i < 128; i++) {
temp_id[i - delta] = line[i + prompt_offset];
2016-09-01 02:12:19 +08:00
if ((temp_id[i - delta] == ' ') || (temp_id[i - delta] == '+')) {
delta++;
2016-09-01 02:12:19 +08:00
}
}
unsigned char *bin_string = hex_string_to_bin(temp_id);
TOX_ERR_FRIEND_ADD error;
uint32_t num = tox_friend_add(m, bin_string, (const uint8_t *)"Install Gentoo", sizeof("Install Gentoo"), &error);
free(bin_string);
2013-07-14 01:31:16 +08:00
char numstring[100];
2013-08-17 01:11:09 +08:00
switch (error) {
case TOX_ERR_FRIEND_ADD_TOO_LONG:
2013-08-17 01:11:09 +08:00
sprintf(numstring, "[i] Message is too long.");
break;
case TOX_ERR_FRIEND_ADD_NO_MESSAGE:
2013-08-17 01:11:09 +08:00
sprintf(numstring, "[i] Please add a message to your request.");
break;
case TOX_ERR_FRIEND_ADD_OWN_KEY:
2013-08-17 01:11:09 +08:00
sprintf(numstring, "[i] That appears to be your own ID.");
break;
case TOX_ERR_FRIEND_ADD_ALREADY_SENT:
2013-08-17 01:11:09 +08:00
sprintf(numstring, "[i] Friend request already sent.");
break;
case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM:
sprintf(numstring, "[i] Address has a bad checksum.");
2013-08-17 01:11:09 +08:00
break;
case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM:
sprintf(numstring, "[i] New nospam set.");
break;
case TOX_ERR_FRIEND_ADD_MALLOC:
sprintf(numstring, "[i] malloc error.");
break;
2013-09-18 04:28:39 +08:00
case TOX_ERR_FRIEND_ADD_NULL:
sprintf(numstring, "[i] message was NULL.");
break;
case TOX_ERR_FRIEND_ADD_OK:
sprintf(numstring, "[i] Added friend as %d.", num);
save_data(m);
2013-08-17 01:11:09 +08:00
break;
}
2013-08-17 01:11:09 +08:00
2013-07-14 01:31:16 +08:00
new_lines(numstring);
2013-08-17 01:11:09 +08:00
} else if (inpt_command == 'd') {
Make self_connection_status callback stateless. **What are we doing?** We are moving towards stateless callbacks. This means that when registering a callback, you no longer pass a user data pointer. Instead, you pass a user data pointer to tox_iterate. This pointer is threaded through the code, passed to each callback. The callback can modify the data pointed at. An extra indirection will be needed if the pointer itself can change. **Why?** Currently, callbacks are registered with a user data pointer. This means the library has N pointers for N different callbacks. These pointers need to be managed by the client code. Managing the lifetime of the pointee can be difficult. In C++, it takes special effort to ensure that the lifetime of user data extends at least beyond the lifetime of the Tox instance. For other languages, the situation is much worse. Java and other garbage collected languages may move objects in memory, so the pointers are not stable. Tox4j goes through a lot of effort to make the Java/Scala user experience a pleasant one by keeping a global array of Tox+userdata on the C++ side, and communicating via protobufs. A Haskell FFI would have to do similarly complex tricks. Stateless callbacks ensure that a user data pointer only needs to live during a single function call. This means that the user code (or language runtime) can move the data around at will, as long as it sets the new location in the callback. **How?** We are doing this change one callback at a time. After each callback, we ensure that everything still works as expected. This means the toxcore change will require 15 Pull Requests.
2016-08-10 20:46:04 +08:00
tox_iterate(m, NULL);
2013-08-17 01:11:09 +08:00
} else if (inpt_command == 'm') { //message command: /m friendnumber messsage
2013-09-18 23:11:10 +08:00
char *posi[1];
int num = strtoul(line + prompt_offset, posi, 0);
if (**posi != 0) {
if (tox_friend_send_message(m, num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) *posi + 1, strlen(*posi + 1), NULL) < 1) {
2013-09-18 23:11:10 +08:00
char sss[256];
sprintf(sss, "[i] could not send message to friend num %u", num);
new_lines(sss);
2013-07-14 01:31:16 +08:00
} else {
print_formatted_message(m, *posi + 1, num, 1);
2013-07-14 01:31:16 +08:00
}
2016-09-01 02:12:19 +08:00
} else {
2013-09-18 23:11:10 +08:00
new_lines("Error, bad input.");
2016-09-01 02:12:19 +08:00
}
2013-08-17 01:11:09 +08:00
} else if (inpt_command == 'n') {
uint8_t name[TOX_MAX_NAME_LENGTH];
2013-09-16 16:37:22 +08:00
size_t i, len = strlen(line);
2013-08-17 01:11:09 +08:00
2013-07-31 14:15:01 +08:00
for (i = 3; i < len; i++) {
2016-09-01 02:12:19 +08:00
if (line[i] == 0 || line[i] == '\n') {
break;
}
2013-08-17 01:11:09 +08:00
name[i - 3] = line[i];
}
2013-08-17 01:11:09 +08:00
name[i - 3] = 0;
tox_self_set_name(m, name, i - 2, NULL);
char numstring[100];
2013-08-17 01:11:09 +08:00
sprintf(numstring, "[i] changed nick to %s", (char *)name);
new_lines(numstring);
2013-08-17 01:11:09 +08:00
} else if (inpt_command == 'l') {
print_friendlist(m);
2013-08-17 01:11:09 +08:00
} else if (inpt_command == 's') {
uint8_t status[TOX_MAX_STATUS_MESSAGE_LENGTH];
2013-09-16 16:37:22 +08:00
size_t i, len = strlen(line);
2013-08-17 01:11:09 +08:00
2013-07-31 14:15:01 +08:00
for (i = 3; i < len; i++) {
2016-09-01 02:12:19 +08:00
if (line[i] == 0 || line[i] == '\n') {
break;
}
2013-08-17 01:11:09 +08:00
status[i - 3] = line[i];
}
2013-08-17 01:11:09 +08:00
status[i - 3] = 0;
tox_self_set_status_message(m, status, strlen((char *)status), NULL);
char numstring[100];
2013-08-17 01:11:09 +08:00
sprintf(numstring, "[i] changed status to %s", (char *)status);
new_lines(numstring);
} else if (inpt_command == 'a') { // /a #: accept
uint8_t numf = atoi(line + 3);
char numchar[100];
2013-08-17 01:11:09 +08:00
2013-08-04 19:01:20 +08:00
if (numf >= num_requests || pending_requests[numf].accepted) {
2013-08-17 01:11:09 +08:00
sprintf(numchar, "[i] you either didn't receive that request or you already accepted it");
new_lines(numchar);
} else {
uint32_t num = tox_friend_add_norequest(m, pending_requests[numf].id, NULL);
2013-08-17 01:11:09 +08:00
if (num != UINT32_MAX) {
pending_requests[numf].accepted = 1;
sprintf(numchar, "[i] friend request %u accepted as friend no. %d", numf, num);
new_lines(numchar);
save_data(m);
} else {
sprintf(numchar, "[i] failed to add friend");
new_lines(numchar);
}
}
} else if (inpt_command == 'r') { // /r #: remove friend
uint8_t numf = atoi(line + 3);
if (!tox_friend_exists(m, numf)) {
char err[64];
sprintf(err, "You don't have a friend %i.", numf);
new_lines(err);
return;
}
char msg[128 + TOX_MAX_NAME_LENGTH];
char fname[TOX_MAX_NAME_LENGTH ];
getfriendname_terminated(m, numf, fname);
sprintf(msg, "Are you sure you want to delete friend %i: %s? (y/n)", numf, fname);
input_line[0] = 0;
new_lines(msg);
int c;
do {
c = getchar();
} while ((c != 'y') && (c != 'n') && (c != EOF));
if (c == 'y') {
int res = tox_friend_delete(m, numf, NULL);
2016-09-01 02:12:19 +08:00
if (res) {
sprintf(msg, "[i] [%i: %s] is no longer your friend", numf, fname);
2016-09-01 02:12:19 +08:00
} else {
sprintf(msg, "[i] failed to remove friend");
2016-09-01 02:12:19 +08:00
}
new_lines(msg);
}
2013-08-17 01:11:09 +08:00
} else if (inpt_command == 'h') { //help
if (line[2] == ' ') {
if (line[3] == 'f') {
new_lines_mark(help_friend1, 1);
new_lines_mark(help_friend2, 1);
return;
}
if (line[3] == 'g') {
new_lines_mark(help_group, 1);
return;
}
}
new_lines_mark(help_main, 1);
} else if (inpt_command == 'x') { //info
2013-08-17 01:11:09 +08:00
char idstring[200];
get_id(m, idstring);
new_lines(idstring);
} else if (inpt_command == 'g') { //create new group chat
char msg[256];
sprintf(msg, "[g] Created new group chat with number: %u", tox_conference_new(m, NULL));
new_lines(msg);
} else if (inpt_command == 'i') { //invite friendnum to groupnum
char *posi[1];
int friendnumber = strtoul(line + prompt_offset, posi, 0);
int groupnumber = strtoul(*posi + 1, NULL, 0);
char msg[256];
sprintf(msg, "[g] Invited friend number %u to group number %u, returned: %u (0 means success)", friendnumber,
groupnumber, tox_conference_invite(m, friendnumber, groupnumber, NULL));
new_lines(msg);
} else if (inpt_command == 'z') { //send message to groupnum
char *posi[1];
int groupnumber = strtoul(line + prompt_offset, posi, 0);
if (**posi != 0) {
int res = tox_conference_send_message(m, groupnumber, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *)*posi + 1, strlen(*posi + 1),
NULL);
if (res == 0) {
char msg[32 + STRING_LENGTH];
sprintf(msg, "[g] #%u: YOU: %s", groupnumber, *posi + 1);
new_lines(msg);
} else {
char msg[128];
sprintf(msg, "[i] could not send message to group no. %u: %i", groupnumber, res);
new_lines(msg);
}
}
2013-10-01 01:13:49 +08:00
} else if (inpt_command == 't') {
char *posi[1];
int friendnum = strtoul(line + prompt_offset, posi, 0);
if (**posi != 0) {
char msg[512];
sprintf(msg, "[t] Sending file %s to friendnum %u filenumber is %i (-1 means failure)", *posi + 1, friendnum,
2013-10-01 01:13:49 +08:00
add_filesender(m, friendnum, *posi + 1));
new_lines(msg);
}
} else if (inpt_command == 'q') { //exit
save_data(m);
2013-07-24 00:11:07 +08:00
endwin();
tox_kill(m);
2013-07-24 00:11:07 +08:00
exit(EXIT_SUCCESS);
} else if (inpt_command == 'c') { //set conversation partner
if (line[2] == 'r') {
if (conversation_default != 0) {
conversation_default = 0;
new_lines("[i] default conversation reset");
2016-09-01 02:12:19 +08:00
} else {
new_lines("[i] default conversation wasn't set, nothing to do");
2016-09-01 02:12:19 +08:00
}
} else if (line[3] != ' ') {
new_lines("[i] invalid command");
} else {
int num = atoi(line + 4);
/* zero is also returned for not-a-number */
2016-09-01 02:12:19 +08:00
if (!num && strcmp(line + 4, "0")) {
num = -1;
2016-09-01 02:12:19 +08:00
}
2016-09-01 02:12:19 +08:00
if (num < 0) {
new_lines("[i] invalid command parameter");
2016-09-01 02:12:19 +08:00
} else if (line[2] == 'f') {
conversation_default = num + 1;
char buffer[128];
sprintf(buffer, "[i] default conversation is now to friend %i", num);
new_lines(buffer);
} else if (line[2] == 'g') {
char buffer[128];
conversation_default = - (num + 1);
sprintf(buffer, "[i] default conversation is now to group %i", num);
new_lines(buffer);
2016-09-01 02:12:19 +08:00
} else {
new_lines("[i] invalid command");
2016-09-01 02:12:19 +08:00
}
}
} else if (inpt_command == 'p') { //list peers
char *posi = NULL;
int group_number = strtoul(line + prompt_offset, &posi, 0);
if (posi != NULL) {
char msg[64];
uint32_t peer_cnt = tox_conference_peer_count(m, group_number, NULL);
if (peer_cnt == UINT32_MAX) {
new_lines("[g] Invalid group number.");
} else if (peer_cnt == 0) {
sprintf(msg, "[g] #%i: No peers in group.", group_number);
new_lines(msg);
} else {
sprintf(msg, "[g] #%i: Group has %i peers. Names:", group_number, peer_cnt);
new_lines(msg);
print_groupchatpeers(m, group_number);
}
}
} else {
2013-07-30 17:31:02 +08:00
new_lines("[i] invalid command");
2013-07-14 01:31:16 +08:00
}
} else {
if (conversation_default != 0) {
if (conversation_default > 0) {
int friendnumber = conversation_default - 1;
uint32_t res = tox_friend_send_message(m, friendnumber, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *)line, strlen(line), NULL);
if (res == 0) {
char sss[128];
sprintf(sss, "[i] could not send message to friend no. %u", friendnumber);
new_lines(sss);
2016-09-01 02:12:19 +08:00
} else {
print_formatted_message(m, line, friendnumber, 1);
2016-09-01 02:12:19 +08:00
}
} else {
int groupnumber = - conversation_default - 1;
int res = tox_conference_send_message(m, groupnumber, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *)line, strlen(line), NULL);
if (res == 0) {
char msg[32 + STRING_LENGTH];
sprintf(msg, "[g] #%u: YOU: %s", groupnumber, line);
new_lines(msg);
} else {
char msg[128];
sprintf(msg, "[i] could not send message to group no. %u: %i", groupnumber, res);
new_lines(msg);
}
}
2016-09-01 02:12:19 +08:00
} else {
new_lines("[i] invalid input: neither command nor in conversation");
2016-09-01 02:12:19 +08:00
}
2013-07-14 01:31:16 +08:00
}
}
/* basic wrap, ignores embedded '\t', '\n' or '|'
* inserts continuation markers if there's enough space left,
* otherwise turns spaces into newlines if possible */
static void wrap(char output[STRING_LENGTH_WRAPPED], char input[STRING_LENGTH], int line_width)
2013-07-14 01:31:16 +08:00
{
size_t i, len = strlen(input);
if ((line_width < 4) || (len < (size_t)line_width)) {
/* if line_width ridiculously tiny, it's not worth the effort */
strcpy(output, input);
return;
}
/* how much can we shift? */
size_t delta_is = 0, delta_remain = STRING_LENGTH_WRAPPED - len - 1;
/* if the line is very very short, don't insert continuation markers,
* as they would use up too much of the line */
2016-09-01 02:12:19 +08:00
if ((size_t)line_width < 2 * wrap_cont_len) {
delta_remain = 0;
2016-09-01 02:12:19 +08:00
}
2013-08-17 01:11:09 +08:00
for (i = line_width; i < len; i += line_width) {
/* look backward for a space to expand/turn into a new line */
size_t k = i;
size_t m = i - line_width;
while (input[k] != ' ' && k > m) {
k--;
}
if (k > m) {
if (delta_remain > wrap_cont_len) {
/* replace space with continuation, then
* set the pos. after the space as new line start
* (i.e. space is being "eaten") */
memcpy(output + m + delta_is, input + m, k - m);
strcpy(output + k + delta_is, wrap_cont_str);
delta_remain -= wrap_cont_len - 1;
delta_is += wrap_cont_len - 1;
i = k + 1;
} else {
/* no more space to push forward: replace the space,
* use its pos. + 1 as starting point for the next line */
memcpy(output + m + delta_is, input + m, k - m);
output[k + delta_is] = '\n';
i = k + 1;
}
} else {
/* string ends right here:
* don't add a continuation marker with nothing following */
2016-09-01 02:12:19 +08:00
if (i == len - 1) {
break;
2016-09-01 02:12:19 +08:00
}
/* nothing found backwards */
if (delta_remain > wrap_cont_len) {
/* break at the end of the line,
* i.e. in the middle of the word at the border */
memcpy(output + m + delta_is, input + m, line_width);
strcpy(output + i + delta_is, wrap_cont_str);
delta_remain -= wrap_cont_len;
delta_is += wrap_cont_len;
} else {
/* no more space to push, no space to convert:
* just copy the whole line and move on;
* means the line count calc'ed will be off */
memcpy(output + m + delta_is, input + m, line_width);
}
}
}
i -= line_width;
memcpy(output + i + delta_is, input + i, len - i);
output[len + delta_is] = 0;
}
/*
* extended wrap, honors '\n', accepts '|' as "break here when necessary"
* marks wrapped lines with "+ " in front, which does expand output
* does NOT honor '\t': would require a lot more work (and tab width isn't always 8)
*/
static void wrap_bars(char output[STRING_LENGTH_WRAPPED], char input[STRING_LENGTH], size_t line_width)
{
size_t len = strlen(input);
size_t ipos, opos = 0;
size_t bar_avail = 0, space_avail = 0, nl_got = 0; /* in opos */
for (ipos = 0; ipos < len; ipos++) {
if (opos - nl_got < line_width) {
/* not yet at the limit */
char c = input[ipos];
2016-09-01 02:12:19 +08:00
if (c == ' ') {
space_avail = opos;
2016-09-01 02:12:19 +08:00
}
output[opos++] = input[ipos];
if (opos >= STRING_LENGTH_WRAPPED) {
opos = STRING_LENGTH_WRAPPED - 1;
break;
}
if (c == '|') {
output[opos - 1] = ' ';
bar_avail = opos;
if (opos + 2 >= STRING_LENGTH_WRAPPED) {
opos = STRING_LENGTH_WRAPPED - 1;
break;
}
2013-08-17 01:11:09 +08:00
output[opos++] = '|';
output[opos++] = ' ';
}
2016-09-01 02:12:19 +08:00
if (c == '\n') {
nl_got = opos;
2016-09-01 02:12:19 +08:00
}
continue;
}
/* at the limit */
if (bar_avail > nl_got) {
/* overwrite */
memcpy(output + bar_avail - 1, wrap_cont_str, wrap_cont_len);
nl_got = bar_avail;
ipos--;
continue;
}
if (space_avail > nl_got) {
if (opos + wrap_cont_len - 1 >= STRING_LENGTH_WRAPPED) {
opos = STRING_LENGTH_WRAPPED - 1;
break;
}
/* move forward by 2 characters */
memmove(output + space_avail + 3, output + space_avail + 1, opos - (space_avail + 1));
memcpy(output + space_avail, wrap_cont_str, wrap_cont_len);
nl_got = space_avail + 1;
opos += 2;
ipos--;
continue;
}
char c = input[ipos];
if ((c == '|') || (c == ' ') || (c == '\n')) {
if (opos + wrap_cont_len >= STRING_LENGTH_WRAPPED) {
opos = STRING_LENGTH_WRAPPED - 1;
break;
}
memcpy(output + opos, wrap_cont_str, wrap_cont_len);
nl_got = opos;
opos += wrap_cont_len;
2013-07-14 01:31:16 +08:00
}
output[opos++] = input[ipos];
if (opos >= STRING_LENGTH_WRAPPED) {
opos = STRING_LENGTH_WRAPPED - 1;
break;
}
continue;
2013-07-14 01:31:16 +08:00
}
2016-09-01 02:12:19 +08:00
if (opos >= STRING_LENGTH_WRAPPED) {
opos = STRING_LENGTH_WRAPPED - 1;
2016-09-01 02:12:19 +08:00
}
output[opos] = 0;
2013-07-14 01:31:16 +08:00
}
static int count_lines(char *string)
2013-07-14 01:31:16 +08:00
{
2013-09-16 16:37:22 +08:00
size_t i, len = strlen(string);
2013-07-14 01:31:16 +08:00
int count = 1;
2013-08-17 01:11:09 +08:00
2013-07-31 14:15:01 +08:00
for (i = 0; i < len; i++) {
2016-09-01 02:12:19 +08:00
if (string[i] == '\n') {
2013-07-14 01:31:16 +08:00
count++;
2016-09-01 02:12:19 +08:00
}
2013-07-14 01:31:16 +08:00
}
2013-08-17 01:11:09 +08:00
2013-07-14 01:31:16 +08:00
return count;
}
static char *appender(char *str, const char c)
2013-07-14 01:31:16 +08:00
{
size_t len = strlen(str);
2013-08-17 01:11:09 +08:00
2013-07-14 01:31:16 +08:00
if (len < STRING_LENGTH) {
2013-08-17 01:11:09 +08:00
str[len + 1] = str[len];
2013-07-14 01:31:16 +08:00
str[len] = c;
}
2013-08-17 01:11:09 +08:00
2013-07-14 01:31:16 +08:00
return str;
}
static void do_refresh(void)
2013-07-14 01:31:16 +08:00
{
2013-08-17 01:11:09 +08:00
int count = 0;
char wrap_output[STRING_LENGTH_WRAPPED];
2013-07-31 14:15:01 +08:00
int i;
2013-08-17 01:11:09 +08:00
2013-07-31 14:15:01 +08:00
for (i = 0; i < HISTORY; i++) {
2016-09-01 02:12:19 +08:00
if (flag[i]) {
wrap_bars(wrap_output, lines[i], x);
2016-09-01 02:12:19 +08:00
} else {
wrap(wrap_output, lines[i], x);
2016-09-01 02:12:19 +08:00
}
int L = count_lines(wrap_output);
2013-07-31 14:15:01 +08:00
count = count + L;
2013-08-17 01:11:09 +08:00
2013-07-14 01:31:16 +08:00
if (count < y) {
2013-08-17 01:11:09 +08:00
move(y - 1 - count, 0);
printw("%s", wrap_output);
2013-07-14 01:31:16 +08:00
clrtoeol();
}
}
2013-08-17 01:11:09 +08:00
move(y - 1, 0);
2013-07-14 01:31:16 +08:00
clrtoeol();
printw(">> ");
printw("%s", input_line);
2013-07-14 01:31:16 +08:00
clrtoeol();
refresh();
}
static void print_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
2013-07-14 01:31:16 +08:00
{
new_lines("[i] received friend request with message:");
new_lines((const char *)data);
char numchar[100];
2013-07-30 02:17:30 +08:00
sprintf(numchar, "[i] accept request with /a %u", num_requests);
new_lines(numchar);
memcpy(pending_requests[num_requests].id, public_key, TOX_PUBLIC_KEY_SIZE);
pending_requests[num_requests].accepted = 0;
++num_requests;
2013-07-14 01:31:16 +08:00
do_refresh();
}
static void print_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length,
void *userdata)
2013-07-14 01:31:16 +08:00
{
/* ensure null termination */
2014-07-03 04:04:41 +08:00
uint8_t null_string[length + 1];
memcpy(null_string, string, length);
null_string[length] = 0;
print_formatted_message(m, (char *)null_string, friendnumber, 0);
}
static void print_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
2013-07-31 14:15:01 +08:00
{
char name[TOX_MAX_NAME_LENGTH + 1];
2013-08-17 01:11:09 +08:00
if (getfriendname_terminated(m, friendnumber, name) != -1) {
2013-08-17 01:11:09 +08:00
char msg[100 + length];
2016-09-01 02:12:19 +08:00
if (name[0] != 0) {
sprintf(msg, "[i] [%d] %s is now known as %s.", friendnumber, name, string);
2016-09-01 02:12:19 +08:00
} else {
sprintf(msg, "[i] [%d] Friend's name is %s.", friendnumber, string);
2016-09-01 02:12:19 +08:00
}
new_lines(msg);
}
}
static void print_statuschange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
2013-07-31 14:15:01 +08:00
{
char name[TOX_MAX_NAME_LENGTH + 1];
2013-08-17 01:11:09 +08:00
if (getfriendname_terminated(m, friendnumber, name) != -1) {
2013-08-17 01:11:09 +08:00
char msg[100 + length + strlen(name) + 1];
2016-09-01 02:12:19 +08:00
if (name[0] != 0) {
sprintf(msg, "[i] [%d] %s's status changed to %s.", friendnumber, name, string);
2016-09-01 02:12:19 +08:00
} else {
sprintf(msg, "[i] [%d] Their status changed to %s.", friendnumber, string);
2016-09-01 02:12:19 +08:00
}
new_lines(msg);
}
2013-07-14 01:31:16 +08:00
}
static const char *data_file_name = NULL;
static Tox *load_data(void)
2013-07-31 14:15:01 +08:00
{
FILE *data_file = fopen(data_file_name, "r");
2013-09-18 04:28:39 +08:00
2013-07-31 14:15:01 +08:00
if (data_file) {
2013-07-24 00:11:07 +08:00
fseek(data_file, 0, SEEK_END);
size_t size = ftell(data_file);
rewind(data_file);
2013-07-24 00:11:07 +08:00
uint8_t data[size];
2013-09-18 04:28:39 +08:00
2013-08-17 01:11:09 +08:00
if (fread(data, sizeof(uint8_t), size, data_file) != size) {
fputs("[!] could not read data file!\n", stderr);
2013-09-16 16:37:22 +08:00
fclose(data_file);
return 0;
2013-07-24 00:11:07 +08:00
}
2013-08-17 01:11:09 +08:00
struct Tox_Options options;
tox_options_default(&options);
options.savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE;
options.savedata_data = data;
options.savedata_length = size;
Tox *m = tox_new(&options, NULL);
if (fclose(data_file) < 0) {
perror("[!] fclose failed");
/* we got it open and the expected data read... let it be ok */
/* return 0; */
}
return m;
}
return tox_new(NULL, NULL);
}
static int save_data(Tox *m)
{
FILE *data_file = fopen(data_file_name, "w");
2013-09-18 04:28:39 +08:00
if (!data_file) {
perror("[!] load_key");
return 0;
2013-07-24 00:11:07 +08:00
}
2013-09-16 16:37:22 +08:00
int res = 1;
size_t size = tox_get_savedata_size(m);
uint8_t data[size];
tox_get_savedata(m, data);
2013-08-17 01:11:09 +08:00
if (fwrite(data, sizeof(uint8_t), size, data_file) != size) {
fputs("[!] could not write data file (1)!", stderr);
2013-09-16 16:37:22 +08:00
res = 0;
}
if (fclose(data_file) < 0) {
perror("[!] could not write data file (2)");
2013-09-16 16:37:22 +08:00
res = 0;
}
2013-09-16 16:37:22 +08:00
return res;
}
2013-08-17 01:11:09 +08:00
static int save_data_file(Tox *m, const char *path)
{
data_file_name = path;
2013-09-18 04:28:39 +08:00
2016-09-01 02:12:19 +08:00
if (save_data(m)) {
return 1;
2016-09-01 02:12:19 +08:00
}
2013-08-17 01:11:09 +08:00
return 0;
}
static void print_help(char *prog_name)
{
printf("nTox %.1f - Command-line tox-core client\n", 0.1);
printf("Usage: %s [--ipv4|--ipv6] IP PORT KEY [-f keyfile]\n", prog_name);
puts("Options: (order IS relevant)");
puts(" --ipv4 / --ipv6 [Optional] Support IPv4 only or IPv4 & IPv6.");
2014-02-23 06:06:07 +08:00
puts(" IP PORT KEY [REQUIRED] A node to connect to (IP/Port) and its key.");
puts(" -f keyfile [Optional] Specify a keyfile to read from and write to.");
}
static void print_invite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *data, size_t length,
void *userdata)
{
char msg[256];
if (type == TOX_CONFERENCE_TYPE_TEXT) {
sprintf(msg, "[i] received group chat invite from: %u, auto accepting and joining. group number: %u", friendnumber,
tox_conference_join(m, friendnumber, data, length, NULL));
} else {
sprintf(msg, "[i] Group chat invite received of type %u that could not be accepted by ntox.", type);
}
new_lines(msg);
}
static void print_groupchatpeers(Tox *m, int groupnumber)
{
uint32_t num = tox_conference_peer_count(m, groupnumber, NULL);
if (num == UINT32_MAX) {
return;
2016-09-01 02:12:19 +08:00
}
if (!num) {
new_lines("[g]+ no peers left in group.");
return;
}
uint8_t names[num][TOX_MAX_NAME_LENGTH];
size_t lengths[num];
uint32_t i;
for (i = 0; i < num; ++i) {
lengths[i] = tox_conference_peer_get_name_size(m, groupnumber, i, NULL);
tox_conference_peer_get_name(m, groupnumber, i, names[i], NULL);
}
char numstr[16];
char header[] = "[g]+ ";
size_t header_len = strlen(header);
char msg[STRING_LENGTH];
strcpy(msg, header);
size_t len_total = header_len;
for (i = 0; i < num; ++i) {
size_t len_name = lengths[i];
size_t len_num = sprintf(numstr, "%i: ", i);
if (len_num + len_name + len_total + 3 >= STRING_LENGTH) {
new_lines_mark(msg, 1);
strcpy(msg, header);
len_total = header_len;
}
strcpy(msg + len_total, numstr);
len_total += len_num;
memcpy(msg + len_total, (char *)names[i], len_name);
len_total += len_name;
if (i < num - 1) {
strcpy(msg + len_total, "|");
len_total++;
}
}
new_lines_mark(msg, 1);
}
static void print_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length,
void *userdata)
{
char msg[256 + length];
TOX_ERR_CONFERENCE_PEER_QUERY error;
size_t len = tox_conference_peer_get_name_size(m, groupnumber, peernumber, &error);
2014-06-08 21:21:37 +08:00
uint8_t name[TOX_MAX_NAME_LENGTH] = {0};
tox_conference_peer_get_name(m, groupnumber, peernumber, name, NULL);
//print_groupchatpeers(m, groupnumber);
if (len == 0 || error != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
name[0] = 0;
2016-09-01 02:12:19 +08:00
}
2016-09-01 02:12:19 +08:00
if (name[0] != 0) {
sprintf(msg, "[g] %u: %u <%s>: %s", groupnumber, peernumber, name, message);
2016-09-01 02:12:19 +08:00
} else {
sprintf(msg, "[g] #%u: %u Unknown: %s", groupnumber, peernumber, message);
2016-09-01 02:12:19 +08:00
}
new_lines(msg);
}
static void print_groupnamelistchange(Tox *m, uint32_t groupnumber, uint32_t peernumber,
TOX_CONFERENCE_STATE_CHANGE change,
void *userdata)
{
char msg[256];
if (change == TOX_CONFERENCE_STATE_CHANGE_PEER_JOIN) {
sprintf(msg, "[g] #%i: New peer %i.", groupnumber, peernumber);
new_lines(msg);
} else if (change == TOX_CONFERENCE_STATE_CHANGE_PEER_EXIT) {
/* if peer was the last in list, it simply dropped,
* otherwise it was overwritten by the last peer
*
* adjust output
*/
uint32_t peers_total = tox_conference_peer_count(m, groupnumber, NULL);
if (peers_total == peernumber) {
sprintf(msg, "[g] #%i: Peer %i left.", groupnumber, peernumber);
new_lines(msg);
} else {
TOX_ERR_CONFERENCE_PEER_QUERY error;
2014-06-08 21:21:37 +08:00
uint8_t peername[TOX_MAX_NAME_LENGTH] = {0};
size_t len = tox_conference_peer_get_name_size(m, groupnumber, peernumber, &error);
tox_conference_peer_get_name(m, groupnumber, peernumber, peername, NULL);
if (len == 0 || error != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
peername[0] = 0;
2016-09-01 02:12:19 +08:00
}
sprintf(msg, "[g] #%i: Peer %i left. Former peer [%i: <%s>] is now peer %i.", groupnumber, peernumber,
peers_total, peername, peernumber);
new_lines(msg);
}
} else if (change == TOX_CONFERENCE_STATE_CHANGE_PEER_NAME_CHANGE) {
2014-06-08 21:21:37 +08:00
uint8_t peername[TOX_MAX_NAME_LENGTH] = {0};
int len = tox_conference_peer_get_name_size(m, groupnumber, peernumber, NULL);
tox_conference_peer_get_name(m, groupnumber, peernumber, peername, NULL);
2016-09-01 02:12:19 +08:00
if (len <= 0) {
peername[0] = 0;
2016-09-01 02:12:19 +08:00
}
sprintf(msg, "[g] #%i: Peer %i's name changed: %s", groupnumber, peernumber, peername);
new_lines(msg);
} else {
sprintf(msg, "[g] #%i: Name list changed (peer %i, change %i?):", groupnumber, peernumber, change);
new_lines(msg);
print_groupchatpeers(m, groupnumber);
}
}
static void file_request_accept(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t type,
uint64_t file_size,
const uint8_t *filename, size_t filename_length, void *user_data)
{
2015-03-11 06:49:06 +08:00
if (type != TOX_FILE_KIND_DATA) {
new_lines("Refused invalid file type.");
tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, 0);
2015-03-11 06:49:06 +08:00
return;
}
char msg[512];
2015-03-11 06:49:06 +08:00
sprintf(msg, "[t] %u is sending us: %s of size %llu", friend_number, filename, (long long unsigned int)file_size);
new_lines(msg);
if (tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME, 0)) {
2015-03-11 06:49:06 +08:00
sprintf(msg, "Accepted file transfer. (saving file as: %u.%u.bin)", friend_number, file_number);
new_lines(msg);
2016-09-01 02:12:19 +08:00
} else {
new_lines("Could not accept file transfer.");
2016-09-01 02:12:19 +08:00
}
}
static void file_print_control(Tox *tox, uint32_t friend_number, uint32_t file_number, TOX_FILE_CONTROL control,
void *user_data)
{
char msg[512] = {0};
2015-03-11 06:49:06 +08:00
sprintf(msg, "[t] control %u received", control);
new_lines(msg);
2015-03-11 06:49:06 +08:00
if (control == TOX_FILE_CONTROL_CANCEL) {
unsigned int i;
2015-03-11 06:49:06 +08:00
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
/* This is slow */
if (file_senders[i].file && file_senders[i].friendnum == friend_number && file_senders[i].filenumber == file_number) {
fclose(file_senders[i].file);
file_senders[i].file = 0;
sprintf(msg, "[t] %u file transfer: %u cancelled", file_senders[i].friendnum, file_senders[i].filenumber);
new_lines(msg);
}
}
}
}
static void write_file(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
size_t length, void *user_data)
2013-10-01 01:13:49 +08:00
{
2015-03-11 06:49:06 +08:00
if (length == 0) {
char msg[512];
sprintf(msg, "[t] %u file transfer: %u completed", friendnumber, filenumber);
new_lines(msg);
2015-03-11 06:49:06 +08:00
return;
}
2015-03-11 06:49:06 +08:00
char filename[256];
sprintf(filename, "%u.%u.bin", friendnumber, filenumber);
FILE *pFile = fopen(filename, "r+b");
2016-09-01 02:12:19 +08:00
if (pFile == NULL) {
2015-03-11 06:49:06 +08:00
pFile = fopen(filename, "wb");
2016-09-01 02:12:19 +08:00
}
2015-03-11 06:49:06 +08:00
fseek(pFile, position, SEEK_SET);
2016-09-01 02:12:19 +08:00
if (fwrite(data, length, 1, pFile) != 1) {
2013-10-05 23:24:30 +08:00
new_lines("Error writing to file");
2016-09-01 02:12:19 +08:00
}
2013-10-05 23:24:30 +08:00
2013-10-01 01:13:49 +08:00
fclose(pFile);
}
static void print_online(Tox *tox, uint32_t friendnumber, TOX_CONNECTION status, void *userdata)
{
2016-09-01 02:12:19 +08:00
if (status) {
printf("\nOther went online.\n");
2016-09-01 02:12:19 +08:00
} else {
printf("\nOther went offline.\n");
unsigned int i;
2016-09-01 02:12:19 +08:00
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
if (file_senders[i].file != 0 && file_senders[i].friendnum == friendnumber) {
fclose(file_senders[i].file);
file_senders[i].file = 0;
}
2016-09-01 02:12:19 +08:00
}
}
}
static char timeout_getch(Tox *m)
2014-06-08 22:08:52 +08:00
{
char c;
int slpval = tox_iteration_interval(m);
2014-06-08 22:08:52 +08:00
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = slpval * 1000;
c = ERR;
int n = select(1, &fds, NULL, NULL, &tv);
2014-06-11 06:35:55 +08:00
2014-06-08 22:08:52 +08:00
if (n < 0) {
new_lines("select error: maybe interupted");
} else if (n == 0) {
} else {
c = getch();
}
return c;
}
2013-07-14 01:31:16 +08:00
int main(int argc, char *argv[])
{
/* minimalistic locale support (i.e. when printing dates) */
setlocale(LC_ALL, "");
if (argc < 4) {
if ((argc == 2) && !strcmp(argv[1], "-h")) {
print_help(argv[0]);
exit(0);
}
printf("Usage: %s [--ipv4|--ipv6] IP PORT KEY [-f keyfile] (or %s -h for help)\n", argv[0], argv[0]);
exit(0);
}
/* let user override default by cmdline */
uint8_t ipv6enabled = 1; /* x */
int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled);
2013-09-15 00:42:17 +08:00
2016-09-01 02:12:19 +08:00
if (argvoffset < 0) {
exit(1);
2016-09-01 02:12:19 +08:00
}
int on = 0;
const char *filename = "data";
char idstring[200] = {0};
Tox *m;
/* [-f keyfile] MUST be last two arguments, no point in walking over the list
* especially not a good idea to accept it anywhere in the middle */
2016-09-01 02:12:19 +08:00
if (argc > argvoffset + 3) {
if (!strcmp(argv[argc - 2], "-f")) {
filename = argv[argc - 1];
2016-09-01 02:12:19 +08:00
}
}
data_file_name = filename;
m = load_data();
2013-08-17 01:11:09 +08:00
if (!m) {
fputs("Failed to allocate Messenger datastructure", stderr);
exit(0);
}
save_data_file(m, filename);
tox_callback_friend_request(m, print_request);
tox_callback_friend_message(m, print_message);
tox_callback_friend_name(m, print_nickchange);
tox_callback_friend_status_message(m, print_statuschange);
2016-09-19 23:30:36 +08:00
tox_callback_conference_invite(m, print_invite);
tox_callback_conference_message(m, print_groupmessage);
tox_callback_conference_namelist_change(m, print_groupnamelistchange);
tox_callback_file_recv_chunk(m, write_file);
tox_callback_file_recv_control(m, file_print_control);
tox_callback_file_recv(m, file_request_accept);
tox_callback_file_chunk_request(m, tox_file_chunk_request);
tox_callback_friend_connection_status(m, print_online);
2013-07-14 01:31:16 +08:00
initscr();
noecho();
raw();
2013-07-31 14:15:01 +08:00
getmaxyx(stdscr, y, x);
new_lines("/h for list of commands");
2013-08-13 23:50:33 +08:00
get_id(m, idstring);
new_lines(idstring);
2013-09-27 17:24:33 +08:00
strcpy(input_line, "");
uint16_t port = atoi(argv[argvoffset + 2]);
unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]);
int res = tox_bootstrap(m, argv[argvoffset + 1], port, binary_string, NULL);
2013-08-17 01:11:09 +08:00
if (!res) {
printf("Failed to convert \"%s\" into an IP address. Exiting...\n", argv[argvoffset + 1]);
endwin();
2013-07-21 02:50:54 +08:00
exit(1);
}
2013-07-14 09:57:09 +08:00
nodelay(stdscr, TRUE);
2013-08-17 01:11:09 +08:00
new_lines("[i] change username with /n");
uint8_t name[TOX_MAX_NAME_LENGTH + 1];
tox_self_get_name(m, name);
uint16_t namelen = tox_self_get_name_size(m);
name[namelen] = 0;
2013-09-18 04:28:39 +08:00
if (namelen > 0) {
char whoami[128 + TOX_MAX_NAME_LENGTH];
snprintf(whoami, sizeof(whoami), "[i] your current username is: %s", name);
new_lines(whoami);
}
time_t timestamp0 = time(NULL);
2013-10-01 01:13:49 +08:00
2013-08-27 19:06:19 +08:00
while (1) {
if (on == 0) {
if (tox_self_get_connection_status(m)) {
new_lines("[i] connected to DHT");
on = 1;
} else {
time_t timestamp1 = time(NULL);
2013-10-01 01:13:49 +08:00
if (timestamp0 + 10 < timestamp1) {
timestamp0 = timestamp1;
tox_bootstrap(m, argv[argvoffset + 1], port, binary_string, NULL);
}
}
}
2013-07-24 00:11:07 +08:00
Make self_connection_status callback stateless. **What are we doing?** We are moving towards stateless callbacks. This means that when registering a callback, you no longer pass a user data pointer. Instead, you pass a user data pointer to tox_iterate. This pointer is threaded through the code, passed to each callback. The callback can modify the data pointed at. An extra indirection will be needed if the pointer itself can change. **Why?** Currently, callbacks are registered with a user data pointer. This means the library has N pointers for N different callbacks. These pointers need to be managed by the client code. Managing the lifetime of the pointee can be difficult. In C++, it takes special effort to ensure that the lifetime of user data extends at least beyond the lifetime of the Tox instance. For other languages, the situation is much worse. Java and other garbage collected languages may move objects in memory, so the pointers are not stable. Tox4j goes through a lot of effort to make the Java/Scala user experience a pleasant one by keeping a global array of Tox+userdata on the C++ side, and communicating via protobufs. A Haskell FFI would have to do similarly complex tricks. Stateless callbacks ensure that a user data pointer only needs to live during a single function call. This means that the user code (or language runtime) can move the data around at will, as long as it sets the new location in the callback. **How?** We are doing this change one callback at a time. After each callback, we ensure that everything still works as expected. This means the toxcore change will require 15 Pull Requests.
2016-08-10 20:46:04 +08:00
tox_iterate(m, NULL);
2013-07-14 01:31:16 +08:00
do_refresh();
2013-07-24 00:11:07 +08:00
int c = timeout_getch(m);
2013-08-17 01:11:09 +08:00
2016-09-01 02:12:19 +08:00
if (c == ERR || c == 27) {
2013-07-24 00:11:07 +08:00
continue;
2016-09-01 02:12:19 +08:00
}
2013-07-24 00:11:07 +08:00
getmaxyx(stdscr, y, x);
2013-08-17 01:11:09 +08:00
2013-08-27 19:06:19 +08:00
if ((c == 0x0d) || (c == 0x0a)) {
2013-09-27 17:24:33 +08:00
line_eval(m, input_line);
strcpy(input_line, "");
} else if (c == 8 || c == 127) {
2013-09-27 17:24:33 +08:00
input_line[strlen(input_line) - 1] = '\0';
2013-07-24 00:11:07 +08:00
} else if (isalnum(c) || ispunct(c) || c == ' ') {
appender(input_line, (char) c);
2013-07-24 00:11:07 +08:00
}
2013-07-14 01:31:16 +08:00
}
2013-08-17 01:11:09 +08:00
free(binary_string);
2015-03-14 22:05:39 +08:00
save_data_file(m, filename);
tox_kill(m);
2013-07-14 01:31:16 +08:00
endwin();
return 0;
}