1
0
mirror of https://github.com/Tha14/toxic.git synced 2025-06-26 22:46:44 +02:00

Compare commits

...

106 Commits

Author SHA1 Message Date
9ee7a48910 add setting to disable welcome message 2014-09-19 01:11:23 -04:00
daf4614ba6 improve behaviour of misused encryption options 2014-09-19 00:29:30 -04:00
919d36369c add option to unencrypt an encrypted data file 2014-09-18 23:50:51 -04:00
618704df76 remove duplicate DHT node 2014-09-18 22:52:05 -04:00
9a70dd9651 possibly fix display bug on some platforms 2014-09-18 13:14:15 -04:00
e2d310b10f Add TCP nodes on 443 2014-09-17 14:00:41 -07:00
a1015a366a update DHTnodes, small fixes 2014-09-17 13:52:27 -04:00
913ec7b3fe Merge pull request #242 from Ansa89/trivial-fix
Usage help: add missing comma
2014-09-16 13:55:20 +02:00
4a52b06954 Usage help: add missing comma 2014-09-16 11:54:58 +02:00
89f9c07b9e add confirmation when creating new password 2014-09-15 19:00:36 -04:00
388d78d11e add option to encrypt an existing unencrypted data file 2014-09-15 16:17:58 -04:00
a2a23b3932 password prompt fixes 2014-09-15 04:13:12 -04:00
f405ae8b42 improvements to password entry screen 2014-09-14 21:49:38 -04:00
cf3f6750eb small fix 2014-09-14 18:25:13 -04:00
4de22d067a Update .travis.yml 2014-09-14 18:08:42 -04:00
51e274ea38 implement data file encryption 2014-09-14 17:46:28 -04:00
0a6ce62363 don't write unsent messages to log 2014-09-11 01:36:33 -04:00
13c5de5531 increase time between message send retries 2014-09-10 16:18:37 -04:00
21f8e7f398 couple small fixes 2014-09-09 15:16:27 -04:00
bcf4a5af90 message queue improvements 2014-09-09 14:06:28 -04:00
09f90d095b properly flag lines with unread mark 2014-09-07 22:28:54 -04:00
416ebc9ab8 another fix 2014-09-07 22:05:17 -04:00
3ca22aa714 fix 2014-09-07 21:43:16 -04:00
8dd25e1f0b code cleanup, add delay for unread message notification 2014-09-07 15:08:01 -04:00
5b9bd603ea implement read receipts 2014-09-07 02:43:53 -04:00
3c2c1f15ce message sending queue for fake offline messaging 2014-09-06 13:18:42 -04:00
6876df4a45 Merge pull request #240 from s3erios/master
Fix some 'clang --analyze' warnings
2014-09-05 13:16:43 -04:00
1ff97161fb temporarily revert a862874740. 2014-09-05 14:15:25 +03:00
667410e879 fix another potential memory leak 2014-09-05 13:17:10 +03:00
a862874740 mark exit_toxic_* functions as noreturn 2014-09-05 13:15:56 +03:00
79bde4e5bf fix potential memory leak and move cleanup to the end of the function 2014-09-05 13:02:26 +03:00
833b724e9f fix bug 2014-09-04 18:48:55 -04:00
96b68058bb code cleanup and fix bug that sometimes prevented tab notifications from working 2014-09-04 18:28:20 -04:00
e823233149 Merge pull request #238 from Ansa89/trivial-fix
Addition to Tox/toxic#235
2014-09-04 13:23:33 -04:00
3ac22fafe4 Add info about DISABLE_X11 usage 2014-09-04 19:20:51 +02:00
71f2ac170c Merge pull request #235 from s3erios/master_cfg
Add X11 option
2014-09-04 13:18:46 -04:00
0ef888eea3 Merge pull request #236 from s3erios/master
Some code simplification
2014-09-04 11:10:55 +02:00
a9b0028a15 move some code inside _X11 ifdef 2014-09-04 01:41:02 +03:00
b18e6cff5a move duplicate code into separate function 2014-09-04 01:36:37 +03:00
424a1c94d9 Add X11 option 2014-09-04 01:19:32 +03:00
009095af24 slight simplification for command list iteration 2014-09-02 02:23:44 -04:00
2ce42ab057 bump version to 0.5.0 2014-09-01 18:56:20 -04:00
934459dea8 show connection change message in chat windows 2014-08-31 14:17:33 -04:00
52bc874675 Merge pull request #234 from Ansa89/trivial-fix
Cosmetic fixes
2014-08-31 11:26:48 +02:00
511907fbc5 better way to check if files exist 2014-08-27 22:51:46 -04:00
155e194174 fix segfault on unknown long option 2014-08-27 14:30:06 -04:00
b1c7e21ca9 fix possible segfaults 2014-08-27 13:54:02 -04:00
7edcf6cb45 add setting to set path for chatlogs 2014-08-26 23:56:37 -04:00
9581940cfa Cosmetic fixes 2014-08-26 10:39:43 +02:00
f2aa57c4fa additional messages for startup options 2014-08-26 02:28:05 -04:00
8bf4405fd0 fix error code 2014-08-25 19:55:00 -04:00
21ef1788ca print init messages to prompt window intead of stderr 2014-08-25 19:26:41 -04:00
68f1dffba7 add command to decline friend requests 2014-08-25 14:08:01 -04:00
092df2c0e4 Merge pull request #231 from Ansa89/trivial-fix
Reworked manpage build system
2014-08-25 14:12:12 +02:00
691f94c75c README.md: second try 2014-08-25 12:59:34 +02:00
d6d4476e85 README.md: better look 2014-08-25 12:56:14 +02:00
924e8e0860 Reworked manpage build system 2014-08-25 12:54:44 +02:00
53193e933f Merge pull request #230 from louipc/manpage
Manpage
2014-08-25 00:49:31 -04:00
328587ad9c doc: Make asciidoc extra optional.
Completely separate man page generation from the main build to satisfy
travis-ci behaviour.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-24 17:00:25 -04:00
1a8fdb1b99 Make asciidoc 'optional'
Run `make doc` in the build directory after editing the asciidoc to
regenerate the manpages. Changes to asciidoc source and generated man
pages will need to be commited together.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-24 16:34:16 -04:00
690f0221b5 Add generated man pages.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-24 00:16:55 -04:00
e117bd3985 Add Asciidoc to README and travis
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-21 05:00:15 -04:00
15cc87bffd Remove old man pages.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-21 04:40:10 -04:00
97d4c97c52 Generate man pages from asciidoc
Working directly with g/roff is a pain.
Also changed the formatting of toxic.conf.5 a bit.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-21 04:39:24 -04:00
c8b22d7e8a Merge pull request #229 from Ansa89/trivial-fix
toxic.conf.example: better formatting
2014-08-20 13:12:41 -04:00
f48ec4f49b array bounds fix 2014-08-20 13:10:21 -04:00
d4ce697bd9 toxic.conf.example: better formatting 2014-08-20 12:40:32 +02:00
bd20513493 a few fixes 2014-08-19 23:41:43 -04:00
fd3f4eb724 Merge branch 'master' of https://github.com/Tox/toxic 2014-08-19 20:55:38 -04:00
75e8486061 add command to list pending friend requests, a few related fixes 2014-08-19 20:52:17 -04:00
bd216709fc Merge pull request #226 from Ansa89/freebsd-man
Fix Tox/toxic#222 and reorganize cfg dir
2014-08-19 13:04:52 -04:00
a3a8f7608a Fix Tox/toxic#222 and reorganize cfg dir 2014-08-19 18:22:57 +02:00
affc88d0a8 error checking for file senders, fixes 2014-08-17 15:32:38 -04:00
eca4882ce2 transfer progress bar fix 2014-08-17 13:50:22 -04:00
58b0a04019 separate file transfer updates from callbacks 2014-08-17 00:11:49 -04:00
b870679f2c allow resuming of broken file transfers (needs testing) 2014-08-16 17:20:53 -04:00
ae83725cb6 Merge pull request #223 from louipc/master
Add debug flag and update man page.
2014-08-16 02:59:55 -04:00
595e42b587 Clarify function of -b switch.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-16 02:35:21 -04:00
f5401df2c7 doc/toxic: Add missing flags and sort alphabetically.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-15 19:26:06 -04:00
3e79a5ca8b Add -b flag for debugging
For now this just prints messages to stderr, so the user should manually
redirect stderr to avoid breaking the ui. This can be later expanded
upon to provide debugging messages in the command window.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-15 19:06:16 -04:00
327081945e update DHTnodes 2014-08-15 18:17:03 -04:00
68ec484a58 don't allow DNS lookups when using a proxy + forced TCP & fix arg parsing 2014-08-15 15:13:35 -04:00
1d6ccf56a8 name conflict with core function might cause linking error 2014-08-15 12:22:39 -04:00
67f637a1e1 redirect stderr before audio init to prevent error spam 2014-08-14 21:31:28 -04:00
5e175d5319 Merge pull request #220 from louipc/master
new tox_bootstrap_from_address() behaviour and a minor ui change
2014-08-14 20:47:17 -04:00
778db0fece core change: ports no longer in network byte order 2014-08-14 20:38:33 -04:00
72010dd2e1 allow connections through proxies 2014-08-14 20:31:52 -04:00
39556b36f3 port is no longer passed in network byte order to tox_bootstrap_from_address()
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-14 20:10:14 -04:00
dc9ffa6e56 ipv6enabled parameter was removed in toxcore.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-14 19:51:52 -04:00
1e92bb3c2b help: lowercase keybinding hints for clarity.
Seeing the uppercase character I expected it to be the actual key.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-14 19:22:59 -04:00
edbdf2966a toxcore API update, add option to force TCP connection 2014-08-14 18:45:41 -04:00
3f6fd734d3 Merge pull request #218 from louipc/louipc
toxic.conf.5: Remove default config from man page
2014-08-14 12:05:33 -04:00
919a06d282 toxic.conf.5: Remove default config from man page
The reference to the config example in the FILES section should be sufficient.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-14 01:13:27 -04:00
35cc815cdb add option to specify DNSservers path 2014-08-12 22:32:36 -04:00
a318bdb034 add option to not connect to the DHT network 2014-08-12 18:59:27 -04:00
d6aaa95b25 ignore invalid file data callbacks 2014-08-12 15:27:42 -04:00
5718ad52db a few fixes 2014-08-12 03:01:18 -04:00
0f4cffbacc a few dns fixes 2014-08-11 01:59:01 -04:00
d9a861331f Use DNS server list instead of hard coding domains/keys 2014-08-10 21:40:19 -04:00
2f12a8d429 prevent statusmessage from wrapping on prompt screen 2014-08-10 00:37:26 -04:00
e75cf4f3ad fix bug where tab alert colours weren't being properly prioritized 2014-08-08 13:39:05 -04:00
bb85f31bb2 convert bytes for file transfer message, general fixes 2014-08-07 19:31:36 -04:00
396d08f0d2 refactor friend/blocklist, dynamically allocate memory 2014-08-07 13:24:29 -04:00
6ab184e7ce file transfer improvements and bug fixes 2014-08-05 17:38:33 -04:00
fd65fbfd0c temporarily disabling clang in travis script due to weird errors unrelated to toxic 2014-08-05 00:27:55 -04:00
637ea0ed55 attempt to fix travis with clang 2014-08-05 00:16:01 -04:00
53 changed files with 2725 additions and 1301 deletions

View File

@ -1,7 +1,7 @@
language: c
compiler:
- gcc
- clang
# - clang # Fix me
before_script:
# Installing yasm (needed for compiling vpx) and openal
@ -9,7 +9,7 @@ before_script:
# Installing libsodium, needed for toxcore
- git clone https://github.com/jedisct1/libsodium.git libsodium
- cd libsodium
- git checkout tags/0.4.2 > /dev/null
- git checkout tags/0.7.0 > /dev/null
- ./autogen.sh > /dev/null
- ./configure > /dev/null
- make check -j2 || make check || exit 1 > /dev/null

View File

@ -1,7 +1,7 @@
# Toxic [![Build Status](https://travis-ci.org/Tox/toxic.png?branch=master)](https://travis-ci.org/Tox/toxic)
Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
![Toxic Screenshot](https://i.imgur.com/ryaEmQZ.png "Home Screen").
![Toxic Screenshot](https://i.imgur.com/ryaEmQZ.png "Home Screen")
## Installation
@ -22,6 +22,11 @@ Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly
##### Desktop notifications
* [libnotify](https://developer.gnome.org/libnotify) (for Debian based systems, 'libnotify-dev')
##### Documentation
* [Asciidoc](http://asciidoc.org/index.html) (only required for regenerating manpages)
* Run `make doc` after editing the asciidoc files to regenerate the manpages.
* **NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.
### Compiling
1. `cd build/`
2. `make PREFIX="/where/to/install"`
@ -31,6 +36,7 @@ Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
* `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking)
* `DISABLE_AV=1` → build toxic without audio call support
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support

View File

@ -1,25 +1,19 @@
TOXIC_VERSION = 0.4.7
REV = $(shell git rev-list HEAD --count)
VERSION = $(TOXIC_VERSION)_r$(REV)
BASE_DIR = $(shell cd .. && pwd -P)
CFG_DIR = $(BASE_DIR)/cfg
CFG_DIR = ../cfg
SRC_DIR = ../src
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
DATADIR = $(PREFIX)/share/toxic
MANDIR = $(PREFIX)/share/man
-include $(CFG_DIR)/global_vars.mk
LIBS = libtoxcore ncursesw libconfig
CFLAGS = -std=gnu99 -pthread -Wall -g
CFLAGS += -DTOXICVER="\"$(VERSION)\"" -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED
CFLAGS += -DPACKAGE_DATADIR="\"$(abspath $(DATADIR))\""
CFLAGS += $(USER_CFLAGS)
LDFLAGS = $(USER_LDFLAGS)
OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_senders.o notify.o
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o
# Check on wich system we are running
UNAME_S = $(shell uname -s)
@ -49,10 +43,10 @@ ifneq ($(filter arm%, $(UNAME_M)),)
endif
# Include all needed checks
-include $(CFG_DIR)/check_features.mk
-include $(CFG_DIR)/checks/check_features.mk
# Targets
all: toxic
all: toxic doc
toxic: $(OBJ)
@echo " LD $@"
@ -68,7 +62,6 @@ clean:
-include $(OBJ:.o=.d)
-include $(CFG_DIR)/install.mk
-include $(CFG_DIR)/help.mk
-include $(CFG_DIR)/targets/*.mk
.PHONY: clean all

View File

@ -8,14 +8,14 @@ else
endif
# Check if we can build audio support
CHECK_AUDIO_LIBS = $(shell pkg-config $(AUDIO_LIBS) || echo -n "error")
CHECK_AUDIO_LIBS = $(shell pkg-config --exists $(AUDIO_LIBS) || echo -n "error")
ifneq ($(CHECK_AUDIO_LIBS), error)
LIBS += $(AUDIO_LIBS)
CFLAGS += $(AUDIO_CFLAGS)
OBJ += $(AUDIO_OBJ)
else
ifneq ($(MAKECMDGOALS), clean)
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without audio support)
$(warning WARNING -- You need these libraries for audio support)
$(warning WARNING -- $(MISSING_AUDIO_LIBS))

View File

@ -1,36 +1,37 @@
# Check if we can use X11
CHECK_X11_LIBS = $(shell pkg-config x11 || echo -n "error")
ifneq ($(CHECK_X11_LIBS), error)
LIBS += x11
CFLAGS += -D_X11
CHECKS_DIR = $(CFG_DIR)/checks
# Check if we want build X11 support
X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(X11), disabled)
-include $(CHECKS_DIR)/x11.mk
endif
# Check if we want build audio support
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(AUDIO), disabled)
-include $(CFG_DIR)/av.mk
-include $(CHECKS_DIR)/av.mk
endif
# Check if we want build sound notifications support
SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(SND_NOTIFY), disabled)
-include $(CFG_DIR)/sound_notifications.mk
-include $(CHECKS_DIR)/sound_notifications.mk
endif
# Check if we want build desktop notifications support
DESK_NOTIFY = $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(DESK_NOTIFY), disabled)
-include $(CFG_DIR)/desktop_notifications.mk
-include $(CHECKS_DIR)/desktop_notifications.mk
endif
# Check if we can build Toxic
CHECK_LIBS = $(shell pkg-config $(LIBS) || echo -n "error")
CHECK_LIBS = $(shell pkg-config --exists $(LIBS) || echo -n "error")
ifneq ($(CHECK_LIBS), error)
CFLAGS += $(shell pkg-config --cflags $(LIBS))
LDFLAGS += $(shell pkg-config --libs $(LIBS))
else
ifneq ($(MAKECMDGOALS), clean)
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
$(warning ERROR -- Cannot compile Toxic)
$(warning ERROR -- You need these libraries)
$(warning ERROR -- $(MISSING_LIBS))

View File

@ -3,13 +3,13 @@ DESK_NOTIFY_LIBS = libnotify
DESK_NOTIFY_CFLAGS = -D_BOX_NOTIFY
# Check if we can build desktop notifications support
CHECK_DESK_NOTIFY_LIBS = $(shell pkg-config $(DESK_NOTIFY_LIBS) || echo -n "error")
CHECK_DESK_NOTIFY_LIBS = $(shell pkg-config --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
ifneq ($(CHECK_DESK_NOTIFY_LIBS), error)
LIBS += $(DESK_NOTIFY_LIBS)
CFLAGS += $(DESK_NOTIFY_CFLAGS)
else
ifneq ($(MAKECMDGOALS), clean)
MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without desktop notifications support)
$(warning WARNING -- You need these libraries for desktop notifications support)
$(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS))

View File

@ -8,14 +8,14 @@ else
endif
# Check if we can build sound notifications support
CHECK_SND_NOTIFY_LIBS = $(shell pkg-config $(SND_NOTIFY_LIBS) || echo -n "error")
CHECK_SND_NOTIFY_LIBS = $(shell pkg-config --exists $(SND_NOTIFY_LIBS) || echo -n "error")
ifneq ($(CHECK_SND_NOTIFY_LIBS), error)
LIBS += $(SND_NOTIFY_LIBS)
CFLAGS += $(SND_NOTIFY_CFLAGS)
OBJ += $(SND_NOTIFY_OBJ)
else
ifneq ($(MAKECMDGOALS), clean)
MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without sound notifications support)
$(warning WARNING -- You need these libraries for sound notifications support)
$(warning WARNING -- $(MISSING_SND_NOTIFY_LIBS))

17
cfg/checks/x11.mk Normal file
View File

@ -0,0 +1,17 @@
# Variables for X11 support
X11_LIBS = x11
X11_CFLAGS = -D_X11
# Check if we can build X11 support
CHECK_X11_LIBS = $(shell pkg-config --exists $(X11_LIBS) || echo -n "error")
ifneq ($(CHECK_X11_LIBS), error)
LIBS += $(X11_LIBS)
CFLAGS += $(X11_CFLAGS)
else
ifneq ($(MAKECMDGOALS), clean)
MISSING_X11_LIBS = $(shell for lib in $(X11_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without x11 support (needed for focus tracking))
$(warning WARNING -- You need these libraries for x11 support)
$(warning WARNING -- $(MISSING_X11_LIBS))
endif
endif

23
cfg/global_vars.mk Normal file
View File

@ -0,0 +1,23 @@
# Version
TOXIC_VERSION = 0.5.0
REV = $(shell git rev-list HEAD --count)
VERSION = $(TOXIC_VERSION)_r$(REV)
# Project directories
DOC_DIR = $(BASE_DIR)/doc
SRC_DIR = $(BASE_DIR)/src
SND_DIR = $(BASE_DIR)/sounds
MISC_DIR = $(BASE_DIR)/misc
# Project files
MANFILES = toxic.1 toxic.conf.5
DATAFILES = DHTnodes DNSservers toxic.conf.example
SNDFILES = ContactLogsIn.wav ContactLogsOut.wav Error.wav IncomingCall.wav
SNDFILES += LogIn.wav LogOut.wav NewMessage.wav OutgoingCall.wav
SNDFILES += TransferComplete.wav TransferPending.wav
# Install directories
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
DATADIR = $(PREFIX)/share/toxic
MANDIR = $(PREFIX)/share/man

View File

@ -1,3 +1,4 @@
# Specials options for freebsd systems
LIBS := $(filter-out ncursesw, $(LIBS))
LDFLAGS += -lncursesw
MANDIR = $(PREFIX)/man

View File

@ -1,3 +1,4 @@
# Specials options for linux systems
CFLAGS +=
LDFLAGS += -ldl -lresolv
MANDIR = $(PREFIX)/share/man

10
cfg/targets/doc.mk Normal file
View File

@ -0,0 +1,10 @@
# Doc target
doc: $(MANFILES:%=$(DOC_DIR)/%)
$(DOC_DIR)/%: $(DOC_DIR)/%.asc
@echo " MAN $(@F)"
@a2x -f manpage -a revdate=$(shell git log -1 --date=short --format="%ad" $<) \
-a manmanual="Toxic Manual" -a mansource=toxic \
-a manversion=__VERSION__ -a datadir=__DATADIR__ $<
.PHONY: doc

View File

@ -1,13 +1,15 @@
# Help target
help:
@echo "-- Targets --"
@echo " all: Build toxic [DEFAULT]"
@echo " all: Build toxic and documentation [DEFAULT]"
@echo " toxic: Build toxic"
@echo " doc: Build documentation"
@echo " install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
@echo " clean: Remove built files"
@echo " help: This help"
@echo
@echo "-- Variables --"
@echo " DISABLE_X11: Set to \"1\" to force building without X11 support"
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"

View File

@ -1,19 +1,13 @@
MISC_DIR = ../misc
DOC_DIR = ../doc
SND_DIR = ../sounds
DATAFILES = DHTnodes toxic.conf.example
MANFILES = toxic.1 toxic.conf.5
SNDFILES = ContactLogsIn.wav ContactLogsOut.wav Error.wav IncomingCall.wav
SNDFILES += LogIn.wav LogOut.wav NewMessage.wav OutgoingCall.wav
SNDFILES += TransferComplete.wav TransferPending.wav
install: toxic
# Install target
install: toxic doc
mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
@echo "Installing toxic executable"
@install -m 0755 toxic $(abspath $(DESTDIR)/$(BINDIR))
@echo "Installing data files"
@for f in $(DATAFILES) ; do \
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)) ;\
@ -23,6 +17,7 @@ install: toxic
@for f in $(SNDFILES) ; do \
install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR))/sounds ;\
done
@echo "Installing man pages"
@for f in $(MANFILES) ; do \
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\

View File

@ -1,63 +1,153 @@
.TH TOXIC 1 "June 2014" "Toxic v__VERSION__" "User Manual"
.SH NAME
Toxic \- CLI client for Tox
.SH SYNOPSYS
.B toxic [\-f
.I data\-file
.B ] [\-x] [\-4] [\-c
.I config\-file
.B ] [\-n
.I nodes\-file
.B ] [\-h]
.SH DESCRIPTION
Toxic is an ncurses-based instant messaging client for Tox which formerly
resided in the Tox core repository, and is now available as a standalone
application.
.SH OPTIONS
.IP "\-f, \-\-file data\-file"
Use specified
.I data\-file
instead of
.IR ~/.config/tox/data
.IP "\-x, \-\-nodata"
Ignore data file
.IP "\-4, \-\-ipv4"
'\" t
.\" Title: toxic
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2014-09-19
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC" "1" "2014\-09\-19" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
toxic \- CLI client for Tox
.SH "SYNOPSIS"
.sp
\fBtoxic\fR [\-f \fIdata\-file\fR] [\-x] [\-4] [\-c \fIconfig\-file\fR] [\-n \fInodes\-file\fR] [\-h]
.SH "DESCRIPTION"
.sp
toxic is an ncurses\-based instant messaging client for Tox which formerly resided in the Tox core repository, and is now available as a standalone application\&.
.SH "OPTIONS"
.PP
\-4, \-\-ipv4
.RS 4
Force IPv4 connection
.IP "\-d, \-\-default_locale
Use default locale
.IP "\-c, \-\-config config\-file"
.RE
.PP
\-b, \-\-debug
.RS 4
Enable stderr for debugging\&. Redirect output to avoid breaking the curses interface and better capture messages\&.
.RE
.PP
\-c, \-\-config config\-file
.RS 4
Use specified
.I config\-file
\fIconfig\-file\fR
instead of
.IR ~/.config/tox/toxic.conf
.IP "\-n, \-\-nodes nodes\-file"
\fI~/\&.config/tox/toxic\&.conf\fR
.RE
.PP
\-d, \-\-default\-locale
.RS 4
Use default locale
.RE
.PP
\-e, \-\-encrypt\-data
.RS 4
Encrypt an unencrypted data file\&. An error will occur if this option is used with an encrypted data file\&.
.RE
.PP
\-f, \-\-file data\-file
.RS 4
Use specified
.I nodes\-file
for DHT bootstrap nodes, instead of
.IR __DATADIR__/DHTnodes
.IP "\-h, \-\-help"
\fIdata\-file\fR
instead of
\fI~/\&.config/tox/data\fR
.RE
.PP
\-h, \-\-help
.RS 4
Show help message
.SH FILES
.IP __DATADIR__/DHTnodes
Default list of DHT bootstrap nodes.
.IP ~/.config/tox/data
Savestate which contains your personal info (nickname, Tox ID,...) and
your contacts list.
.IP ~/.config/tox/toxic.conf
Configuration file. See
.BR toxic.conf (5)
for more details.
.IP __DATADIR__/toxic.conf.example
Configuration example.
.SH BUGS
Unicode characters with a width larger than 1 column may cause
strange behaviour. Expect more bugs and bad
behaviour: this software is in a pre\-alpha stage.
.SH AUTHORS
JFreegman <JFreegman@gmail.com>
.SH SEE ALSO
.BR toxic.conf (5)
.SH LINKS
Project page on github: https://github.com/Tox/toxic
.br
IRC channel on Freenode: chat.freenode.net#tox
.RE
.PP
\-n, \-\-nodes nodes\-file
.RS 4
Use specified
\fInodes\-file\fR
for DHT bootstrap nodes, instead of
\fI__DATADIR__/DHTnodes\fR
.RE
.PP
\-o, \-\-noconnect
.RS 4
Do not connect to the DHT network
.RE
.PP
\-p, \-\-proxy
.RS 4
Use proxy: Requires [IP] [port]
.RE
.PP
\-r, \-\-dnslist
.RS 4
Use specified DNSservers file
.RE
.PP
\-t, \-\-force\-tcp
.RS 4
Force TCP connection (use this with proxies)
.RE
.PP
\-u, \-\-unencrypt\-data
.RS 4
Unencrypt a data file\&. A warning will appear if this option is used with a data file that is already unencrypted\&.
.RE
.PP
\-x, \-\-nodata
.RS 4
Ignore data file
.RE
.SH "FILES"
.PP
__DATADIR__/DHTnodes
.RS 4
Default list of DHT bootstrap nodes\&.
.RE
.PP
~/\&.config/tox/data
.RS 4
Savestate which contains your personal info (nickname, Tox ID, contacts, etc)
.RE
.PP
~/\&.config/tox/toxic\&.conf
.RS 4
Configuration file\&. See
\fBtoxic\&.conf\fR(5) for more details\&.
.RE
.PP
__DATADIR__/toxic\&.conf\&.example
.RS 4
Configuration example\&.
.RE
.SH "BUGS"
.sp
Unicode characters with a width larger than 1 column may cause strange behaviour\&. Expect more bugs and bad behaviour: this software is in a pre\-alpha stage\&.
.SH "AUTHORS"
.sp
JFreegman <JFreegman@gmail\&.com>
.SH "SEE ALSO"
.sp
\fBtoxic\&.conf\fR(5)
.SH "LINKS"
.sp
Project page: https://github\&.com/Tox/toxic
.sp
IRC channel: chat\&.freenode\&.net#tox

99
doc/toxic.1.asc Normal file
View File

@ -0,0 +1,99 @@
toxic(1)
========
NAME
----
toxic - CLI client for Tox
SYNOPSIS
--------
*toxic* [-f 'data-file'] [-x] [-4] [-c 'config-file'] [-n 'nodes-file'] [-h]
DESCRIPTION
-----------
toxic is an ncurses-based instant messaging client for Tox which formerly
resided in the Tox core repository, and is now available as a standalone
application.
OPTIONS
-------
-4, --ipv4::
Force IPv4 connection
-b, --debug::
Enable stderr for debugging. Redirect output to
avoid breaking the curses interface and better capture messages.
-c, --config config-file::
Use specified 'config-file' instead of '~/.config/tox/toxic.conf'
-d, --default-locale::
Use default locale
-e, --encrypt-data::
Encrypt an unencrypted data file. An error will occur if this option
is used with an encrypted data file.
-f, --file data-file::
Use specified 'data-file' instead of '~/.config/tox/data'
-h, --help::
Show help message
-n, --nodes nodes-file::
Use specified 'nodes-file' for DHT bootstrap nodes, instead of
'{datadir}/DHTnodes'
-o, --noconnect::
Do not connect to the DHT network
-p, --proxy::
Use proxy: Requires [IP] [port]
-r, --dnslist::
Use specified DNSservers file
-t, --force-tcp::
Force TCP connection (use this with proxies)
-u, --unencrypt-data::
Unencrypt a data file. A warning will appear if this option is used
with a data file that is already unencrypted.
-x, --nodata::
Ignore data file
FILES
-----
{datadir}/DHTnodes::
Default list of DHT bootstrap nodes.
~/.config/tox/data::
Savestate which contains your personal info (nickname, Tox ID, contacts,
etc)
~/.config/tox/toxic.conf::
Configuration file. See *toxic.conf*(5) for more details.
{datadir}/toxic.conf.example::
Configuration example.
BUGS
----
Unicode characters with a width larger than 1 column may cause strange
behaviour. Expect more bugs and bad behaviour: this software is in a
pre-alpha stage.
AUTHORS
-------
JFreegman <JFreegman@gmail.com>
SEE ALSO
--------
*toxic.conf*(5)
LINKS
-----
Project page: <https://github.com/Tox/toxic>
IRC channel: chat.freenode.net#tox

View File

@ -1,413 +1,276 @@
.TH TOXIC.CONF 5 "June 2014" "Toxic v__VERSION__" "User Manual"
.SH NAME
toxic.conf \- Configuration file for toxic(1)
.SH DESCRIPTION
The
.I toxic.conf
file is the main configuration file for
.BR toxic (1)
client.
.SH SYNTAX
.I <SECTION>
.B = {
.PP
.IB <KEY1> = <VALUE1> ;
.br
.IB <KEY2> = <VALUE2> ;
.br
...
.PP
.B };
.PP
Uses syntax accepted by libconfig.
.br
Lines starting with "//" are comments and will be ignored.
.PP
Sections:
.PP
.B ui
.RS
Configurations related to user interface elements.
.PP
Keys:
.br
.B timestamps
.RS
Enable or disable timestamps.
.br
Values: 'true' to enable, 'false' to disable
.RE
.PP
.B alerts
.RS
Enable or disable terminal alerts on events.
.br
Values: 'true' to enable, 'false' to disable
.RE
.PP
.B native_colors
.RS
Select between native terminal colors and toxic color theme.
.br
Values: 'true' for terminal colours, 'false' for toxic colours
.RE
.PP
.B autolog
.RS
Enable or disable autologging.
.br
Values: 'true' to enable, 'false' to disable
.RE
.PP
.B time_format
.RS
Select between 24 and 12 hour time.
.br
Values: 24, 12
.RE
.PP
.B show_typing_other
.RS
Show you when others are typing in a 1-on-1 chat
.br
Values: 'true' to enable, 'false' to disable
.RE
.PP
.B show_typing_self
.RS
Show others when you're typing in a 1-on-1 chat
.br
Values: 'true' to enable, 'false' to disable
.RE
.PP
.B history_size
.RS
Maximum lines for chat window history.
.br
Values: <INTEGER> (for example: 700)
.RE
.RE
.PP
.B audio
.RS
Configurations related to audio devices.
.PP
Keys:
.br
.B input_device
.RS
Audio input device.
.br
Values: <INTEGER> (number correspond to "/lsdev in")
.RE
.PP
.B output_device
.RS
Audio output device.
.br
Values: <INTEGER> (number correspond to "/lsdev out")
.RE
.PP
.B VAD_treshold
.RS
Voice Activity Detection treshold.
.br
Values: <FLOAT> (recommended values are around 40.0)
.RE
.RE
.PP
.B tox
.RS
Configurations related to file transfer.
.PP
Keys:
.br
.B download_path
.RS
Default path for downloads.
.br
Values: <STRING> (absolute path where to store downloaded files)
.RE
.RE
.PP
.B sounds
.RS
Configurations related to notification sounds.
.br
(Special value "silent" can be used to disable a specific notification)
.PP
Keys:
.br
.B error
.RS
Sound to play when an error occurs.
.br
Values: <STRING> (sound file absolute path)
.RE
.PP
.B self_log_in
.RS
Sound to play when you log in.
.br
Values: <STRING> (sound file absolute path)
.RE
.PP
.B self_log_out
.RS
Sound to play when you log out.
.br
Values: <STRING> (sound file absolute path)
.RE
.PP
.B user_log_in
.RS
Sound to play when a contact become online.
.br
Values: <STRING> (sound file absolute path)
.RE
.PP
.B user_log_out
.RS
Sound to play when a contact become offline.
.br
Values: <STRING> (sound file absolute path)
.RE
.PP
.B call_incoming
.RS
Sound to play when you receive an incoming call.
.br
Values: <STRING> (sound file absolute path)
.RE
.PP
.B call_outgoing
.RS
Sound to play when you start a call.
.br
Values: <STRING> (sound file absolute path)
.RE
.PP
.B generic_message
.RS
Sound to play when an event occurs.
.br
Values: <STRING> (sound file absolute path)
.RE
.PP
.B transfer_pending
.RS
Sound to play when you receive a file transfer request.
.br
Values: <STRING> (sound file absolute path)
.RE
.PP
.B transfer_completed
.RS
Sound to play when a file transfer is completed.
.br
Values: <STRING> (sound file absolute path)
.RE
.RE
.PP
.B keys
.RS
Configurations related to user interface interaction.
.br
(Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive))
.PP
Keys:
.br
.B next_tab
.RS
Key combination to switch next tab.
.br
Values: <STRING> (key combination)
.RE
.PP
.B prev_tab
.RS
Key combination to switch previous tab.
.br
Values: <STRING> (key combination)
.RE
.PP
.B scroll_line_up
.RS
Key combination to scroll one line up.
.br
Values: <STRING> (key combination)
.RE
.PP
.B scroll_line_down
.RS
Key combination to scroll one line down.
.br
Values: <STRING> (key combination)
.RE
.PP
.B half_page_up
.RS
Key combination to scroll half page up.
.br
Values: <STRING> (key combination)
.RE
.PP
.B half_page_down
.RS
Key combination to scroll half page down.
.br
Values: <STRING> (key combination)
.RE
.PP
.B page_bottom
.RS
Key combination to scroll to page bottom.
.br
Values: <STRING> (key combination)
.RE
.PP
.B peer_list_up
.RS
Key combination to scroll contacts list up.
.br
Values: <STRING> (key combination)
.RE
.PP
.B peer_list_down
.RS
Key combination to scroll contacts list down.
.br
Values: <STRING> (key combination)
.RE
.RE
.SH EXAMPLES
Default settings from __DATADIR__/toxic.conf.exmaple:
.PP
// SAMPLE TOXIC CONFIGURATION
.br
// USES LIBCONFIG-ACCEPTED SYNTAX
.br
'\" t
.\" Title: toxic.conf
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2014-08-26
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC\&.CONF" "5" "2014\-08\-26" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
toxic.conf \- Configuration file for toxic
.SH "SYNOPSIS"
.sp
~/\&.config/tox/toxic\&.conf
.SH "DESCRIPTION"
.sp
The \fItoxic\&.conf\fR file is the main configuration file for \fBtoxic\fR(1) client\&. It uses syntax accepted by \fBlibconfig\fR\&. Lines starting with "//" are comments and will be ignored\&.
.SH "EXAMPLE"
.sp
.if n \{\
.RS 4
.\}
.nf
// Configuration for interface
ui = {
.RS
// true to enable timestamps, false to disable
.br
timestamps=true;
.br
// true to enable terminal alerts on messages, false to disable
.br
alerts=true;
.br
// true to use native terminal colours, false to use toxic default colour theme
.br
native_colors=false;
.br
// true to enable autologging, false to disable
.br
autolog=false;
.br
// 24 or 12 hour time
.br
time_format=24;
.br
// true to show you when others are typing a message in 1-on-1 chats
.br
show_typing_other=true;
.br
// true to show others when you're typing a message in 1-on-1 chats
.br
show_typing_self=true;
.br
// maximum lines for chat window history
.br
history_size=700;
.RE
timestamps = true;
alerts = false;
};
.PP
// Configuration for audio
audio = {
.RS
// preferred audio input device; numbers correspond to /lsdev in
.br
input_device=2;
.br
// preferred audio output device; numbers correspond to /lsdev out
.br
output_device=0;
.br
// default VAD treshold; float (recommended values are around 40)
.br
VAD_treshold=40.0;
.RE
input_device = 1;
};
.fi
.if n \{\
.RE
.\}
.SH "OPTIONS"
.PP
tox = {
.RS
// where to store received files
.br
//download_path="/home/USERNAME/Downloads/";
.RE
};
\fBui\fR
.RS 4
Configuration related to interface elements\&.
.PP
// To disable a sound set the path to "silent"
.br
sounds = {
.RS
error="__DATADIR__/sounds/Error.wav";
.br
self_log_in="__DATADIR__/sounds/LogIn.wav";
.br
self_log_out="__DATADIR__/sounds/LogOut.wav";
.br
user_log_in="__DATADIR__/sounds/ContactLogsIn.wav";
.br
user_log_out="__DATADIR__/sounds/ContactLogsOut.wav";
.br
call_incoming="__DATADIR__/sounds/IncomingCall.wav";
.br
call_outgoing="__DATADIR__/sounds/OutgoingCall.wav";
.br
generic_message="__DATADIR__/sounds/NewMessage.wav";
.br
transfer_pending="__DATADIR__/sounds/TransferPending.wav";
.br
transfer_completed="__DATADIR__/sounds/TransferComplete.wav";
\fBtimestamps\fR
.RS 4
Enable or disable timestamps\&. true or false
.RE
};
.PP
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
.br
// Note: All printable keys register as input
.br
keys = {
.RS
next_tab="Ctrl+P";
.br
prev_tab="Ctrl+O";
.br
scroll_line_up="PAGEUP";
.br
scroll_line_down="PAGEDOWN";
.br
half_page_up="Ctrl+F";
.br
half_page_down="Ctrl+V";
.br
page_bottom="Ctrl+H";
.br
peer_list_up="Ctrl+[";
.br
peer_list_down="Ctrl+]";
\fBalerts\fR
.RS 4
Enable or disable terminal alerts on events\&. true or false
.RE
};
.SH FILES
.IP ~/.config/tox/toxic.conf
Main configuration file.
.IP __DATADIR__/toxic.conf.example
Configuration example.
.SH AUTHORS
JFreegman <JFreegman@gmail.com>
.SH SEE ALSO
.BR toxic (1)
.SH LINKS
Project page on github: https://github.com/Tox/toxic
.br
IRC channel on Freenode: chat.freenode.net#tox
.PP
\fBnative_colors\fR
.RS 4
Select between native terminal colors and toxic color theme\&. true or false
.RE
.PP
\fBautolog\fR
.RS 4
Enable or disable autologging\&. true or false
.RE
.PP
\fBtime_format\fR
.RS 4
Select between 24 and 12 hour time\&. Specify 24 or 12
.RE
.PP
\fBshow_typing_other\fR
.RS 4
Show when others are typing in a 1\-on\-1 chat\&. true or false
.RE
.PP
\fBshow_typing_self\fR
.RS 4
Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false
.RE
.PP
\fBshow_welcome_msg\fR
.RS 4
Show welcome message on startup\&. true or false
.RE
.PP
\fBhistory_size\fR
.RS 4
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
.RE
.RE
.PP
\fBaudio\fR
.RS 4
Configuration related to audio devices\&.
.PP
\fBinput_device\fR
.RS 4
Audio input device\&. Integer value\&. Number corresponds to
/lsdev in
.RE
.PP
\fBoutput_device\fR
.RS 4
Audio output device\&. Integer value\&. Number corresponds to
/lsdev out
.RE
.PP
\fBVAD_treshold\fR
.RS 4
Voice Activity Detection treshold\&. Float value\&. Recommended values are around 40\&.0
.RE
.RE
.PP
\fBtox\fR
.RS 4
Configuration related to paths\&.
.PP
\fBdownload_path\fR
.RS 4
Default path for downloads\&. String value\&. Absolute path for downloaded files\&.
.RE
.PP
\fBchatlogs_path\fR
.RS 4
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
.RE
.RE
.PP
\fBsounds\fR
.RS 4
Configuration related to notification sounds\&. Special value "silent" can be used to disable a specific notification\&.
Each value is a string which corresponds to the absolute path of a wav sound file\&.
.PP
\fBerror\fR
.RS 4
Sound to play when an error occurs\&.
.RE
.PP
\fBself_log_in\fR
.RS 4
Sound to play when you log in\&.
.RE
.PP
\fBself_log_out\fR
.RS 4
Sound to play when you log out\&.
.RE
.PP
\fBuser_log_in\fR
.RS 4
Sound to play when a contact become online\&.
.RE
.PP
\fBuser_log_out\fR
.RS 4
Sound to play when a contact become offline\&.
.RE
.PP
\fBcall_incoming\fR
.RS 4
Sound to play when you receive an incoming call\&.
.RE
.PP
\fBcall_outgoing\fR
.RS 4
Sound to play when you start a call\&.
.RE
.PP
\fBgeneric_message\fR
.RS 4
Sound to play when an event occurs\&.
.RE
.PP
\fBtransfer_pending\fR
.RS 4
Sound to play when you receive a file transfer request\&.
.RE
.PP
\fBtransfer_completed\fR
.RS 4
Sound to play when a file transfer is completed\&.
.RE
.RE
.PP
\fBkeys\fR
.RS 4
Configuration related to user interface interaction\&. Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN\&.
Each value is a string which corresponds to a key combination\&.
.PP
\fBnext_tab\fR
.RS 4
Key combination to switch next tab\&.
.RE
.PP
\fBprev_tab\fR
.RS 4
Key combination to switch previous tab\&.
.RE
.PP
\fBscroll_line_up\fR
.RS 4
Key combination to scroll one line up\&.
.RE
.PP
\fBscroll_line_down\fR
.RS 4
Key combination to scroll one line down\&.
.RE
.PP
\fBhalf_page_up\fR
.RS 4
Key combination to scroll half page up\&.
.RE
.PP
\fBhalf_page_down\fR
.RS 4
Key combination to scroll half page down\&.
.RE
.PP
\fBpage_bottom\fR
.RS 4
Key combination to scroll to page bottom\&.
.RE
.PP
\fBpeer_list_up\fR
.RS 4
Key combination to scroll contacts list up\&.
.RE
.PP
\fBpeer_list_down\fR
.RS 4
Key combination to scroll contacts list down\&.
.RE
.RE
.SH "FILES"
.PP
~/\&.config/tox/toxic\&.conf
.RS 4
Main configuration file\&.
.RE
.PP
__DATADIR__/toxic\&.conf\&.example
.RS 4
Configuration example\&.
.RE
.SH "SEE ALSO"
.sp
\fBtoxic\fR(1)
.SH "RESOURCES"
.sp
Project page: https://github\&.com/Tox/toxic
.sp
IRC channel: chat\&.freenode\&.net#tox
.SH "AUTHORS"
.sp
JFreegman <JFreegman@gmail\&.com>

185
doc/toxic.conf.5.asc Normal file
View File

@ -0,0 +1,185 @@
toxic.conf(5)
=============
NAME
----
toxic.conf - Configuration file for toxic
SYNOPSIS
--------
~/.config/tox/toxic.conf
DESCRIPTION
-----------
The 'toxic.conf' file is the main configuration file for *toxic*(1) client.
It uses syntax accepted by *libconfig*.
Lines starting with "//" are comments and will be ignored.
EXAMPLE
-------
----
// Configuration for interface
ui = {
timestamps = true;
alerts = false;
};
// Configuration for audio
audio = {
input_device = 1;
};
----
OPTIONS
-------
*ui*::
Configuration related to interface elements.
*timestamps*;;
Enable or disable timestamps. true or false
*alerts*;;
Enable or disable terminal alerts on events. true or false
*native_colors*;;
Select between native terminal colors and toxic color theme. true or false
*autolog*;;
Enable or disable autologging. true or false
*time_format*;;
Select between 24 and 12 hour time. Specify 24 or 12
*show_typing_other*;;
Show when others are typing in a 1-on-1 chat. true or false
*show_typing_self*;;
Show others when you're typing in a 1-on-1 chat. true or false
*show_welcome_msg*;;
Show welcome message on startup. true or false
*history_size*;;
Maximum lines for chat window history. Integer value. (for example: 700)
*audio*::
Configuration related to audio devices.
*input_device*;;
Audio input device. Integer value. Number corresponds to `/lsdev in`
*output_device*;;
Audio output device. Integer value. Number corresponds to `/lsdev out`
*VAD_treshold*;;
Voice Activity Detection treshold. Float value. Recommended values are
around 40.0
*tox*::
Configuration related to paths.
*download_path*;;
Default path for downloads. String value. Absolute path for downloaded
files.
*chatlogs_path*;;
Default path for chatlogs. String value. Absolute path for chatlog files.
*sounds*::
Configuration related to notification sounds.
Special value "silent" can be used to disable a specific notification. +
Each value is a string which corresponds to the absolute path of a wav
sound file.
*error*;;
Sound to play when an error occurs.
*self_log_in*;;
Sound to play when you log in.
*self_log_out*;;
Sound to play when you log out.
*user_log_in*;;
Sound to play when a contact become online.
*user_log_out*;;
Sound to play when a contact become offline.
*call_incoming*;;
Sound to play when you receive an incoming call.
*call_outgoing*;;
Sound to play when you start a call.
*generic_message*;;
Sound to play when an event occurs.
*transfer_pending*;;
Sound to play when you receive a file transfer request.
*transfer_completed*;;
Sound to play when a file transfer is completed.
*keys*::
Configuration related to user interface interaction.
Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN. +
Each value is a string which corresponds to a key combination.
*next_tab*;;
Key combination to switch next tab.
*prev_tab*;;
Key combination to switch previous tab.
*scroll_line_up*;;
Key combination to scroll one line up.
*scroll_line_down*;;
Key combination to scroll one line down.
*half_page_up*;;
Key combination to scroll half page up.
*half_page_down*;;
Key combination to scroll half page down.
*page_bottom*;;
Key combination to scroll to page bottom.
*peer_list_up*;;
Key combination to scroll contacts list up.
*peer_list_down*;;
Key combination to scroll contacts list down.
FILES
-----
~/.config/tox/toxic.conf::
Main configuration file.
{datadir}/toxic.conf.example::
Configuration example.
SEE ALSO
--------
*toxic*(1)
RESOURCES
---------
Project page: <https://github.com/Tox/toxic>
IRC channel: chat.freenode.net#tox
AUTHORS
-------
JFreegman <JFreegman@gmail.com>

View File

@ -2,7 +2,10 @@
37.187.46.132 33445 A9D98212B3F972BD11DA52BEB0658C326FCCC1BFD49F347F9C2D3D8B61E1B927
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
37.187.20.216 33445 4FD54CFD426A338399767E56FD0F44F5E35FA8C38C8E87C8DC3FEAC0160F8E17
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
109.169.46.133 33445 7F31BFC93B8E4016A902144D0B110C3EA97CB7D43F1C4D21BCAE998A7C838821
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
37.59.102.176 33445 B98A2CEAA6C6A2FADC2C3632D284318B60FE5375CCB41EFA081AB67F500C1B0B
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
107.161.17.51 33445 7BE3951B97CA4B9ECDDA768E8C52BA19E9E2690AB584787BF4C90E04DBB75111
31.7.57.236 443 2A4B50D1D525DA2E669592A20C327B5FAD6C7E5962DC69296F9FEC77C4436E4E
63.165.243.15 443 8CD087E31C67568103E8C2A28653337E90E6B8EDA0D765D57C6B5172B4F1F04C

2
misc/DNSservers Normal file
View File

@ -0,0 +1,2 @@
utox.org d3154f65d28a5b41a05d4ac7e4b39c6b1c233cc857fb365c56e8392737462a12
toxme.se 5d72c517df6aec54f1e977a6b6f25914ea4cf7277a85027cd9f5196df17e0b13

View File

@ -23,6 +23,9 @@ ui = {
// true to show others when you're typing a message in 1-on-1 chats
show_typing_self=true;
// true to show the welcome message on startup
show_welcome_msg=true;
// maximum lines for chat window history
history_size=700;
};
@ -39,8 +42,11 @@ audio = {
};
tox = {
// where to store received files
//download_path="/home/USERNAME/Downloads/";
// Path for downloaded files
// download_path="/home/USERNAME/Downloads/";
// Path for chatlogs
// chatlogs_path="/home/USERNAME/toxic_chatlogs/";
};
// To disable a sound set the path to "silent"
@ -60,14 +66,14 @@ sounds = {
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
// Note: All printable keys register as input
keys = {
next_tab="Ctrl+P";
prev_tab="Ctrl+O";
scroll_line_up="PAGEUP";
scroll_line_down="PAGEDOWN";
half_page_up="Ctrl+F";
half_page_down="Ctrl+V";
page_bottom="Ctrl+H";
peer_list_up="Ctrl+[";
peer_list_down="Ctrl+]";
next_tab="Ctrl+P";
prev_tab="Ctrl+O";
scroll_line_up="PAGEUP";
scroll_line_down="PAGEDOWN";
half_page_up="Ctrl+F";
half_page_down="Ctrl+V";
page_bottom="Ctrl+H";
peer_list_up="Ctrl+[";
peer_list_down="Ctrl+]";
};

View File

@ -244,7 +244,7 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line)
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
return -1;
if (!strncmp(b_path, "~/", 2))
if (b_path[0] == '~')
complt_home_dir(self, b_path, sizeof(b_path));
int si = char_rfind(b_path, '/', strlen(b_path));

View File

@ -43,6 +43,7 @@
#include "help.h"
#include "autocomplete.h"
#include "notify.h"
#include "message_queue.h"
#ifdef _AUDIO
#include "audio_call.h"
@ -52,7 +53,7 @@
extern char *DATA_FILE;
extern FileSender file_senders[MAX_FILES];
extern ToxicFriend friends[MAX_FRIENDS_NUM];
extern _Friends Friends;
extern struct _Winthread Winthread;
extern struct user_settings *user_settings_;
@ -124,13 +125,17 @@ static void chat_set_window_name(ToxWindow *self, char *nick, int len)
snprintf(self->name, sizeof(self->name), "%s", nick);
}
void kill_chat_window(ToxWindow *self)
static void close_all_file_receivers(Tox *m, int friendnum);
void kill_chat_window(ToxWindow *self, Tox *m)
{
ChatContext *ctx = self->chatwin;
StatusBar *statusbar = self->stb;
close_all_file_receivers(m, self->num);
log_disable(ctx->log);
line_info_cleanup(ctx->hst);
cqueue_cleanup(ctx->cqueue);
#ifdef _AUDIO
stop_current_call(self);
@ -141,7 +146,6 @@ void kill_chat_window(ToxWindow *self)
delwin(statusbar->topline);
free(ctx->log);
free(ctx->hst);
free(ctx);
free(self->help);
free(statusbar);
@ -173,24 +177,45 @@ static void chat_onMessage(ToxWindow *self, Tox *m, int32_t num, const char *msg
}
static void chat_resume_file_transfers(Tox *m, int fnum);
static void chat_stop_file_senders(int fnum);
static void chat_onConnectionChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
{
if (self->num != num)
return;
StatusBar *statusbar = self->stb;
ChatContext *ctx = self->chatwin;
const char *msg;
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num);
if (status == 1) { /* Friend goes online */
statusbar->is_online = true;
friends[num].is_typing = user_settings_->show_typing_other == SHOW_TYPING_ON
Friends.list[num].is_typing = user_settings_->show_typing_other == SHOW_TYPING_ON
? tox_get_is_typing(m, num) : 0;
chat_resume_file_transfers(m, num);
msg = "has come online";
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
write_to_log(msg, nick, ctx->log, true);
} else { /* Friend goes offline */
statusbar->is_online = false;
friends[num].is_typing = 0;
Friends.list[num].is_typing = 0;
if (self->chatwin->self_is_typing)
set_self_typingstatus(self, m, 0);
chat_stop_file_senders(num);
msg = "has gone offline";
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, RED, msg);
write_to_log(msg, nick, ctx->log, true);
}
}
@ -199,7 +224,7 @@ static void chat_onTypingChange(ToxWindow *self, Tox *m, int32_t num, uint8_t is
if (self->num != num)
return;
friends[num].is_typing = is_typing;
Friends.list[num].is_typing = is_typing;
}
static void chat_onAction(ToxWindow *self, Tox *m, int32_t num, const char *action, uint16_t len)
@ -215,13 +240,13 @@ static void chat_onAction(ToxWindow *self, Tox *m, int32_t num, const char *acti
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, nick, NULL, ACTION, 0, 0, "%s", action);
line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action);
write_to_log(action, nick, ctx->log, true);
if (self->active_box != -1)
box_notify2(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
else
box_notify(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action );
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action );
}
static void chat_onNickChange(ToxWindow *self, Tox *m, int32_t num, const char *nick, uint16_t len)
@ -263,27 +288,31 @@ static void chat_onStatusMessageChange(ToxWindow *self, int32_t num, const char
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
}
static void chat_onReadReceipt(ToxWindow *self, Tox *m, int32_t num, uint32_t receipt)
{
cqueue_remove(self, m, receipt);
}
static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum,
uint64_t filesize, const char *pathname, uint16_t path_len)
{
if (self->num != num)
return;
const char *errmsg;
/* holds the filename appended to the user specified path */
char filename_path[MAX_STR_SIZE] = {0};
/* holds the lone filename */
char filename_nopath[MAX_STR_SIZE];
get_file_name(filename_nopath, sizeof(filename_nopath), pathname);
char sizestr[32];
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
int len = strlen(filename_nopath);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%llu bytes).",
filename_nopath, (long long unsigned int) filesize);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)",
filename_nopath, sizestr);
if (filenum >= MAX_FILES) {
errmsg = "Too many pending file requests; discarding.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Too many pending file requests; discarding.");
return;
}
@ -293,9 +322,8 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t
len += strlen(user_settings_->download_path);
}
if (len >= sizeof(friends[num].file_receiver.filenames[filenum])) {
errmsg = "File name too long; discarding.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
if (len >= sizeof(Friends.list[num].file_receiver[filenum].filename)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File name too long; discarding.");
return;
}
@ -325,26 +353,56 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t
filename[len + d_len] = '\0';
if (count > 999) {
errmsg = "Error saving file to disk.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error saving file to disk.");
return;
}
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %d' to accept the file transfer.", filenum);
friends[num].file_receiver.pending[filenum] = true;
friends[num].file_receiver.size[filenum] = filesize;
strcpy(friends[num].file_receiver.filenames[filenum], filename);
Friends.list[num].file_receiver[filenum].pending = true;
Friends.list[num].file_receiver[filenum].size = filesize;
Friends.list[num].file_receiver[filenum].filenum = filenum;
strcpy(Friends.list[num].file_receiver[filenum].filename, filename);
if (self->active_box != -1)
box_notify2(self, transfer_pending, NT_WNDALERT_2 | NT_NOFOCUS, self->active_box,
box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box,
"Incoming file: %s", filename );
else
box_notify(self, transfer_pending, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, self->name,
box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, &self->active_box, self->name,
"Incoming file: %s", filename );
}
/* Stops active file senders for this friend. Call when a friend goes offline */
static void chat_stop_file_senders(int fnum)
{
int i;
for (i = 0; i < MAX_FILES; ++i) {
if (file_senders[i].active && file_senders[i].friendnum == fnum)
file_senders[i].noconnection = true;
}
}
/* Tries to resume broken file transfers. Call when a friend comes online */
static void chat_resume_file_transfers(Tox *m, int fnum)
{
if (Friends.list[fnum].active_file_receivers == 0)
return;
int i;
for (i = 0; i < MAX_FILES; ++i) {
if (Friends.list[fnum].file_receiver[i].active) {
uint8_t bytes_recv[sizeof(uint64_t)];
memcpy(bytes_recv, &Friends.list[fnum].file_receiver[i].bytes_recv, sizeof(uint64_t));
net_to_host(bytes_recv, sizeof(uint64_t));
int filenum = Friends.list[fnum].file_receiver[i].filenum;
tox_file_send_control(m, fnum, 1, filenum, TOX_FILECONTROL_RESUME_BROKEN, bytes_recv, sizeof(uint64_t));
}
}
}
/* set CTRL to -1 if we don't want to send a control signal.
set msg to NULL if we don't want to display a message */
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL)
@ -352,13 +410,22 @@ void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL)
if (CTRL > 0)
tox_file_send_control(m, friendnum, 1, filenum, CTRL, 0, 0);
friends[friendnum].file_receiver.active[filenum] = false;
friends[friendnum].file_receiver.pending[filenum] = false;
FILE *file = friends[friendnum].file_receiver.files[filenum];
FILE *file = Friends.list[friendnum].file_receiver[filenum].file;
if (file != NULL) {
if (file != NULL)
fclose(file);
friends[friendnum].file_receiver.files[filenum] = NULL;
memset(&Friends.list[friendnum].file_receiver[filenum], 0, sizeof(struct FileReceiver));
--Friends.list[friendnum].active_file_receivers;
}
static void close_all_file_receivers(Tox *m, int friendnum)
{
int i;
for (i = 0; i < MAX_FILES; ++i) {
if (Friends.list[friendnum].file_receiver[i].active)
chat_close_file_receiver(m, i, friendnum, TOX_FILECONTROL_KILL);
}
}
@ -370,17 +437,27 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int32_t num, uint8_t rec
const char *filename;
char msg[MAX_STR_SIZE] = {0};
int i = 0; /* file_sender index */
int send_idx = 0; /* file sender index */
if (receive_send == 0) {
filename = friends[num].file_receiver.filenames[filenum];
if (!Friends.list[num].file_receiver[filenum].active)
return;
filename = Friends.list[num].file_receiver[filenum].filename;
} else {
int i;
for (i = 0; i < MAX_FILES; ++i) {
send_idx = i;
if (file_senders[i].active && file_senders[i].filenum == filenum)
break;
}
filename = file_senders[i].filename;
if (!file_senders[send_idx].active)
return;
filename = file_senders[send_idx].filename;
}
switch (control_type) {
@ -392,7 +469,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int32_t num, uint8_t rec
char progline[MAX_STR_SIZE];
prep_prog_line(progline);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
file_senders[i].line_id = self->chatwin->hst->line_end->id + 2;
file_senders[send_idx].line_id = self->chatwin->hst->line_end->id + 2;
sound_notify(self, silent, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2, NULL);
}
@ -411,29 +488,50 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int32_t num, uint8_t rec
if (receive_send == 0)
chat_close_file_receiver(m, filenum, num, -1);
else
close_file_sender(self, m, i, NULL, -1, filenum, num);
close_file_sender(self, m, send_idx, NULL, -1, filenum, num);
break;
case TOX_FILECONTROL_FINISHED:
if (receive_send == 0) {
snprintf(msg, sizeof(msg), "File transfer for '%s' complete.", filename);
print_progress_bar(self, filenum, num, 100.0);
char filename_nopath[MAX_STR_SIZE];
get_file_name(filename_nopath, sizeof(filename_nopath), filename);
snprintf(msg, sizeof(msg), "File transfer for '%s' complete.", filename_nopath);
chat_close_file_receiver(m, filenum, num, TOX_FILECONTROL_FINISHED);
} else {
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", filename);
close_file_sender(self, m, i, msg, TOX_FILECONTROL_FINISHED, filenum, num);
return;
close_file_sender(self, m, send_idx, NULL, TOX_FILECONTROL_FINISHED, filenum, num);
}
if (self->active_box != -1)
box_notify2(self, transfer_completed, NT_NOFOCUS | NT_WNDALERT_2,
self->active_box, "%s", msg);
box_notify2(self, transfer_completed, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
else
box_notify(self, transfer_completed, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box,
self->name, "%s", msg);
break;
case TOX_FILECONTROL_RESUME_BROKEN:
if (receive_send == 0)
break;
FILE *fp = file_senders[send_idx].file;
if (fp == NULL)
break;
uint8_t tmp[sizeof(uint64_t)];
memcpy(tmp, &data, sizeof(uint64_t));
net_to_host(tmp, sizeof(uint64_t));
uint64_t datapos;
memcpy(&datapos, tmp, sizeof(uint64_t));
fseek(fp, datapos, SEEK_SET);
tox_file_send_control(m, num, 0, filenum, TOX_FILECONTROL_ACCEPT, 0, 0);
file_senders[send_idx].noconnection = false;
break;
}
if (msg[0])
@ -446,7 +544,7 @@ static void chat_onFileData(ToxWindow *self, Tox *m, int32_t num, uint8_t filenu
if (self->num != num)
return;
FILE *fp = friends[num].file_receiver.files[filenum];
FILE *fp = Friends.list[num].file_receiver[filenum].file;
if (fp) {
if (fwrite(data, length, 1, fp) != 1) {
@ -455,18 +553,8 @@ static void chat_onFileData(ToxWindow *self, Tox *m, int32_t num, uint8_t filenu
}
}
friends[num].file_receiver.bps[filenum] += length;
double remain = (double) tox_file_data_remaining(m, num, filenum, 1);
uint64_t curtime = get_unix_time();
/* refresh line with percentage complete and transfer speed (must be called once per second) */
if (!remain || timed_out(friends[num].file_receiver.last_progress[filenum], curtime, 1)) {
friends[num].file_receiver.last_progress[filenum] = curtime;
uint64_t size = friends[num].file_receiver.size[filenum];
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
print_progress_bar(self, filenum, num, pct_done);
friends[num].file_receiver.bps[filenum] = 0;
}
Friends.list[num].file_receiver[filenum].bps += length;
Friends.list[num].file_receiver[filenum].bytes_recv += length;
}
static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, const char *group_pub_key)
@ -480,9 +568,9 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, co
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a group chat.", name);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
memcpy(friends[friendnumber].groupchat_key, group_pub_key,
sizeof(friends[friendnumber].groupchat_key));
friends[friendnumber].groupchat_pending = true;
memcpy(Friends.list[friendnumber].groupchat_key, group_pub_key,
sizeof(Friends.list[friendnumber].groupchat_key));
Friends.list[friendnumber].groupchat_pending = true;
sound_notify(self, generic_message, NT_WNDALERT_2, NULL);
@ -748,13 +836,8 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, ACTION, 0, 0, "%s", action);
if (tox_send_action(m, self->num, (uint8_t *) action, strlen(action)) == 0) {
line_info_add(self, NULL, selfname, NULL, SYS_MSG, 0, RED, " * Failed to send action.");
} else {
write_to_log(action, selfname, ctx->log, true);
}
line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action);
cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, ctx->hst->line_end->id + 1);
}
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
@ -819,7 +902,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (line[0] == '/') {
if (strcmp(line, "/close") == 0) {
kill_chat_window(self);
kill_chat_window(self, m);
return;
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_action(self, ctx, m, line + strlen("/me "));
@ -835,12 +918,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
if (!statusbar->is_online || tox_send_message(m, self->num, (uint8_t *) line, strlen(line)) == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message.");
} else {
write_to_log(line, selfname, ctx->log, false);
}
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1);
}
wclear(ctx->linewin);
@ -899,14 +977,14 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
wprintw(statusbar->topline, " %s", ONLINE_CHAR);
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
if (friends[self->num].is_typing)
if (Friends.list[self->num].is_typing)
wattron(statusbar->topline, COLOR_PAIR(YELLOW));
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s ", statusbar->nick);
wattroff(statusbar->topline, A_BOLD);
if (friends[self->num].is_typing)
if (Friends.list[self->num].is_typing)
wattroff(statusbar->topline, COLOR_PAIR(YELLOW));
} else {
wprintw(statusbar->topline, " %s", OFFLINE_CHAR);
@ -930,10 +1008,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
self->x = x2;
/* Truncate note if it doesn't fit in statusbar */
uint16_t maxlen = x2 - getcurx(statusbar->topline) - (KEY_IDENT_DIGITS * 2) - 7;
uint16_t maxlen = x2 - getcurx(statusbar->topline) - (KEY_IDENT_DIGITS * 2) - 6;
if (statusbar->statusmsg_len > maxlen) {
statusbar->statusmsg[maxlen] = '\0';
statusbar->statusmsg[maxlen - 3] = '\0';
strcat(statusbar->statusmsg, "...");
statusbar->statusmsg_len = maxlen;
}
@ -947,7 +1026,7 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
int i;
for (i = 0; i < KEY_IDENT_DIGITS; ++i)
wprintw(statusbar->topline, "%02X", friends[self->num].pub_key[i] & 0xff);
wprintw(statusbar->topline, "%02X", Friends.list[self->num].pub_key[i] & 0xff);
wprintw(statusbar->topline, "}\n");
@ -1005,14 +1084,15 @@ static void chat_onInit(ToxWindow *self, Tox *m)
ctx->hst = calloc(1, sizeof(struct history));
ctx->log = calloc(1, sizeof(struct chatlog));
ctx->cqueue = calloc(1, sizeof(struct chat_queue));
if (ctx->log == NULL || ctx->hst == NULL)
if (ctx->log == NULL || ctx->hst == NULL || ctx->cqueue == NULL)
exit_toxic_err("failed in chat_onInit", FATALERR_MEMORY);
line_info_init(ctx->hst);
if (friends[self->num].logging_on)
log_enable(nick, friends[self->num].pub_key, ctx->log);
if (Friends.list[self->num].logging_on)
log_enable(nick, Friends.list[self->num].pub_key, ctx->log);
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
@ -1042,6 +1122,7 @@ ToxWindow new_chat(Tox *m, int32_t friendnum)
ret.onFileSendRequest = &chat_onFileSendRequest;
ret.onFileControl = &chat_onFileControl;
ret.onFileData = &chat_onFileData;
ret.onReadReceipt = &chat_onReadReceipt;
#ifdef _AUDIO
ret.onInvite = &chat_onInvite;

View File

@ -29,7 +29,7 @@
/* set CTRL to -1 if we don't want to send a control signal.
set msg to NULL if we don't want to display a message */
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
void kill_chat_window(ToxWindow *self);
void kill_chat_window(ToxWindow *self, Tox *m);
ToxWindow new_chat(Tox *m, int32_t friendnum);
#endif /* end of include guard: CHAT_H_6489PZ13 */

View File

@ -34,8 +34,7 @@
#include "file_senders.h"
extern ToxWindow *prompt;
extern ToxicFriend friends[MAX_FRIENDS_NUM];
extern _Friends Friends;
extern FileSender file_senders[MAX_FILES];
extern uint8_t max_file_senders_index;
@ -57,12 +56,12 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
}
if (strcasecmp(inoutstr, "in") == 0) { /* cancel an incoming file transfer */
if (!friends[self->num].file_receiver.active[filenum]) {
if (!Friends.list[self->num].file_receiver[filenum].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
const char *filepath = friends[self->num].file_receiver.filenames[filenum];
const char *filepath = Friends.list[self->num].file_receiver[filenum].filename;
char name[MAX_STR_SIZE];
get_file_name(name, sizeof(name), filepath);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer for '%s' canceled.", name);
@ -124,9 +123,9 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
return;
}
const char *groupkey = friends[self->num].groupchat_key;
const char *groupkey = Friends.list[self->num].groupchat_key;
if (!friends[self->num].groupchat_pending) {
if (!Friends.list[self->num].groupchat_pending) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
return;
}
@ -159,12 +158,12 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
return;
}
if (!friends[self->num].file_receiver.pending[filenum]) {
if (!Friends.list[self->num].file_receiver[filenum].pending) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
const char *filename = friends[self->num].file_receiver.filenames[filenum];
const char *filename = Friends.list[self->num].file_receiver[filenum].filename;
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", filenum, filename);
@ -173,18 +172,20 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
char progline[MAX_STR_SIZE];
prep_prog_line(progline);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
friends[self->num].file_receiver.line_id[filenum] = self->chatwin->hst->line_end->id + 2;
Friends.list[self->num].file_receiver[filenum].line_id = self->chatwin->hst->line_end->id + 2;
if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) {
if ((Friends.list[self->num].file_receiver[filenum].file = fopen(filename, "a")) == NULL) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "* Error writing to file.");
tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
} else {
Friends.list[self->num].file_receiver[filenum].active = true;
++Friends.list[self->num].active_file_receivers;
}
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed.");
}
friends[self->num].file_receiver.pending[filenum] = false;
friends[self->num].file_receiver.active[filenum] = true;
Friends.list[self->num].file_receiver[filenum].pending = false;
}
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -241,7 +242,6 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
for (i = 0; i < MAX_FILES; ++i) {
if (!file_senders[i].active) {
file_senders[i].queue_pos = num_active_file_senders;
memcpy(file_senders[i].filename, filename, namelen + 1);
file_senders[i].active = true;
file_senders[i].toxwin = self;
@ -253,13 +253,17 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
tox_file_data_size(m, self->num), file_to_send);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s'", filenum, filename);
char sizestr[32];
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"Sending file [%d]: '%s' (%s)", filenum, filename, sizestr);
++num_active_file_senders;
if (i == max_file_senders_index)
++max_file_senders_index;
reset_file_sender_queue();
return;
}
}

156
src/dns.c
View File

@ -40,19 +40,23 @@
#include "dns.h"
#include "global_commands.h"
#include "misc_tools.h"
#include "configdir.h"
#define MAX_DNS_REQST_SIZE 256
#define NUM_DNS3_SERVERS 2 /* must correspond to number of items in dns3_servers array */
#define DNS3_KEY_SIZE 32
#define MAX_DNS_REQST_SIZE 255
#define TOX_DNS3_TXT_PREFIX "v=tox3;id="
#define DNS3_KEY_SZ 32
extern struct _Winthread Winthread;
extern struct _dns3_servers dns3_servers;
extern struct arg_opts arg_opts;
/* TODO: process keys from key file instead of hard-coding like a noob */
static struct dns3_server {
#define NUM_DNS3_BACKUP_SERVERS 2
/* Hardcoded backup in case domain list is not loaded */
static struct dns3_server_backup {
const char *name;
char key[DNS3_KEY_SZ];
} dns3_servers[] = {
char key[DNS3_KEY_SIZE];
} dns3_servers_backup[] = {
{
"utox.org",
{
@ -84,6 +88,61 @@ static struct _dns_thread {
} dns_thread;
#define MAX_DNS_SERVERS 50
#define MAX_DOMAIN_SIZE 32
#define MAX_DNS_LINE MAX_DOMAIN_SIZE + (DNS3_KEY_SIZE * 2) + 3
struct _dns3_servers {
bool loaded;
int lines;
char names[MAX_DNS_SERVERS][MAX_DOMAIN_SIZE];
char keys[MAX_DNS_SERVERS][DNS3_KEY_SIZE];
} dns3_servers;
static int load_dns_domainlist(const char *path)
{
FILE *fp = fopen(path, "r");
if (fp == NULL)
return -1;
char line[MAX_DNS_LINE];
while (fgets(line, sizeof(line), fp) && dns3_servers.lines < MAX_DNS_SERVERS) {
int linelen = strlen(line);
if (linelen < DNS3_KEY_SIZE * 2 + 5)
continue;
if (line[linelen - 1] == '\n')
line[--linelen] = '\0';
const char *name = strtok(line, " ");
const char *keystr = strtok(NULL, " ");
if (name == NULL || keystr == NULL)
continue;
if (strlen(keystr) != DNS3_KEY_SIZE * 2)
continue;
snprintf(dns3_servers.names[dns3_servers.lines], sizeof(dns3_servers.names[dns3_servers.lines]), "%s", name);
int res = hex_string_to_bytes(dns3_servers.keys[dns3_servers.lines], DNS3_KEY_SIZE, keystr);
if (res == -1)
continue;
++dns3_servers.lines;
}
fclose(fp);
if (dns3_servers.lines < 1)
return -2;
return 0;
}
static int dns_error(ToxWindow *self, const char *errmsg)
{
pthread_mutex_lock(&Winthread.lock);
@ -161,6 +220,9 @@ static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char
if (!size || txt_len >= size || !txt_len)
return dns_error(self, "No record found.");
if (txt_len > MAX_DNS_REQST_SIZE)
return dns_error(self, "Invalid DNS response.");
ans_pt++;
ans_pt[txt_len] = '\0';
memcpy(buf, ans_pt, txt_len + 1);
@ -191,35 +253,61 @@ static int parse_addr(const char *addr, char *namebuf, char *dombuf)
return strlen(namebuf);
}
/* matches input domain name with domains in list and obtains key. Return 0 on success, -1 on failure */
static int get_domain_match(char *pubkey, char *domain, const char *inputdomain)
{
/* check server list first */
int i;
bool match = false;
for (i = 0; i < dns3_servers.lines; ++i) {
if (strcmp(dns3_servers.names[i], inputdomain) == 0) {
memcpy(pubkey, dns3_servers.keys[i], DNS3_KEY_SIZE);
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers.names[i]);
match = true;
break;
}
}
/* fall back to hard-coded domains on server list failure */
if (!match) {
for (i = 0; i < NUM_DNS3_BACKUP_SERVERS; ++i) {
if (strcmp(dns3_servers_backup[i].name, inputdomain) == 0) {
memcpy(pubkey, dns3_servers_backup[i].key, DNS3_KEY_SIZE);
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers_backup[i].name);
match = true;
break;
}
}
if (!match)
return -1;
}
return 0;
}
/* Does DNS lookup for addr and puts resulting tox id in id_bin. */
void *dns3_lookup_thread(void *data)
{
ToxWindow *self = t_data.self;
char domain[MAX_STR_SIZE];
char inputdomain[MAX_STR_SIZE];
char name[MAX_STR_SIZE];
int namelen = parse_addr(t_data.addr, name, domain);
int namelen = parse_addr(t_data.addr, name, inputdomain);
if (namelen == -1) {
dns_error(self, "Must be a Tox ID or an address in the form username@domain");
kill_dns_thread(NULL);
}
/* get domain name/pub key */
const char *DNS_pubkey = NULL;
const char *domname = NULL;
int i;
char DNS_pubkey[DNS3_KEY_SIZE];
char domain[MAX_DOMAIN_SIZE];
for (i = 0; i < NUM_DNS3_SERVERS; ++i) {
if (strcmp(dns3_servers[i].name, domain) == 0) {
DNS_pubkey = dns3_servers[i].key;
domname = dns3_servers[i].name;
break;
}
}
int match = get_domain_match(DNS_pubkey, domain, inputdomain);
if (domname == NULL) {
if (match == -1) {
dns_error(self, "Domain not found.");
kill_dns_thread(NULL);
}
@ -231,7 +319,7 @@ void *dns3_lookup_thread(void *data)
kill_dns_thread(NULL);
}
char string[MAX_DNS_REQST_SIZE];
char string[MAX_DNS_REQST_SIZE + 1];
uint32_t request_id;
int str_len = tox_generate_dns3_string(dns_obj, (uint8_t *) string, sizeof(string), &request_id,
@ -245,10 +333,10 @@ void *dns3_lookup_thread(void *data)
string[str_len] = '\0';
u_char answer[PACKETSZ];
char d_string[MAX_DNS_REQST_SIZE];
char d_string[MAX_DOMAIN_SIZE + MAX_DNS_REQST_SIZE + 10];
/* format string and create dns query */
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domname);
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domain);
int ans_len = res_query(d_string, C_IN, T_TXT, answer, sizeof(answer));
if (ans_len <= 0) {
@ -256,13 +344,13 @@ void *dns3_lookup_thread(void *data)
kill_dns_thread(dns_obj);
}
char ans_id[MAX_DNS_REQST_SIZE];
char ans_id[MAX_DNS_REQST_SIZE + 1];
/* extract TXT from DNS response */
if (parse_dns_response(self, answer, ans_len, ans_id) == -1)
kill_dns_thread(dns_obj);
char encrypted_id[MAX_DNS_REQST_SIZE];
char encrypted_id[MAX_DNS_REQST_SIZE + 1];
int prfx_len = strlen(TOX_DNS3_TXT_PREFIX);
/* extract the encrypted ID from TXT response */
@ -290,12 +378,28 @@ void *dns3_lookup_thread(void *data)
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg)
{
if (arg_opts.use_proxy && arg_opts.force_tcp) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "DNS lookups are disabled.");
return;
}
if (t_data.busy) {
const char *err = "Please wait for previous user lookup to finish.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, err);
return;
}
if (!dns3_servers.loaded) {
const char *path = arg_opts.dns_path[0] ? arg_opts.dns_path : PACKAGE_DATADIR "/DNSservers";
dns3_servers.loaded = true;
int ret = load_dns_domainlist(path);
if (ret < 0) {
const char *errmsg = "DNS server list failed to load with error code %d. Falling back to hard-coded list.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg, ret);
}
}
snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin);
snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr);
snprintf(t_data.msg, sizeof(t_data.msg), "%s", msg);

View File

@ -43,6 +43,7 @@ static struct cmd_func global_commands[] = {
{ "/add", cmd_add },
{ "/clear", cmd_clear },
{ "/connect", cmd_connect },
{ "/decline", cmd_decline },
{ "/exit", cmd_quit },
{ "/groupchat", cmd_groupchat },
{ "/help", cmd_prompt_help },
@ -52,12 +53,13 @@ static struct cmd_func global_commands[] = {
{ "/note", cmd_note },
{ "/q", cmd_quit },
{ "/quit", cmd_quit },
{ "/requests", cmd_requests },
{ "/status", cmd_status },
#ifdef _AUDIO
{ "/lsdev", cmd_list_devices },
{ "/sdev", cmd_change_device },
#endif /* _AUDIO */
{ NULL, NULL },
};
static struct cmd_func chat_commands[] = {
@ -66,7 +68,6 @@ static struct cmd_func chat_commands[] = {
{ "/join", cmd_join_group },
{ "/savefile", cmd_savefile },
{ "/sendfile", cmd_sendfile },
#ifdef _AUDIO
{ "/call", cmd_call },
{ "/answer", cmd_answer },
@ -76,6 +77,7 @@ static struct cmd_func chat_commands[] = {
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
#endif /* _AUDIO */
{ NULL, NULL },
};
/* Parses input command and puts args into arg array.
@ -99,7 +101,7 @@ static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*a
i = char_find(1, cmd, '\"');
if (cmd[i] == '\0') {
char *errmsg = "Invalid argument. Did you forget a closing \"?";
const char *errmsg = "Invalid argument. Did you forget a closing \"?";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
free(cmd);
return -1;
@ -116,7 +118,7 @@ static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*a
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
strcpy(cmd, tmp);
strcpy(cmd, tmp); /* tmp will always fit inside cmd */
}
free(cmd);
@ -124,12 +126,12 @@ static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*a
}
/* Matches command to respective function. Returns 0 on match, 1 on no match */
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_cmds,
struct cmd_func *commands, char (*args)[MAX_STR_SIZE])
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
char (*args)[MAX_STR_SIZE])
{
int i;
for (i = 0; i < num_cmds; ++i) {
for (i = 0; commands[i].name != NULL; ++i) {
if (strcmp(args[0], commands[i].name) == 0) {
(commands[i].func)(w, self, m, num_args - 1, args);
return 0;
@ -156,7 +158,7 @@ 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:
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
if (do_command(w, self, m, num_args, chat_commands, args) == 0)
return;
break;
@ -165,12 +167,8 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
break;
}
if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0)
if (do_command(w, self, m, num_args, global_commands, args) == 0)
return;
#ifdef _SOUND_NOTIFY
sound_notify(self, error, 0, NULL);
#else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
#endif
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
}

View File

@ -28,14 +28,6 @@
#define MAX_NUM_ARGS 4 /* Includes command */
#ifdef _AUDIO
#define GLOBAL_NUM_COMMANDS 16
#define CHAT_NUM_COMMANDS 12
#else
#define GLOBAL_NUM_COMMANDS 14
#define CHAT_NUM_COMMANDS 5
#endif /* _AUDIO */
enum {
GLOBAL_COMMAND_MODE,
CHAT_COMMAND_MODE,

View File

@ -36,11 +36,7 @@
FileSender file_senders[MAX_FILES];
uint8_t max_file_senders_index;
uint8_t num_active_file_senders;
extern ToxicFriend friends[MAX_FRIENDS_NUM];
#define KiB 1024
#define MiB 1048576 /* 1024 ^ 2 */
#define GiB 1073741824 /* 1024 ^ 3 */
extern _Friends Friends;
/* creates initial progress line that will be updated during file transfer.
Assumes progline is of size MAX_STR_SIZE */
@ -66,35 +62,20 @@ void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_done
bps = file_senders[idx].bps;
line_id = file_senders[idx].line_id;
} else {
bps = friends[friendnum].file_receiver.bps[idx];
line_id = friends[friendnum].file_receiver.line_id[idx];
}
const char *unit;
if (bps < KiB) {
unit = "B/s";
} else if (bps < MiB) {
unit = "KiB/s";
bps /= (double) KiB;
} else if (bps < GiB) {
unit = "MiB/s";
bps /= (double) MiB;
} else {
unit = "GiB/s";
bps /= (double) GiB;
bps = Friends.list[friendnum].file_receiver[idx].bps;
line_id = Friends.list[friendnum].file_receiver[idx].line_id;
}
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "%.1f %s [", bps, unit);
bytes_convert_str(msg, sizeof(msg), bps);
strcat(msg, "/s [");
int n = pct_done / (100 / NUM_PROG_MARKS);
int i;
int i, j;
for (i = 0; i < n; ++i)
strcat(msg, "#");
int j;
for (j = i; j < NUM_PROG_MARKS; ++j)
strcat(msg, "-");
@ -108,6 +89,64 @@ void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_done
line_info_set(self, line_id, msg);
}
/* refreshes active file receiver status bars */
static void refresh_recv_prog(Tox *m)
{
int i;
uint64_t curtime = get_unix_time();
for (i = 2; i < MAX_WINDOWS_NUM; ++i) {
ToxWindow *toxwin = get_window_ptr(i);
if (toxwin == NULL || !toxwin->is_chat)
continue;
int fnum = toxwin->num;
int j;
for (j = 0; j < MAX_FILES; ++j) {
if (!Friends.list[fnum].file_receiver[j].active)
continue;
int filenum = Friends.list[fnum].file_receiver[j].filenum;
double remain = (double) tox_file_data_remaining(m, fnum, filenum, 1);
/* must be called once per second */
if (timed_out(Friends.list[fnum].file_receiver[filenum].last_progress, curtime, 1)) {
Friends.list[fnum].file_receiver[filenum].last_progress = curtime;
uint64_t size = Friends.list[fnum].file_receiver[filenum].size;
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
print_progress_bar(toxwin, filenum, fnum, pct_done);
Friends.list[fnum].file_receiver[filenum].bps = 0;
}
}
}
}
/* refreshes active file sender status bars */
static void refresh_sender_prog(Tox *m)
{
int i;
uint64_t curtime = get_unix_time();
for (i = 0; i < max_file_senders_index; ++i) {
if (!file_senders[i].active || file_senders[i].finished)
continue;
int filenum = file_senders[i].filenum;
int32_t friendnum = file_senders[i].friendnum;
double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0);
/* must be called once per second */
if (timed_out(file_senders[i].last_progress, curtime, 1)) {
file_senders[i].last_progress = curtime;
double pct_done = remain > 0 ? (1 - (remain / file_senders[i].size)) * 100 : 100;
print_progress_bar(file_senders[i].toxwin, i, -1, pct_done);
file_senders[i].bps = 0;
}
}
}
static void set_max_file_senders_index(void)
{
int j;
@ -120,6 +159,18 @@ static void set_max_file_senders_index(void)
max_file_senders_index = j;
}
/* called whenever a file sender is opened or closed */
void reset_file_sender_queue(void)
{
int i;
int pos = 0;
for (i = 0; i < max_file_senders_index; ++i) {
if (file_senders[i].active)
file_senders[i].queue_pos = pos++;
}
}
/* set CTRL to -1 if we don't want to send a control signal.
set msg to NULL if we don't want to display a message */
void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, int32_t friendnum)
@ -133,6 +184,7 @@ void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL
fclose(file_senders[i].file);
memset(&file_senders[i], 0, sizeof(FileSender));
set_max_file_senders_index();
reset_file_sender_queue();
--num_active_file_senders;
}
@ -161,27 +213,30 @@ static void send_file_data(ToxWindow *self, Tox *m, int i, int32_t friendnum, in
file_senders[i].piecelen) == -1)
return;
uint64_t curtime = get_unix_time();
file_senders[i].timestamp = curtime;
file_senders[i].timestamp = get_unix_time();
file_senders[i].bps += file_senders[i].piecelen;
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
tox_file_data_size(m, friendnum), fp);
double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0);
/* note: file sender is closed in chat_onFileControl callback after receiving reply */
if (file_senders[i].piecelen == 0) {
if (feof(fp) != 0) { /* make sure we're really at eof */
print_progress_bar(self, i, -1, 100.0);
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
file_senders[i].finished = true;
} else {
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Read error.", file_senders[i].filename);
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL);
if (self->active_box != -1)
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
else
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg);
}
/* refresh line with percentage complete and transfer speed (must be called once per second) */
if ( (self->chatwin != NULL && timed_out(file_senders[i].last_progress, curtime, 1))
|| (!remain && !file_senders[i].finished) ) {
file_senders[i].last_progress = curtime;
double pct_done = remain > 0 ? (1 - (remain / file_senders[i].size)) * 100 : 100;
print_progress_bar(self, i, -1, pct_done);
file_senders[i].bps = 0;
}
/* file sender is closed in chat_onFileControl callback after receiving reply */
if (file_senders[i].piecelen == 0 && !file_senders[i].finished) {
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
file_senders[i].finished = true;
return;
}
}
}
@ -218,15 +273,19 @@ void do_file_senders(Tox *m)
sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL);
if (self->active_box != -1)
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2,
self->active_box, "File transfer for '%s' timed out.", filename );
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
else
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box,
self->name, "File transfer for '%s' timed out.", filename );
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg);
continue;
}
if (!file_senders[i].noconnection && !file_senders[i].finished)
send_file_data(self, m, i, friendnum, filenum, filename);
file_senders[i].queue_pos = num_active_file_senders - 1;
send_file_data(self, m, i, friendnum, filenum, filename);
}
refresh_sender_prog(m);
refresh_recv_prog(m);
}

View File

@ -26,8 +26,12 @@
#include "toxic.h"
#include "windows.h"
#define KiB 1024
#define MiB 1048576 /* 1024 ^ 2 */
#define GiB 1073741824 /* 1024 ^ 3 */
#define FILE_PIECE_SIZE 2048 /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */
#define MAX_FILES 255
#define MAX_FILES 32
#define TIMEOUT_FILESENDER 120
#define NUM_PROG_MARKS 50 /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
@ -36,6 +40,7 @@ typedef struct {
ToxWindow *toxwin;
int32_t friendnum;
bool active;
bool noconnection;
bool finished;
int filenum;
char nextpiece[FILE_PIECE_SIZE];
@ -61,6 +66,9 @@ void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_rema
set msg to NULL if we don't want to display a message */
void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, int32_t friendnum);
/* called whenever a file sender is opened or closed */
void reset_file_sender_queue(void);
void close_all_file_senders(Tox *m);
void do_file_senders(Tox *m);

View File

@ -51,20 +51,17 @@ extern struct user_settings *user_settings_;
extern struct arg_opts arg_opts;
static uint8_t blocklist_view = 0; /* 0 if we're in friendlist view, 1 if we're in blocklist view */
static int num_selected = 0;
static int max_friends_index = 0; /* 1 + the index of the last friend in friends array */
static int num_friends = 0;
ToxicFriend friends[MAX_FRIENDS_NUM];
static int friendlist_index[MAX_FRIENDS_NUM] = {0};
_Friends Friends;
static struct _Blocked_Contacts {
static struct _Blocked {
int num_selected;
int max_index;
int max_idx;
int num_blocked;
BlockedFriend list[MAX_FRIENDS_NUM];
int index[MAX_FRIENDS_NUM];
} Blocked_Contacts;
int *index;
BlockedFriend *list;
} Blocked;
static struct _pendingDel {
int num;
@ -72,6 +69,52 @@ static struct _pendingDel {
WINDOW *popup;
} pendingdelete;
static void realloc_friends(int n)
{
if (n <= 0) {
free(Friends.list);
free(Friends.index);
Friends.list = NULL;
Friends.index = NULL;
return;
}
ToxicFriend *f = realloc(Friends.list, n * sizeof(ToxicFriend));
int *f_idx = realloc(Friends.index, n * sizeof(int));
if (f == NULL || f_idx == NULL)
exit_toxic_err("failed in realloc_friends", FATALERR_MEMORY);
Friends.list = f;
Friends.index = f_idx;
}
static void realloc_blocklist(int n)
{
if (n <= 0) {
free(Blocked.list);
free(Blocked.index);
Blocked.list = NULL;
Blocked.index = NULL;
return;
}
BlockedFriend *b = realloc(Blocked.list, n * sizeof(BlockedFriend));
int *b_idx = realloc(Blocked.index, n * sizeof(int));
if (b == NULL || b_idx == NULL)
exit_toxic_err("failed in realloc_blocklist", FATALERR_MEMORY);
Blocked.list = b;
Blocked.index = b_idx;
}
void kill_friendlist(void)
{
realloc_blocklist(0);
realloc_friends(0);
}
static int save_blocklist(char *path)
{
if (arg_opts.ignore_data_file)
@ -80,29 +123,30 @@ static int save_blocklist(char *path)
if (path == NULL)
return -1;
int len = sizeof(BlockedFriend) * Blocked_Contacts.num_blocked;
int len = sizeof(BlockedFriend) * Blocked.num_blocked;
char *data = malloc(len);
if (data == NULL)
exit_toxic_err("Failed in save_blocklist", FATALERR_MEMORY);
int i;
int ret = -1;
int count = 0;
for (i = 0; i < Blocked_Contacts.max_index; ++i) {
if (count > Blocked_Contacts.num_blocked)
return -1;
for (i = 0; i < Blocked.max_idx; ++i) {
if (count > Blocked.num_blocked)
goto on_error;
if (Blocked_Contacts.list[i].active) {
if (Blocked.list[i].active) {
BlockedFriend tmp;
memset(&tmp, 0, sizeof(BlockedFriend));
tmp.namelength = htons(Blocked_Contacts.list[i].namelength);
memcpy(tmp.name, Blocked_Contacts.list[i].name, Blocked_Contacts.list[i].namelength + 1);
memcpy(tmp.pub_key, Blocked_Contacts.list[i].pub_key, TOX_CLIENT_ID_SIZE);
tmp.namelength = htons(Blocked.list[i].namelength);
memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1);
memcpy(tmp.pub_key, Blocked.list[i].pub_key, TOX_CLIENT_ID_SIZE);
uint8_t lastonline[sizeof(uint64_t)];
memcpy(lastonline, &Blocked_Contacts.list[i].last_on, sizeof(uint64_t));
host_to_net(lastonline, sizeof(uint64_t));
memcpy(lastonline, &Blocked.list[i].last_on, sizeof(uint64_t));
hst_to_net(lastonline, sizeof(uint64_t));
memcpy(&tmp.last_on, lastonline, sizeof(uint64_t));
memcpy(data + count * sizeof(BlockedFriend), &tmp, sizeof(BlockedFriend));
@ -112,17 +156,14 @@ static int save_blocklist(char *path)
FILE *fp = fopen(path, "wb");
if (fp == NULL) {
free(data);
return -1;
}
if (fp == NULL)
goto on_error;
int ret = 0;
if (fwrite(data, len, 1, fp) != 1)
ret = -1;
if (fwrite(data, len, 1, fp) == 1)
ret = 0;
fclose(fp);
on_error:
free(data);
return ret;
}
@ -163,27 +204,30 @@ int load_blocklist(char *path)
}
int num = len / sizeof(BlockedFriend);
Blocked.max_idx = num;
realloc_blocklist(num);
int i;
for (i = 0; i < num; ++i) {
memset(&Blocked.list[i], 0, sizeof(BlockedFriend));
BlockedFriend tmp;
memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend));
Blocked_Contacts.list[i].active = true;
Blocked_Contacts.list[i].num = i;
Blocked_Contacts.list[i].namelength = ntohs(tmp.namelength);
memcpy(Blocked_Contacts.list[i].name, tmp.name, Blocked_Contacts.list[i].namelength + 1);
memcpy(Blocked_Contacts.list[i].pub_key, tmp.pub_key, TOX_CLIENT_ID_SIZE);
Blocked.list[i].active = true;
Blocked.list[i].num = i;
Blocked.list[i].namelength = ntohs(tmp.namelength);
memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1);
memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_CLIENT_ID_SIZE);
uint8_t lastonline[sizeof(uint64_t)];
memcpy(lastonline, &tmp.last_on, sizeof(uint64_t));
net_to_host(lastonline, sizeof(uint64_t));
memcpy(&Blocked_Contacts.list[i].last_on, lastonline, sizeof(uint64_t));
memcpy(&Blocked.list[i].last_on, lastonline, sizeof(uint64_t));
++Blocked_Contacts.num_blocked;
++Blocked.num_blocked;
}
Blocked_Contacts.max_index = i + 1;
free(data);
fclose(fp);
sort_blocklist_index();
@ -194,32 +238,32 @@ int load_blocklist(char *path)
#define S_WEIGHT 100000
static int index_name_cmp(const void *n1, const void *n2)
{
int res = qsort_strcasecmp_hlpr(friends[*(int *) n1].name, friends[*(int *) n2].name);
int res = qsort_strcasecmp_hlpr(Friends.list[*(int *) n1].name, Friends.list[*(int *) n2].name);
/* Use weight to make qsort always put online friends before offline */
res = friends[*(int *) n1].online ? (res - S_WEIGHT) : (res + S_WEIGHT);
res = friends[*(int *) n2].online ? (res + S_WEIGHT) : (res - S_WEIGHT);
res = Friends.list[*(int *) n1].online ? (res - S_WEIGHT) : (res + S_WEIGHT);
res = Friends.list[*(int *) n2].online ? (res + S_WEIGHT) : (res - S_WEIGHT);
return res;
}
/* sorts friendlist_index first by connection status then alphabetically */
/* sorts Friends.index first by connection status then alphabetically */
void sort_friendlist_index(void)
{
int i;
int n = 0;
for (i = 0; i < max_friends_index; ++i) {
if (friends[i].active)
friendlist_index[n++] = friends[i].num;
for (i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].active)
Friends.index[n++] = Friends.list[i].num;
}
qsort(friendlist_index, num_friends, sizeof(int), index_name_cmp);
qsort(Friends.index, Friends.num_friends, sizeof(int), index_name_cmp);
}
static int index_name_cmp_block(const void *n1, const void *n2)
{
return qsort_strcasecmp_hlpr(Blocked_Contacts.list[*(int *) n1].name, Blocked_Contacts.list[*(int *) n2].name);
return qsort_strcasecmp_hlpr(Blocked.list[*(int *) n1].name, Blocked.list[*(int *) n2].name);
}
static void sort_blocklist_index(void)
@ -227,33 +271,33 @@ static void sort_blocklist_index(void)
int i;
int n = 0;
for (i = 0; i < Blocked_Contacts.max_index; ++i) {
if (Blocked_Contacts.list[i].active)
Blocked_Contacts.index[n++] = Blocked_Contacts.list[i].num;
for (i = 0; i < Blocked.max_idx; ++i) {
if (Blocked.list[i].active)
Blocked.index[n++] = Blocked.list[i].num;
}
qsort(Blocked_Contacts.index, Blocked_Contacts.num_blocked, sizeof(int), index_name_cmp_block);
qsort(Blocked.index, Blocked.num_blocked, sizeof(int), index_name_cmp_block);
}
static void update_friend_last_online(int32_t num, uint64_t timestamp)
{
friends[num].last_online.last_on = timestamp;
friends[num].last_online.tm = *localtime((const time_t*)&timestamp);
Friends.list[num].last_online.last_on = timestamp;
Friends.list[num].last_online.tm = *localtime((const time_t*)&timestamp);
/* if the format changes make sure TIME_STR_SIZE is the correct size */
const char *t = user_settings_->time == TIME_12 ? "%I:%M %p" : "%H:%M";
strftime(friends[num].last_online.hour_min_str, TIME_STR_SIZE, t,
&friends[num].last_online.tm);
strftime(Friends.list[num].last_online.hour_min_str, TIME_STR_SIZE, t,
&Friends.list[num].last_online.tm);
}
static void friendlist_onMessage(ToxWindow *self, Tox *m, int32_t num, const char *str, uint16_t len)
{
if (num >= max_friends_index)
if (num >= Friends.max_idx)
return;
if (friends[num].chatwin == -1) {
if (Friends.list[num].chatwin == -1) {
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
} else {
char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num);
@ -272,10 +316,10 @@ static void friendlist_onMessage(ToxWindow *self, Tox *m, int32_t num, const cha
static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
{
if (num >= max_friends_index)
if (num >= Friends.max_idx)
return;
friends[num].online = status;
Friends.list[num].online = status;
update_friend_last_online(num, get_unix_time());
store_data(m, DATA_FILE);
sort_friendlist_index();
@ -283,106 +327,110 @@ static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int32_t num,
static void friendlist_onNickChange(ToxWindow *self, Tox *m, int32_t num, const char *nick, uint16_t len)
{
if (len > TOX_MAX_NAME_LENGTH || num >= max_friends_index)
if (len > TOX_MAX_NAME_LENGTH || num >= Friends.max_idx)
return;
char tempname[TOX_MAX_NAME_LENGTH];
strcpy(tempname, nick);
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
tempname[len] = '\0';
snprintf(friends[num].name, sizeof(friends[num].name), "%s", tempname);
friends[num].namelength = len;
snprintf(Friends.list[num].name, sizeof(Friends.list[num].name), "%s", tempname);
Friends.list[num].namelength = len;
sort_friendlist_index();
}
static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
{
if (num >= max_friends_index)
if (num >= Friends.max_idx)
return;
friends[num].status = status;
Friends.list[num].status = status;
}
static void friendlist_onStatusMessageChange(ToxWindow *self, int32_t num, const char *status, uint16_t len)
{
if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= max_friends_index)
if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= Friends.max_idx)
return;
snprintf(friends[num].statusmsg, sizeof(friends[num].statusmsg), "%s", status);
friends[num].statusmsg_len = strlen(friends[num].statusmsg);
snprintf(Friends.list[num].statusmsg, sizeof(Friends.list[num].statusmsg), "%s", status);
Friends.list[num].statusmsg_len = strlen(Friends.list[num].statusmsg);
}
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort)
{
if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM)
if (Friends.max_idx < 0)
return;
Friends.num_friends = tox_count_friendlist(m);
realloc_friends(Friends.max_idx + 1);
memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend));
int i;
for (i = 0; i <= max_friends_index; ++i) {
if (!friends[i].active) {
friends[i].num = num;
friends[i].active = true;
friends[i].chatwin = -1;
friends[i].online = false;
friends[i].status = TOX_USERSTATUS_NONE;
friends[i].logging_on = (bool) user_settings_->autolog == AUTOLOG_ON;
tox_get_client_id(m, num, (uint8_t *) friends[i].pub_key);
update_friend_last_online(i, tox_get_last_online(m, i));
for (i = 0; i <= Friends.max_idx; ++i) {
if (Friends.list[i].active)
continue;
char tempname[TOX_MAX_NAME_LENGTH] = {0};
int len = get_nick_truncate(m, tempname, num);
Friends.list[i].num = num;
Friends.list[i].active = true;
Friends.list[i].chatwin = -1;
Friends.list[i].online = false;
Friends.list[i].status = TOX_USERSTATUS_NONE;
Friends.list[i].logging_on = (bool) user_settings_->autolog == AUTOLOG_ON;
tox_get_client_id(m, num, (uint8_t *) Friends.list[i].pub_key);
update_friend_last_online(i, tox_get_last_online(m, i));
if (len == -1 || tempname[0] == '\0') {
strcpy(friends[i].name, UNKNOWN_NAME);
friends[i].namelength = strlen(UNKNOWN_NAME);
} else { /* Enforce toxic's maximum name length */
friends[i].namelength = len;
snprintf(friends[i].name, sizeof(friends[i].name), "%s", tempname);
}
char tempname[TOX_MAX_NAME_LENGTH] = {0};
int len = get_nick_truncate(m, tempname, num);
num_friends = tox_count_friendlist(m);
if (i == max_friends_index)
++max_friends_index;
if (sort)
sort_friendlist_index();
return;
if (len == -1 || tempname[0] == '\0') {
strcpy(Friends.list[i].name, UNKNOWN_NAME);
Friends.list[i].namelength = strlen(UNKNOWN_NAME);
} else { /* Enforce toxic's maximum name length */
Friends.list[i].namelength = len;
snprintf(Friends.list[i].name, sizeof(Friends.list[i].name), "%s", tempname);
}
if (i == Friends.max_idx)
++Friends.max_idx;
if (sort)
sort_friendlist_index();
return;
}
}
/* puts blocked friend back in friendlist. fnum is new friend number, bnum is blocked number */
static void friendlist_add_blocked(Tox *m, int32_t fnum, int32_t bnum)
{
if (max_friends_index >= MAX_FRIENDS_NUM)
return;
Friends.num_friends = tox_count_friendlist(m);
realloc_friends(Friends.max_idx + 1);
memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend));
int i;
for (i = 0; i <= max_friends_index; ++i) {
if (friends[i].active)
for (i = 0; i <= Friends.max_idx; ++i) {
if (Friends.list[i].active)
continue;
friends[i].num = fnum;
friends[i].active = true;
friends[i].chatwin = -1;
friends[i].status = TOX_USERSTATUS_NONE;
friends[i].logging_on = (bool) user_settings_->autolog == AUTOLOG_ON;
friends[i].namelength = Blocked_Contacts.list[bnum].namelength;
update_friend_last_online(i, Blocked_Contacts.list[bnum].last_on);
memcpy(friends[i].name, Blocked_Contacts.list[bnum].name, friends[i].namelength + 1);
memcpy(friends[i].pub_key, Blocked_Contacts.list[bnum].pub_key, TOX_CLIENT_ID_SIZE);
Friends.list[i].num = fnum;
Friends.list[i].active = true;
Friends.list[i].chatwin = -1;
Friends.list[i].status = TOX_USERSTATUS_NONE;
Friends.list[i].logging_on = (bool) user_settings_->autolog == AUTOLOG_ON;
Friends.list[i].namelength = Blocked.list[bnum].namelength;
update_friend_last_online(i, Blocked.list[bnum].last_on);
memcpy(Friends.list[i].name, Blocked.list[bnum].name, Friends.list[i].namelength + 1);
memcpy(Friends.list[i].pub_key, Blocked.list[bnum].pub_key, TOX_CLIENT_ID_SIZE);
num_friends = tox_count_friendlist(m);
if (i == max_friends_index)
++max_friends_index;
if (i == Friends.max_idx)
++Friends.max_idx;
sort_blocklist_index();
sort_friendlist_index();
return;
}
}
@ -390,12 +438,12 @@ static void friendlist_add_blocked(Tox *m, int32_t fnum, int32_t bnum)
static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum,
uint64_t filesize, const char *filename, uint16_t filename_len)
{
if (num >= max_friends_index)
if (num >= Friends.max_idx)
return;
if (friends[num].chatwin == -1) {
if (Friends.list[num].chatwin == -1) {
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
} else {
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
@ -412,20 +460,12 @@ static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, u
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, const char *group_pub_key)
{
if (num >= max_friends_index)
if (num >= Friends.max_idx)
return;
if (friends[num].chatwin == -1) {
if (Friends.list[num].chatwin == -1) {
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
if (self->active_box != -1)
box_notify2(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box,
"You are invited to join group" );
else
box_notify(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS, &self->active_box, self->name,
"You are invited to join group" );
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
} else {
char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num);
@ -452,31 +492,32 @@ static void select_friend(ToxWindow *self, wint_t key, int *selected, int num)
static void delete_friend(Tox *m, int32_t f_num)
{
if (friends[f_num].chatwin >= 0) {
ToxWindow *toxwin = get_window_ptr(friends[f_num].chatwin);
if (Friends.list[f_num].chatwin >= 0) {
ToxWindow *toxwin = get_window_ptr(Friends.list[f_num].chatwin);
if (toxwin != NULL) {
kill_chat_window(toxwin);
kill_chat_window(toxwin, m);
set_active_window(1); /* keep friendlist focused */
}
}
tox_del_friend(m, f_num);
memset(&friends[f_num], 0, sizeof(ToxicFriend));
memset(&Friends.list[f_num], 0, sizeof(ToxicFriend));
int i;
for (i = max_friends_index; i > 0; --i) {
if (friends[i - 1].active)
for (i = Friends.max_idx; i > 0; --i) {
if (Friends.list[i - 1].active)
break;
}
max_friends_index = i;
num_friends = tox_count_friendlist(m);
Friends.max_idx = i;
Friends.num_friends = tox_count_friendlist(m);
realloc_friends(i);
/* make sure num_selected stays within num_friends range */
if (num_friends && num_selected == num_friends)
--num_selected;
/* make sure num_selected stays within Friends.num_friends range */
if (Friends.num_friends && Friends.num_selected == Friends.num_friends)
--Friends.num_selected;
store_data(m, DATA_FILE);
}
@ -524,9 +565,9 @@ static void draw_del_popup(void)
wattron(pendingdelete.popup, A_BOLD);
if (blocklist_view == 0)
wprintw(pendingdelete.popup, "%s", friends[pendingdelete.num].name);
wprintw(pendingdelete.popup, "%s", Friends.list[pendingdelete.num].name);
else
wprintw(pendingdelete.popup, "%s", Blocked_Contacts.list[pendingdelete.num].name);
wprintw(pendingdelete.popup, "%s", Blocked.list[pendingdelete.num].name);
wattroff(pendingdelete.popup, A_BOLD);
wprintw(pendingdelete.popup, "? y/n");
@ -537,46 +578,50 @@ static void draw_del_popup(void)
/* deletes contact from blocked list */
static void delete_blocked_friend(int32_t bnum)
{
memset(&Blocked_Contacts.list[bnum], 0, sizeof(BlockedFriend));
memset(&Blocked.list[bnum], 0, sizeof(BlockedFriend));
int i;
for (i = Blocked_Contacts.max_index; i > 0; --i) {
if (Blocked_Contacts.list[i - 1].active)
for (i = Blocked.max_idx; i > 0; --i) {
if (Blocked.list[i - 1].active)
break;
}
--Blocked_Contacts.num_blocked;
Blocked_Contacts.max_index = i;
--Blocked.num_blocked;
Blocked.max_idx = i;
realloc_blocklist(i);
save_blocklist(BLOCK_FILE);
if (Blocked_Contacts.num_blocked && Blocked_Contacts.num_selected == Blocked_Contacts.num_blocked)
--Blocked_Contacts.num_selected;
if (Blocked.num_blocked && Blocked.num_selected == Blocked.num_blocked)
--Blocked.num_selected;
}
/* deletes contact from friendlist and puts in blocklist */
void block_friend(Tox *m, int32_t fnum)
{
if (Blocked_Contacts.max_index >= MAX_FRIENDS_NUM || num_friends <= 0)
if (Friends.num_friends <= 0)
return;
realloc_blocklist(Blocked.max_idx + 1);
memset(&Blocked.list[Blocked.max_idx], 0, sizeof(BlockedFriend));
int i;
for (i = 0; i <= Blocked_Contacts.max_index; ++i) {
if (Blocked_Contacts.list[i].active)
for (i = 0; i <= Blocked.max_idx; ++i) {
if (Blocked.list[i].active)
continue;
Blocked_Contacts.list[i].active = true;
Blocked_Contacts.list[i].num = i;
Blocked_Contacts.list[i].namelength = friends[fnum].namelength;
Blocked_Contacts.list[i].last_on = friends[fnum].last_online.last_on;
memcpy(Blocked_Contacts.list[i].pub_key, friends[fnum].pub_key, TOX_CLIENT_ID_SIZE);
memcpy(Blocked_Contacts.list[i].name, friends[fnum].name, friends[fnum].namelength + 1);
Blocked.list[i].active = true;
Blocked.list[i].num = i;
Blocked.list[i].namelength = Friends.list[fnum].namelength;
Blocked.list[i].last_on = Friends.list[fnum].last_online.last_on;
memcpy(Blocked.list[i].pub_key, Friends.list[fnum].pub_key, TOX_CLIENT_ID_SIZE);
memcpy(Blocked.list[i].name, Friends.list[fnum].name, Friends.list[fnum].namelength + 1);
++Blocked_Contacts.num_blocked;
++Blocked.num_blocked;
if (i == Blocked_Contacts.max_index)
++Blocked_Contacts.max_index;
if (i == Blocked.max_idx)
++Blocked.max_idx;
delete_friend(m, fnum);
save_blocklist(BLOCK_FILE);
@ -590,10 +635,10 @@ void block_friend(Tox *m, int32_t fnum)
/* removes friend from blocklist, puts back in friendlist */
static void unblock_friend(Tox *m, int32_t bnum)
{
if (Blocked_Contacts.num_blocked <= 0)
if (Blocked.num_blocked <= 0)
return;
int32_t friendnum = tox_add_friend_norequest(m, (uint8_t *) Blocked_Contacts.list[bnum].pub_key);
int32_t friendnum = tox_add_friend_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key);
if (friendnum == -1) {
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend");
@ -619,14 +664,18 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
return;
}
if (!blocklist_view && !num_friends && (key != KEY_RIGHT && key != KEY_LEFT))
if (!blocklist_view && !Friends.num_friends && (key != KEY_RIGHT && key != KEY_LEFT))
return;
if (blocklist_view && !Blocked_Contacts.num_blocked && (key != KEY_RIGHT && key != KEY_LEFT))
if (blocklist_view && !Blocked.num_blocked && (key != KEY_RIGHT && key != KEY_LEFT))
return;
int f = blocklist_view == 1 ? Blocked_Contacts.index[Blocked_Contacts.num_selected]
: friendlist_index[num_selected];
int f = 0;
if (blocklist_view == 1 && Blocked.num_blocked)
f = Blocked.index[Blocked.num_selected];
else if (Friends.num_friends)
f = Friends.index[Friends.num_selected];
/* lock screen and force decision on deletion popup */
if (pendingdelete.active) {
@ -645,11 +694,11 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
break;
/* Jump to chat window if already open */
if (friends[f].chatwin != -1) {
set_active_window(friends[f].chatwin);
if (Friends.list[f].chatwin != -1) {
set_active_window(Friends.list[f].chatwin);
} else if (get_num_active_windows() < MAX_WINDOWS_NUM) {
friends[f].chatwin = add_window(m, new_chat(m, friends[f].num));
set_active_window(friends[f].chatwin);
Friends.list[f].chatwin = add_window(m, new_chat(m, Friends.list[f].num));
set_active_window(Friends.list[f].chatwin);
} else {
const char *msg = "* Warning: Too many windows are open.";
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, msg);
@ -676,9 +725,9 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
default:
if (blocklist_view == 0)
select_friend(self, key, &num_selected, num_friends);
select_friend(self, key, &Friends.num_selected, Friends.num_friends);
else
select_friend(self, key, &Blocked_Contacts.num_selected, Blocked_Contacts.num_blocked);
select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked);
break;
}
}
@ -690,7 +739,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
wattron(self->window, A_BOLD);
wprintw(self->window, " Blocked: ");
wattroff(self->window, A_BOLD);
wprintw(self->window, "%d\n\n", Blocked_Contacts.num_blocked);
wprintw(self->window, "%d\n\n", Blocked.num_blocked);
if ((y2 - FLIST_OFST) <= 0)
return;
@ -698,17 +747,17 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
int selected_num = 0;
/* Determine which portion of friendlist to draw based on current position */
int page = Blocked_Contacts.num_selected / (y2 - FLIST_OFST);
int page = Blocked.num_selected / (y2 - FLIST_OFST);
int start = (y2 - FLIST_OFST) * page;
int end = y2 - FLIST_OFST + start;
int i;
for (i = start; i < Blocked_Contacts.num_blocked && i < end; ++i) {
int f = Blocked_Contacts.index[i];
for (i = start; i < Blocked.num_blocked && i < end; ++i) {
int f = Blocked.index[i];
bool f_selected = false;
if (i == Blocked_Contacts.num_selected) {
if (i == Blocked.num_selected) {
wattron(self->window, A_BOLD);
wprintw(self->window, " > ");
wattroff(self->window, A_BOLD);
@ -726,7 +775,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
wattron(self->window, COLOR_PAIR(BLUE));
wattron(self->window, A_BOLD);
wprintw(self->window, " %s\n", Blocked_Contacts.list[f].name);
wprintw(self->window, " %s\n", Blocked.list[f].name);
wattroff(self->window, A_BOLD);
if (f_selected)
@ -736,17 +785,17 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
wprintw(self->window, "\n");
self->x = x2;
if (Blocked_Contacts.num_blocked) {
if (Blocked.num_blocked) {
wmove(self->window, y2 - 1, 1);
wattron(self->window, A_BOLD);
wprintw(self->window, "ID: ");
wprintw(self->window, "Key: ");
wattroff(self->window, A_BOLD);
int i;
for (i = 0; i < TOX_CLIENT_ID_SIZE; ++i)
wprintw(self->window, "%02X", Blocked_Contacts.list[selected_num].pub_key[i] & 0xff);
wprintw(self->window, "%02X", Blocked.list[selected_num].pub_key[i] & 0xff);
}
wrefresh(self->window);
@ -768,7 +817,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wattron(self->window, COLOR_PAIR(CYAN));
wprintw(self->window, " Press the");
wattron(self->window, A_BOLD);
wprintw(self->window, " H ");
wprintw(self->window, " h ");
wattroff(self->window, A_BOLD);
wprintw(self->window, "key for help\n\n");
wattroff(self->window, COLOR_PAIR(CYAN));
@ -788,7 +837,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wattron(self->window, A_BOLD);
wprintw(self->window, " Online: ");
wattroff(self->window, A_BOLD);
wprintw(self->window, "%d/%d \n\n", nf, num_friends);
wprintw(self->window, "%d/%d \n\n", nf, Friends.num_friends);
if ((y2 - FLIST_OFST) <= 0)
return;
@ -796,18 +845,18 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
int selected_num = 0;
/* Determine which portion of friendlist to draw based on current position */
int page = num_selected / (y2 - FLIST_OFST);
int page = Friends.num_selected / (y2 - FLIST_OFST);
int start = (y2 - FLIST_OFST) * page;
int end = y2 - FLIST_OFST + start;
int i;
for (i = start; i < num_friends && i < end; ++i) {
int f = friendlist_index[i];
for (i = start; i < Friends.num_friends && i < end; ++i) {
int f = Friends.index[i];
bool f_selected = false;
if (friends[f].active) {
if (i == num_selected) {
if (Friends.list[f].active) {
if (i == Friends.num_selected) {
wattron(self->window, A_BOLD);
wprintw(self->window, " > ");
wattroff(self->window, A_BOLD);
@ -817,8 +866,8 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wprintw(self->window, " ");
}
if (friends[f].online) {
uint8_t status = friends[f].status;
if (Friends.list[f].online) {
uint8_t status = Friends.list[f].status;
int colour = WHITE;
switch (status) {
@ -847,36 +896,36 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wattron(self->window, COLOR_PAIR(BLUE));
wattron(self->window, A_BOLD);
wprintw(self->window, "%s", friends[f].name);
wprintw(self->window, "%s", Friends.list[f].name);
wattroff(self->window, A_BOLD);
if (f_selected)
wattroff(self->window, COLOR_PAIR(BLUE));
/* Reset friends[f].statusmsg on window resize */
/* Reset Friends.list[f].statusmsg on window resize */
if (fix_statuses) {
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
pthread_mutex_lock(&Winthread.lock);
tox_get_status_message(m, friends[f].num, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
tox_get_status_message(m, Friends.list[f].num, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
pthread_mutex_unlock(&Winthread.lock);
snprintf(friends[f].statusmsg, sizeof(friends[f].statusmsg), "%s", statusmsg);
friends[f].statusmsg_len = strlen(friends[f].statusmsg);
snprintf(Friends.list[f].statusmsg, sizeof(Friends.list[f].statusmsg), "%s", statusmsg);
Friends.list[f].statusmsg_len = strlen(Friends.list[f].statusmsg);
}
/* Truncate note if it doesn't fit on one line */
uint16_t maxlen = x2 - getcurx(self->window) - 2;
if (friends[f].statusmsg_len > maxlen) {
friends[f].statusmsg[maxlen - 3] = '\0';
strcat(friends[f].statusmsg, "...");
friends[f].statusmsg[maxlen] = '\0';
friends[f].statusmsg_len = maxlen;
if (Friends.list[f].statusmsg_len > maxlen) {
Friends.list[f].statusmsg[maxlen - 3] = '\0';
strcat(Friends.list[f].statusmsg, "...");
Friends.list[f].statusmsg[maxlen] = '\0';
Friends.list[f].statusmsg_len = maxlen;
}
if (friends[f].statusmsg[0])
wprintw(self->window, " %s", friends[f].statusmsg);
if (Friends.list[f].statusmsg[0])
wprintw(self->window, " %s", Friends.list[f].statusmsg);
wprintw(self->window, "\n");
} else {
@ -886,17 +935,17 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wattron(self->window, COLOR_PAIR(BLUE));
wattron(self->window, A_BOLD);
wprintw(self->window, "%s", friends[f].name);
wprintw(self->window, "%s", Friends.list[f].name);
wattroff(self->window, A_BOLD);
if (f_selected)
wattroff(self->window, COLOR_PAIR(BLUE));
uint64_t last_seen = friends[f].last_online.last_on;
uint64_t last_seen = Friends.list[f].last_online.last_on;
if (last_seen != 0) {
int day_dist = (cur_loc_tm.tm_yday - friends[f].last_online.tm.tm_yday) % 365;
const char *hourmin = friends[f].last_online.hour_min_str;
int day_dist = (cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday) % 365;
const char *hourmin = Friends.list[f].last_online.hour_min_str;
switch (day_dist) {
case 0:
@ -920,17 +969,17 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
self->x = x2;
if (num_friends) {
if (Friends.num_friends) {
wmove(self->window, y2 - 1, 1);
wattron(self->window, A_BOLD);
wprintw(self->window, "ID: ");
wprintw(self->window, "Key: ");
wattroff(self->window, A_BOLD);
int i;
for (i = 0; i < TOX_CLIENT_ID_SIZE; ++i)
wprintw(self->window, "%02X", friends[selected_num].pub_key[i] & 0xff);
wprintw(self->window, "%02X", Friends.list[selected_num].pub_key[i] & 0xff);
}
wrefresh(self->window);
@ -942,7 +991,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
void disable_chatwin(int32_t f_num)
{
friends[f_num].chatwin = -1;
Friends.list[f_num].chatwin = -1;
}
#ifdef _AUDIO
@ -950,20 +999,19 @@ static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index)
{
int id = toxav_get_peer_id(av, call_index, 0);
/*id++;*/
if ( id != ErrorInternal && id >= max_friends_index)
if ( id != ErrorInternal && id >= Friends.max_idx)
return;
Tox *m = toxav_get_tox(av);
if (friends[id].chatwin == -1) {
if (Friends.list[id].chatwin == -1) {
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
if (toxav_get_call_state(av, call_index) == av_CallStarting) { /* Only open windows when call is incoming */
friends[id].chatwin = add_window(m, new_chat(m, friends[id].num));
Friends.list[id].chatwin = add_window(m, new_chat(m, Friends.list[id].num));
}
} else {
char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, friends[id].num);
get_nick_truncate(m, nick, Friends.list[id].num);
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick);
const char *errmsg = "* Warning: Too many windows are open.";

View File

@ -30,15 +30,16 @@
#include "file_senders.h"
struct FileReceiver {
char filenames[MAX_FILES][MAX_STR_SIZE];
FILE *files[MAX_FILES];
bool pending[MAX_FILES];
bool active[MAX_FILES];
uint64_t size[MAX_FILES];
double bps[MAX_FILES];
uint64_t last_progress[MAX_FILES];
uint32_t line_id[MAX_FILES];
int filenums[MAX_FILES];
char filename[MAX_STR_SIZE];
int filenum;
FILE *file;
bool pending;
bool active;
uint64_t size;
uint64_t bytes_recv;
double bps;
uint64_t last_progress; /* unix-time when we last updated progress */
uint32_t line_id;
};
struct LastOnline {
@ -60,10 +61,11 @@ typedef struct {
bool active;
bool online;
uint8_t is_typing;
bool logging_on; /* saves preference for friend irrespective of chat windows */
bool logging_on; /* saves preference for friend irrespective of global settings */
uint8_t status;
struct LastOnline last_online;
struct FileReceiver file_receiver;
struct FileReceiver file_receiver[MAX_FILES];
uint8_t active_file_receivers;
} ToxicFriend;
typedef struct {
@ -75,11 +77,19 @@ typedef struct {
uint64_t last_on;
} BlockedFriend;
typedef struct {
int num_selected;
int max_idx; /* 1 + the index of the last friend in list */
int num_friends;
int *index;
ToxicFriend *list;
} _Friends;
ToxWindow new_friendlist(void);
void disable_chatwin(int32_t f_num);
int get_friendnum(uint8_t *name);
int load_blocklist(char *data);
void kill_friendlist(void);
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort);
/* sorts friendlist_index first by connection status then alphabetically */

View File

@ -37,11 +37,8 @@
extern char *DATA_FILE;
extern ToxWindow *prompt;
extern ToxicFriend friends[MAX_FRIENDS_NUM];
extern char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
extern uint8_t num_frnd_requests;
extern _Friends Friends;
extern _FriendRequests FriendRequests;
/* command functions */
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -53,18 +50,18 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
int req = atoi(argv[1]);
if ((req == 0 && strcmp(argv[1], "0")) || req >= MAX_FRIENDS_NUM) {
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
if (!strlen(pending_frnd_requests[req])) {
if (!FriendRequests.request[req].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
const char *msg;
int32_t friendnum = tox_add_friend_norequest(m, (uint8_t *) pending_frnd_requests[req]);
int32_t friendnum = tox_add_friend_norequest(m, FriendRequests.request[req].key);
if (friendnum == -1)
msg = "Failed to add friend.";
@ -73,16 +70,17 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
on_friendadded(m, friendnum, true);
}
memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE);
memset(&FriendRequests.request[req], 0, sizeof(struct _friend_request));
int i;
for (i = num_frnd_requests; i > 0; --i) {
if (!strlen(pending_frnd_requests[i - 1]))
for (i = FriendRequests.max_idx; i > 0; --i) {
if (FriendRequests.request[i - 1].active)
break;
}
num_frnd_requests = i;
FriendRequests.max_idx = i;
--FriendRequests.num_requests;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
}
@ -211,10 +209,42 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
}
char *binary_string = hex_string_to_bin(key);
tox_bootstrap_from_address(m, ip, TOX_ENABLE_IPV6_DEFAULT, htons(atoi(port)), (uint8_t *) binary_string);
tox_bootstrap_from_address(m, ip, atoi(port), (uint8_t *) binary_string);
free(binary_string);
}
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
return;
}
int req = atoi(argv[1]);
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
if (!FriendRequests.request[req].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
memset(&FriendRequests.request[req], 0, sizeof(struct _friend_request));
int i;
for (i = FriendRequests.max_idx; i > 0; --i) {
if (FriendRequests.request[i - 1].active)
break;
}
FriendRequests.max_idx = i;
--FriendRequests.num_requests;
}
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
@ -258,8 +288,8 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
if (self->is_chat) {
friends[self->num].logging_on = true;
log_enable(self->name, friends[self->num].pub_key, log);
Friends.list[self->num].logging_on = true;
log_enable(self->name, Friends.list[self->num].pub_key, log);
} else if (self->is_prompt) {
char myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, (uint8_t *) myid);
@ -273,7 +303,7 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
return;
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
if (self->is_chat)
friends[self->num].logging_on = false;
Friends.list[self->num].logging_on = false;
log_disable(log);
@ -354,8 +384,7 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
int len = strlen(msg) - 1;
msg[len] = '\0';
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
prompt_update_statusmessage(prompt, msg);
prompt_update_statusmessage(prompt, m, msg);
}
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -368,6 +397,36 @@ void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
exit_toxic_success(m);
}
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (FriendRequests.num_requests == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
return;
}
int i, j;
int count = 0;
for (i = 0; i < FriendRequests.max_idx; ++i) {
if (!FriendRequests.request[i].active)
continue;
char id[TOX_CLIENT_ID_SIZE * 2 + 1] = {0};
for (j = 0; j < TOX_CLIENT_ID_SIZE; ++j) {
char d[3];
snprintf(d, sizeof(d), "%02X", FriendRequests.request[i].key[j] & 0xff);
strcat(id, d);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FriendRequests.request[i].msg);
if (++count < FriendRequests.num_requests)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
}
}
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
bool have_note = false;
@ -414,7 +473,6 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
int len = strlen(msg) - 1;
msg[len] = '\0';
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
prompt_update_statusmessage(prompt, msg);
prompt_update_statusmessage(prompt, m, msg);
}
}

View File

@ -30,6 +30,7 @@ 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_clear(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_groupchat(WINDOW *, ToxWindow *, Tox *, 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]);
@ -37,6 +38,7 @@ void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg);

View File

@ -97,7 +97,6 @@ void kill_groupchat_window(ToxWindow *self)
delwin(ctx->history);
delwin(ctx->sidebar);
free(ctx->log);
free(ctx->hst);
free(ctx);
free(self->help);
del_window(self);
@ -198,7 +197,7 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, nick, NULL, ACTION, 0, 0, "%s", action);
line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action);
write_to_log(action, nick, ctx->log, true);
}

View File

@ -77,26 +77,26 @@ static void help_draw_menu(ToxWindow *self)
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " G");
wprintw(win, " g");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "lobal commands\n");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " C");
wprintw(win, " c");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "hat commands\n");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " F");
wprintw(win, " f");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "riendlist controls\n");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " K");
wprintw(win, " k");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "ey bindings\n");
wprintw(win, " E");
wprintw(win, " e");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "x");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
@ -115,11 +115,11 @@ static void help_draw_bottom_menu(WINDOW *win)
wmove(win, y2 - 2, 1);
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " M");
wprintw(win, " m");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "ain menu |");
wprintw(win, " E");
wprintw(win, " e");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "x");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
@ -138,6 +138,8 @@ 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, " /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> <msg> : Set status with optional note\n");
wprintw(win, " /note <msg> : Set a personal note\n");
@ -264,9 +266,9 @@ void help_onKey(ToxWindow *self, wint_t key)
case 'g':
#ifdef _AUDIO
help_init_window(self, 21, 80);
help_init_window(self, 23, 80);
#else
help_init_window(self, 17, 80);
help_init_window(self, 19, 80);
#endif
self->help->type = HELP_GLOBAL;
break;

View File

@ -31,6 +31,8 @@
#include "groupchat.h"
#include "settings.h"
#include "notify.h"
#include "message_queue.h"
#include "misc_tools.h"
extern struct user_settings *user_settings_;
@ -89,6 +91,8 @@ void line_info_cleanup(struct history *hst)
if (hst->queue[i])
free(hst->queue[i]);
}
free(hst);
}
/* moves root forward and frees previous root */
@ -107,15 +111,6 @@ static void line_info_root_fwd(struct history *hst)
hst->line_root = tmp;
}
/* adds a line_info line to queue */
static void line_info_add_queue(struct history *hst, struct line_info *line)
{
if (hst->queue_sz >= MAX_QUEUE)
return;
hst->queue[hst->queue_sz++] = line;
}
/* returns ptr to queue item 0 and removes it from queue */
static struct line_info *line_info_ret_queue(struct history *hst)
{
@ -134,12 +129,15 @@ static struct line_info *line_info_ret_queue(struct history *hst)
return ret;
}
/* creates new line_info line and puts it in the queue.
SYS_MSG lines may contain an arbitrary number of arguments for string formatting */
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint8_t type, uint8_t bold,
/* creates new line_info line and puts it in the queue. */
void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uint8_t type, uint8_t bold,
uint8_t colour, const char *msg, ...)
{
struct history *hst = self->chatwin->hst;
if (hst->queue_sz >= MAX_QUEUE)
return;
struct line_info *new_line = calloc(1, sizeof(struct line_info));
if (new_line == NULL)
@ -156,7 +154,16 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint
/* for type-specific formatting in print function */
switch (type) {
case ACTION:
case IN_ACTION:
case OUT_ACTION:
len += 3;
break;
case IN_MSG:
case OUT_MSG:
len += 2;
break;
case CONNECTION:
len += 3;
break;
@ -185,9 +192,9 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint
}
}
if (tmstmp) {
snprintf(new_line->timestamp, sizeof(new_line->timestamp), "%s", tmstmp);
len += strlen(new_line->timestamp);
if (timestr) {
snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr);
len += strlen(new_line->timestr);
}
if (name1) {
@ -204,8 +211,10 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint
new_line->type = type;
new_line->bold = bold;
new_line->colour = colour;
new_line->noread_flag = false;
new_line->timestamp = get_unix_time();
line_info_add_queue(hst, new_line);
hst->queue[hst->queue_sz++] = new_line;
}
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
@ -247,6 +256,8 @@ static void line_info_check_queue(ToxWindow *self)
}
}
#define NOREAD_FLAG_TIMEOUT 5 /* seconds before a sent message with no read receipt is flagged as unread */
void line_info_print(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
@ -280,9 +291,10 @@ void line_info_print(ToxWindow *self)
switch (type) {
case OUT_MSG:
case OUT_MSG_READ:
case IN_MSG:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wprintw(win, "%s", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
int nameclr = GREEN;
@ -299,28 +311,54 @@ void line_info_print(ToxWindow *self)
if (line->msg[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
wprintw(win, "%s\n", line->msg);
wprintw(win, "%s", line->msg);
if (line->msg[0] == '>')
wattroff(win, COLOR_PAIR(GREEN));
if (type == OUT_MSG && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) {
wattron(win, COLOR_PAIR(RED));
wprintw(win, " x", line->msg);
wattroff(win, COLOR_PAIR(RED));
if (line->noread_flag == false) {
line->noread_flag = true;
line->len += 2;
}
}
wprintw(win, "\n", line->msg);
break;
case ACTION:
case OUT_ACTION_READ:
case OUT_ACTION:
case IN_ACTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wprintw(win, "%s", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(YELLOW));
wprintw(win, "* %s %s\n", line->name1, line->msg);
wprintw(win, "* %s %s", line->name1, line->msg);
wattroff(win, COLOR_PAIR(YELLOW));
if (type == OUT_ACTION && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) {
wattron(win, COLOR_PAIR(RED));
wprintw(win, " x", line->msg);
wattroff(win, COLOR_PAIR(RED));
if (line->noread_flag == false) {
line->noread_flag = true;
line->len += 2;
}
}
wprintw(win, "\n", line->msg);
break;
case SYS_MSG:
if (line->timestamp[0]) {
if (line->timestr[0]) {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wprintw(win, "%s", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
}
@ -353,7 +391,7 @@ void line_info_print(ToxWindow *self)
case CONNECTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wprintw(win, "%s", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(line->colour));
@ -367,7 +405,7 @@ void line_info_print(ToxWindow *self)
case NAME_CHANGE:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wprintw(win, "%s", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(MAGENTA));
@ -393,6 +431,7 @@ void line_info_print(ToxWindow *self)
line_info_print(self);
}
/* puts msg in specified line_info msg buffer */
void line_info_set(ToxWindow *self, uint32_t id, char *msg)
{
struct line_info *line = self->chatwin->hst->line_end;

View File

@ -34,22 +34,27 @@ enum {
SYS_MSG,
IN_MSG,
OUT_MSG,
OUT_MSG_READ, /* for sent messages that have received a read reply. don't set this with line_info_add */
IN_ACTION,
OUT_ACTION,
OUT_ACTION_READ, /* same as OUT_MSG_READ but for actions */
PROMPT,
ACTION,
CONNECTION,
NAME_CHANGE,
} LINE_TYPE;
struct line_info {
char timestamp[TIME_STR_SIZE];
char timestr[TIME_STR_SIZE];
char name1[TOXIC_MAX_NAME_LENGTH];
char name2[TOXIC_MAX_NAME_LENGTH];
char msg[TOX_MAX_MESSAGE_LENGTH];
uint64_t timestamp;
uint8_t type;
uint8_t bold;
uint8_t colour;
uint8_t noread_flag; /* true if a line should be flagged as unread */
uint32_t id;
uint16_t len; /* combined len of all strings */
uint16_t len; /* combined len of entire line */
uint8_t newlines;
struct line_info *prev;
@ -67,9 +72,8 @@ struct history {
int queue_sz;
};
/* creates new line_info line and puts it in the queue.
SYS_MSG lines may contain an arbitrary number of arguments for string formatting */
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint8_t type, uint8_t bold,
/* creates new line_info line and puts it in the queue. */
void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uint8_t type, uint8_t bold,
uint8_t colour, const char *msg, ...);
/* Prints a section of history starting at line_start */

View File

@ -42,15 +42,17 @@ void init_logging_session(char *name, const char *key, struct chatlog *log)
if (!valid_nick(name))
name = UNKNOWN_NAME;
const char *set_path = user_settings_->chatlogs_path;
char *user_config_dir = get_user_config_dir();
int path_len = strlen(user_config_dir) + strlen(LOGDIR) + strlen(name);
int path_len = strlen(set_path) + strlen(name) ? *set_path
: strlen(user_config_dir) + strlen(LOGDIR) + strlen(name);
/* use first 4 digits of key as log ident. If no key use a timestamp */
char ident[32];
if (key != NULL) {
path_len += (KEY_IDENT_DIGITS * 2 + 5);
sprintf(&ident[0], "%02X", key[0] & 0xff);
sprintf(&ident[2], "%02X", key[1] & 0xff);
ident[KEY_IDENT_DIGITS * 2 + 1] = '\0';
@ -66,7 +68,11 @@ void init_logging_session(char *name, const char *key, struct chatlog *log)
}
char log_path[MAX_STR_SIZE];
snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log", user_config_dir, LOGDIR, name, ident);
if (*set_path)
snprintf(log_path, sizeof(log_path), "%s%s-%s.log", set_path, name, ident);
else
snprintf(log_path, sizeof(log_path), "%s%s%s-%s.log", user_config_dir, LOGDIR, name, ident);
free(user_config_dir);
@ -80,6 +86,8 @@ void init_logging_session(char *name, const char *key, struct chatlog *log)
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
}
#define LOG_FLUSH_LIMIT 1 /* limits calls to fflush to a max of one per LOG_FLUSH_LIMIT seconds */
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
{
if (!log->log_on)

View File

@ -23,12 +23,9 @@
#ifndef _log_h
#define _log_h
#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */
struct chatlog {
FILE *file;
uint64_t lastwrite;
int pos;
bool log_on; /* specific to current chat window */
};

156
src/message_queue.c Normal file
View File

@ -0,0 +1,156 @@
/* message_queue.c
*
*
* Copyright (C) 2014 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 <stdlib.h>
#include "toxic.h"
#include "windows.h"
#include "message_queue.h"
#include "misc_tools.h"
#include "line_info.h"
#include "log.h"
void cqueue_cleanup(struct chat_queue *q)
{
struct cqueue_msg *tmp1 = q->root;
while (tmp1) {
struct cqueue_msg *tmp2 = tmp1->next;
free(tmp1);
tmp1 = tmp2;
}
free(q);
}
void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id)
{
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
if (new_m == NULL)
exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY);
snprintf(new_m->message, sizeof(new_m->message), "%s", msg);
new_m->len = len;
new_m->type = type;
new_m->line_id = line_id;
new_m->last_send_try = 0;
new_m->receipt = 0;
new_m->next = NULL;
if (q->root == NULL) {
new_m->prev = NULL;
q->root = new_m;
} else {
new_m->prev = q->end;
q->end->next = new_m;
}
q->end = new_m;
}
/* update line to show receipt was received after queue removal */
static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg)
{
struct line_info *line = self->chatwin->hst->line_end;
while (line) {
if (line->id != msg->line_id) {
line = line->prev;
continue;
}
line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ;
if (line->noread_flag == true) {
line->len -= 2;
line->noread_flag = false;
}
return;
}
}
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
{
struct chat_queue *q = self->chatwin->cqueue;
struct cqueue_msg *msg = q->root;
while (msg) {
if (msg->receipt != receipt) {
msg = msg->next;
continue;
}
char selfname[TOX_MAX_NAME_LENGTH];
uint16_t len = tox_get_self_name(m, (uint8_t *) selfname);
selfname[len] = '\0';
write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION);
cqueue_mark_read(self, msg);
struct cqueue_msg *next = msg->next;
if (msg->prev == NULL) { /* root */
if (next)
next->prev = NULL;
free(msg);
q->root = next;
} else {
struct cqueue_msg *prev = msg->prev;
free(msg);
prev->next = next;
}
return;
}
}
#define CQUEUE_TRY_SEND_INTERVAL 10
/* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m)
{
struct chat_queue *q = self->chatwin->cqueue;
struct cqueue_msg *msg = q->root;
uint64_t curtime = get_unix_time();
while (msg) {
if (msg->receipt != 0 && !timed_out(msg->last_send_try, curtime, CQUEUE_TRY_SEND_INTERVAL)) {
msg = msg->next;
continue;
}
uint32_t receipt = 0;
if (msg->type == OUT_MSG)
receipt = tox_send_message(m, self->num, (uint8_t *) msg->message, msg->len);
else
receipt = tox_send_action(m, self->num, (uint8_t *) msg->message, msg->len);
msg->last_send_try = curtime;
msg->receipt = receipt;
return;
}
}

46
src/message_queue.h Normal file
View File

@ -0,0 +1,46 @@
/* message_queue.h
*
*
* Copyright (C) 2014 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/>.
*
*/
struct cqueue_msg {
char message[MAX_STR_SIZE];
int len;
int line_id;
uint8_t type;
uint32_t receipt;
uint64_t last_send_try;
struct cqueue_msg *next;
struct cqueue_msg *prev;
};
struct chat_queue {
struct cqueue_msg *root;
struct cqueue_msg *end;
};
void cqueue_cleanup(struct chat_queue *q);
void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id);
/* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m);
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt);

View File

@ -26,18 +26,20 @@
#include <time.h>
#include <limits.h>
#include <dirent.h>
#include <sys/stat.h>
#include "toxic.h"
#include "windows.h"
#include "misc_tools.h"
#include "settings.h"
#include "file_senders.h"
extern ToxWindow *prompt;
extern struct user_settings *user_settings_;
static uint64_t current_unix_time;
void host_to_net(uint8_t *num, uint16_t numbytes)
void hst_to_net(uint8_t *num, uint16_t numbytes)
{
#ifndef WORDS_BIGENDIAN
uint32_t i;
@ -123,6 +125,25 @@ char *hex_string_to_bin(const char *hex_string)
return val;
}
int hex_string_to_bytes(char *buf, int size, const char *keystr)
{
if (size % 2 != 0)
return -1;
int i, res;
const char *pos = keystr;
for (i = 0; i < size; ++i) {
res = sscanf(pos, "%2hhx", &buf[i]);
pos += 2;
if (res == EOF || res < 1)
return -1;
}
return 0;
}
/* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(const char *string)
{
@ -261,3 +282,32 @@ int char_rfind(const char *s, char ch, int len)
return i;
}
/* Converts bytes to appropriate unit and puts in buf as a string */
void bytes_convert_str(char *buf, int size, uint64_t bytes)
{
double conv = bytes;
const char *unit;
if (conv < KiB) {
unit = "Bytes";
} else if (conv < MiB) {
unit = "KiB";
conv /= (double) KiB;
} else if (conv < GiB) {
unit = "MiB";
conv /= (double) MiB;
} else {
unit = "GiB";
conv /= (double) GiB;
}
snprintf(buf, size, "%.1f %s", conv, unit);
}
/* checks if a file exists. Returns true or false */
bool file_exists(const char *path)
{
struct stat s;
return stat(path, &s) == 0;
}

View File

@ -34,18 +34,21 @@
#endif
#ifndef net_to_host
#define net_to_host(x, y) host_to_net(x, y)
#define net_to_host(x, y) hst_to_net(x, y)
#endif
void host_to_net(uint8_t *num, uint16_t numbytes);
void hst_to_net(uint8_t *num, uint16_t numbytes);
/* convert a hex string to binary */
char *hex_string_to_bin(const char *hex_string);
/* convert a hex string to bytes. returns 0 on success, -1 on failure */
int hex_string_to_bytes(char *buf, int size, const char *keystr);
/* get the current unix time */
uint64_t get_unix_time(void);
/*Puts the current time in buf in the format of [HH:mm:ss] */
/* Puts the current time in buf in the format of [HH:mm:ss] */
void get_time_str(char *buf, int bufsize);
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
@ -103,4 +106,10 @@ int char_find(int idx, const char *s, char ch);
returns 0 if char not found */
int char_rfind(const char *s, char ch, int len);
/* Converts bytes to appropriate unit and puts in buf as a string */
void bytes_convert_str(char *buf, int size, uint64_t bytes);
/* checks if a file exists. Returns true or false */
bool file_exists(const char *path);
#endif /* #define _misc_tools_h */

View File

@ -24,10 +24,12 @@
#include "device.h"
#include "settings.h"
#include "line_info.h"
#include "misc_tools.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>
@ -59,6 +61,7 @@
#include <libnotify/notify.h>
#endif
#define MAX_BOX_MSG_LEN 127
#define SOUNDS_SIZE 10
#define ACTIVE_NOTIFS_MAX 50
@ -67,14 +70,14 @@ extern struct user_settings *user_settings_;
struct _Control {
time_t cooldown;
time_t notif_timeout;
unsigned long this_window;
#ifdef _X11
Display *display;
unsigned long this_window;
#endif /* _X11 */
#if defined(_SOUND_NOTIFY) || defined(_BOX_NOTIFY)
pthread_mutex_t poll_mutex[1];
_Bool poll_active;
bool poll_active;
#endif
#ifdef _SOUND_NOTIFY
@ -87,13 +90,13 @@ struct _ActiveNotifications {
#ifdef _SOUND_NOTIFY
uint32_t source;
uint32_t buffer;
_Bool looping;
bool looping;
#endif
_Bool active;
bool active;
int *id_indicator;
#ifdef _BOX_NOTIFY
NotifyNotification* box;
char messages[128][128];
char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
char title[24];
size_t size;
time_t n_timeout;
@ -105,18 +108,40 @@ struct _ActiveNotifications {
/**********************************************************************************/
/**********************************************************************************/
/* coloured tab notifications: primary notification type */
static void tab_notify(ToxWindow *self, uint64_t flags)
{
if (self == NULL)
return;
if (flags & NT_WNDALERT_0)
self->alert = WINDOW_ALERT_0;
else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) )
self->alert = WINDOW_ALERT_1;
else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) )
self->alert = WINDOW_ALERT_2;
}
#ifdef _X11
long unsigned int get_focused_window_id()
{
#ifdef _X11
if (!Control.display) return 0;
Window focus;
int revert;
XGetInputFocus(Control.display, &focus, &revert);
return focus;
#else
return 0;
}
#endif /* _X11 */
static bool notifications_are_disabled(uint64_t flags)
{
bool res = flags & NT_RESTOL && Control.cooldown > get_unix_time();
#ifdef _X11
return res || (flags & NT_NOFOCUS && Control.this_window == get_focused_window_id());
#else
return res;
#endif
}
static void control_lock()
@ -134,14 +159,14 @@ static void control_unlock()
}
#ifdef _SOUND_NOTIFY
_Bool is_playing(int source)
bool is_playing(int source)
{
int ready;
alGetSourcei(source, AL_SOURCE_STATE, &ready);
return ready == AL_PLAYING;
}
/* Terminate all sounds but wait them to finish first */
/* Terminate all sounds but wait for them to finish first */
void graceful_clear()
{
int i;
@ -167,7 +192,6 @@ void graceful_clear()
else break;
}
}
}
if (i == ACTIVE_NOTIFS_MAX) {
@ -224,7 +248,7 @@ void* do_playing(void* _p)
pthread_exit(NULL);
}
int play_source(uint32_t source, uint32_t buffer, _Bool looping)
int play_source(uint32_t source, uint32_t buffer, bool looping)
{
int i = 0;
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
@ -317,8 +341,6 @@ int init_notify(int login_cooldown, int notification_timeout)
#ifdef _X11
Control.display = XOpenDisplay(NULL);
Control.this_window = get_focused_window_id();
#else
Control.this_window = 1;
#endif /* _X11 */
@ -365,7 +387,7 @@ int set_sound(Notification sound, const char* value)
return stat(value, &buf) == 0;
}
int play_sound_internal(Notification what, _Bool loop)
int play_sound_internal(Notification what, bool loop)
{
uint32_t source;
uint32_t buffer;
@ -442,15 +464,9 @@ void m_notify_action(NotifyNotification *box, char *action, void* data)
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator)
{
/* Consider colored notify as primary */
if (self && self->alert == WINDOW_ALERT_NONE) {
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
else if (flags & NT_WNDALERT_1) self->alert = WINDOW_ALERT_1;
else if (flags & NT_WNDALERT_2) self->alert = WINDOW_ALERT_2;
}
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
tab_notify(self, flags);
if (notifications_are_disabled(flags))
return -1;
int id = -1;
@ -486,15 +502,9 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
{
/* Consider colored notify as primary */
if (self && self->alert == WINDOW_ALERT_NONE) {
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
else if (flags & NT_WNDALERT_1) self->alert = WINDOW_ALERT_1;
else if (flags & NT_WNDALERT_2) self->alert = WINDOW_ALERT_2;
}
tab_notify(self, flags);
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
if (notifications_are_disabled(flags))
return -1;
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
@ -532,41 +542,42 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, char* title, const char* format, ...)
{
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
if (notifications_are_disabled(flags)) {
tab_notify(self, flags);
return -1;
}
#ifdef _BOX_NOTIFY
int id = sound_notify(self, notif, flags, id_indicator);
control_lock();
#ifdef _SOUND_NOTIFY
if (id == -1) { /* Could not play */
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
}
actives[id].active = 1;
actives[id].id_indicator = id_indicator;
if (id_indicator) *id_indicator = id;
}
#endif
strncpy(actives[id].title, title, 24);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], 127, format, __ARGS__);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[0]) > 124)
strcpy(actives[id].messages[0] + 124, "...");
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
@ -575,7 +586,7 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
notify_notification_set_app_name(actives[id].box, "toxic");
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
@ -585,48 +596,48 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...)
{
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
if (notifications_are_disabled(flags)) {
tab_notify(self, flags);
return -1;
}
#ifdef _BOX_NOTIFY
if (sound_notify2(self, notif, flags, id) == -1)
return -1;
control_lock();
if (!actives[id].box || actives[id].size >= 128 ) {
if (!actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1) {
control_unlock();
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], 127, format, __ARGS__);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > 124)
strcpy(actives[id].messages[actives[id].size] + 124, "...");
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}
formated[strlen(formated) - 1] = '\0';
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
return sound_notify2(self, notif, flags, id);
@ -635,53 +646,47 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
{
/* Always do colored notify */
if (self && self->alert == WINDOW_ALERT_NONE) {
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
else if (flags & NT_WNDALERT_1) self->alert = WINDOW_ALERT_1;
else if (flags & NT_WNDALERT_2) self->alert = WINDOW_ALERT_2;
}
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
tab_notify(self, flags);
if (notifications_are_disabled(flags))
return -1;
#ifdef _BOX_NOTIFY
control_lock();
int id;
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
}
if (id_indicator) {
actives[id].id_indicator = id_indicator;
*id_indicator = id;
}
strncpy(actives[id].title, title, 24);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], 127, format, __ARGS__);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[0]) > 124)
strcpy(actives[id].messages[0] + 124, "...");
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].active = 1;
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
notify_notification_set_app_name(actives[id].box, "toxic");
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
@ -691,51 +696,45 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...)
{
/* Always do colored notify */
if (self && self->alert == WINDOW_ALERT_NONE) {
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
else if (flags & NT_WNDALERT_1) self->alert = WINDOW_ALERT_1;
else if (flags & NT_WNDALERT_2) self->alert = WINDOW_ALERT_2;
}
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
tab_notify(self, flags);
if (notifications_are_disabled(flags))
return -1;
#ifdef _BOX_NOTIFY
control_lock();
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= 128 ) {
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1 ) {
control_unlock();
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], 127, format, __ARGS__);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > 124)
strcpy(actives[id].messages[actives[id].size] + 124, "...");
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}
formated[strlen(formated) - 1] = '\0';
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
return -1;

View File

@ -42,12 +42,11 @@
#include "notify.h"
#include "autocomplete.h"
char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
uint16_t num_frnd_requests = 0;
extern ToxWindow *prompt;
struct _Winthread Winthread;
extern struct user_settings *user_settings_;
extern struct _Winthread Winthread;
_FriendRequests FriendRequests;
/* Array of global command names used for tab completion. */
const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
@ -56,6 +55,7 @@ const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/clear" },
{ "/close" }, /* rm /close when groupchats gets its own list */
{ "/connect" },
{ "/decline" },
{ "/exit" },
{ "/groupchat" },
{ "/help" },
@ -64,6 +64,7 @@ const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/nick" },
{ "/note" },
{ "/quit" },
{ "/requests" },
{ "/status" },
#ifdef _AUDIO
@ -87,7 +88,6 @@ void kill_prompt_window(ToxWindow *self)
delwin(statusbar->topline);
free(ctx->log);
free(ctx->hst);
free(ctx);
free(self->help);
free(statusbar);
@ -103,12 +103,14 @@ void prompt_update_nick(ToxWindow *prompt, const char *nick)
statusbar->nick_len = strlen(statusbar->nick);
}
/* Updates own statusmessage in prompt statusbar */
void prompt_update_statusmessage(ToxWindow *prompt, const char *statusmsg)
/* Updates own statusmessage */
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg)
{
StatusBar *statusbar = prompt->stb;
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
int len = strlen(statusbar->statusmsg);
statusbar->statusmsg_len = len;
tox_set_status_message(m, (uint8_t *) statusmsg, (uint64_t) len);
}
/* Updates own status in prompt statusbar */
@ -126,20 +128,24 @@ void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
}
/* Adds friend request to pending friend requests.
Returns request number on success, -1 if queue is full or other error. */
static int add_friend_request(const char *public_key)
Returns request number on success, -1 if queue is full. */
static int add_friend_request(const char *public_key, const char *data)
{
if (num_frnd_requests >= MAX_FRIENDS_NUM)
if (FriendRequests.max_idx >= MAX_FRIEND_REQUESTS)
return -1;
int i;
for (i = 0; i <= num_frnd_requests; ++i) {
if (!strlen(pending_frnd_requests[i])) {
memcpy(pending_frnd_requests[i], public_key, TOX_CLIENT_ID_SIZE);
for (i = 0; i <= FriendRequests.max_idx; ++i) {
if (!FriendRequests.request[i].active) {
FriendRequests.request[i].active = true;
memcpy(FriendRequests.request[i].key, public_key, TOX_CLIENT_ID_SIZE);
snprintf(FriendRequests.request[i].msg, sizeof(FriendRequests.request[i].msg), "%s", data);
if (i == num_frnd_requests)
++num_frnd_requests;
if (i == FriendRequests.max_idx)
++FriendRequests.max_idx;
++FriendRequests.num_requests;
return i;
}
@ -269,6 +275,29 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
wattroff(statusbar->topline, A_BOLD);
}
/* Reset statusbar->statusmsg on window resize */
if (x2 != self->x) {
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {0};
pthread_mutex_lock(&Winthread.lock);
tox_get_self_status_message(m, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
pthread_mutex_unlock(&Winthread.lock);
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
}
self->x = x2;
/* Truncate note if it doesn't fit in statusbar */
uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3;
if (statusbar->statusmsg_len > maxlen) {
statusbar->statusmsg[maxlen - 3] = '\0';
strcat(statusbar->statusmsg, "...");
statusbar->statusmsg_len = maxlen;
}
if (statusbar->statusmsg[0])
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
@ -340,16 +369,15 @@ static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, con
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data);
write_to_log("Friend request with the message '%s'", "", ctx->log, true);
int n = add_friend_request(key);
int n = add_friend_request(key, data);
if (n == -1) {
const char *errmsg = "Friend request queue is full. Discarding request.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
write_to_log(errmsg, "", ctx->log, true);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" to accept it.", n);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n);
sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL);
}
@ -379,13 +407,13 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
strcpy(ver, TOXICVER);
const char *toxic_ver = strtok(ver, "_");
if ( (!statusmsg[0] || !strncmp("Toxing on Toxic", statusmsg, 15)) && toxic_ver != NULL) {
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver);
if ( (s_len <= 0 || !strncmp("Toxing on Toxic", statusmsg, strlen("Toxing on Toxic"))) && toxic_ver != NULL) {
snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic v.%s", toxic_ver);
s_len = strlen(statusmsg);
statusmsg[s_len] = '\0';
}
prompt_update_statusmessage(prompt, statusmsg);
prompt_update_statusmessage(prompt, m, statusmsg);
prompt_update_status(prompt, status);
prompt_update_nick(prompt, nick);
@ -436,7 +464,8 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
scrollok(ctx->history, 0);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
print_welcome_msg(self);
if (user_settings_->show_welcome_msg == SHOW_WELCOME_MSG_ON)
print_welcome_msg(self);
}
ToxWindow new_prompt(void)

View File

@ -27,16 +27,30 @@
#include "windows.h"
#ifdef _AUDIO
#define AC_NUM_GLOB_COMMANDS 16
#define AC_NUM_GLOB_COMMANDS 18
#else
#define AC_NUM_GLOB_COMMANDS 14
#define AC_NUM_GLOB_COMMANDS 16
#endif /* _AUDIO */
#define MAX_FRIEND_REQUESTS 32
struct _friend_request {
bool active;
char msg[MAX_STR_SIZE];
uint8_t key[TOX_CLIENT_ID_SIZE];
};
typedef struct {
int max_idx;
int num_requests;
struct _friend_request request[MAX_FRIEND_REQUESTS];
} _FriendRequests;
ToxWindow new_prompt(void);
void prep_prompt_win(void);
void prompt_init_statusbar(ToxWindow *self, Tox *m);
void prompt_update_nick(ToxWindow *prompt, const char *nick);
void prompt_update_statusmessage(ToxWindow *prompt, const char *statusmsg);
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg);
void prompt_update_status(ToxWindow *prompt, uint8_t status);
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
void kill_prompt_window(ToxWindow *self);

View File

@ -29,6 +29,7 @@
#include "windows.h"
#include "configdir.h"
#include "notify.h"
#include "misc_tools.h"
#ifdef _AUDIO
#include "device.h"
@ -41,7 +42,9 @@
#define PACKAGE_DATADIR "."
#endif
const struct _ui_strings {
#define NO_SOUND "silent"
static struct _ui_strings {
const char* self;
const char* timestamps;
const char* alerts;
@ -51,6 +54,7 @@ const struct _ui_strings {
const char* history_size;
const char* show_typing_self;
const char* show_typing_other;
const char* show_welcome_msg;
} ui_strings = {
"ui",
"timestamps",
@ -61,6 +65,7 @@ const struct _ui_strings {
"history_size",
"show_typing_self",
"show_typing_other",
"show_welcome_msg",
};
static void ui_defaults(struct user_settings* settings)
@ -73,9 +78,10 @@ static void ui_defaults(struct user_settings* settings)
settings->history_size = 700;
settings->show_typing_self = SHOW_TYPING_ON;
settings->show_typing_other = SHOW_TYPING_ON;
settings->show_welcome_msg = SHOW_WELCOME_MSG_ON;
}
const struct _keys_strings {
static const struct _keys_strings {
const char* self;
const char* next_tab;
const char* prev_tab;
@ -113,21 +119,24 @@ static void key_defaults(struct user_settings* settings)
settings->key_peer_list_down = T_KEY_C_RB;
}
const struct _tox_strings {
static const struct _tox_strings {
const char* self;
const char* download_path;
const char* chatlogs_path;
} tox_strings = {
"tox",
"download_path",
"chatlogs_path",
};
static void tox_defaults(struct user_settings* settings)
{
strcpy(settings->download_path, ""); /* explicitly set default to pwd */
strcpy(settings->download_path, "");
strcpy(settings->chatlogs_path, "");
}
#ifdef _AUDIO
const struct _audio_strings {
static const struct _audio_strings {
const char* self;
const char* input_device;
const char* output_device;
@ -148,7 +157,7 @@ static void audio_defaults(struct user_settings* settings)
#endif
#ifdef _SOUND_NOTIFY
const struct _sound_strings {
static const struct _sound_strings {
const char* self;
const char* error;
const char* self_log_in;
@ -196,12 +205,13 @@ int settings_load(struct user_settings *s, const char *patharg)
{
config_t cfg[1];
config_setting_t *setting;
const char *str;
const char *str = NULL;
/* Load default settings */
ui_defaults(s);
tox_defaults(s);
key_defaults(s);
#ifdef _AUDIO
audio_defaults(s);
#endif
@ -217,13 +227,14 @@ int settings_load(struct user_settings *s, const char *patharg)
free(user_config_dir);
/* make sure path exists or is created on first time running */
FILE *fp = fopen(path, "r");
if (fp == NULL) {
if ((fp = fopen(path, "w")) == NULL)
return -1;
}
if (!file_exists(path)) {
FILE *fp = fopen(path, "w");
fclose(fp);
if (fp == NULL)
return -1;
fclose(fp);
}
} else {
snprintf(path, sizeof(path), "%s", patharg);
}
@ -242,28 +253,47 @@ int settings_load(struct user_settings *s, const char *patharg)
config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size);
config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self);
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_int(setting, ui_strings.time_format, &s->time);
s->time = s->time == TIME_24 || s->time == TIME_12 ? s->time : TIME_24; /* Check defaults */
}
/* paths */
if ((setting = config_lookup(cfg, tox_strings.self)) != NULL) {
if ( config_setting_lookup_string(setting, tox_strings.download_path, &str) ) {
strcpy(s->download_path, str);
snprintf(s->download_path, sizeof(s->download_path), "%s", str);
int len = strlen(s->download_path);
/* make sure path ends with a '/' */
if (len >= sizeof(s->download_path) - 2)
s->download_path[0] = '\0';
else if (s->download_path[len - 1] != '/')
strcat(&s->download_path[len - 1], "/");
}
if ( config_setting_lookup_string(setting, tox_strings.chatlogs_path, &str) ) {
snprintf(s->chatlogs_path, sizeof(s->chatlogs_path), "%s", str);
int len = strlen(s->chatlogs_path);
if (len >= sizeof(s->chatlogs_path) - 2)
s->chatlogs_path[0] = '\0';
else if (s->chatlogs_path[len - 1] != '/')
strcat(&s->chatlogs_path[len - 1], "/");
}
}
/* keys */
if((setting = config_lookup(cfg, key_strings.self)) != NULL) {
if ((setting = config_lookup(cfg, key_strings.self)) != NULL) {
const char* tmp = NULL;
if(config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) s->key_next_tab = key_parse(&tmp);
if(config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) s->key_prev_tab = key_parse(&tmp);
if(config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) s->key_scroll_line_up = key_parse(&tmp);
if(config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) s->key_scroll_line_down= key_parse(&tmp);
if(config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) s->key_half_page_up = key_parse(&tmp);
if(config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) s->key_half_page_down = key_parse(&tmp);
if(config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) s->key_page_bottom = key_parse(&tmp);
if(config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) s->key_peer_list_up = key_parse(&tmp);
if(config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp)) s->key_peer_list_down = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) s->key_next_tab = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) s->key_prev_tab = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) s->key_scroll_line_up = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) s->key_scroll_line_down= key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) s->key_half_page_up = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) s->key_half_page_down = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) s->key_page_bottom = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) s->key_peer_list_up = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp)) s->key_peer_list_down = key_parse(&tmp);
}
#ifdef _AUDIO
@ -282,61 +312,61 @@ int settings_load(struct user_settings *s, const char *patharg)
if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) {
if ( (config_setting_lookup_string(setting, sound_strings.error, &str) != CONFIG_TRUE) ||
!set_sound(error, str) ) {
if (strcasecmp(str, NO_SOUND) != 0)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(error, PACKAGE_DATADIR "/sounds/Error.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.user_log_in, &str) ||
!set_sound(user_log_in, str) ) {
if (strcasecmp(str, NO_SOUND) != 0)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ContactLogsIn.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.self_log_in, &str) ||
!set_sound(self_log_in, str) ) {
if (strcasecmp(str, NO_SOUND) != 0)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(self_log_in, PACKAGE_DATADIR "/sounds/LogIn.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.user_log_out, &str) ||
!set_sound(user_log_out, str) ) {
if (strcasecmp(str, NO_SOUND) != 0)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ContactLogsOut.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.self_log_out, &str) ||
!set_sound(self_log_out, str) ) {
if (strcasecmp(str, NO_SOUND) != 0)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(self_log_out, PACKAGE_DATADIR "/sounds/LogOut.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.call_incoming, &str) ||
!set_sound(call_incoming, str) ) {
if (strcasecmp(str, NO_SOUND) != 0)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/IncomingCall.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) ||
!set_sound(call_outgoing, str) ) {
if (strcasecmp(str, NO_SOUND) != 0)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/OutgoingCall.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.generic_message, &str) ||
!set_sound(generic_message, str) ) {
if (strcasecmp(str, NO_SOUND) != 0)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(generic_message, PACKAGE_DATADIR "/sounds/NewMessage.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) ||
!set_sound(transfer_pending, str) ) {
if (strcasecmp(str, NO_SOUND) != 0)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/TransferPending.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) ||
!set_sound(transfer_completed, str) ) {
if (strcasecmp(str, NO_SOUND) != 0)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/TransferComplete.wav");
}
}

View File

@ -23,7 +23,7 @@
#ifndef _settings_h
#define _settings_h
#define NO_SOUND "silent"
#include <limits.h>
/* holds user setting values */
struct user_settings {
@ -35,8 +35,10 @@ struct user_settings {
int history_size; /* int between MIN_HISTORY and MAX_HISTORY */
int show_typing_self; /* boolean */
int show_typing_other; /* boolean */
int show_welcome_msg; /* boolean */
char download_path[MAX_STR_SIZE];
char download_path[PATH_MAX];
char chatlogs_path[PATH_MAX];
int key_next_tab; /* character code */
int key_prev_tab; /* character code */
@ -73,6 +75,9 @@ enum {
SHOW_TYPING_OFF = 0,
SHOW_TYPING_ON = 1,
SHOW_WELCOME_MSG_OFF = 0,
SHOW_WELCOME_MSG_ON = 1,
DFLT_HST_SIZE = 700,
} settings_values;

View File

@ -37,8 +37,11 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <limits.h>
#include <termios.h>
#include <tox/tox.h>
#include <tox/toxencryptsave.h>
#include "configdir.h"
#include "toxic.h"
@ -52,6 +55,7 @@
#include "log.h"
#include "notify.h"
#include "device.h"
#include "message_queue.h"
#ifdef _AUDIO
#include "audio_call.h"
@ -73,9 +77,19 @@ ToxWindow *prompt = NULL;
#define AUTOSAVE_FREQ 60
struct _Winthread Winthread;
struct _cqueue_thread cqueue_thread;
struct arg_opts arg_opts;
struct user_settings *user_settings_ = NULL;
#define MIN_PASSWORD_LEN 6
#define MAX_PASSWORD_LEN 64
static struct _user_password {
bool data_is_encrypted;
char pass[MAX_PASSWORD_LEN + 1];
int len;
} user_password;
static void catch_SIGINT(int sig)
{
Winthread.sig_exit_toxic = true;
@ -104,8 +118,9 @@ static void init_signal_catchers(void)
void exit_toxic_success(Tox *m)
{
store_data(m, DATA_FILE);
memset(&user_password, 0, sizeof(struct _user_password));
close_all_file_senders(m);
kill_all_windows();
kill_all_windows(m);
free(DATA_FILE);
free(BLOCK_FILE);
@ -175,23 +190,100 @@ static void init_term(void)
refresh();
}
static Tox *init_tox(int ipv4)
{
/* Init core */
int ipv6 = !ipv4;
Tox *m = tox_new(ipv6);
static struct _init_messages {
char **msgs;
int num;
} init_messages;
/*
* TOX_ENABLE_IPV6_DEFAULT is always 1.
* Checking it is redundant, this *should* be doing ipv4 fallback
*/
if (ipv6 && m == NULL) {
fprintf(stderr, "IPv6 didn't initialize, trying IPv4\n");
m = tox_new(0);
/* One-time queue for messages created during init. Do not use after program init. */
static void queue_init_message(const char *msg, ...)
{
char frmt_msg[MAX_STR_SIZE] = {0};
va_list args;
va_start(args, msg);
vsnprintf(frmt_msg, sizeof(frmt_msg), msg, args);
va_end(args);
int i = init_messages.num;
++init_messages.num;
char **new_msgs = realloc(init_messages.msgs, sizeof(char *) * init_messages.num);
if (new_msgs == NULL)
exit_toxic_err("Failed in queue_init_message", FATALERR_MEMORY);
new_msgs[i] = malloc(MAX_STR_SIZE);
if (new_msgs[i] == NULL)
exit_toxic_err("Failed in queue_init_message", FATALERR_MEMORY);
snprintf(new_msgs[i], MAX_STR_SIZE, "%s", frmt_msg);
init_messages.msgs = new_msgs;
}
/* called after messages have been printed to console and are no longer needed */
static void cleanup_init_messages(void)
{
if (init_messages.num <= 0)
return;
int i;
for (i = 0; i < init_messages.num; ++i)
free(init_messages.msgs[i]);
free(init_messages.msgs);
}
static void print_init_messages(ToxWindow *toxwin)
{
int i;
for (i = 0; i < init_messages.num; ++i)
line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]);
}
static Tox *init_tox(void)
{
Tox_Options tox_opts;
tox_opts.ipv6enabled = !arg_opts.use_ipv4;
tox_opts.udp_disabled = arg_opts.force_tcp;
tox_opts.proxy_enabled = arg_opts.use_proxy;
if (tox_opts.proxy_enabled) {
tox_opts.proxy_port = arg_opts.proxy_port;
snprintf(tox_opts.proxy_address, sizeof(tox_opts.proxy_address), "%s", arg_opts.proxy_address);
char tmp[48];
snprintf(tmp, sizeof(tmp), "Using proxy %s : %d",
arg_opts.proxy_address, arg_opts.proxy_port);
queue_init_message("%s", tmp);
}
if (ipv4)
fprintf(stderr, "Forcing IPv4 connection\n");
if (tox_opts.udp_disabled) {
queue_init_message("UDP disabled");
} else if (tox_opts.proxy_enabled) {
const char *msg = "WARNING: Using a proxy without disabling UDP may leak your real IP address.";
queue_init_message("%s", msg);
msg = "Use the -t option to disable UDP.";
queue_init_message("%s", msg);
}
/* Init core */
Tox *m = tox_new(&tox_opts);
if (tox_opts.ipv6enabled && m == NULL) {
queue_init_message("IPv6 failed to initialize");
tox_opts.ipv6enabled = 0;
m = tox_new(&tox_opts);
}
if (!tox_opts.ipv6enabled)
queue_init_message("Forcing IPv4 connection");
if (tox_opts.proxy_enabled && m == NULL)
exit_toxic_err("Proxy error", FATALERR_PROXY);
if (m == NULL)
return NULL;
@ -212,6 +304,7 @@ static Tox *init_tox(int ipv4)
tox_callback_file_send_request(m, on_file_sendrequest, NULL);
tox_callback_file_control(m, on_file_control, NULL);
tox_callback_file_data(m, on_file_data, NULL);
tox_callback_read_receipt(m, on_read_receipt, NULL);
#ifdef __linux__
tox_set_name(m, (uint8_t *) "Cool dude", strlen("Cool dude"));
@ -226,10 +319,10 @@ static Tox *init_tox(int ipv4)
return m;
}
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */
#define MAXNODES 50
#define NODELEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
#define NODELEN (MAX_NODE_LINE - TOX_CLIENT_ID_SIZE - 7)
static struct _toxNodes {
int lines;
@ -238,7 +331,7 @@ static struct _toxNodes {
char keys[MAXNODES][TOX_CLIENT_ID_SIZE];
} toxNodes;
static int nodelist_load(const char *filename)
static int load_nodelist(const char *filename)
{
if (!filename)
return 1;
@ -248,10 +341,10 @@ static int nodelist_load(const char *filename)
if (fp == NULL)
return 1;
char line[MAXLINE];
char line[MAX_NODE_LINE];
while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) {
if (strlen(line) > MINLINE) {
if (strlen(line) > MIN_NODE_LINE) {
const char *name = strtok(line, " ");
const char *port = strtok(NULL, " ");
const char *key_ascii = strtok(NULL, " ");
@ -262,7 +355,7 @@ static int nodelist_load(const char *filename)
snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name);
toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0;
toxNodes.ports[toxNodes.lines] = htons(atoi(port));
toxNodes.ports[toxNodes.lines] = atoi(port);
char *key_binary = hex_string_to_bin(key_ascii);
memcpy(toxNodes.keys[toxNodes.lines], key_binary, TOX_CLIENT_ID_SIZE);
@ -272,19 +365,17 @@ static int nodelist_load(const char *filename)
}
}
if (toxNodes.lines < 1) {
fclose(fp);
return 2;
}
fclose(fp);
if (toxNodes.lines < 1)
return 1;
return 0;
}
int init_connection_helper(Tox *m, int line)
{
return tox_bootstrap_from_address(m, toxNodes.nodes[line], TOX_ENABLE_IPV6_DEFAULT,
toxNodes.ports[line], (uint8_t *) toxNodes.keys[line]);
return tox_bootstrap_from_address(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line]);
}
/* Connects to a random DHT node listed in the DHTnodes file
@ -313,11 +404,11 @@ int init_connection(Tox *m)
int res;
if (!arg_opts.nodes_path[0])
res = nodelist_load(PACKAGE_DATADIR "/DHTnodes");
res = load_nodelist(PACKAGE_DATADIR "/DHTnodes");
else
res = nodelist_load(arg_opts.nodes_path);
res = load_nodelist(arg_opts.nodes_path);
if (toxNodes.lines < 1)
if (res != 0)
return res;
res = 3;
@ -340,6 +431,9 @@ int init_connection(Tox *m)
static void do_connection(Tox *m, ToxWindow *prompt)
{
if (arg_opts.no_connect == 1)
return;
char msg[MAX_STR_SIZE] = {0};
static int conn_err = 0;
@ -354,7 +448,7 @@ static void do_connection(Tox *m, ToxWindow *prompt)
if (!was_connected && is_connected) {
was_connected = true;
prompt_update_connectionstatus(prompt, was_connected);
snprintf(msg, sizeof(msg), "DHT connected.");
snprintf(msg, sizeof(msg), "DHT connected");
} else if (was_connected && !is_connected) {
was_connected = false;
prompt_update_connectionstatus(prompt, was_connected);
@ -384,6 +478,104 @@ static void load_friendlist(Tox *m)
sort_friendlist_index();
}
/* return length of password on success, 0 on failure */
static int password_prompt(char *buf, int size)
{
buf[0] = '\0';
/* disable terminal echo */
struct termios oflags, nflags;
tcgetattr(fileno(stdin), &oflags);
nflags = oflags;
nflags.c_lflag &= ~ECHO;
nflags.c_lflag |= ECHONL;
if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0)
return 0;
const char *p = fgets(buf, size, stdin);
int len = strlen(buf);
/* re-enable terminal echo */
tcsetattr(fileno(stdin), TCSANOW, &oflags);
if (p == NULL || len <= 1)
return 0;
/* eat overflowed stdin and return error */
if (buf[--len] != '\n') {
int ch;
while ((ch = getchar()) != '\n' && ch > 0)
;
return 0;
}
buf[len] = '\0';
return len;
}
/* Ask user if they would like to encrypt the data file and set password */
static void first_time_encrypt(const char *msg)
{
char ch[256] = {0};
do {
system("clear");
printf("%s ", msg);
if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n")
|| !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n"))
break;
} while (fgets(ch, sizeof(ch), stdin));
printf("\n");
if (ch[0] == 'q' || ch[0] == 'Q')
exit(0);
if (ch[0] == 'y' || ch[0] == 'Y') {
int len = 0;
bool valid_password = false;
char passconfirm[MAX_PASSWORD_LEN + 1] = {0};
printf("Enter a new password (must be at least %d characters) ", MIN_PASSWORD_LEN);
while (valid_password == false) {
len = password_prompt(user_password.pass, sizeof(user_password.pass));
user_password.len = len;
if (strcasecmp(user_password.pass, "q") == 0)
exit(0);
if (string_is_empty(passconfirm) && (len < MIN_PASSWORD_LEN || len > MAX_PASSWORD_LEN)) {
printf("Password must be between %d and %d characters long. ", MIN_PASSWORD_LEN, MAX_PASSWORD_LEN);
continue;
}
if (string_is_empty(passconfirm)) {
printf("Enter password again ");
snprintf(passconfirm, sizeof(passconfirm), "%s", user_password.pass);
continue;
}
if (strcmp(user_password.pass, passconfirm) != 0) {
memset(passconfirm, 0, sizeof(passconfirm));
memset(user_password.pass, 0, sizeof(user_password.pass));
printf("Passwords don't match. Try again. ");
continue;
}
valid_password = true;
}
queue_init_message("Data file '%s' has been encrypted", DATA_FILE);
memset(passconfirm, 0, sizeof(passconfirm));
user_password.data_is_encrypted = true;
}
system("clear");
}
/*
* Store Messenger to given location
* Return 0 stored successfully or ignoring data file
@ -392,7 +584,7 @@ static void load_friendlist(Tox *m)
* Return -3 opening path failed
* Return -4 fwrite failed
*/
int store_data(Tox *m, char *path)
int store_data(Tox *m, const char *path)
{
if (arg_opts.ignore_data_file)
return 0;
@ -400,13 +592,16 @@ int store_data(Tox *m, char *path)
if (path == NULL)
return -1;
int len = tox_size(m);
int len = user_password.data_is_encrypted ? tox_encrypted_size(m) : tox_size(m);
char *buf = malloc(len);
if (buf == NULL)
return -2;
tox_save(m, (uint8_t *) buf);
if (user_password.data_is_encrypted && !arg_opts.unencrypt_data)
tox_encrypted_save(m, (uint8_t *) buf, (uint8_t *) user_password.pass, user_password.len);
else
tox_save(m, (uint8_t *) buf);
FILE *fd = fopen(path, "wb");
@ -451,7 +646,51 @@ static void load_data(Tox *m, char *path)
exit_toxic_err("failed in load_data", FATALERR_FREAD);
}
tox_load(m, (uint8_t *) buf, len);
bool is_encrypted = tox_is_data_encrypted((uint8_t *) buf);
/* attempt to encrypt an already encrypted data file */
if (arg_opts.encrypt_data && is_encrypted)
exit_toxic_err("failed in load_data", FATALERR_ENCRYPT);
if (arg_opts.unencrypt_data && is_encrypted)
queue_init_message("Data file '%s' has been unencrypted", path);
else if (arg_opts.unencrypt_data)
queue_init_message("Warning: passed --unencrypt-data option with unencrypted data file '%s'", path);
if (is_encrypted) {
if (!arg_opts.unencrypt_data)
user_password.data_is_encrypted = true;
int pwlen = 0;
system("clear");
printf("Enter password (q to quit) ");
while (true) {
pwlen = password_prompt(user_password.pass, sizeof(user_password.pass));
user_password.len = pwlen;
if (strcasecmp(user_password.pass, "q") == 0)
exit(0);
if (pwlen < MIN_PASSWORD_LEN) {
system("clear");
sleep(1);
printf("Invalid password. Try again. ");
continue;
}
if (tox_encrypted_load(m, (uint8_t *) buf, len, (uint8_t *) user_password.pass, pwlen) == 0) {
break;
} else {
system("clear");
sleep(1);
printf("Invalid password. Try again. ");
}
}
} else {
tox_load(m, (uint8_t *) buf, len);
}
load_friendlist(m);
load_blocklist(BLOCK_FILE);
@ -468,7 +707,10 @@ static void do_toxic(Tox *m, ToxWindow *prompt)
pthread_mutex_lock(&Winthread.lock);
do_connection(m, prompt);
do_file_senders(m);
tox_do(m); /* main tox-core loop */
if (arg_opts.no_connect == 0)
tox_do(m); /* main tox-core loop */
pthread_mutex_unlock(&Winthread.lock);
}
@ -498,24 +740,50 @@ void *thread_winref(void *data)
}
}
void *thread_cqueue(void *data)
{
Tox *m = (Tox *) data;
while (true) {
pthread_mutex_lock(&Winthread.lock);
int i;
for (i = 2; i < MAX_WINDOWS_NUM; ++i) {
ToxWindow *toxwin = get_window_ptr(i);
if (toxwin != NULL && toxwin->is_chat && tox_get_friend_connection_status(m, toxwin->num) == 1)
cqueue_try_send(toxwin, m);
}
pthread_mutex_unlock(&Winthread.lock);
usleep(7000);
}
}
static void print_usage(void)
{
fprintf(stderr, "usage: toxic [OPTION] [FILE ...]\n");
fprintf(stderr, " -f, --file Use specified data file\n");
fprintf(stderr, " -x, --nodata Ignore data file\n");
fprintf(stderr, " -4, --ipv4 Force IPv4 connection\n");
fprintf(stderr, " -d, --default_locale Use default locale\n");
fprintf(stderr, " -b, --debug Enable stderr for debugging\n");
fprintf(stderr, " -c, --config Use specified config file\n");
fprintf(stderr, " -n, --nodes Use specified DHTnodes file\n");
fprintf(stderr, " -d, --default-locale Use default POSIX locale\n");
fprintf(stderr, " -e, --encrypt-data Encrypt an unencrypted data file\n");
fprintf(stderr, " -f, --file Use specified data file\n");
fprintf(stderr, " -h, --help Show this message and exit\n");
fprintf(stderr, " -n, --nodes Use specified DHTnodes file\n");
fprintf(stderr, " -o, --noconnect Do not connect to the DHT network\n");
fprintf(stderr, " -p, --proxy Use proxy: Requires [IP] [port]\n");
fprintf(stderr, " -r, --dnslist Use specified DNSservers file\n");
fprintf(stderr, " -t, --force-tcp Force TCP connection (use this with proxies)\n");
fprintf(stderr, " -u, --unencrypt-data Unencrypt an encrypted data file\n");
fprintf(stderr, " -x, --nodata Ignore data file\n");
}
static void set_default_opts(void)
{
arg_opts.use_ipv4 = 0;
arg_opts.ignore_data_file = 0;
arg_opts.default_locale = 0;
arg_opts.use_custom_data = 0;
memset(&arg_opts, 0, sizeof(struct arg_opts));
/* set any non-zero defaults here*/
}
static void parse_args(int argc, char *argv[])
@ -526,17 +794,51 @@ static void parse_args(int argc, char *argv[])
{"file", required_argument, 0, 'f'},
{"nodata", no_argument, 0, 'x'},
{"ipv4", no_argument, 0, '4'},
{"default_locale", no_argument, 0, 'd'},
{"debug", no_argument, 0, 'b'},
{"default-locale", no_argument, 0, 'd'},
{"config", required_argument, 0, 'c'},
{"encrypt-data", no_argument, 0, 'e'},
{"nodes", required_argument, 0, 'n'},
{"help", no_argument, 0, 'h'},
{"noconnect", no_argument, 0, 'o'},
{"dnslist", required_argument, 0, 'r'},
{"force-tcp", no_argument, 0, 't'},
{"proxy", required_argument, 0, 'p'},
{"unencrypt-data", no_argument, 0, 'u'},
{NULL, no_argument, NULL, 0},
};
const char *opts_str = "4xdf:c:n:h";
const char *opts_str = "4bdehotuxc:f:n:r:p:";
int opt, indexptr;
while ((opt = getopt_long(argc, argv, opts_str, long_opts, &indexptr)) != -1) {
switch (opt) {
case '4':
arg_opts.use_ipv4 = 1;
break;
case 'b':
arg_opts.debug = 1;
queue_init_message("stderr enabled");
break;
case 'c':
snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg);
if (!file_exists(arg_opts.config_path))
queue_init_message("Config file not found");
break;
case 'd':
arg_opts.default_locale = 1;
queue_init_message("Using default POSIX locale");
break;
case 'e':
arg_opts.encrypt_data = 1;
break;
case 'f':
arg_opts.use_custom_data = 1;
DATA_FILE = strdup(optarg);
@ -547,26 +849,53 @@ static void parse_args(int argc, char *argv[])
strcpy(BLOCK_FILE, optarg);
strcat(BLOCK_FILE, "-blocklist");
break;
case 'x':
arg_opts.ignore_data_file = 1;
break;
queue_init_message("Using '%s' data file", DATA_FILE);
case '4':
arg_opts.use_ipv4 = 1;
break;
case 'c':
snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg);
break;
case 'n':
snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg);
if (!file_exists(arg_opts.nodes_path))
queue_init_message("DHTnodes file not found");
break;
case 'd':
arg_opts.default_locale = 1;
case 'o':
arg_opts.no_connect = 1;
queue_init_message("DHT disabled");
break;
case 'p':
arg_opts.use_proxy = 1;
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
if (++optind > argc || argv[optind-1][0] == '-')
exit_toxic_err("Proxy error", FATALERR_PROXY);
arg_opts.proxy_port = (uint16_t) atoi(argv[optind-1]);
break;
case 'r':
snprintf(arg_opts.dns_path, sizeof(arg_opts.dns_path), "%s", optarg);
if (!file_exists(arg_opts.dns_path))
queue_init_message("DNSservers file not found");
break;
case 't':
arg_opts.force_tcp = 1;
break;
case 'u':
arg_opts.unencrypt_data = 1;
break;
case 'x':
arg_opts.ignore_data_file = 1;
queue_init_message("Ignoring data file");
break;
case 'h':
@ -579,7 +908,7 @@ static void parse_args(int argc, char *argv[])
#define DATANAME "data"
#define BLOCKNAME "data-blocklist"
static int init_data_files(void)
static int init_default_data_files(void)
{
if (arg_opts.use_custom_data)
return 0;
@ -587,28 +916,26 @@ static int init_data_files(void)
char *user_config_dir = get_user_config_dir();
int config_err = create_user_config_dirs(user_config_dir);
if (DATA_FILE == NULL ) {
if (config_err) {
DATA_FILE = strdup(DATANAME);
BLOCK_FILE = strdup(BLOCKNAME);
if (config_err) {
DATA_FILE = strdup(DATANAME);
BLOCK_FILE = strdup(BLOCKNAME);
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
} else {
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1);
BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1);
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
} else {
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1);
BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1);
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
strcpy(DATA_FILE, user_config_dir);
strcat(DATA_FILE, CONFIGDIR);
strcat(DATA_FILE, DATANAME);
strcpy(DATA_FILE, user_config_dir);
strcat(DATA_FILE, CONFIGDIR);
strcat(DATA_FILE, DATANAME);
strcpy(BLOCK_FILE, user_config_dir);
strcat(BLOCK_FILE, CONFIGDIR);
strcat(BLOCK_FILE, BLOCKNAME);
}
strcpy(BLOCK_FILE, user_config_dir);
strcat(BLOCK_FILE, CONFIGDIR);
strcat(BLOCK_FILE, BLOCKNAME);
}
free(user_config_dir);
@ -639,9 +966,28 @@ int main(int argc, char *argv[])
init_signal_catchers();
parse_args(argc, argv);
if (arg_opts.encrypt_data && arg_opts.unencrypt_data) {
arg_opts.encrypt_data = 0;
arg_opts.unencrypt_data = 0;
queue_init_message("Warning: Using --unencrypt-data and --encrypt-data simultaneously has no effect");
}
/* Use the -b flag to enable stderr */
if (!arg_opts.debug)
freopen("/dev/null", "w", stderr);
/* Make sure all written files are read/writeable only by the current user. */
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
int config_err = init_data_files();
int config_err = init_default_data_files();
bool datafile_exists = file_exists(DATA_FILE);
if (!arg_opts.ignore_data_file) {
if (!datafile_exists && !arg_opts.unencrypt_data)
first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)");
else if (arg_opts.encrypt_data)
first_time_encrypt("Encrypt existing data file? Y/n (q to quit)");
}
/* init user_settings struct and load settings from conf file */
user_settings_ = calloc(1, sizeof(struct user_settings));
@ -649,18 +995,23 @@ int main(int argc, char *argv[])
if (user_settings_ == NULL)
exit_toxic_err("failed in main", FATALERR_MEMORY);
char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
int settings_err = settings_load(user_settings_, p);
Tox *m = init_tox(arg_opts.use_ipv4);
init_term();
Tox *m = init_tox();
if (m == NULL)
exit_toxic_err("failed in main", FATALERR_NETWORKINIT);
if (!arg_opts.ignore_data_file)
if (!arg_opts.ignore_data_file) {
if (arg_opts.encrypt_data && !datafile_exists)
arg_opts.encrypt_data = 0;
load_data(m, DATA_FILE);
}
init_term();
prompt = init_windows(m);
prompt_init_statusbar(prompt, m);
@ -671,16 +1022,20 @@ int main(int argc, char *argv[])
if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0)
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
/* thread for message queue */
if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0)
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
#ifdef _AUDIO
av = init_audio(prompt, m);
set_primary_device(input, user_settings_->audio_in_dev);
set_primary_device(output, user_settings_->audio_out_dev);
#elif _SOUND_NOTIFY
if ( init_devices() == de_InternalError )
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices");
queue_init_message("Failed to init audio devices");
#endif /* _AUDIO */
@ -694,17 +1049,14 @@ int main(int argc, char *argv[])
if (config_err) {
msg = "Unable to determine configuration directory. Defaulting to 'data' for data file...";
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
queue_init_message("%s", msg);
}
if (settings_err == -1) {
msg = "Failed to load user settings";
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
}
if (settings_err == -1)
queue_init_message("Failed to load user settings");
/* Redirect stderr to /dev/null
NOTE: Might not be best solution. Comment out for debugging. */
freopen("/dev/null", "w", stderr);
print_init_messages(prompt);
cleanup_init_messages();
uint64_t last_save = (uint64_t) time(NULL);
uint64_t looptimer = last_save;

View File

@ -42,7 +42,6 @@
#define UNKNOWN_NAME "Anonymous"
#define MAX_FRIENDS_NUM 999
#define MAX_STR_SIZE TOX_MAX_MESSAGE_LENGTH
#define MAX_CMDNAME_SIZE 64
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */
@ -79,6 +78,8 @@ typedef enum _FATAL_ERRS {
FATALERR_NETWORKINIT = -8, /* Tox network failed to init */
FATALERR_INFLOOP = -9, /* infinite loop detected */
FATALERR_WININIT = -10, /* window init failed */
FATALERR_PROXY = -11, /* Tox network failed to init using a proxy */
FATALERR_ENCRYPT = -12, /* Data file encryption failure */
} FATAL_ERRS;
/* Fixes text color problem on some terminals.
@ -88,7 +89,7 @@ typedef enum _FATAL_ERRS {
void exit_toxic_success(Tox *m);
void exit_toxic_err(const char *errmsg, int errcode);
int store_data(Tox *m, char *path);
int store_data(Tox *m, const char *path);
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata);
void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata);
@ -108,5 +109,6 @@ void on_file_control(Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t
const uint8_t *data, uint16_t length, void *userdata);
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata);
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata);
void on_read_receipt(Tox *m, int32_t, uint32_t, void *userdata);
#endif /* #define _toxic_h */

View File

@ -100,9 +100,6 @@ void on_action(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t len
void on_nickchange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
{
if (friendnumber < 0 || friendnumber > MAX_FRIENDS_NUM)
return;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
@ -222,6 +219,15 @@ void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_
}
}
void on_read_receipt(Tox *m, int32_t friendnumber, uint32_t receipt, void *userdata)
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onReadReceipt != NULL)
windows[i].onReadReceipt(&windows[i], m, friendnumber, receipt);
}
}
/* CALLBACKS END */
int add_window(Tox *m, ToxWindow w)
@ -371,13 +377,12 @@ void on_window_resize(void)
}
}
static void draw_window_tab(ToxWindow toxwin)
static void draw_window_tab(ToxWindow *toxwin)
{
if (toxwin.alert) attron(COLOR_PAIR(toxwin.alert));
if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert));
clrtoeol();
printw(" [%s]", toxwin.name);
if (toxwin.alert) attroff(COLOR_PAIR(toxwin.alert));
printw(" [%s]", toxwin->name);
if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert));
}
static void draw_bar(void)
@ -407,7 +412,7 @@ static void draw_bar(void)
attron(A_BOLD);
draw_window_tab(windows[i]);
draw_window_tab(&windows[i]);
if (windows + i == active_window)
@ -485,7 +490,7 @@ ToxWindow *get_window_ptr(int i)
{
ToxWindow *toxwin = NULL;
if (windows[i].active)
if (i >= 0 && i <= MAX_WINDOWS_NUM && windows[i].active)
toxwin = &windows[i];
return toxwin;
@ -497,16 +502,17 @@ int get_num_active_windows(void)
}
/* destroys all chat and groupchat windows (should only be called on shutdown) */
void kill_all_windows(void)
void kill_all_windows(Tox *m)
{
kill_prompt_window(prompt);
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].is_chat)
kill_chat_window(&windows[i]);
kill_chat_window(&windows[i], m);
else if (windows[i].is_groupchat)
kill_groupchat_window(&windows[i]);
}
kill_prompt_window(prompt);
kill_friendlist();
}

View File

@ -52,7 +52,7 @@ enum {
BLACK,
} C_COLOURS;
/* tab alert types: lower types take priority */
/* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */
typedef enum {
WINDOW_ALERT_NONE = 0,
WINDOW_ALERT_0 = GREEN,
@ -71,13 +71,27 @@ struct _Winthread {
bool flag_resize;
};
struct _cqueue_thread {
pthread_t tid;
};
struct arg_opts {
int ignore_data_file;
int use_ipv4;
int force_tcp;
int debug;
int default_locale;
int use_custom_data;
int no_connect;
int encrypt_data;
int unencrypt_data;
char dns_path[MAX_STR_SIZE];
char config_path[MAX_STR_SIZE];
char nodes_path[MAX_STR_SIZE];
int use_proxy;
char proxy_address[256];
uint16_t proxy_port;
};
typedef struct ToxWindow ToxWindow;
@ -106,6 +120,7 @@ struct ToxWindow {
void(*onFileControl)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t, uint8_t, const char *, uint16_t);
void(*onFileData)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t);
void(*onReadReceipt)(ToxWindow *, Tox *, int32_t, uint32_t);
#ifdef _AUDIO
@ -199,6 +214,7 @@ struct ChatContext {
struct history *hst;
struct chatlog *log;
struct chat_queue *cqueue;
#ifdef _AUDIO
struct infobox infobox;
@ -223,7 +239,7 @@ int add_window(Tox *m, ToxWindow w);
void del_window(ToxWindow *w);
void set_active_window(int ch);
int get_num_active_windows(void);
void kill_all_windows(void); /* should only be called on shutdown */
void kill_all_windows(Tox *m); /* should only be called on shutdown */
void on_window_resize(void);
ToxWindow *get_window_ptr(int i);