Compare commits

...

52 Commits

Author SHA1 Message Date
jfreegman 57a439c9b4
Remove testnet specific code
Preparing for ngc merge
2022-09-22 14:05:23 -04:00
jfreegman 0aea5d7fbe
Remove testnet specific code
Preparing for ngc merge
2022-09-22 13:51:00 -04:00
jfreegman e56edd556f
Remove error code that no longer exists 2022-07-14 00:17:39 -04:00
jfreegman 7ed031b9bf
Fix static build script 2022-06-28 10:54:19 -04:00
Tha14 6ef2c784c5
Fix tab alert bug on peer exit 2022-06-28 17:31:27 +03:00
jfreegman 058057c64d
Fix style issues 2022-06-27 19:42:38 -04:00
Tha14 146b00b8ca
Fix chatlog saving with new hide connection msg pr 2022-06-28 01:58:32 +03:00
jfreegman 85024bfce8
Update config man page 2022-06-27 10:40:03 -04:00
Tha14 129bb6ba68
New option: Hide peer connection status in groups 2022-06-27 17:31:05 +03:00
jfreegman 68b4713ef7
Update group API 2022-06-02 10:20:38 -04:00
jfreegman 46eb69412e
Update group API 2022-04-23 13:14:44 -04:00
jfreegman f47b4c1c3c
Remove duplicate help menu entry 2022-03-18 12:24:07 -04:00
jfreegman 22eb6797b1
Fix another group peerlist scrolling bug 2022-03-18 12:24:06 -04:00
jfreegman 0edad7b862
Fix group peerlist scrolling bug 2022-03-18 12:24:06 -04:00
jfreegman cc80da00fc
Increase how long we wait after joining a group before showing peer join messages 2022-03-18 12:24:06 -04:00
jfreegman 32efac931c
Connect second testnet bootstrap node to ipv4 instead of 6 2022-03-18 12:24:05 -04:00
jfreegman 4f936e5f9c
Add higher precision for logging timestamps 2022-03-18 12:24:05 -04:00
jfreegman 529d905bd0
Add timestamps to logger and enable widechar for bazel build 2022-03-18 12:24:05 -04:00
jfreegman 631b1d8b08
Fix second bootstrap node info 2022-03-18 12:24:04 -04:00
jfreegman 9ac8d517e9
Add second testnet bootstrap node 2022-03-18 12:24:04 -04:00
jfreegman 90152f5e17
some logging code cleanup 2022-03-18 12:24:04 -04:00
jfreegman 4d501aeb2a
Add support for new voice state feature 2022-03-18 12:24:03 -04:00
jfreegman 1b9f4f337a
Fix name change notifications
Due to recent core changes we now need to store a peer's
previous name instead of relying on API lookups via the
peer_id in the name change callback in order to display
the correct info
2022-03-18 12:24:03 -04:00
jfreegman 812f2d1ff9
Fix some weird/incorrect code for printing private messages 2022-03-18 12:24:03 -04:00
jfreegman 2eeb1de20d
Remove usage of deprecated API typedefs 2022-03-18 12:24:02 -04:00
jfreegman ba896fd124
Hardcode testnet bootstrap node 2022-03-18 12:24:02 -04:00
jfreegman 3ff3516e42
Add more descriptive errors to some moderation commands 2022-03-18 12:24:02 -04:00
jfreegman 2ef3d68173
Properly handle group mod event
We now update all peer roles if we get a moderation event
in which the peer ID is invalid, per recent API changes
2022-03-18 12:24:01 -04:00
jfreegman 7a099f86c8
Fix duplicate config example line 2022-03-18 12:24:01 -04:00
jfreegman 9a629072b3
Fix possible null deref 2022-03-18 12:24:00 -04:00
jfreegman 27259ff23f
Update function name (merge conflict) 2022-03-18 12:24:00 -04:00
jfreegman f786b7ae6a
Refactor peer-specific commands
This takes into account the possibility of multiple peers in
the group using the same nick. The /whois command now lists
all peers associated with the input name, and all commands
that target a peer with a non-unique nick will require the
public key to be used.
2022-03-18 12:24:00 -04:00
jfreegman cd4a5e5fde
Fix rebase mistakes 2022-03-18 12:23:59 -04:00
jfreegman 0b32b985f1
Add topic lock command and callbacks 2022-03-18 12:23:59 -04:00
jfreegman 5851cb2dd8
Don't announce/alert on self group join 2022-03-18 12:23:59 -04:00
jfreegman 1847025213
Make /whois show both public key and name 2022-03-18 12:23:58 -04:00
jfreegman 1a2320b035
Add the ability to make peer-specific commands using public keys
This allows us to use commands on peers who may be using nicks with strange
or unsupported unicode characters
2022-03-18 12:23:58 -04:00
jfreegman 1211899a40
Add /list command that lists all group peers w/ public keys 2022-03-18 12:23:58 -04:00
jfreegman 5dabaac804
Remove some unnecessary code 2022-03-18 12:23:57 -04:00
jfreegman 4f26171e3f
Re-add /group command
Another one lost in a rebase
2022-03-18 12:23:57 -04:00
jfreegman fa075f1972
Add connection type to whois group peer queries 2022-03-18 12:23:57 -04:00
jfreegman cbf3145aee
Remove redundant command /mykey
You can get your own key with the /whois command
2022-03-18 12:23:56 -04:00
jfreegman 06bdb0d167
Add groupchat help menu
It seems to have disappeared after a recent rebase
2022-03-18 12:23:56 -04:00
jfreegman 0212391d7d
Disable DHT nodeslist fetching temporarily
We don't want to fetch the mainnet nodes list when we're on
the NGC testnet. This should be fixed before the NGC merge
2022-03-18 12:23:56 -04:00
jfreegman 535a8402d6
Merge with master (V0.11.1) 2022-03-18 12:23:55 -04:00
jfreegman 2c51afd9ef
Sync with master branch 2022-03-18 12:23:55 -04:00
jfreegman 4aca8b6ecb
Fix/update groupchat UI 2022-03-18 12:23:55 -04:00
jfreegman 28be56aad9
Fix bug causing group PM's to increment window notifications by 2 instead of 1 2022-03-18 12:23:54 -04:00
jfreegman 38004367a1
Port fix for invalid error handling of widechar conversions from master 2022-03-18 12:23:54 -04:00
jfreegman 5499bb6645
Fix/update group logging implementation 2022-03-18 12:23:53 -04:00
jfreegman ed8ba89cd8
Fix merge conflicts with master 2022-03-18 12:23:53 -04:00
jfreegman 812210d63f
Implement new groupchats 2022-03-18 12:23:53 -04:00
36 changed files with 3885 additions and 43 deletions

View File

@ -21,6 +21,7 @@ cc_binary(
"-std=gnu99",
"-DAUDIO",
"-DGAMES",
"-DHAVE_WIDECHAR",
"-DPACKAGE_DATADIR='\"data\"'",
"-DPYTHON",
"-DQRCODE",

View File

@ -12,9 +12,9 @@ CFLAGS += ${USER_CFLAGS}
LDFLAGS ?=
LDFLAGS += ${USER_LDFLAGS}
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o
OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o conference.o help.o input.o line_info.o
OBJ += log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o conference.o configdir.o curl_util.o execute.o
OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o groupchats.o groupchat_commands.o help.o
OBJ += input.o line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
# Check if debug build is enabled

View File

@ -2,12 +2,12 @@
.\" Title: toxic.conf
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
.\" Date: 2021-05-24
.\" Date: 2020-11-12
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC\&.CONF" "5" "2021\-05\-24" "toxic __VERSION__" "Toxic Manual"
.TH "TOXIC\&.CONF" "5" "2020\-11\-12" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@ -138,6 +138,11 @@ Show welcome message on startup\&. true or false
Enable friend connection change notifications\&. true or false
.RE
.PP
\fBshow_group_connection_msg\fR
.RS 4
Enable group connection change notifications (does not include quit messages)\&. true or false
.RE
.PP
\fBnodelist_update_freq\fR
.RS 4
How often in days to update the DHT nodes list\&. (integer; 0 to disable)
@ -186,6 +191,11 @@ Set user status when attaching and detaching from GNU screen or tmux\&. true or
\fBmplex_away_note\fR
.RS 4
Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&.
.RE
.PP
\fBgroup_part_message\fR
.RS 4
Parting message that will be sent to all groupchat peers when you leave the group\&.
.sp
.if n \{\
.RS 4

View File

@ -87,6 +87,9 @@ OPTIONS
*show_connection_msg*;;
Enable friend connection change notifications. true or false
*show_group_connection_msg*;;
Enable group connection change notifications (does not include quit messages). true or false
*nodelist_update_freq*;;
How often in days to update the DHT nodes list. (integer; 0 to disable)
@ -121,6 +124,9 @@ OPTIONS
detach. When attaching, the status message is set back to the original
value.
*group_part_message*;;
Parting message that will be sent to all groupchat peers when you leave the group.
The following options control whether to output a terminal bell on certain events.
Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window.
These options don't affect the "alerts" option.

View File

@ -56,6 +56,9 @@ ui = {
// true to show friend connection change messages on the home screen
show_connection_msg=true;
// true to show peer connection change messages in groups (setting to false does not include user quit messages)
show_group_connection_msg=true;
// How often in days to update the DHT nodes list. (0 to disable updates)
nodeslist_update_freq=7;
@ -80,11 +83,17 @@ ui = {
// Indicator for normal messages.
line_normal="-";
// Indicator for special messages.
line_special=">";
// true to change status based on screen/tmux attach/detach, false to disable
mplex_away=true;
// Status message to use when status set to away due to screen/tmux detach
mplex_away_note="Away from keyboard, be back soon!"
// Parting message that will be sent to all groupchat peers when you leave the group
group_part_message="Toxic user signing out"
};
audio = {

View File

@ -114,7 +114,6 @@ apk add \
libsodium-dev \
libsodium-static \
linux-headers \
msgpack-c-dev \
ncurses-dev \
ncurses-static \
ncurses-terminfo \
@ -137,20 +136,32 @@ mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
# The git hash of the c-toxcore version we're using
TOXCORE_VERSION="v0.2.16"
TOXCORE_VERSION="0a277b52ea22fe297a8487ac05a0c507a74c6480"
# The sha256sum of the c-toxcore tarball for TOXCORE_VERSION
TOXCORE_HASH="653aa42654b607f0940cecfac873e9ce55605119a90d1dc454d1090ff6ca29c0"
TOXCORE_HASH="a63f3d8c9ee8bf51dd40fd0035914a73b4214e8d314c06f38a366c79c20278fb"
TOXCORE_FILENAME="c-toxcore-$TOXCORE_VERSION.tar.gz"
wget --timeout=10 -O "$TOXCORE_FILENAME" "https://github.com/TokTok/c-toxcore/archive/$TOXCORE_VERSION.tar.gz"
check_sha256 "$TOXCORE_HASH" "$TOXCORE_FILENAME"
tar -o -xf "$TOXCORE_FILENAME"
rm "$TOXCORE_FILENAME"
cd c-toxcore*
mkdir -p "third_party" && cd "third_party"
CMP_VERSION="074e0df43e8a61ea938c4f77f65d1fbccc0c3bf9"
CMP_FILENAME="cmp-$CMP_VERSION.tar.gz"
wget --timeout=10 -O "$CMP_FILENAME" "https://github.com/TokTok/cmp/archive/$CMP_VERSION.tar.gz"
tar -o -xf "$CMP_FILENAME"
mv cmp\-*/* "cmp/"
cd ..
cmake -B_build -H. \
-DUSE_TEST_NETWORK=OFF \
-DENABLE_STATIC=ON \
-DENABLE_SHARED=OFF \
-DCMAKE_BUILD_TYPE=Release \

View File

@ -79,7 +79,6 @@ extern struct user_settings *user_settings;
/* Maximum allowable size of the nodes list */
#define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE)
static struct Thread_Data {
pthread_t tid;
pthread_attr_t attr;

View File

@ -69,11 +69,15 @@ static const char *chat_cmd_list[] = {
"/add",
"/avatar",
"/cancel",
"/cinvite",
"/cjoin",
"/clear",
"/close",
"/connect",
"/exit",
"/gaccept",
"/conference",
"/group",
#ifdef GAMES
"/game",
"/play",
@ -757,8 +761,46 @@ static void chat_onConferenceInvite(ToxWindow *self, Tox *m, int32_t friendnumbe
"invites you to join %s", description);
}
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to %s.", name, description);
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a conference.", name);
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type \"/cjoin\" to join the chat.");
}
static void chat_onGroupInvite(ToxWindow *self, Tox *m, uint32_t friendnumber, const char *invite_data, size_t length,
const char *group_name, size_t group_name_length)
{
UNUSED_VAR(group_name_length);
if (self->num != friendnumber) {
return;
}
if (Friends.list[friendnumber].group_invite.data) {
free(Friends.list[friendnumber].group_invite.data);
}
Friends.list[friendnumber].group_invite.data = malloc(length * sizeof(uint8_t));
if (Friends.list[friendnumber].group_invite.data == NULL) {
return;
}
memcpy(Friends.list[friendnumber].group_invite.data, invite_data, length);
Friends.list[friendnumber].group_invite.length = length;
sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, NULL);
char name[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, name, friendnumber);
if (self->active_box != -1) {
box_silent_notify2(self, NT_WNDALERT_2 | NT_NOFOCUS, self->active_box, "invites you to join group chat");
} else {
box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join group chat");
}
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to join group chat \"%s\"", name, group_name);
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
"Type \"/gaccept <password>\" to join the chat (password is optional).");
}
#ifdef GAMES
@ -1581,6 +1623,7 @@ ToxWindow *new_chat(Tox *m, uint32_t friendnum)
ret->onFileControl = &chat_onFileControl;
ret->onFileRecv = &chat_onFileRecv;
ret->onReadReceipt = &chat_onReadReceipt;
ret->onGroupInvite = &chat_onGroupInvite;
#ifdef AUDIO
ret->onInvite = &chat_onInvite;

View File

@ -29,6 +29,7 @@
#include "file_transfers.h"
#include "friendlist.h"
#include "line_info.h"
#include "groupchats.h"
#include "misc_tools.h"
#include "toxic.h"
#include "windows.h"
@ -178,6 +179,77 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char
#endif
}
void cmd_group_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
if (Friends.list[self->num].group_invite.length == 0) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending group invite");
return;
}
const char *passwd = NULL;
uint16_t passwd_len = 0;
if (argc > 0) {
passwd = argv[1];
passwd_len = strlen(passwd);
}
size_t nick_len = tox_self_get_name_size(m);
char self_nick[TOX_MAX_NAME_LENGTH + 1];
tox_self_get_name(m, (uint8_t *) self_nick);
self_nick[nick_len] = '\0';
Tox_Err_Group_Invite_Accept err;
uint32_t groupnumber = tox_group_invite_accept(m, self->num, Friends.list[self->num].group_invite.data,
Friends.list[self->num].group_invite.length, (const uint8_t *) self_nick, nick_len,
(const uint8_t *) passwd, passwd_len, &err);
if (err != TOX_ERR_GROUP_INVITE_ACCEPT_OK) {
if (err == TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group: Password too long.");
} else {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group (error %d).", err);
}
return;
}
if (init_groupchat_win(m, groupnumber, NULL, 0, Group_Join_Type_Join) == -1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_group_leave(m, groupnumber, NULL, 0, NULL);
return;
}
}
void cmd_group_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
return;
}
int groupnumber = atoi(argv[1]);
if (groupnumber == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
return;
}
Tox_Err_Group_Invite_Friend err;
if (!tox_group_invite_friend(m, groupnumber, self->num, &err)) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d).", err);
return;
}
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnumber);
}
#ifdef GAMES
void cmd_game_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])

View File

@ -29,6 +29,8 @@
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_conference_invite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_conference_join(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_group_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_group_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_game_join(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);

View File

@ -85,11 +85,13 @@ static const char *conference_cmd_list[] = {
"/connect",
"/decline",
"/exit",
"/group",
"/conference",
#ifdef GAMES
"/game",
#endif
"/help",
"/join",
"/log",
#ifdef AUDIO
"/mute",

View File

@ -1,7 +1,7 @@
/* conference_commands.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
* Copyright (C) 2020 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
@ -33,3 +33,4 @@ void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, cha
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* CONFERENCE_COMMANDS_H */

View File

@ -29,6 +29,7 @@
#include "execute.h"
#include "global_commands.h"
#include "conference_commands.h"
#include "groupchat_commands.h"
#include "line_info.h"
#include "misc_tools.h"
#include "notify.h"
@ -49,10 +50,12 @@ static struct cmd_func global_commands[] = {
{ "/decline", cmd_decline },
{ "/exit", cmd_quit },
{ "/conference", cmd_conference },
{ "/group", cmd_groupchat },
#ifdef GAMES
{ "/game", cmd_game },
#endif
{ "/help", cmd_prompt_help },
{ "/join", cmd_join },
{ "/log", cmd_log },
{ "/myid", cmd_myid },
#ifdef QRCODE
@ -81,8 +84,10 @@ static struct cmd_func global_commands[] = {
static struct cmd_func chat_commands[] = {
{ "/cancel", cmd_cancelfile },
{ "/invite", cmd_conference_invite },
{ "/join", cmd_conference_join },
{ "/cinvite", cmd_conference_invite },
{ "/cjoin", cmd_conference_join },
{ "/gaccept", cmd_group_accept },
{ "/invite", cmd_group_invite },
#ifdef GAMES
{ "/play", cmd_game_join },
#endif
@ -117,12 +122,50 @@ static struct cmd_func conference_commands[] = {
{ NULL, NULL },
};
static struct cmd_func groupchat_commands[] = {
{ "/chatid", cmd_chatid },
{ "/disconnect", cmd_disconnect },
{ "/ignore", cmd_ignore },
{ "/kick", cmd_kick },
{ "/list", cmd_list },
{ "/locktopic", cmd_set_topic_lock },
{ "/mod", cmd_mod },
{ "/passwd", cmd_set_passwd },
{ "/peerlimit", cmd_set_peerlimit },
{ "/privacy", cmd_set_privacy },
{ "/rejoin", cmd_rejoin },
{ "/silence", cmd_silence },
{ "/topic", cmd_set_topic },
{ "/unignore", cmd_unignore },
{ "/unmod", cmd_unmod },
{ "/unsilence", cmd_unsilence },
{ "/voice", cmd_set_voice },
{ "/whois", cmd_whois },
#ifdef AUDIO
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
#endif /* AUDIO */
{ NULL, NULL },
};
/* Special commands are commands that only take one argument even if it contains spaces */
static const char special_commands[][MAX_CMDNAME_SIZE] = {
"/add",
"/avatar",
"/gaccept",
"/group",
"/ignore",
"/kick",
"/mod",
"/nick",
"/note",
"/passwd",
"/silence",
"/topic",
"/unignore",
"/unmod",
"/unsilence",
"/whois",
#ifdef PYTHON
"/run",
#endif /* PYTHON */
@ -243,19 +286,29 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
* Note: Global commands must come last in case of duplicate command names
*/
switch (mode) {
case CHAT_COMMAND_MODE:
case CHAT_COMMAND_MODE: {
if (do_command(w, self, m, num_args, chat_commands, args) == 0) {
return;
}
break;
}
case CONFERENCE_COMMAND_MODE:
case CONFERENCE_COMMAND_MODE: {
if (do_command(w, self, m, num_args, conference_commands, args) == 0) {
return;
}
break;
}
case GROUPCHAT_COMMAND_MODE: {
if (do_command(w, self, m, num_args, groupchat_commands, args) == 0) {
return;
}
break;
}
}
if (do_command(w, self, m, num_args, global_commands, args) == 0) {

View File

@ -32,6 +32,7 @@ enum {
GLOBAL_COMMAND_MODE,
CHAT_COMMAND_MODE,
CONFERENCE_COMMAND_MODE,
GROUPCHAT_COMMAND_MODE,
};
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);

View File

@ -116,6 +116,10 @@ void kill_friendlist(ToxWindow *self)
free(Friends.list[i].game_invite.data);
#endif
}
if (Friends.list[i].group_invite.data != NULL) {
free(Friends.list[i].group_invite.data);
}
}
realloc_blocklist(0);
@ -674,6 +678,35 @@ static void friendlist_onConferenceInvite(ToxWindow *self, Tox *m, int32_t num,
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
}
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, uint32_t num, const char *data, size_t length,
const char *group_name, size_t group_name_length)
{
UNUSED_VAR(self);
UNUSED_VAR(data);
UNUSED_VAR(length);
if (num >= Friends.max_idx) {
return;
}
if (Friends.list[num].chatwin != -1) {
return;
}
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
return;
}
char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num);
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED,
"* Group chat invite from %s failed: too many windows are open.", nick);
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
}
/* move friendlist/blocklist cursor up and down */
static void select_friend(wint_t key, int *selected, int num)
{
@ -1398,6 +1431,7 @@ ToxWindow *new_friendlist(void)
ret->onStatusMessageChange = &friendlist_onStatusMessageChange;
ret->onFileRecv = &friendlist_onFileRecv;
ret->onConferenceInvite = &friendlist_onConferenceInvite;
ret->onGroupInvite = &friendlist_onGroupInvite;
#ifdef AUDIO
ret->onInvite = &friendlist_onAV;

View File

@ -46,6 +46,11 @@ struct ConferenceInvite {
bool pending;
};
struct GroupInvite {
uint8_t *data;
uint16_t length;
};
#ifdef GAMES
struct GameInvite {
@ -73,14 +78,16 @@ typedef struct {
Tox_User_Status status;
struct LastOnline last_online;
struct ConferenceInvite conference_invite;
#ifdef GAMES
struct GameInvite game_invite;
#endif
FileTransfer file_receiver[MAX_FILES];
FileTransfer file_sender[MAX_FILES];
struct ConferenceInvite conference_invite;
struct GroupInvite group_invite;
struct FileTransfer file_receiver[MAX_FILES];
struct FileTransfer file_sender[MAX_FILES];
PendingFileTransfer file_send_queue[MAX_FILES];
} ToxicFriend;

View File

@ -26,6 +26,7 @@
#include "avatars.h"
#include "conference.h"
#include "friendlist.h"
#include "groupchats.h"
#include "help.h"
#include "line_info.h"
#include "log.h"
@ -459,6 +460,146 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference [%d] created.", conferencenum);
}
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Group name required");
return;
}
const char *tmp_name = argv[1];
int len = strlen(tmp_name);
if (len == 0 || len > TOX_GROUP_MAX_GROUP_NAME_LENGTH) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid group name.");
return;
}
char name[TOX_GROUP_MAX_GROUP_NAME_LENGTH];
snprintf(name, sizeof(name), "%s", argv[1]);
size_t nick_length = tox_self_get_name_size(m);
char self_nick[TOX_MAX_NAME_LENGTH + 1];
tox_self_get_name(m, (uint8_t *) self_nick);
self_nick[nick_length] = '\0';
Tox_Err_Group_New err;
uint32_t groupnumber = tox_group_new(m, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *) name, len,
(const uint8_t *) self_nick, nick_length, &err);
if (err != TOX_ERR_GROUP_NEW_OK) {
switch (err) {
case TOX_ERR_GROUP_NEW_TOO_LONG: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Group name length cannot exceed %d.",
TOX_GROUP_MAX_GROUP_NAME_LENGTH);
break;
}
case TOX_ERR_GROUP_NEW_EMPTY: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Group name cannot be empty.");
break;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d).", err);
break;
}
}
return;
}
int init = init_groupchat_win(m, groupnumber, name, len, Group_Join_Type_Create);
if (init == -1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_group_leave(m, groupnumber, NULL, 0, NULL);
} else if (init == -2) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
"You have been kicked from a group. Close the window and try again.");
tox_group_leave(m, groupnumber, NULL, 0, NULL);
}
}
void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Chat ID is required.");
return;
}
const char *chat_id = argv[1];
if (strlen(chat_id) != TOX_GROUP_CHAT_ID_SIZE * 2) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID");
return;
}
char id_bin[TOX_GROUP_CHAT_ID_SIZE] = {0};
size_t i;
char xch[3];
uint32_t x;
for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
xch[0] = chat_id[2 * i];
xch[1] = chat_id[2 * i + 1];
xch[2] = '\0';
if (sscanf(xch, "%02x", &x) != 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID.");
return;
}
id_bin[i] = x;
}
const char *passwd = NULL;
uint16_t passwd_len = 0;
if (argc > 1) {
passwd = argv[2];
passwd_len = strlen(passwd);
}
size_t nick_length = tox_self_get_name_size(m);
char self_nick[TOX_MAX_NAME_LENGTH + 1];
tox_self_get_name(m, (uint8_t *) self_nick);
self_nick[nick_length] = '\0';
Tox_Err_Group_Join err;
uint32_t groupnumber = tox_group_join(m, (uint8_t *) id_bin, (const uint8_t *) self_nick, nick_length,
(const uint8_t *) passwd, passwd_len, &err);
if (err != TOX_ERR_GROUP_JOIN_OK) {
if (err == TOX_ERR_GROUP_JOIN_TOO_LONG) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Password length cannot exceed %d.", TOX_GROUP_MAX_PASSWORD_SIZE);
} else {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group (error %d).", err);
}
return;
}
int init = init_groupchat_win(m, groupnumber, NULL, 0, Group_Join_Type_Join);
if (init == -1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_group_leave(m, groupnumber, NULL, 0, NULL);
}
}
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
@ -636,6 +777,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
tox_self_set_name(m, (uint8_t *) nick, len, NULL);
prompt_update_nick(prompt, nick);
set_nick_all_groups(m, nick, len);
store_data(m, DATA_FILE);
}
@ -762,6 +904,8 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
tox_self_set_status(m, status);
prompt_update_status(prompt, status);
set_status_all_groups(m, status);
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);

View File

@ -30,9 +30,11 @@ void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_conference(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_conference(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef QRCODE

912
src/groupchat_commands.c Normal file
View File

@ -0,0 +1,912 @@
/* groupchat_commands.c
*
*
* Copyright (C) 2020 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic 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.
*
* Toxic 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 Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <stdlib.h>
#include "toxic.h"
#include "windows.h"
#include "line_info.h"
#include "misc_tools.h"
#include "log.h"
#include "groupchats.h"
extern GroupChat groupchats[MAX_GROUPCHAT_NUM];
void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char chatid[TOX_GROUP_CHAT_ID_SIZE * 2 + 1] = {0};
char chat_public_key[TOX_GROUP_CHAT_ID_SIZE];
Tox_Err_Group_State_Queries err;
if (!tox_group_get_chat_id(m, self->num, (uint8_t *) chat_public_key, &err)) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve the Chat ID (error %d).", err);
return;
}
for (size_t i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
char xx[3];
snprintf(xx, sizeof(xx), "%02X", chat_public_key[i] & 0xff);
strcat(chatid, xx);
}
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", chatid);
}
void cmd_disconnect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
Tox_Err_Group_Disconnect err;
tox_group_disconnect(m, self->num, &err);
switch (err) {
case TOX_ERR_GROUP_DISCONNECT_OK: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Disconnected from group. Type '/rejoin' to reconnect.");
return;
}
case TOX_ERR_GROUP_DISCONNECT_ALREADY_DISCONNECTED: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Already disconnected. Type '/rejoin' to connect.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to disconnect from group. Error: %d", err);
return;
}
}
}
void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_peer_id_of_identifier(self, nick, &peer_id) != 0) {
return;
}
Tox_Err_Group_Set_Ignore err;
tox_group_set_ignore(m, self->num, peer_id, true, &err);
switch (err) {
case TOX_ERR_GROUP_SET_IGNORE_OK: {
break;
}
case TOX_ERR_GROUP_SET_IGNORE_SELF: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot ignore yourself.");
return;
}
case TOX_ERR_GROUP_SET_IGNORE_PEER_NOT_FOUND: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to toggle ignore on %s (error %d).", nick, err);
return;
}
}
line_info_add(self, true, NULL, NULL, SYS_MSG, 1, BLUE, "-!- Ignoring %s", nick);
}
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
return;
}
const char *nick = argv[1];
uint32_t target_peer_id;
if (group_get_peer_id_of_identifier(self, nick, &target_peer_id) != 0) {
return;
}
Tox_Err_Group_Mod_Kick_Peer err;
tox_group_mod_kick_peer(m, self->num, target_peer_id, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_KICK_PEER_OK: {
char self_nick[TOX_MAX_NAME_LENGTH + 1];
get_group_self_nick_truncate(m, self_nick, self->num);
line_info_add(self, true, NULL, NULL, SYS_MSG, 1, RED, "-!- %s has been kicked by %s", nick, self_nick);
groupchat_onGroupPeerExit(self, m, self->num, target_peer_id, TOX_GROUP_EXIT_TYPE_KICK, nick, strlen(nick), NULL, 0);
return;
}
case TOX_ERR_GROUP_MOD_KICK_PEER_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to kick %s.", nick);
return;
}
case TOX_ERR_GROUP_MOD_KICK_PEER_SELF: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot kick yourself.");
return;
}
case TOX_ERR_GROUP_MOD_KICK_PEER_PEER_NOT_FOUND: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Specified nick or public key is invalid.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to kick %s from the group (error %d).", nick,
err);
return;
}
}
}
void cmd_list(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
GroupChat *chat = get_groupchat(self->num);
if (!chat) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch GroupChat object.");
return;
}
for (size_t i = 0; i < chat->max_idx; ++i) {
GroupPeer *peer = &chat->peer_list[i];
if (!peer->active) {
continue;
}
char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
for (size_t j = 0; j < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++j) {
char d[3];
snprintf(d, sizeof(d), "%02X", peer->public_key[j] & 0xff);
strcat(pk_string, d);
}
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s : %s", pk_string, peer->name);
}
}
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
return;
}
const char *nick = argv[1];
uint32_t target_peer_id;
if (group_get_peer_id_of_identifier(self, nick, &target_peer_id) != 0) {
return;
}
Tox_Err_Group_Self_Query s_err;
uint32_t self_peer_id = tox_group_self_get_peer_id(m, self->num, &s_err);
if (s_err != TOX_ERR_GROUP_SELF_QUERY_OK) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch self peer_id.");
return;
}
Tox_Err_Group_Mod_Set_Role err;
tox_group_mod_set_role(m, self->num, target_peer_id, TOX_GROUP_ROLE_MODERATOR, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
groupchat_onGroupModeration(self, m, self->num, self_peer_id, target_peer_id, TOX_GROUP_MOD_EVENT_MODERATOR);
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to promote moderators.");
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s is already a moderator.", nick);
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_SELF: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot make yourself a moderator.");
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to promote peer to moderator (error %d).", err);
return;
}
}
}
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
return;
}
const char *nick = argv[1];
uint32_t target_peer_id;
if (group_get_peer_id_of_identifier(self, nick, &target_peer_id) != 0) {
return;
}
Tox_Err_Group_Self_Query s_err;
uint32_t self_peer_id = tox_group_self_get_peer_id(m, self->num, &s_err);
if (s_err != TOX_ERR_GROUP_SELF_QUERY_OK) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch self peer_id.");
return;
}
if (tox_group_peer_get_role(m, self->num, target_peer_id, NULL) != TOX_GROUP_ROLE_MODERATOR) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s is not a moderator.", nick);
return;
}
Tox_Err_Group_Mod_Set_Role err;
tox_group_mod_set_role(m, self->num, target_peer_id, TOX_GROUP_ROLE_USER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
groupchat_onGroupModeration(self, m, self->num, self_peer_id, target_peer_id, TOX_GROUP_MOD_EVENT_USER);
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unmod %s.", nick);
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_SELF: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot remove your own moderator status.");
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to revoke moderator powers from %s (error %d).", nick,
err);
return;
}
}
}
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *passwd = NULL;
size_t len = 0;
if (argc > 0) {
passwd = argv[1];
len = strlen(passwd);
}
Tox_Err_Group_Founder_Set_Password err;
tox_group_founder_set_password(m, self->num, (uint8_t *) passwd, len, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK: {
if (len > 0) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Password has been set to %s.", passwd);
} else {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Password has been unset.");
}
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Password length must not exceed %d.",
TOX_GROUP_MAX_PASSWORD_SIZE);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the password.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set password (error %d).", err);
return;
}
}
}
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
int maxpeers = 0;
if (argc < 1) {
Tox_Err_Group_State_Queries err;
uint32_t maxpeers = tox_group_get_peer_limit(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve peer limit (error %d).", err);
return;
}
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer limit is set to %d", maxpeers);
return;
}
maxpeers = atoi(argv[1]);
if (maxpeers <= 0) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer limit must be a value greater than 0.");
return;
}
Tox_Err_Group_Founder_Set_Peer_Limit err;
tox_group_founder_set_peer_limit(m, self->num, maxpeers, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer limit has been set to %d.", maxpeers);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the peer limit.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the peer limit (error %d).", err);
return;
}
}
}
void cmd_set_voice(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
Tox_Group_Voice_State voice_state;
if (argc < 1) {
Tox_Err_Group_State_Queries err;
voice_state = tox_group_get_voice_state(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve voice state (error %d).", err);
return;
}
switch (voice_state) {
case TOX_GROUP_VOICE_STATE_ALL: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Voice state is set to ALL");
break;
}
case TOX_GROUP_VOICE_STATE_MODERATOR: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Voice state is set to MODERATOR");
break;
}
case TOX_GROUP_VOICE_STATE_FOUNDER: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Voice state is set to FOUNDER");
break;
}
default:
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Error: Unknown voice state: %d", voice_state);
return;
}
return;
}
const char *vstate_str = argv[1];
if (strcasecmp(vstate_str, "mod") == 0) {
voice_state = TOX_GROUP_VOICE_STATE_MODERATOR;
} else if (strcasecmp(vstate_str, "founder") == 0) {
voice_state = TOX_GROUP_VOICE_STATE_FOUNDER;
} else if (strcasecmp(vstate_str, "all") == 0) {
voice_state = TOX_GROUP_VOICE_STATE_ALL;
} else {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
"voice state must be \"all\", \"mod\", or \"founder\".");
return;
}
Tox_Err_Group_Founder_Set_Voice_State err;
tox_group_founder_set_voice_state(m, self->num, voice_state, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_OK: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Voice state has been set to %s.", vstate_str);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the voice state.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Error setting voice state (error %d).", err);
return;
}
}
}
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *pstate_str = NULL;
Tox_Group_Privacy_State privacy_state;
if (argc < 1) {
Tox_Err_Group_State_Queries err;
privacy_state = tox_group_get_privacy_state(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve privacy state (error %d).", err);
return;
}
pstate_str = privacy_state == TOX_GROUP_PRIVACY_STATE_PRIVATE ? "private" : "public";
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Privacy state is set to %s.", pstate_str);
return;
}
pstate_str = argv[1];
if (strcasecmp(pstate_str, "private") != 0 && strcasecmp(pstate_str, "public") != 0) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Privacy state must be \"private\" or \"public\".");
return;
}
privacy_state = strcasecmp(pstate_str,
"private") == 0 ? TOX_GROUP_PRIVACY_STATE_PRIVATE : TOX_GROUP_PRIVACY_STATE_PUBLIC;
Tox_Err_Group_Founder_Set_Privacy_State err;
tox_group_founder_set_privacy_state(m, self->num, privacy_state, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Privacy state has been set to %s.", pstate_str);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the privacy state.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Error setting privacy state (error %d).", err);
return;
}
}
}
void cmd_set_topic_lock(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
Tox_Group_Topic_Lock topic_lock;
const char *tlock_str = NULL;
if (argc < 1) {
Tox_Err_Group_State_Queries err;
topic_lock = tox_group_get_topic_lock(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic lock (error %d).", err);
return;
}
tlock_str = topic_lock == TOX_GROUP_TOPIC_LOCK_ENABLED ? "Enabled" : "Disabled";
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic lock is %s.", tlock_str);
return;
}
tlock_str = argv[1];
if (strcasecmp(tlock_str, "on") != 0 && strcasecmp(tlock_str, "off") != 0) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic lock must be \"on\" or \"off\".");
return;
}
topic_lock = strcasecmp(tlock_str, "on") == 0 ? TOX_GROUP_TOPIC_LOCK_ENABLED : TOX_GROUP_TOPIC_LOCK_DISABLED;
const char *display_str = (topic_lock == TOX_GROUP_TOPIC_LOCK_ENABLED) ? "enabled" : "disabled";
Tox_Err_Group_Founder_Set_Topic_Lock err;
tox_group_founder_set_topic_lock(m, self->num, topic_lock, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic lock has been %s.", display_str);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the topic lock.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Error setting topic lock (%d).", err);
return;
}
}
}
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
return;
}
const char *nick = argv[1];
uint32_t target_peer_id;
if (group_get_peer_id_of_identifier(self, nick, &target_peer_id) != 0) {
return;
}
Tox_Err_Group_Self_Query s_err;
uint32_t self_peer_id = tox_group_self_get_peer_id(m, self->num, &s_err);
if (s_err != TOX_ERR_GROUP_SELF_QUERY_OK) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch self peer_id.");
return;
}
Tox_Err_Group_Mod_Set_Role err;
tox_group_mod_set_role(m, self->num, target_peer_id, TOX_GROUP_ROLE_OBSERVER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
groupchat_onGroupModeration(self, m, self->num, self_peer_id, target_peer_id, TOX_GROUP_MOD_EVENT_OBSERVER);
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to silence %s.", nick);
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s is already silenced.", nick);
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_SELF: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot silence yourself.");
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to silence %s (error %d).", nick, err);
return;
}
}
}
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
return;
}
const char *nick = argv[1];
uint32_t target_peer_id;
if (group_get_peer_id_of_identifier(self, nick, &target_peer_id) != 0) {
return;
}
if (tox_group_peer_get_role(m, self->num, target_peer_id, NULL) != TOX_GROUP_ROLE_OBSERVER) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s is not silenced.", nick);
return;
}
Tox_Err_Group_Self_Query s_err;
uint32_t self_peer_id = tox_group_self_get_peer_id(m, self->num, &s_err);
if (s_err != TOX_ERR_GROUP_SELF_QUERY_OK) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch self peer_id.");
return;
}
Tox_Err_Group_Mod_Set_Role err;
tox_group_mod_set_role(m, self->num, target_peer_id, TOX_GROUP_ROLE_USER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
groupchat_onGroupModeration(self, m, self->num, self_peer_id, target_peer_id, TOX_GROUP_MOD_EVENT_USER);
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unsilence %s.", nick);
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s is not silenced.", nick);
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_SELF: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot unsilence yourself.");
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to unsilence %s (error %d).", nick, err);
return;
}
}
}
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
Tox_Err_Group_Reconnect err;
if (!tox_group_reconnect(m, self->num, &err)) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to rejoin group (error %d).", err);
return;
}
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Reconnecting to group...");
groupchat_rejoin(self, m);
}
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
Tox_Err_Group_State_Queries err;
size_t tlen = tox_group_get_topic_size(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic length (error %d).", err);
return;
}
if (tlen > 0) {
char cur_topic[TOX_GROUP_MAX_TOPIC_LENGTH + 1];
if (!tox_group_get_topic(m, self->num, (uint8_t *) cur_topic, &err)) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic (error %d).", err);
return;
}
cur_topic[tlen] = 0;
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic is set to: %s", cur_topic);
} else {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic is not set.");
}
return;
}
const char *topic = argv[1];
Tox_Err_Group_Topic_Set err;
tox_group_set_topic(m, self->num, (uint8_t *) topic, strlen(topic), &err);
switch (err) {
case TOX_ERR_GROUP_TOPIC_SET_OK: {
/* handled below switch */
break;
}
case TOX_ERR_GROUP_TOPIC_SET_TOO_LONG: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic length must not exceed %d.", TOX_GROUP_MAX_TOPIC_LENGTH);
return;
}
case TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the topic.");
return;
}
case TOX_ERR_GROUP_TOPIC_SET_DISCONNECTED: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You are disconnected from the group.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the topic (error %d).", err);
return;
}
}
char self_nick[TOX_MAX_NAME_LENGTH + 1];
get_group_self_nick_truncate(m, self_nick, self->num);
line_info_add(self, true, NULL, NULL, SYS_MSG, 1, MAGENTA, "-!- You set the topic to: %s", topic);
char tmp_event[MAX_STR_SIZE];
snprintf(tmp_event, sizeof(tmp_event), "set topic to %s", topic);
write_to_log(tmp_event, self_nick, self->chatwin->log, true);
}
void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_peer_id_of_identifier(self, nick, &peer_id) != 0) {
return;
}
Tox_Err_Group_Set_Ignore err;
tox_group_set_ignore(m, self->num, peer_id, false, &err);
switch (err) {
case TOX_ERR_GROUP_SET_IGNORE_OK: {
break;
}
case TOX_ERR_GROUP_SET_IGNORE_SELF: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot unignore yourself.");
return;
}
case TOX_ERR_GROUP_SET_IGNORE_PEER_NOT_FOUND: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to toggle ignore on %s (error %d).", nick, err);
return;
}
}
line_info_add(self, true, NULL, NULL, SYS_MSG, 1, BLUE, "-!- You are no longer ignoring %s", nick);
}
void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified.");
return;
}
GroupChat *chat = get_groupchat(self->num);
if (!chat) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch GroupChat object.");
return;
}
const char *identifier = argv[1];
bool is_public_key = false;
for (size_t i = 0; i < chat->max_idx && !is_public_key; ++i) {
uint32_t peer_id;
if (group_get_public_key_peer_id(self->num, identifier, &peer_id) == 0) {
is_public_key = true;
} else {
GroupPeer *peer = &chat->peer_list[i];
if (!peer->active) {
continue;
}
if (strcmp(identifier, peer->name) != 0) {
continue;
}
peer_id = peer->peer_id;
}
int peer_index = get_peer_index(self->num, peer_id);
if (peer_index < 0) {
continue;
}
GroupPeer *peer = &chat->peer_list[peer_index];
const char *status_str = "Online";
if (peer->status == TOX_USER_STATUS_BUSY) {
status_str = "Busy";
} else if (peer->status == TOX_USER_STATUS_AWAY) {
status_str = "Away";
}
const char *role_str = "User";
if (peer->role == TOX_GROUP_ROLE_FOUNDER) {
role_str = "Founder";
} else if (peer->role == TOX_GROUP_ROLE_MODERATOR) {
role_str = "Moderator";
} else if (peer->role == TOX_GROUP_ROLE_OBSERVER) {
role_str = "Observer";
}
char last_seen_str[128];
get_elapsed_time_str_alt(last_seen_str, sizeof(last_seen_str),
get_unix_time() - peer->last_active);
char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
for (size_t i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) {
char d[3];
snprintf(d, sizeof(d), "%02X", peer->public_key[i] & 0xff);
strcat(pk_string, d);
}
Tox_Err_Group_Peer_Query conn_err;
Tox_Connection connection_type = tox_group_peer_get_connection_status(m, self->num, peer_id, &conn_err);
const char *connection_type_str = "Unknown";
if (conn_err == TOX_ERR_GROUP_PEER_QUERY_OK || connection_type != TOX_CONNECTION_NONE) {
connection_type_str = connection_type == TOX_CONNECTION_UDP ? "UDP" : "TCP";
}
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Public key: %s", pk_string);
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Name: %s", peer->name);
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Role: %s", role_str);
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Status: %s", status_str);
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Connection: %s", connection_type_str);
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Last active: %s", last_seen_str);
}
}

50
src/groupchat_commands.h Normal file
View File

@ -0,0 +1,50 @@
/* groupchat_commands.h
*
*
* Copyright (C) 2020 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic 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.
*
* Toxic 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 Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GROUPCHAT_COMMANDS_H
#define GROUPCHAT_COMMANDS_H
#include "windows.h"
#include "toxic.h"
void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_disconnect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_list(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_prune(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_voice(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_topic_lock(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* GROUPCHAT_COMMANDS_H */

1832
src/groupchats.c Normal file

File diff suppressed because it is too large Load Diff

111
src/groupchats.h Normal file
View File

@ -0,0 +1,111 @@
/* groupchats.h
*
*
* Copyright (C) 2020 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic 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.
*
* Toxic 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 Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GROUPCHATS_H
#define GROUPCHATS_H
#include "toxic.h"
#include "windows.h"
#ifndef SIDEWAR_WIDTH
#define SIDEBAR_WIDTH 16
#endif
#define MAX_GROUPCHAT_NUM (MAX_WINDOWS_NUM - 2)
typedef enum Group_Join_Type {
Group_Join_Type_Create,
Group_Join_Type_Join,
Group_Join_Type_Load,
} Group_Join_Type;
typedef struct GroupPeer {
bool active;
char name[TOX_MAX_NAME_LENGTH];
size_t name_length;
char prev_name[TOX_MAX_NAME_LENGTH];
uint32_t peer_id;
uint8_t public_key[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
TOX_USER_STATUS status;
Tox_Group_Role role;
uint64_t last_active;
} GroupPeer;
typedef struct {
GroupPeer *peer_list;
char **name_list; /* List of peer names, needed for tab completion */
uint32_t num_peers; /* Number of peers in the chat/name_list array */
uint32_t max_idx; /* Maximum peer list index - 1 */
char group_name[TOX_GROUP_MAX_GROUP_NAME_LENGTH + 1];
size_t group_name_length;
uint32_t groupnumber;
bool active;
uint64_t time_connected; /* The time we successfully connected to the group */
int chatwin;
int side_pos; /* current position of the sidebar - used for scrolling up and down */
} GroupChat;
void exit_groupchat(ToxWindow *self, Tox *m, uint32_t groupnumber, const char *partmessage, size_t length);
int init_groupchat_win(Tox *m, uint32_t groupnumber, const char *groupname, size_t length, Group_Join_Type join_type);
void set_nick_all_groups(Tox *m, const char *new_nick, size_t length);
void set_status_all_groups(Tox *m, uint8_t status);
int get_peer_index(uint32_t groupnumber, uint32_t peer_id);
void groupchat_onGroupPeerExit(ToxWindow *self, Tox *m, uint32_t groupnumber, uint32_t peer_id,
Tox_Group_Exit_Type exit_type,
const char *name, size_t name_len, const char *partmessage, size_t len);
void groupchat_onGroupModeration(ToxWindow *self, Tox *m, uint32_t groupnumber, uint32_t src_peer_id,
uint32_t tgt_peer_id, Tox_Group_Mod_Event type);
void groupchat_rejoin(ToxWindow *self, Tox *m);
/* Puts the peer_id associated with `identifier` in `peer_id`. The string may be
* either a nick or a public key.
*
* On failure, `peer_id` is set to (uint32_t)-1.
*
* This function is intended to be a helper for groupchat_commands.c and will print
* error messages to `self`.
* Return 0 on success.
* Return -1 if the identifier does not correspond with a peer in the group.
* Return -2 if the identifier is a nick and the nick is in use by multiple peers.
*/
int group_get_peer_id_of_identifier(ToxWindow *self, const char *identifier, uint32_t *peer_id);
/* Gets the peer_id associated with `public_key`.
*
* Returns 0 on success.
* Returns -1 on failure or if `public_key` is invalid.
*/
int group_get_public_key_peer_id(uint32_t groupnumber, const char *public_key, uint32_t *peer_id);
/* destroys and re-creates groupchat window */
void redraw_groupchat_win(ToxWindow *self);
/*
* Return a GroupChat pointer associated with groupnumber.
* Return NULL if groupnumber is invalid.
*/
GroupChat *get_groupchat(uint32_t groupnumber);
#endif /* #define GROUPCHATS_H */

View File

@ -110,6 +110,12 @@ static void help_draw_menu(ToxWindow *self)
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "nference commands\n");
wprintw(win, " g");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "r");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "oupchat commands\n");
#ifdef PYTHON
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " p");
@ -171,16 +177,17 @@ static void help_draw_global(ToxWindow *self)
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
wprintw(win, " /accept <id> : Accept friend request\n");
wprintw(win, " /avatar <path> : Set an avatar (leave path empty to unset)\n");
wprintw(win, " /conference <type> : Create a conference where type: text | audio\n");
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
wprintw(win, " /decline <id> : Decline friend request\n");
wprintw(win, " /requests : List pending friend requests\n");
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
wprintw(win, " /status <type> : Set status (Online, Busy, Away)\n");
wprintw(win, " /note <msg> : Set a personal note\n");
wprintw(win, " /nick <nick> : Set your nickname\n");
wprintw(win, " /nospam <value> : Change part of your Tox ID to stop spam\n");
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
wprintw(win, " /conference <type> : Create a conference where type: text | audio\n");
wprintw(win, " /myid : Print your Tox ID\n");
wprintw(win, " /group <name> : Create a new group chat\n");
#ifdef GAMES
wprintw(win, " /game : Play a game\n");
#endif /* GAMES */
@ -278,6 +285,44 @@ static void help_draw_chat(ToxWindow *self)
wnoutrefresh(win);
}
static void help_draw_groupchats(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, "Groupchat commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " /chatid : Print this group's ID\n");
wprintw(win, " /close <m> : Leave the group with an optional part message\n");
wprintw(win, " /disconnect : Disconnect from the group (credentials retained)\n");
wprintw(win, " /ignore <name> : Ignore a peer\n");
wprintw(win, " /unignore <name> : Unignore an ignored peer\n");
wprintw(win, " /kick <name> : Remove a peer from the group\n");
wprintw(win, " /list : Print a list of peers currently in the group\n");
wprintw(win, " /locktopic : Set the topic lock: on | off\n");
wprintw(win, " /mod <name> : Promote a peer to moderator\n");
wprintw(win, " /passwd <s> : Set a password needed to join the group\n");
wprintw(win, " /peerlimit <n> : Set the maximum number of peers that can join\n");
wprintw(win, " /privacy <state> : Set the privacy state: private | public\n");
wprintw(win, " /rejoin : Reconnect to the groupchat\n");
wprintw(win, " /silence <name> : Silence a peer for the entire group\n");
wprintw(win, " /unsilence <name> : Unsilence a silenced peer\n");
wprintw(win, " /status <type> : Set your status\n");
wprintw(win, " /topic <m> : Set the group topic\n");
wprintw(win, " /unmod <name> : Demote a moderator\n");
wprintw(win, " /voice <state> : Set the voice state: all | mod | founder\n");
wprintw(win, " /whisper <name> <m> : Send a private message to a peer\n");
wprintw(win, " /whois <name> : Print whois info for a peer\n");
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wnoutrefresh(win);
}
static void help_draw_keys(ToxWindow *self)
{
WINDOW *win = self->help->win;
@ -443,6 +488,11 @@ void help_onKey(ToxWindow *self, wint_t key)
help_init_menu(self);
self->help->type = HELP_MENU;
break;
case L'r':
help_init_window(self, 26, 80);
self->help->type = HELP_GROUP;
break;
}
}
@ -479,5 +529,9 @@ void help_onDraw(ToxWindow *self)
help_draw_plugin(self);
break;
#endif /* PYTHON */
case HELP_GROUP:
help_draw_groupchats(self);
break;
}
}

View File

@ -29,6 +29,7 @@
typedef enum {
HELP_MENU,
HELP_GLOBAL,
HELP_GROUP,
HELP_CHAT,
HELP_CONFERENCE,
HELP_KEYS,

View File

@ -30,6 +30,7 @@
#include "line_info.h"
#include "misc_tools.h"
#include "notify.h"
#include "groupchats.h"
#include "settings.h"
#include "toxic.h"
#include "toxic_strings.h"
@ -337,6 +338,9 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x)
if (self->type == WINDOW_TYPE_CONFERENCE) {
self->show_peerlist ^= 1;
redraw_conference_win(self);
} else if (self->type == WINDOW_TYPE_GROUPCHAT) {
self->show_peerlist ^= 1;
redraw_groupchat_win(self);
}
match = true;

View File

@ -30,6 +30,7 @@
#include <string.h>
#include "conference.h"
#include "groupchats.h"
#include "line_info.h"
#include "message_queue.h"
#include "misc_tools.h"
@ -421,18 +422,29 @@ int line_info_add(ToxWindow *self, bool show_timestamp, const char *name1, const
switch (type) {
case IN_ACTION:
/* fallthrough */
case OUT_ACTION_READ:
/* fallthrough */
case OUT_ACTION:
len += strlen(user_settings->line_normal) + 2; // two spaces
break;
case IN_MSG:
case OUT_MSG_READ:
/* fallthrough */
case OUT_MSG:
len += strlen(user_settings->line_normal) + 3; // two spaces and a ':' char
break;
case IN_PRVT_MSG:
/* fallthrough */
case OUT_PRVT_MSG:
len += strlen(user_settings->line_special) + 3;
break;
case CONNECTION:
len += strlen(user_settings->line_join) + 2; // two spaces
break;
@ -546,7 +558,7 @@ void line_info_print(ToxWindow *self)
return;
}
if (self->type == WINDOW_TYPE_CONFERENCE) {
if (self->type == WINDOW_TYPE_CONFERENCE || self->type == WINDOW_TYPE_GROUPCHAT) {
wmove(win, 0, 0);
} else {
wmove(win, TOP_BAR_HEIGHT, 0);
@ -582,7 +594,7 @@ void line_info_print(ToxWindow *self)
case OUT_MSG_READ:
/* fallthrough */
case IN_MSG:
case IN_MSG: {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
@ -620,6 +632,49 @@ void line_info_print(ToxWindow *self)
waddch(win, '\n');
break;
}
case IN_PRVT_MSG:
/* fallthrough */
case OUT_PRVT_MSG: {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
int nameclr = GREEN;
if (line->colour) {
nameclr = line->colour;
} else if (type == IN_MSG) {
nameclr = CYAN;
}
wattron(win, COLOR_PAIR(nameclr));
wprintw(win, "%s %s: ", (type != OUT_PRVT_MSG && type != IN_PRVT_MSG) ?
user_settings->line_normal :
user_settings->line_special,
line->name1);
wattroff(win, COLOR_PAIR(nameclr));
if (line->msg[0] == '>') {
wattron(win, COLOR_PAIR(GREEN));
} else if (line->msg[0] == '<') {
wattron(win, COLOR_PAIR(RED));
}
print_wrap(win, line, max_x, max_y);
if (line->msg[0] == '>') {
wattroff(win, COLOR_PAIR(GREEN));
} else if (line->msg[0] == '<') {
wattroff(win, COLOR_PAIR(RED));
}
waddch(win, '\n');
break;
}
case OUT_ACTION_READ:
@ -627,7 +682,7 @@ void line_info_print(ToxWindow *self)
case OUT_ACTION:
/* fallthrough */
case IN_ACTION:
case IN_ACTION: {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
@ -639,8 +694,9 @@ void line_info_print(ToxWindow *self)
waddch(win, '\n');
break;
}
case SYS_MSG:
case SYS_MSG: {
if (line->timestr[0]) {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr);
@ -667,8 +723,9 @@ void line_info_print(ToxWindow *self)
}
break;
}
case PROMPT:
case PROMPT: {
wattron(win, COLOR_PAIR(GREEN));
wprintw(win, "$ ");
wattroff(win, COLOR_PAIR(GREEN));
@ -679,8 +736,9 @@ void line_info_print(ToxWindow *self)
waddch(win, '\n');
break;
}
case CONNECTION:
case CONNECTION: {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
@ -698,8 +756,9 @@ void line_info_print(ToxWindow *self)
wattroff(win, COLOR_PAIR(line->colour));
break;
}
case DISCONNECTION:
case DISCONNECTION: {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
@ -717,8 +776,9 @@ void line_info_print(ToxWindow *self)
wattroff(win, COLOR_PAIR(line->colour));
break;
}
case NAME_CHANGE:
case NAME_CHANGE: {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
@ -737,6 +797,7 @@ void line_info_print(ToxWindow *self)
wattroff(win, COLOR_PAIR(MAGENTA));
break;
}
}
line = line->next;

View File

@ -39,6 +39,8 @@ typedef enum LINE_TYPE {
IN_ACTION,
OUT_ACTION,
OUT_ACTION_READ, /* same as OUT_MSG_READ but for actions */
IN_PRVT_MSG, /* PRVT should only be used for groups */
OUT_PRVT_MSG,
PROMPT,
CONNECTION,
DISCONNECTION,

View File

@ -136,6 +136,26 @@ void get_elapsed_time_str(char *buf, int bufsize, time_t secs)
}
}
/* Converts seconds to string in format H hours, m minutes, s seconds */
void get_elapsed_time_str_alt(char *buf, int bufsize, uint64_t secs)
{
if (!secs) {
return;
}
long int seconds = secs % 60;
long int minutes = (secs % 3600) / 60;
long int hours = secs / 3600;
if (!minutes && !hours) {
snprintf(buf, bufsize, "%ld seconds", seconds);
} else if (!hours) {
snprintf(buf, bufsize, "%ld minutes, %ld seconds", minutes, seconds);
} else {
snprintf(buf, bufsize, "%ld hours, %ld minutes, %ld seconds", hours, minutes, seconds);
}
}
/*
* Converts a hexidecimal string representation of a Tox public key to binary format and puts
* the result in output.
@ -470,6 +490,59 @@ on_error:
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
buf[len] = '\0';
return len;
}
/* same as get_nick_truncate but for groupchats */
size_t get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, uint32_t groupnum)
{
Tox_Err_Group_Peer_Query err;
size_t len = tox_group_peer_get_name_size(m, groupnum, peer_id, &err);
if (err != TOX_ERR_GROUP_PEER_QUERY_OK || len == 0) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
} else {
tox_group_peer_get_name(m, groupnum, peer_id, (uint8_t *) buf, &err);
if (err != TOX_ERR_GROUP_PEER_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
}
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
filter_str(buf, len);
return len;
}
/* same as get_group_nick_truncate() but for self. */
size_t get_group_self_nick_truncate(Tox *m, char *buf, uint32_t groupnum)
{
Tox_Err_Group_Self_Query err;
size_t len = tox_group_self_get_name_size(m, groupnum, &err);
if (err != TOX_ERR_GROUP_SELF_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
} else {
tox_group_self_get_name(m, groupnum, (uint8_t *) buf, &err);
if (err != TOX_ERR_GROUP_SELF_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
}
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = 0;
filter_str(buf, len);
return len;
}
@ -605,7 +678,8 @@ void set_window_title(ToxWindow *self, const char *title, int len)
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
if (self->type == WINDOW_TYPE_CONFERENCE) { /* keep conferencenumber in title for invites */
/* keep conferencenumber in title */
if (self->type == WINDOW_TYPE_CONFERENCE || self->type == WINDOW_TYPE_GROUPCHAT) {
snprintf(cpy, sizeof(cpy), "%u %s", self->num, title);
} else {
snprintf(cpy, sizeof(cpy), "%s", title);

View File

@ -97,6 +97,9 @@ time_t get_unix_time(void);
/* Puts the current time in buf in the format of specified by the config */
void get_time_str(char *buf, size_t bufsize);
/* Converts seconds to string in format H hours, m minutes, s seconds */
void get_elapsed_time_str_alt(char *buf, int bufsize, uint64_t secs);
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
void get_elapsed_time_str(char *buf, int bufsize, time_t secs);
@ -168,6 +171,12 @@ size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
/* same as get_nick_truncate but for conferences */
int get_conference_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t conferencenum);
/* same as get_nick_truncate but for groupchats */
size_t get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, uint32_t groupnum);
/* same as get_group_nick_truncate() but for self. */
size_t get_group_self_nick_truncate(Tox *m, char *buf, uint32_t groupnum);
/* copies data to msg buffer.
returns length of msg, which will be no larger than size-1 */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length);

View File

@ -59,11 +59,13 @@ static const char *glob_cmd_list[] = {
"/connect",
"/decline",
"/exit",
"/group",
"/conference",
#ifdef GAMES
"/game",
#endif
"/help",
"/join",
"/log",
"/myid",
#ifdef QRCODE

View File

@ -63,6 +63,7 @@ static struct ui_strings {
const char *show_typing_other;
const char *show_welcome_msg;
const char *show_connection_msg;
const char *show_group_connection_msg;
const char *nodeslist_update_freq;
const char *autosave_freq;
@ -70,6 +71,8 @@ static struct ui_strings {
const char *line_quit;
const char *line_alert;
const char *line_normal;
const char *line_special;
const char *group_part_message;
const char *mplex_away;
const char *mplex_away_note;
@ -97,14 +100,19 @@ static struct ui_strings {
"show_typing_other",
"show_welcome_msg",
"show_connection_msg",
"show_group_connection_msg",
"nodeslist_update_freq",
"autosave_freq",
"line_join",
"line_quit",
"line_alert",
"line_normal",
"line_special",
"group_part_message",
"mplex_away",
"mplex_away_note",
"color_bar_bg",
"color_bar_fg",
"color_bar_accent",
@ -130,13 +138,15 @@ static void ui_defaults(struct user_settings *settings)
settings->show_typing_other = SHOW_TYPING_ON;
settings->show_welcome_msg = SHOW_WELCOME_MSG_ON;
settings->show_connection_msg = SHOW_CONNECTION_MSG_ON;
settings->nodeslist_update_freq = 7;
settings->show_group_connection_msg = SHOW_GROUP_CONNECTION_MSG_ON;
settings->nodeslist_update_freq = 1;
settings->autosave_freq = 600;
snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN);
snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT);
snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT);
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
snprintf(settings->line_special, LINE_HINT_MAX + 1, "%s", LINE_SPECIAL);
settings->mplex_away = MPLEX_ON;
snprintf(settings->mplex_away_note, sizeof(settings->mplex_away_note), "%s", MPLEX_AWAY_NOTE);
@ -398,6 +408,7 @@ int settings_load(struct user_settings *s, const char *patharg)
config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other);
config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg);
config_setting_lookup_bool(setting, ui_strings.show_connection_msg, &s->show_connection_msg);
config_setting_lookup_bool(setting, ui_strings.show_group_connection_msg, &s->show_group_connection_msg);
config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size);
config_setting_lookup_int(setting, ui_strings.notification_timeout, &s->notification_timeout);
@ -420,11 +431,19 @@ int settings_load(struct user_settings *s, const char *patharg)
snprintf(s->line_normal, sizeof(s->line_normal), "%s", str);
}
if (config_setting_lookup_string(setting, ui_strings.line_special, &str)) {
snprintf(s->line_special, sizeof(s->line_special), "%s", str);
}
config_setting_lookup_bool(setting, ui_strings.mplex_away, &s->mplex_away);
if (config_setting_lookup_string(setting, ui_strings.mplex_away_note, &str)) {
snprintf(s->mplex_away_note, sizeof(s->mplex_away_note), "%s", str);
}
if (config_setting_lookup_string(setting, ui_strings.group_part_message, &str)) {
snprintf(s->group_part_message, sizeof(s->group_part_message), "%s", str);
}
}
/* paths */

View File

@ -54,6 +54,7 @@ struct user_settings {
int show_typing_other; /* boolean */
int show_welcome_msg; /* boolean */
int show_connection_msg; /* boolean */
int show_group_connection_msg; /* boolean */
int nodeslist_update_freq; /* int (<= 0 to disable updates) */
int autosave_freq; /* int (<= 0 to disable autosave) */
@ -61,6 +62,7 @@ struct user_settings {
char line_quit[LINE_HINT_MAX + 1];
char line_alert[LINE_HINT_MAX + 1];
char line_normal[LINE_HINT_MAX + 1];
char line_special[LINE_HINT_MAX + 1];
char download_path[PATH_MAX];
char chatlogs_path[PATH_MAX];
@ -85,6 +87,7 @@ struct user_settings {
int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */
char mplex_away_note [TOX_MAX_STATUS_MESSAGE_LENGTH];
char group_part_message[TOX_GROUP_MAX_PART_LENGTH];
#ifdef AUDIO
int audio_in_dev;
@ -120,6 +123,9 @@ enum settings_values {
SHOW_CONNECTION_MSG_OFF = 0,
SHOW_CONNECTION_MSG_ON = 1,
SHOW_GROUP_CONNECTION_MSG_OFF = 0,
SHOW_GROUP_CONNECTION_MSG_ON = 1,
DFLT_HST_SIZE = 700,
MPLEX_OFF = 0,
@ -130,6 +136,7 @@ enum settings_values {
#define LINE_QUIT "<--"
#define LINE_ALERT "-!-"
#define LINE_NORMAL "-"
#define LINE_SPECIAL ">"
#define TIMESTAMP_DEFAULT "%H:%M"
#define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]"
#define MPLEX_AWAY_NOTE "Away from keyboard, be back soon!"

View File

@ -37,6 +37,7 @@
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <termios.h>
#include <time.h>
@ -53,6 +54,7 @@
#include "execute.h"
#include "file_transfers.h"
#include "friendlist.h"
#include "groupchats.h"
#include "line_info.h"
#include "log.h"
#include "message_queue.h"
@ -229,20 +231,59 @@ void exit_toxic_err(const char *errmsg, int errcode)
exit(EXIT_FAILURE);
}
void cb_toxcore_logger(Tox *m, Tox_Log_Level level, const char *file, uint32_t line, const char *func,
static const char *tox_log_level_show(Tox_Log_Level level)
{
switch (level) {
case TOX_LOG_LEVEL_TRACE:
return "TRACE";
case TOX_LOG_LEVEL_DEBUG:
return "DEBUG";
case TOX_LOG_LEVEL_INFO:
return "INFO";
case TOX_LOG_LEVEL_WARNING:
return "WARNING";
case TOX_LOG_LEVEL_ERROR:
return "ERROR";
}
return "<invalid>";
}
void cb_toxcore_logger(Tox *m, TOX_LOG_LEVEL level, const char *file, uint32_t line, const char *func,
const char *message, void *user_data)
{
UNUSED_VAR(user_data);
UNUSED_VAR(file);
UNUSED_VAR(m);
if (user_data) {
FILE *fp = (FILE *)user_data;
fprintf(fp, "[%d] %u:%s() - %s\n", level, line, func, message);
fflush(fp);
} else {
fprintf(stderr, "[%d] %u:%s() - %s\n", level, line, func, message);
if (level == TOX_LOG_LEVEL_TRACE) {
return;
}
FILE *fp = (FILE *)user_data;
if (!fp) {
fp = stderr;
}
struct timeval tv;
gettimeofday(&tv, NULL);
struct tm tmp;
gmtime_r(&tv.tv_sec, &tmp);
char timestamp[200];
strftime(timestamp, sizeof(timestamp), "%F %T", &tmp);
fprintf(fp, "%c %s.%06ld %s:%u(%s) - %s\n", tox_log_level_show(level)[0], timestamp, tv.tv_usec, file, line, func,
message);
fflush(fp);
}
/* Sets ncurses refresh rate. Lower values make it refresh more often. */
@ -472,6 +513,17 @@ static void load_friendlist(Tox *m)
sort_friendlist_index();
}
static void load_groups(Tox *m)
{
size_t numgroups = tox_group_get_number_groups(m);
for (size_t i = 0; i < numgroups; ++i) {
if (init_groupchat_win(m, i, NULL, 0, Group_Join_Type_Load) != 0) {
tox_group_leave(m, i, NULL, 0, NULL);
}
}
}
static void load_conferences(Tox *m)
{
size_t num_chats = tox_conference_get_chatlist_size(m);
@ -815,6 +867,22 @@ static void init_tox_callbacks(Tox *m)
tox_callback_file_recv_control(m, on_file_recv_control);
tox_callback_file_recv_chunk(m, on_file_recv_chunk);
tox_callback_friend_lossless_packet(m, on_lossless_custom_packet);
tox_callback_group_invite(m, on_group_invite);
tox_callback_group_message(m, on_group_message);
tox_callback_group_private_message(m, on_group_private_message);
tox_callback_group_peer_status(m, on_group_status_change);
tox_callback_group_peer_join(m, on_group_peer_join);
tox_callback_group_peer_exit(m, on_group_peer_exit);
tox_callback_group_peer_name(m, on_group_nick_change);
tox_callback_group_topic(m, on_group_topic_change);
tox_callback_group_peer_limit(m, on_group_peer_limit);
tox_callback_group_privacy_state(m, on_group_privacy_state);
tox_callback_group_topic_lock(m, on_group_topic_lock);
tox_callback_group_password(m, on_group_password);
tox_callback_group_self_join(m, on_group_self_join);
tox_callback_group_join_fail(m, on_group_rejected);
tox_callback_group_moderation(m, on_group_moderation);
tox_callback_group_voice_state(m, on_group_voice_state);
}
static void init_tox_options(struct Tox_Options *tox_opts)
@ -1584,6 +1652,7 @@ int main(int argc, char **argv)
prompt = init_windows(m);
prompt_init_statusbar(prompt, m, !datafile_exists);
load_groups(m);
load_conferences(m);
set_active_window_index(0);

View File

@ -148,6 +148,32 @@ void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t k
void on_friend_typing(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata);
void on_friend_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata);
void on_lossless_custom_packet(Tox *m, uint32_t friendnumber, const uint8_t *data, size_t length, void *userdata);
void on_group_invite(Tox *m, uint32_t friendnumber, const uint8_t *invite_data, size_t length,
const uint8_t *group_name,
size_t group_name_length, void *userdata);
void on_group_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, uint32_t message_id, void *userdata);
void on_group_private_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length,
void *userdata);
void on_group_peer_join(Tox *m, uint32_t groupnumber, uint32_t peernumber, void *userdata);
void on_group_peer_exit(Tox *m, uint32_t groupnumber, uint32_t peer_id, Tox_Group_Exit_Type exit_type,
const uint8_t *nick,
size_t nick_len, const uint8_t *partmsg, size_t length, void *userdata);
void on_group_topic_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *topic, size_t length,
void *userdata);
void on_group_peer_limit(Tox *m, uint32_t groupnumber, uint32_t peer_limit, void *userdata);
void on_group_privacy_state(Tox *m, uint32_t groupnumber, Tox_Group_Privacy_State privacy_state, void *userdata);
void on_group_topic_lock(Tox *m, uint32_t groupnumber, Tox_Group_Topic_Lock topic_lock, void *userdata);
void on_group_password(Tox *m, uint32_t groupnumber, const uint8_t *password, size_t length, void *userdata);
void on_group_nick_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *newname, size_t length,
void *userdata);
void on_group_status_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_USER_STATUS status, void *userdata);
void on_group_self_join(Tox *m, uint32_t groupnumber, void *userdata);
void on_group_rejected(Tox *m, uint32_t groupnumber, Tox_Group_Join_Fail type, void *userdata);
void on_group_moderation(Tox *m, uint32_t groupnumber, uint32_t source_peernum, uint32_t target_peernum,
Tox_Group_Mod_Event type, void *userdata);
void on_group_voice_state(Tox *m, uint32_t groupnumber, Tox_Group_Voice_State voice_state, void *userdata);
extern char *DATA_FILE;
extern char *BLOCK_FILE;

View File

@ -30,6 +30,7 @@
#include "conference.h"
#include "file_transfers.h"
#include "friendlist.h"
#include "groupchats.h"
#include "line_info.h"
#include "misc_tools.h"
#include "prompt.h"
@ -379,6 +380,192 @@ void on_lossless_custom_packet(Tox *m, uint32_t friendnumber, const uint8_t *dat
}
}
void on_group_invite(Tox *m, uint32_t friendnumber, const uint8_t *invite_data, size_t length,
const uint8_t *group_name,
size_t group_name_length, void *userdata)
{
char gname[MAX_STR_SIZE + 1];
group_name_length = copy_tox_str(gname, sizeof(gname), (const char *) group_name, group_name_length);
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupInvite != NULL) {
windows[i]->onGroupInvite(windows[i], m, friendnumber, (char *) invite_data, length, gname, group_name_length);
}
}
}
void on_group_message(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, uint32_t message_id, void *userdata)
{
UNUSED_VAR(message_id);
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupMessage != NULL) {
windows[i]->onGroupMessage(windows[i], m, groupnumber, peer_id, type, msg, length);
}
}
}
void on_group_private_message(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type,
const uint8_t *message,
size_t length, void *userdata)
{
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupPrivateMessage != NULL) {
windows[i]->onGroupPrivateMessage(windows[i], m, groupnumber, peer_id, msg, length);
}
}
}
void on_group_status_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_USER_STATUS status, void *userdata)
{
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupStatusChange != NULL) {
windows[i]->onGroupStatusChange(windows[i], m, groupnumber, peer_id, status);
}
}
flag_interface_refresh();
}
void on_group_peer_join(Tox *m, uint32_t groupnumber, uint32_t peer_id, void *userdata)
{
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupPeerJoin != NULL) {
windows[i]->onGroupPeerJoin(windows[i], m, groupnumber, peer_id);
}
}
flag_interface_refresh();
}
void on_group_peer_exit(Tox *m, uint32_t groupnumber, uint32_t peer_id, Tox_Group_Exit_Type exit_type,
const uint8_t *nick,
size_t nick_len, const uint8_t *part_message, size_t length, void *userdata)
{
char toxic_nick[TOXIC_MAX_NAME_LENGTH + 1];
nick_len = copy_tox_str(toxic_nick, sizeof(toxic_nick), (const char *) nick, nick_len);
char buf[MAX_STR_SIZE + 1] = {0};
size_t buf_len = 0;
if (part_message) {
buf_len = copy_tox_str(buf, sizeof(buf), (const char *) part_message, length);
}
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupPeerExit != NULL) {
windows[i]->onGroupPeerExit(windows[i], m, groupnumber, peer_id, exit_type, toxic_nick, nick_len, buf, buf_len);
}
}
}
void on_group_topic_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *topic, size_t length,
void *userdata)
{
char data[MAX_STR_SIZE + 1];
length = copy_tox_str(data, sizeof(data), (const char *) topic, length);
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupTopicChange != NULL) {
windows[i]->onGroupTopicChange(windows[i], m, groupnumber, peer_id, data, length);
}
}
}
void on_group_peer_limit(Tox *m, uint32_t groupnumber, uint32_t peer_limit, void *userdata)
{
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupPeerLimit != NULL) {
windows[i]->onGroupPeerLimit(windows[i], m, groupnumber, peer_limit);
}
}
}
void on_group_privacy_state(Tox *m, uint32_t groupnumber, Tox_Group_Privacy_State privacy_state, void *userdata)
{
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupPrivacyState != NULL) {
windows[i]->onGroupPrivacyState(windows[i], m, groupnumber, privacy_state);
}
}
}
void on_group_topic_lock(Tox *m, uint32_t groupnumber, Tox_Group_Topic_Lock topic_lock, void *userdata)
{
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupTopicLock != NULL) {
windows[i]->onGroupTopicLock(windows[i], m, groupnumber, topic_lock);
}
}
}
void on_group_password(Tox *m, uint32_t groupnumber, const uint8_t *password, size_t length, void *userdata)
{
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupPassword != NULL) {
windows[i]->onGroupPassword(windows[i], m, groupnumber, (char *) password, length);
}
}
}
void on_group_nick_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *newname, size_t length,
void *userdata)
{
char name[TOXIC_MAX_NAME_LENGTH + 1];
length = copy_tox_str(name, sizeof(name), (const char *) newname, length);
filter_str(name, length);
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupNickChange != NULL) {
windows[i]->onGroupNickChange(windows[i], m, groupnumber, peer_id, name, length);
}
}
}
void on_group_self_join(Tox *m, uint32_t groupnumber, void *userdata)
{
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupSelfJoin != NULL) {
windows[i]->onGroupSelfJoin(windows[i], m, groupnumber);
}
}
}
void on_group_rejected(Tox *m, uint32_t groupnumber, Tox_Group_Join_Fail type, void *userdata)
{
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupRejected != NULL) {
windows[i]->onGroupRejected(windows[i], m, groupnumber, type);
}
}
}
void on_group_moderation(Tox *m, uint32_t groupnumber, uint32_t source_peer_id, uint32_t target_peer_id,
Tox_Group_Mod_Event type, void *userdata)
{
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupModeration != NULL) {
windows[i]->onGroupModeration(windows[i], m, groupnumber, source_peer_id, target_peer_id, type);
}
}
}
void on_group_voice_state(Tox *m, uint32_t groupnumber, Tox_Group_Voice_State voice_state, void *userdata)
{
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupVoiceState != NULL) {
windows[i]->onGroupVoiceState(windows[i], m, groupnumber, voice_state);
}
}
}
/* CALLBACKS END */
int add_window(Tox *m, ToxWindow *w)
@ -549,7 +736,7 @@ void on_window_resize(void)
wclear(w->help->win);
}
if (w->type == WINDOW_TYPE_CONFERENCE) {
if (w->type == WINDOW_TYPE_CONFERENCE || w->type == WINDOW_TYPE_GROUPCHAT) {
delwin(w->chatwin->sidebar);
w->chatwin->sidebar = NULL;
} else {
@ -577,7 +764,7 @@ void on_window_resize(void)
} else {
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0);
if (w->type != WINDOW_TYPE_CONFERENCE) {
if (!(w->type == WINDOW_TYPE_CONFERENCE || w->type == WINDOW_TYPE_GROUPCHAT)) {
w->stb->topline = subwin(w->window, TOP_BAR_HEIGHT, x2, 0, 0);
}
}
@ -976,6 +1163,12 @@ void kill_all_windows(Tox *m)
#endif // GAMES
case WINDOW_TYPE_GROUPCHAT: {
exit_groupchat(w, m, w->num, user_settings->group_part_message,
strlen(user_settings->group_part_message));
break;
}
default: {
break;
}

View File

@ -87,6 +87,7 @@ typedef enum {
WINDOW_TYPE_PROMPT,
WINDOW_TYPE_CHAT,
WINDOW_TYPE_CONFERENCE,
WINDOW_TYPE_GROUPCHAT,
WINDOW_TYPE_FRIEND_LIST,
#ifdef GAMES
@ -192,6 +193,24 @@ struct ToxWindow {
void(*onGameData)(ToxWindow *, Tox *, uint32_t, const uint8_t *, size_t);
#endif // GAMES
void(*onGroupInvite)(ToxWindow *, Tox *, uint32_t, const char *, size_t, const char *, size_t);
void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t);
void(*onGroupPrivateMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupPeerJoin)(ToxWindow *, Tox *, uint32_t, uint32_t);
void(*onGroupPeerExit)(ToxWindow *, Tox *, uint32_t, uint32_t, Tox_Group_Exit_Type, const char *, size_t, const char *,
size_t);
void(*onGroupNickChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupStatusChange)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_USER_STATUS);
void(*onGroupTopicChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupPeerLimit)(ToxWindow *, Tox *, uint32_t, uint32_t);
void(*onGroupPrivacyState)(ToxWindow *, Tox *, uint32_t, Tox_Group_Privacy_State);
void(*onGroupTopicLock)(ToxWindow *, Tox *, uint32_t, Tox_Group_Topic_Lock);
void(*onGroupPassword)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onGroupSelfJoin)(ToxWindow *, Tox *, uint32_t);
void(*onGroupRejected)(ToxWindow *, Tox *, uint32_t, Tox_Group_Join_Fail);
void(*onGroupModeration)(ToxWindow *, Tox *, uint32_t, uint32_t, uint32_t, Tox_Group_Mod_Event);
void(*onGroupVoiceState)(ToxWindow *, Tox *, uint32_t, Tox_Group_Voice_State);
#ifdef AUDIO
void(*onInvite)(ToxWindow *, ToxAV *, uint32_t, int);