mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-26 22:46:44 +02:00
Compare commits
402 Commits
Author | SHA1 | Date | |
---|---|---|---|
74c1eef1d1 | |||
65c07a57db | |||
ab99c1ac73 | |||
05f5f16af3 | |||
d16be574f3 | |||
7e0b8b4870 | |||
39c4b7ecdd | |||
c5d9aca3e1 | |||
fa0e645a79 | |||
14a8bdb874 | |||
93a73cbef2 | |||
6aab9a79d8 | |||
dfff777283 | |||
a95fc7824c | |||
f707dce2da | |||
0d07d14b13 | |||
6cc1525daa | |||
49f5efaab0 | |||
a5e5e98afc | |||
4ab99c73a0 | |||
e02cf1bb7d | |||
9751cfc407 | |||
36963a5b38 | |||
7cf9c37aef | |||
2b4b8c0289 | |||
368a1465ec | |||
fea317ee24 | |||
8584feab80 | |||
f7e48d294e | |||
928f25bd89 | |||
941ac1d951 | |||
7af9327b37 | |||
6b97df2615 | |||
cea5f1fe04 | |||
abfdbfe468 | |||
462cfca175 | |||
db410cb01e | |||
a920f3edfe | |||
2c3921a9fb | |||
f295352495 | |||
ffcc804efe | |||
69be1bc398 | |||
b4464eda4d | |||
28dd43608d | |||
11701d22a1 | |||
4e2db756be | |||
19cfe3d393 | |||
c546df3917 | |||
ed0a4fb3b8 | |||
271ca08eb2 | |||
0e79b8a076 | |||
1606d01158 | |||
c8a9ac21f3 | |||
e91aaf6c73 | |||
619fdc1098 | |||
b7e613de32 | |||
929fca3de1 | |||
b67792f9f2 | |||
96162bf254 | |||
8a66c3fa4c | |||
2cdcbc07a7 | |||
6e0d19b01d | |||
ad04fa4dcd | |||
c2c612b85a | |||
d359ba6a54 | |||
54e2fe8d6f | |||
53353825e2 | |||
fcdc8e8b67 | |||
9b6efb65de | |||
c8ea02376e | |||
2369b5e9e2 | |||
8f28f1d748 | |||
a33e5f4bec | |||
e0a35a6569 | |||
9863dfc2ae | |||
c755247434 | |||
879b2b236e | |||
c6b9a288b6 | |||
e9e5b5af8d | |||
d175ff2480 | |||
750258adef | |||
60b4d62657 | |||
ea78dca756 | |||
3cb412632b | |||
8301ab1bc2 | |||
b6e90d2ebb | |||
06c268417f | |||
1458a6bbc5 | |||
737d29864b | |||
0a2ad23c15 | |||
a455c80a1f | |||
43bda5f7d9 | |||
f2121fae74 | |||
3241551cfb | |||
6e90072fb8 | |||
ca1fca5aa5 | |||
ef1068b6aa | |||
72982cee97 | |||
9a4eaa8693 | |||
64e7553fb0 | |||
dd8df1df76 | |||
c8d102b02d | |||
a3fa7fd524 | |||
b2ed8c0ead | |||
bbdf4c96b9 | |||
5496890b34 | |||
fd85d8f87b | |||
37e7b4c3d3 | |||
92d76c7f99 | |||
2a787c1097 | |||
327259c4c8 | |||
f173f4275e | |||
48eaf8a14f | |||
083611f18e | |||
48ffae68a9 | |||
c39f8909cd | |||
32e541bd1c | |||
f559bdabfe | |||
0047ba0e9f | |||
ecefc19b23 | |||
e83b397494 | |||
688ea927f8 | |||
904f58c0e8 | |||
035420e5c7 | |||
444d8e7a74 | |||
84a0276668 | |||
312d0c3f42 | |||
d8eca8393c | |||
374b78c763 | |||
409e4ddd96 | |||
1beb35025b | |||
51a1c660b4 | |||
85d3c18ba6 | |||
d0a7ca17d2 | |||
36640224af | |||
231078b6b9 | |||
414f58d896 | |||
4d73f8b241 | |||
82e76a3b5b | |||
0bc610e18d | |||
02e6d2db3c | |||
5a2c341259 | |||
1a7eaeddba | |||
f656d0a722 | |||
09c1ad4566 | |||
8b9e34db75 | |||
dd9186e834 | |||
5ff1517b28 | |||
bbb639c5aa | |||
860db2f612 | |||
523f205646 | |||
e998c8a866 | |||
eaea68c33e | |||
4780cfeafc | |||
bdb0951c84 | |||
e3130c92c0 | |||
12c880ab51 | |||
522aabd4e4 | |||
bf09b3b6c4 | |||
2d3c5c9450 | |||
ae87b2eb2d | |||
c34ecc8dfd | |||
2c2938c647 | |||
22dd883f28 | |||
dfbb1338a5 | |||
f8dc82516e | |||
59b16f7760 | |||
a11289de79 | |||
a64b8cae89 | |||
470eaeb97b | |||
6503349ed3 | |||
ec23cedc3a | |||
ce1ae1d7dc | |||
6669d5632f | |||
59e1114997 | |||
c20510e5aa | |||
2b2e746549 | |||
dc5a8d44e5 | |||
090d3339a0 | |||
442d9e22b4 | |||
0c39e7b158 | |||
3e3f2614b5 | |||
53e4b2c971 | |||
d6c57c7aa3 | |||
3efc984ae9 | |||
7618a0871a | |||
b87f73cc47 | |||
785d39d78f | |||
38a0f6fae4 | |||
710be51cad | |||
50438425e5 | |||
4b4bfe8876 | |||
67da4bdd5b | |||
e230badb47 | |||
d48d9ed4dc | |||
b9fe6bfa10 | |||
5cbbb62ce2 | |||
3369c9d808 | |||
dfc594f949 | |||
d714cda145 | |||
428e1e4100 | |||
2b19f56e63 | |||
4badc983ea | |||
c8a4f88ff3 | |||
eeeaf8d707 | |||
37b3b5a5ed | |||
a561ef8c49 | |||
89f200e870 | |||
2823115a6c | |||
5599d73760 | |||
bac66cb5ad | |||
61c50972e3 | |||
ff0fbbc379 | |||
4a124dea43 | |||
e722b665d1 | |||
4365b8d5ad | |||
e55e5f3f7e | |||
d1036c8538 | |||
9240f62829 | |||
1b89bc9051 | |||
7700ef83e0 | |||
9922b3a4f3 | |||
d561d2e1ac | |||
7865cea284 | |||
424189cad9 | |||
82aa64e8f7 | |||
a0418520a2 | |||
f451d961bc | |||
a4da9fd49e | |||
0a0891fa98 | |||
9464b369a4 | |||
47692edee4 | |||
73c94b25ba | |||
ab2ea5936d | |||
90985af007 | |||
5aad8764b1 | |||
5a175f374a | |||
4acfe84171 | |||
e995a1cb69 | |||
867f041c23 | |||
7e49ba92b2 | |||
2fa53c0531 | |||
673800c947 | |||
835b821b75 | |||
78587ad20b | |||
09bbec79cf | |||
751b5f9943 | |||
4a337ae3cb | |||
1baeb15073 | |||
141b36af04 | |||
4238c20e72 | |||
4e177d60b0 | |||
1bd880708b | |||
76c21c8b34 | |||
dd8c2caac7 | |||
7a7e8a7f8d | |||
43f45d67a4 | |||
adb36a39bb | |||
5a0d9f5e9f | |||
a4e8cb3971 | |||
e22b7a336c | |||
317fa42b20 | |||
884f8dda37 | |||
f5090532fb | |||
ae04dd8fa5 | |||
00e4075937 | |||
d65f3ea58d | |||
008163bf6c | |||
f09002a49a | |||
431290d47a | |||
b66874b7b3 | |||
1b9fd7f936 | |||
0a1457acde | |||
d2008aa92f | |||
e83356faef | |||
fd4161832b | |||
0a132b0eb5 | |||
9c7ac9043a | |||
b86c0d357f | |||
75dd26518f | |||
2cc261c619 | |||
f7d46f1100 | |||
be264528d2 | |||
c825adc1e3 | |||
4e9f125e95 | |||
d26c8fe447 | |||
e42d635195 | |||
a32cd1c19b | |||
14dc02ac83 | |||
e4a28d1839 | |||
0908920b51 | |||
8a882916fb | |||
55ada8ad2f | |||
2094186c0f | |||
d3a489b756 | |||
e6b18231c0 | |||
cc0145d561 | |||
9f0daca5eb | |||
1f2bd44dce | |||
6492bd12f9 | |||
43552161f9 | |||
e5d45fdf1d | |||
bd69c8da18 | |||
8c3e3ebe24 | |||
7eebbd982d | |||
5c66f5c161 | |||
40f70fc1e3 | |||
9cf4427375 | |||
23cf9686cb | |||
70add920fe | |||
c56c6cc219 | |||
e9a0296851 | |||
de30433e26 | |||
fa64fa8b93 | |||
50c7942cb5 | |||
e7697b8fba | |||
b46a77f977 | |||
38909afa89 | |||
5af561c811 | |||
ae984d4f04 | |||
97a8ecd115 | |||
64d782569a | |||
6248baf98b | |||
33cfca7ecc | |||
a01cc35368 | |||
b10eebd77e | |||
05661ca9b6 | |||
784883f773 | |||
a9e5723ca4 | |||
95dfea8d29 | |||
e574af7d68 | |||
2918ca45a2 | |||
6ad744f770 | |||
5b394e6f35 | |||
940af2c711 | |||
544c402f78 | |||
cee9e624b8 | |||
2e65ee3609 | |||
3a176e1cab | |||
00cccad22c | |||
a432d733d7 | |||
893e88294b | |||
b071a9e992 | |||
48cf4ebf02 | |||
773a75b948 | |||
133c0e8d63 | |||
bf54cb36ef | |||
032853b5c9 | |||
650c13ca7a | |||
9d5fe03285 | |||
c3f68b60d3 | |||
1c16467eb9 | |||
3c74385f5c | |||
c07c0028bb | |||
dc3b2e04ab | |||
7fd3aa9164 | |||
4c60312e2d | |||
16e29aa4e0 | |||
bd7b073155 | |||
f25cf870e6 | |||
13291d0365 | |||
9ee7a48910 | |||
daf4614ba6 | |||
919d36369c | |||
618704df76 | |||
9a70dd9651 | |||
e2d310b10f | |||
a1015a366a | |||
913ec7b3fe | |||
4a52b06954 | |||
89f9c07b9e | |||
388d78d11e | |||
a2a23b3932 | |||
f405ae8b42 | |||
cf3f6750eb | |||
4de22d067a | |||
51e274ea38 | |||
0a6ce62363 | |||
13c5de5531 | |||
21f8e7f398 | |||
bcf4a5af90 | |||
09f90d095b | |||
416ebc9ab8 | |||
3ca22aa714 | |||
8dd25e1f0b | |||
5b9bd603ea | |||
3c2c1f15ce | |||
6876df4a45 | |||
1ff97161fb | |||
667410e879 | |||
a862874740 | |||
79bde4e5bf | |||
833b724e9f | |||
96b68058bb | |||
e823233149 | |||
3ac22fafe4 | |||
71f2ac170c | |||
0ef888eea3 | |||
a9b0028a15 | |||
b18e6cff5a | |||
424a1c94d9 | |||
009095af24 |
53
.travis.yml
53
.travis.yml
@ -1,53 +0,0 @@
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
# - clang # Fix me
|
||||
|
||||
before_script:
|
||||
# Installing yasm (needed for compiling vpx) and openal
|
||||
- sudo apt-get -yq install yasm libopenal-dev libconfig-dev libalut-dev
|
||||
# Installing libsodium, needed for toxcore
|
||||
- git clone https://github.com/jedisct1/libsodium.git libsodium
|
||||
- cd libsodium
|
||||
- git checkout tags/0.4.2 > /dev/null
|
||||
- ./autogen.sh > /dev/null
|
||||
- ./configure > /dev/null
|
||||
- make check -j2 || make check || exit 1 > /dev/null
|
||||
- sudo make install > /dev/null
|
||||
- cd ..
|
||||
# Installing libopus, needed for audio encoding/decoding
|
||||
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz
|
||||
- tar xzf opus-1.0.3.tar.gz > /dev/null
|
||||
- cd opus-1.0.3
|
||||
- ./configure > /dev/null
|
||||
- make -j2 || make || exit 1 > /dev/null
|
||||
- sudo make install > /dev/null
|
||||
- cd ..
|
||||
# Installing vpx
|
||||
- git clone http://git.chromium.org/webm/libvpx.git libvpx
|
||||
- cd libvpx
|
||||
- ./configure --enable-shared > /dev/null
|
||||
- make -j2 || make || exit 1 > /dev/null
|
||||
- sudo make install > /dev/null
|
||||
- cd ..
|
||||
# Creating libraries links and updating cache
|
||||
- sudo ldconfig > /dev/null
|
||||
# Installing toxcore
|
||||
- git clone https://github.com/irungentoo/toxcore.git toxcore
|
||||
- cd toxcore
|
||||
- autoreconf -i
|
||||
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
|
||||
- make -j2 || make || exit 1
|
||||
- sudo make install
|
||||
- cd ..
|
||||
script:
|
||||
- cd build
|
||||
- make -j2 || make || exit 1
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#tox-dev"
|
||||
on_success: always
|
||||
on_failure: always
|
70
INSTALL.md
Normal file
70
INSTALL.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Installation
|
||||
* [Dependencies](#deps)
|
||||
* [OS X Notes](#deps_osx)
|
||||
* [Compiling](#compiling)
|
||||
* [Documentation](#docs)
|
||||
* [Notes](#notes)
|
||||
* [Compilation variables](#comp_vars)
|
||||
* [Packaging](#packaging)
|
||||
|
||||
<a name="deps" />
|
||||
## Dependencies
|
||||
| Name | Needed by | Debian package |
|
||||
|------------------------------------------------------|----------------------------|---------------------|
|
||||
| [Tox Core](https://github.com/irungentoo/toxcore) | BASE | *None* |
|
||||
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
|
||||
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
|
||||
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
|
||||
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
|
||||
| [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev |
|
||||
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO | *None* |
|
||||
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
|
||||
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
|
||||
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
|
||||
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
|
||||
<sup>1</sup>: see [Documentation](#docs)
|
||||
|
||||
<a name="deps_osx" />
|
||||
#### OS X Notes
|
||||
Using [Homebrew](http://brew.sh):
|
||||
```
|
||||
brew install openal-soft freealut libconfig
|
||||
brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
|
||||
brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb
|
||||
```
|
||||
|
||||
You can omit `libnotify` if you intend to build without desktop notifications enabled.
|
||||
|
||||
<a name="Compiling">
|
||||
## Compiling
|
||||
```
|
||||
make PREFIX="/where/to/install"
|
||||
sudo make install PREFIX="/where/to/install"
|
||||
```
|
||||
|
||||
<a name="docs" />
|
||||
#### Documentation
|
||||
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
|
||||
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br />
|
||||
**NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
|
||||
|
||||
<a name="notes" />
|
||||
## Notes
|
||||
|
||||
<a name="comp_vars" />
|
||||
#### Compilation variables
|
||||
* 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
|
||||
|
||||
<a name="packaging" />
|
||||
#### Packaging
|
||||
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
|
||||
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
|
||||
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
|
||||
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
|
||||
|
84
Makefile
Normal file
84
Makefile
Normal file
@ -0,0 +1,84 @@
|
||||
BASE_DIR = $(shell pwd -P)
|
||||
CFG_DIR = $(BASE_DIR)/cfg
|
||||
|
||||
-include $(CFG_DIR)/global_vars.mk
|
||||
|
||||
LIBS = libtoxcore ncursesw libconfig libqrencode
|
||||
|
||||
CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all
|
||||
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
|
||||
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
|
||||
CFLAGS += $(USER_CFLAGS)
|
||||
LDFLAGS = $(USER_LDFLAGS)
|
||||
|
||||
OBJ = chat.o chat_commands.o configdir.o execute.o file_transfers.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 message_queue.o
|
||||
OBJ += group_commands.o term_mplex.o avatars.o name_lookup.o qr_code.o
|
||||
|
||||
# Check on wich system we are running
|
||||
UNAME_S = $(shell uname -s)
|
||||
ifeq ($(UNAME_S), Linux)
|
||||
-include $(CFG_DIR)/systems/Linux.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), FreeBSD)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), DragonFly)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), OpenBSD)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), Darwin)
|
||||
-include $(CFG_DIR)/systems/Darwin.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), Solaris)
|
||||
-include $(CFG_DIR)/systems/Solaris.mk
|
||||
endif
|
||||
|
||||
# Check on which platform we are running
|
||||
UNAME_M = $(shell uname -m)
|
||||
ifeq ($(UNAME_M), x86_64)
|
||||
-include $(CFG_DIR)/platforms/x86_64.mk
|
||||
endif
|
||||
ifneq ($(filter %86, $(UNAME_M)),)
|
||||
-include $(CFG_DIR)/platforms/x86.mk
|
||||
endif
|
||||
ifneq ($(filter arm%, $(UNAME_M)),)
|
||||
-include $(CFG_DIR)/platforms/arm.mk
|
||||
endif
|
||||
|
||||
# Include all needed checks
|
||||
-include $(CFG_DIR)/checks/check_features.mk
|
||||
|
||||
# Fix path for object files
|
||||
OBJ := $(addprefix $(BUILD_DIR)/, $(OBJ))
|
||||
|
||||
# Targets
|
||||
all: $(BUILD_DIR)/toxic
|
||||
|
||||
$(BUILD_DIR)/toxic: $(OBJ)
|
||||
@echo " LD $(@:$(BUILD_DIR)/%=%)"
|
||||
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS)
|
||||
|
||||
$(BUILD_DIR)/osx_video.o: $(SRC_DIR)/$(OSX_VIDEO)
|
||||
@echo " CC $(@:$(BUILD_DIR)/)osx_video.o"
|
||||
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/osx_video.o -c $(SRC_DIR)/$(OSX_VIDEO)
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
@if [ ! -e $(BUILD_DIR) ]; then \
|
||||
mkdir -p $(BUILD_DIR) ;\
|
||||
fi
|
||||
@echo " CC $(@:$(BUILD_DIR)/%=%)"
|
||||
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c
|
||||
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d
|
||||
|
||||
clean:
|
||||
rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic
|
||||
|
||||
-include $(BUILD_DIR)/$(OBJ:.o=.d)
|
||||
|
||||
-include $(CFG_DIR)/targets/*.mk
|
||||
|
||||
.PHONY: clean all
|
66
README.md
66
README.md
@ -1,52 +1,21 @@
|
||||
# Toxic [](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.
|
||||
<a href="https://scan.coverity.com/projects/toxic-tox">
|
||||
<img alt="Coverity Scan Build Status"
|
||||
src="https://scan.coverity.com/projects/4975/badge.svg"/>
|
||||
</a>
|
||||
|
||||

|
||||
Toxic is a [Tox](https://tox.chat)-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.
|
||||
|
||||
[](https://i.imgur.com/san99Z2.png)
|
||||
|
||||
## Installation
|
||||
[Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br />
|
||||
[Compile it yourself](/INSTALL.md)
|
||||
|
||||
### Dependencies
|
||||
##### Base
|
||||
* [libtoxcore](https://github.com/irungentoo/toxcore)
|
||||
* [ncurses](https://www.gnu.org/software/ncurses) (for Debian based systems, 'libncursesw5-dev')
|
||||
* [libconfig](http://www.hyperrealm.com/libconfig) (for Debian based systems, 'libconfig-dev')
|
||||
## Settings
|
||||
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.
|
||||
You can view our example config file [here](misc/toxic.conf.example).
|
||||
|
||||
##### Audio
|
||||
* libtoxav ([libtoxcore](https://github.com/irungentoo/toxcore) compiled with audio support)
|
||||
* [openal](http://openal.org) (for Debian based systems, 'libopenal-dev')
|
||||
|
||||
##### Sound notifications
|
||||
* [openal](http://openal.org) (for Debian based systems, 'libopenal-dev')
|
||||
* [openalut](http://openal.org) (for Debian based systems, 'libalut-dev')
|
||||
|
||||
##### 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"`
|
||||
3. `sudo make install PREFIX="/where/to/install"`
|
||||
|
||||
### Compilation Notes
|
||||
* 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_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
|
||||
|
||||
### Packaging
|
||||
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
|
||||
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
|
||||
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
|
||||
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
|
||||
|
||||
### Troubleshooting
|
||||
## Troubleshooting
|
||||
If your default prefix is "/usr/local" and you receive the following:
|
||||
```
|
||||
error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory
|
||||
@ -57,12 +26,3 @@ echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
|
||||
sudo ldconfig
|
||||
```
|
||||
|
||||
## Precompiled binaries
|
||||
You can download precompiled binaries from [jenkins](https://jenkins.libtoxcore.so):
|
||||
* [Linux 32 bit](https://jenkins.libtoxcore.so/job/toxic_linux_i386/lastSuccessfulBuild/artifact/toxic_linux_i386.tar.xz)
|
||||
* [Linux 64 bit](https://jenkins.libtoxcore.so/job/toxic_linux_amd64/lastSuccessfulBuild/artifact/toxic_linux_amd64.tar.xz)
|
||||
|
||||
## Settings
|
||||
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.
|
||||
You can view our example config file [here](misc/toxic.conf.example).
|
||||
|
||||
|
@ -1,67 +0,0 @@
|
||||
BASE_DIR = $(shell cd .. && pwd -P)
|
||||
CFG_DIR = $(BASE_DIR)/cfg
|
||||
|
||||
-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 += -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
|
||||
|
||||
# Check on wich system we are running
|
||||
UNAME_S = $(shell uname -s)
|
||||
ifeq ($(UNAME_S), Linux)
|
||||
-include $(CFG_DIR)/systems/Linux.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), FreeBSD)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), Darwin)
|
||||
-include $(CFG_DIR)/systems/Darwin.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), Solaris)
|
||||
-include $(CFG_DIR)/systems/Solaris.mk
|
||||
endif
|
||||
|
||||
# Check on which platform we are running
|
||||
UNAME_M = $(shell uname -m)
|
||||
ifeq ($(UNAME_M), x86_64)
|
||||
-include $(CFG_DIR)/platforms/x86_64.mk
|
||||
endif
|
||||
ifneq ($(filter %86, $(UNAME_M)),)
|
||||
-include $(CFG_DIR)/platforms/x86.mk
|
||||
endif
|
||||
ifneq ($(filter arm%, $(UNAME_M)),)
|
||||
-include $(CFG_DIR)/platforms/arm.mk
|
||||
endif
|
||||
|
||||
# Include all needed checks
|
||||
-include $(CFG_DIR)/checks/check_features.mk
|
||||
|
||||
# Targets
|
||||
all: toxic doc
|
||||
|
||||
toxic: $(OBJ)
|
||||
@echo " LD $@"
|
||||
@$(CC) $(CFLAGS) -o toxic $(OBJ) $(LDFLAGS)
|
||||
|
||||
%.o: $(SRC_DIR)/%.c
|
||||
@echo " CC $@"
|
||||
@$(CC) $(CFLAGS) -o $*.o -c $(SRC_DIR)/$*.c
|
||||
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $*.d
|
||||
|
||||
clean:
|
||||
rm -rf *.d *.o toxic
|
||||
|
||||
-include $(OBJ:.o=.d)
|
||||
|
||||
-include $(CFG_DIR)/targets/*.mk
|
||||
|
||||
.PHONY: clean all
|
21
cfg/checks/audio.mk
Normal file
21
cfg/checks/audio.mk
Normal file
@ -0,0 +1,21 @@
|
||||
# Variables for audio call support
|
||||
AUDIO_LIBS = libtoxav openal
|
||||
AUDIO_CFLAGS = -DAUDIO
|
||||
ifneq (, $(findstring audio_device.o, $(OBJ)))
|
||||
AUDIO_OBJ = audio_call.o
|
||||
else
|
||||
AUDIO_OBJ = audio_call.o audio_device.o
|
||||
endif
|
||||
|
||||
# Check if we can build audio support
|
||||
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) --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))
|
||||
endif
|
@ -1,23 +0,0 @@
|
||||
# Variables for audio call support
|
||||
AUDIO_LIBS = libtoxav openal
|
||||
AUDIO_CFLAGS = -D_AUDIO
|
||||
ifneq (, $(findstring device.o, $(OBJ)))
|
||||
AUDIO_OBJ = audio_call.o
|
||||
else
|
||||
AUDIO_OBJ = audio_call.o device.o
|
||||
endif
|
||||
|
||||
# Check if we can build audio support
|
||||
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 --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))
|
||||
endif
|
||||
endif
|
@ -1,41 +1,48 @@
|
||||
CHECKS_DIR = $(CFG_DIR)/checks
|
||||
|
||||
# Check if we can use X11
|
||||
CHECK_X11_LIBS = $(shell pkg-config --exists x11 || echo -n "error")
|
||||
ifneq ($(CHECK_X11_LIBS), error)
|
||||
LIBS += x11
|
||||
CFLAGS += -D_X11
|
||||
# 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 $(CHECKS_DIR)/av.mk
|
||||
-include $(CHECKS_DIR)/audio.mk
|
||||
endif
|
||||
|
||||
# Check if we want build video support
|
||||
VIDEO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(X11), disabled)
|
||||
ifneq ($(AUDIO), disabled)
|
||||
ifneq ($(VIDEO), disabled)
|
||||
-include $(CHECKS_DIR)/video.mk
|
||||
endif
|
||||
endif
|
||||
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 $(CHECKS_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 $(CHECKS_DIR)/desktop_notifications.mk
|
||||
-include $(CHECKS_DIR)/desktop_notifications.mk
|
||||
endif
|
||||
|
||||
# Check if we can build Toxic
|
||||
CHECK_LIBS = $(shell pkg-config --exists $(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 --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning ERROR -- Cannot compile Toxic)
|
||||
$(warning ERROR -- You need these libraries)
|
||||
$(warning ERROR -- $(MISSING_LIBS))
|
||||
$(error ERROR)
|
||||
endif
|
||||
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) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning ERROR -- Cannot compile Toxic)
|
||||
$(warning ERROR -- You need these libraries)
|
||||
$(warning ERROR -- $(MISSING_LIBS))
|
||||
$(error ERROR)
|
||||
endif
|
||||
|
@ -1,17 +1,15 @@
|
||||
# Variables for desktop notifications support
|
||||
DESK_NOTIFY_LIBS = libnotify
|
||||
DESK_NOTIFY_CFLAGS = -D_BOX_NOTIFY
|
||||
DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY
|
||||
|
||||
# Check if we can build desktop notifications support
|
||||
CHECK_DESK_NOTIFY_LIBS = $(shell pkg-config --exists $(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 --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))
|
||||
endif
|
||||
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) --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))
|
||||
endif
|
||||
|
@ -1,23 +1,21 @@
|
||||
# Variables for sound notifications support
|
||||
SND_NOTIFY_LIBS = openal freealut
|
||||
SND_NOTIFY_CFLAGS = -D_SOUND_NOTIFY
|
||||
ifneq (, $(findstring device.o, $(OBJ)))
|
||||
SND_NOTIFY_OBJ =
|
||||
SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY
|
||||
ifneq (, $(findstring audio_device.o, $(OBJ)))
|
||||
SND_NOTIFY_OBJ =
|
||||
else
|
||||
SND_NOTIFY_OBJ = device.o
|
||||
SND_NOTIFY_OBJ = audio_device.o
|
||||
endif
|
||||
|
||||
# Check if we can build sound notifications support
|
||||
CHECK_SND_NOTIFY_LIBS = $(shell pkg-config --exists $(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 --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))
|
||||
endif
|
||||
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) --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))
|
||||
endif
|
||||
|
21
cfg/checks/video.mk
Normal file
21
cfg/checks/video.mk
Normal file
@ -0,0 +1,21 @@
|
||||
# Variables for video call support
|
||||
VIDEO_LIBS = libtoxav vpx x11
|
||||
VIDEO_CFLAGS = -DVIDEO
|
||||
ifneq (, $(findstring video_device.o, $(OBJ)))
|
||||
VIDEO_OBJ = video_call.o
|
||||
else
|
||||
VIDEO_OBJ = video_call.o video_device.o
|
||||
endif
|
||||
|
||||
# Check if we can build video support
|
||||
CHECK_VIDEO_LIBS = $(shell pkg-config --exists $(VIDEO_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_VIDEO_LIBS), error)
|
||||
LIBS += $(VIDEO_LIBS)
|
||||
CFLAGS += $(VIDEO_CFLAGS)
|
||||
OBJ += $(VIDEO_OBJ)
|
||||
else ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning WARNING -- Toxic will be compiled without video support)
|
||||
$(warning WARNING -- You will need these libraries for video support)
|
||||
$(warning WARNING -- $(MISSING_VIDEO_LIBS))
|
||||
endif
|
17
cfg/checks/x11.mk
Normal file
17
cfg/checks/x11.mk
Normal file
@ -0,0 +1,17 @@
|
||||
# Variables for X11 support
|
||||
X11_LIBS = x11
|
||||
X11_CFLAGS = -DX11
|
||||
X11_OBJ = xtra.o
|
||||
|
||||
# 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)
|
||||
OBJ += $(X11_OBJ)
|
||||
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 and drag&drop support))
|
||||
$(warning WARNING -- You need these libraries for x11 support)
|
||||
$(warning WARNING -- $(MISSING_X11_LIBS))
|
||||
endif
|
@ -1,9 +1,14 @@
|
||||
# Version
|
||||
TOXIC_VERSION = 0.5.0
|
||||
REV = $(shell git rev-list HEAD --count)
|
||||
VERSION = $(TOXIC_VERSION)_r$(REV)
|
||||
TOXIC_VERSION = 0.7.0
|
||||
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
||||
ifneq (, $(findstring error, $(REV)))
|
||||
VERSION = $(TOXIC_VERSION)
|
||||
else
|
||||
VERSION = $(TOXIC_VERSION)_r$(REV)
|
||||
endif
|
||||
|
||||
# Project directories
|
||||
BUILD_DIR = $(BASE_DIR)/build
|
||||
DOC_DIR = $(BASE_DIR)/doc
|
||||
SRC_DIR = $(BASE_DIR)/src
|
||||
SND_DIR = $(BASE_DIR)/sounds
|
||||
@ -11,13 +16,18 @@ 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
|
||||
DATAFILES = DHTnodes nameservers toxic.conf.example
|
||||
DESKFILE = toxic.desktop
|
||||
SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav
|
||||
SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
|
||||
SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav
|
||||
|
||||
# Install directories
|
||||
PREFIX = /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
DATADIR = $(PREFIX)/share/toxic
|
||||
MANDIR = $(PREFIX)/share/man
|
||||
APPDIR = $(PREFIX)/share/applications
|
||||
|
||||
# Platform tools
|
||||
PKG_CONFIG = pkg-config
|
||||
|
18
cfg/systems/Darwin.mk
Normal file
18
cfg/systems/Darwin.mk
Normal file
@ -0,0 +1,18 @@
|
||||
# Special options for OS X
|
||||
# This assumes the use of Homebrew. Change the paths if using MacPorts or Fink.
|
||||
|
||||
PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/opt/libconfig/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig)
|
||||
|
||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||
|
||||
# OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw.
|
||||
LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -lcurl -lconfig -ltoxencryptsave -g
|
||||
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
|
||||
|
||||
OSX_LIBRARIES = -lobjc -lresolv
|
||||
OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \
|
||||
-framework QuartzCore -framework CoreMedia
|
||||
OSX_VIDEO = osx_video.m
|
||||
|
||||
LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS)
|
||||
OBJ += osx_video.o
|
@ -1,4 +1,4 @@
|
||||
# Specials options for freebsd systems
|
||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||
LDFLAGS += -lncursesw
|
||||
LDFLAGS += -lncursesw -lcurl
|
||||
MANDIR = $(PREFIX)/man
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Specials options for linux systems
|
||||
CFLAGS +=
|
||||
LDFLAGS += -ldl -lresolv
|
||||
LDFLAGS += -ldl -lrt -lcurl
|
||||
MANDIR = $(PREFIX)/share/man
|
||||
|
@ -1,14 +1,16 @@
|
||||
# Help target
|
||||
help:
|
||||
@echo "-- Targets --"
|
||||
@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 " 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 " uninstall: Remove toxic from 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"
|
||||
|
@ -1,31 +1,40 @@
|
||||
# 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))
|
||||
|
||||
install: $(BUILD_DIR)/toxic
|
||||
@echo "Installing toxic executable"
|
||||
@install -m 0755 toxic $(abspath $(DESTDIR)/$(BINDIR))
|
||||
|
||||
@mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
|
||||
@install -m 0755 $(BUILD_DIR)/toxic $(abspath $(DESTDIR)/$(BINDIR)/toxic)
|
||||
|
||||
@echo "Installing desktop file"
|
||||
@mkdir -p $(abspath $(DESTDIR)/$(APPDIR))
|
||||
@install -m 0644 $(MISC_DIR)/$(DESKFILE) $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
|
||||
|
||||
@echo "Installing data files"
|
||||
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
|
||||
@for f in $(DATAFILES) ; do \
|
||||
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)) ;\
|
||||
file=$(abspath $(DESTDIR)/$(DATADIR))/$$f ;\
|
||||
sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\
|
||||
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||
file=$(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
|
||||
mv temp_file $$file ;\
|
||||
done
|
||||
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
|
||||
@for f in $(SNDFILES) ; do \
|
||||
install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR))/sounds ;\
|
||||
install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
|
||||
done
|
||||
|
||||
|
||||
@echo "Installing man pages"
|
||||
@mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
|
||||
@for f in $(MANFILES) ; do \
|
||||
if [ ! -e "$(DOC_DIR)/$$f" ]; then \
|
||||
continue ;\
|
||||
fi ;\
|
||||
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
||||
file=$$section/$$f ;\
|
||||
mkdir -p $$section ;\
|
||||
install -m 0644 $(DOC_DIR)/$$f $$file ;\
|
||||
sed -i'' -e 's:__VERSION__:'$(VERSION)':g' $$file ;\
|
||||
sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\
|
||||
sed -e 's:__VERSION__:'$(VERSION)':g' $$file > temp_file && \
|
||||
mv temp_file $$file ;\
|
||||
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
|
||||
mv temp_file $$file ;\
|
||||
gzip -f -9 $$file ;\
|
||||
done
|
||||
|
||||
|
24
cfg/targets/uninstall.mk
Normal file
24
cfg/targets/uninstall.mk
Normal file
@ -0,0 +1,24 @@
|
||||
# Uninstall target
|
||||
uninstall:
|
||||
@echo "Removing toxic executable"
|
||||
@rm -f $(abspath $(DESTDIR)/$(BINDIR)/toxic)
|
||||
|
||||
@echo "Removing desktop file"
|
||||
@rm -f $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
|
||||
|
||||
@echo "Removing data files"
|
||||
@for f in $(DATAFILES) ; do \
|
||||
rm -f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||
done
|
||||
@for f in $(SNDFILES) ; do \
|
||||
rm -f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
|
||||
done
|
||||
|
||||
@echo "Removing man pages"
|
||||
@for f in $(MANFILES) ; do \
|
||||
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
||||
file=$$section/$$f ;\
|
||||
rm -f $$file $$file.gz ;\
|
||||
done
|
||||
|
||||
.PHONY: uninstall
|
33
doc/toxic.1
33
doc/toxic.1
@ -2,12 +2,12 @@
|
||||
.\" Title: toxic
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2014-08-21
|
||||
.\" Date: 2015-07-08
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC" "1" "2014\-08\-21" "toxic __VERSION__" "Toxic Manual"
|
||||
.TH "TOXIC" "1" "2015\-07\-08" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
@ -55,11 +55,16 @@ instead of
|
||||
\fI~/\&.config/tox/toxic\&.conf\fR
|
||||
.RE
|
||||
.PP
|
||||
\-d, \-\-default_locale
|
||||
\-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
|
||||
@ -86,14 +91,19 @@ for DHT bootstrap nodes, instead of
|
||||
Do not connect to the DHT network
|
||||
.RE
|
||||
.PP
|
||||
\-p, \-\-proxy
|
||||
\-p, \-\-SOCKS5\-proxy
|
||||
.RS 4
|
||||
Use proxy: Requires [IP] [port]
|
||||
Use a SOCKS5 proxy: Requires [IP] [port]
|
||||
.RE
|
||||
.PP
|
||||
\-r, \-\-dnslist
|
||||
\-P, \-\-HTTP\-proxy
|
||||
.RS 4
|
||||
Use specified DNSservers file
|
||||
Use a HTTP proxy: Requires [IP] [port]
|
||||
.RE
|
||||
.PP
|
||||
\-r, \-\-namelist
|
||||
.RS 4
|
||||
Use specified nameservers list
|
||||
.RE
|
||||
.PP
|
||||
\-t, \-\-force\-tcp
|
||||
@ -101,9 +111,14 @@ Use specified DNSservers file
|
||||
Force TCP connection (use this with proxies)
|
||||
.RE
|
||||
.PP
|
||||
\-x, \-\-nodata
|
||||
\-T, \-\-tcp\-relay
|
||||
.RS 4
|
||||
Ignore data file
|
||||
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
|
||||
.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
|
||||
.SH "FILES"
|
||||
.PP
|
||||
|
@ -27,11 +27,15 @@ OPTIONS
|
||||
-c, --config config-file::
|
||||
Use specified 'config-file' instead of '~/.config/tox/toxic.conf'
|
||||
|
||||
-d, --default_locale::
|
||||
-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'
|
||||
Use specified 'data-file' instead of '~/.config/tox/data'
|
||||
|
||||
-h, --help::
|
||||
Show help message
|
||||
@ -43,17 +47,24 @@ OPTIONS
|
||||
-o, --noconnect::
|
||||
Do not connect to the DHT network
|
||||
|
||||
-p, --proxy::
|
||||
Use proxy: Requires [IP] [port]
|
||||
-p, --SOCKS5-proxy::
|
||||
Use a SOCKS5 proxy: Requires [IP] [port]
|
||||
|
||||
-r, --dnslist::
|
||||
Use specified DNSservers file
|
||||
-P, --HTTP-proxy::
|
||||
Use a HTTP proxy: Requires [IP] [port]
|
||||
|
||||
-r, --namelist::
|
||||
Use specified nameservers list
|
||||
|
||||
-t, --force-tcp::
|
||||
Force TCP connection (use this with proxies)
|
||||
|
||||
-x, --nodata::
|
||||
Ignore data file
|
||||
-T, --tcp-relay::
|
||||
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
|
||||
|
||||
-u, --unencrypt-data::
|
||||
Unencrypt a data file. A warning will appear if this option is used
|
||||
with a data file that is already unencrypted.
|
||||
|
||||
FILES
|
||||
-----
|
||||
|
@ -2,12 +2,12 @@
|
||||
.\" Title: toxic.conf
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2014-08-21
|
||||
.\" Date: 2015-03-28
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC\&.CONF" "5" "2014\-08\-21" "toxic __VERSION__" "Toxic Manual"
|
||||
.TH "TOXIC\&.CONF" "5" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
@ -66,6 +66,23 @@ Configuration related to interface elements\&.
|
||||
Enable or disable timestamps\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBtime_format\fR
|
||||
.RS 4
|
||||
Select between 24 and 12 hour time\&. Specify 24 or 12\&. Setting timestamp_format and log_timestamp_format will override this setting\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBtimestamp_format\fR
|
||||
.RS 4
|
||||
Time format string for the interface enclosed by double quotes\&. See
|
||||
\fBdate\fR(1)
|
||||
.RE
|
||||
.PP
|
||||
\fBlog_timestamp_format\fR
|
||||
.RS 4
|
||||
Time format string for logging enclosed by double quotes\&. See
|
||||
\fBdate\fR(1)
|
||||
.RE
|
||||
.PP
|
||||
\fBalerts\fR
|
||||
.RS 4
|
||||
Enable or disable terminal alerts on events\&. true or false
|
||||
@ -81,11 +98,6 @@ Select between native terminal colors and toxic color theme\&. true or false
|
||||
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
|
||||
@ -96,10 +108,45 @@ Show when others are typing in a 1\-on\-1 chat\&. true or false
|
||||
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
|
||||
.PP
|
||||
\fBline_join\fR
|
||||
.RS 4
|
||||
Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBline_quit\fR
|
||||
.RS 4
|
||||
Indicator for when someone disconnects or leaves a group\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBline_alert\fR
|
||||
.RS 4
|
||||
Indicator for alert messages\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBline_normal\fR
|
||||
.RS 4
|
||||
Indicator for normal messages\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBmplex_away\fR
|
||||
.RS 4
|
||||
Set user status when attaching and detaching from GNU screen or tmux\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBmplex_away_note\fR
|
||||
.RS 4
|
||||
Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\fBaudio\fR
|
||||
@ -133,6 +180,11 @@ Configuration related to paths\&.
|
||||
Default path for downloads\&. String value\&. Absolute path for downloaded files\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBavatar_path\fR
|
||||
.RS 4
|
||||
Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
|
||||
.RE
|
||||
.PP
|
||||
\fBchatlogs_path\fR
|
||||
.RS 4
|
||||
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
|
||||
@ -145,7 +197,7 @@ Configuration related to notification sounds\&. Special value "silent" can be us
|
||||
|
||||
Each value is a string which corresponds to the absolute path of a wav sound file\&.
|
||||
.PP
|
||||
\fBerror\fR
|
||||
\fBnotif_error\fR
|
||||
.RS 4
|
||||
Sound to play when an error occurs\&.
|
||||
.RE
|
||||
@ -246,6 +298,11 @@ Key combination to scroll contacts list up\&.
|
||||
.RS 4
|
||||
Key combination to scroll contacts list down\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBtoggle_peerlist\fR
|
||||
.RS 4
|
||||
Toggle the peer list on and off\&.
|
||||
.RE
|
||||
.RE
|
||||
.SH "FILES"
|
||||
.PP
|
||||
|
@ -42,6 +42,18 @@ OPTIONS
|
||||
*timestamps*;;
|
||||
Enable or disable timestamps. true or false
|
||||
|
||||
*time_format*;;
|
||||
Select between 24 and 12 hour time. Specify 24 or 12. Setting
|
||||
timestamp_format and log_timestamp_format will override this setting.
|
||||
|
||||
*timestamp_format*;;
|
||||
Time format string for the interface enclosed by double quotes.
|
||||
See *date*(1)
|
||||
|
||||
*log_timestamp_format*;;
|
||||
Time format string for logging enclosed by double quotes.
|
||||
See *date*(1)
|
||||
|
||||
*alerts*;;
|
||||
Enable or disable terminal alerts on events. true or false
|
||||
|
||||
@ -51,18 +63,39 @@ OPTIONS
|
||||
*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)
|
||||
|
||||
*line_join*;;
|
||||
Indicator for when someone connects or joins a group.
|
||||
Three characters max for line_ settings.
|
||||
|
||||
*line_quit*;;
|
||||
Indicator for when someone disconnects or leaves a group.
|
||||
|
||||
*line_alert*;;
|
||||
Indicator for alert messages.
|
||||
|
||||
*line_normal*;;
|
||||
Indicator for normal messages.
|
||||
|
||||
*mplex_away*;;
|
||||
Set user status when attaching and detaching from GNU screen or tmux.
|
||||
true or false
|
||||
|
||||
*mplex_away_note*;;
|
||||
Status message to set when status is set to away due to screen/tmux
|
||||
detach. When attaching, the status message is set back to the original
|
||||
value.
|
||||
|
||||
*audio*::
|
||||
Configuration related to audio devices.
|
||||
@ -84,6 +117,9 @@ OPTIONS
|
||||
Default path for downloads. String value. Absolute path for downloaded
|
||||
files.
|
||||
|
||||
*avatar_path*;;
|
||||
Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
|
||||
|
||||
*chatlogs_path*;;
|
||||
Default path for chatlogs. String value. Absolute path for chatlog files.
|
||||
|
||||
@ -93,7 +129,7 @@ OPTIONS
|
||||
Each value is a string which corresponds to the absolute path of a wav
|
||||
sound file.
|
||||
|
||||
*error*;;
|
||||
*notif_error*;;
|
||||
Sound to play when an error occurs.
|
||||
|
||||
*self_log_in*;;
|
||||
@ -155,6 +191,9 @@ OPTIONS
|
||||
*peer_list_down*;;
|
||||
Key combination to scroll contacts list down.
|
||||
|
||||
*toggle_peerlist*;;
|
||||
Toggle the peer list on and off.
|
||||
|
||||
|
||||
FILES
|
||||
-----
|
||||
|
@ -1,10 +1,17 @@
|
||||
192.254.75.98 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
|
||||
37.187.46.132 33445 A9D98212B3F972BD11DA52BEB0658C326FCCC1BFD49F347F9C2D3D8B61E1B927
|
||||
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
|
||||
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
||||
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
|
||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
37.59.102.176 33445 B98A2CEAA6C6A2FADC2C3632D284318B60FE5375CCB41EFA081AB67F500C1B0B
|
||||
95.85.13.245 33445 7187969BB10B54C98538BAE94C069CE5C84E650D54F7E596543D8FB1ECF4CF23
|
||||
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
||||
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
|
||||
76.191.23.96 33445 4BA57660DE3E854C530EED601BF8D54B7EFAE960523B6CFC10210CC08E2CB808
|
||||
195.154.119.113 33445 E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354
|
||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
46.38.239.179 33445 F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A
|
||||
178.62.250.138 33445 788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B
|
||||
130.133.110.14 33445 461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F
|
||||
104.167.101.29 33445 5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57
|
||||
205.185.116.116 33445 A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702
|
||||
198.98.51.198 33445 1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F
|
||||
80.232.246.79 33445 A7A060D553B017D9D8F038E265C7AFB6C70BAAC55070197F9C007432D0038E0F
|
||||
108.61.165.198 33445 8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832
|
||||
212.71.252.109 33445 C4CEB8C7AC607C6B374E2E782B3C00EA3A63B80D4910B8649CCACDD19F260819
|
||||
194.249.212.109 33445 3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B
|
||||
185.25.116.107 33445 DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43
|
||||
192.99.168.140 33445 6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F
|
||||
|
@ -1,2 +0,0 @@
|
||||
utox.org d3154f65d28a5b41a05d4ac7e4b39c6b1c233cc857fb365c56e8392737462a12
|
||||
toxme.se 5d72c517df6aec54f1e977a6b6f25914ea4cf7277a85027cd9f5196df17e0b13
|
2
misc/nameservers
Normal file
2
misc/nameservers
Normal file
@ -0,0 +1,2 @@
|
||||
toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25
|
||||
|
@ -17,14 +17,38 @@ ui = {
|
||||
// 24 or 12 hour time
|
||||
time_format=24;
|
||||
|
||||
// timestamp format string according to date/strftime format. Overrides time_format setting
|
||||
timestamp_format="%H:%M:%S";
|
||||
|
||||
// true to show you when others are typing a message in 1-on-1 chats
|
||||
show_typing_other=true;
|
||||
|
||||
// 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;
|
||||
|
||||
// Indicator for display when someone connects or joins a group.
|
||||
line_join="-->";
|
||||
|
||||
// Indicator for display when someone disconnects or leaves a group.
|
||||
line_quit="<--";
|
||||
|
||||
// Indicator for alert messages.
|
||||
line_alert="-!-";
|
||||
|
||||
// Indicator for normal messages.
|
||||
line_normal="---";
|
||||
|
||||
// true to change status based on screen/tmux attach/detach, false to disable
|
||||
mplex_away=true;
|
||||
|
||||
// Status message to use when status set to away due to screen/tmux detach
|
||||
mplex_away_note="Away from keyboard, be back soon!"
|
||||
};
|
||||
|
||||
audio = {
|
||||
@ -42,22 +66,23 @@ tox = {
|
||||
// Path for downloaded files
|
||||
// download_path="/home/USERNAME/Downloads/";
|
||||
|
||||
// Path for your avatar (file must be a .png and cannot exceed 64 KiB)
|
||||
// avatar_path="/home/USERNAME/Pictures/youravatar.png";
|
||||
|
||||
// Path for chatlogs
|
||||
// chatlogs_path="/home/USERNAME/toxic_chatlogs/";
|
||||
};
|
||||
|
||||
// To disable a sound set the path to "silent"
|
||||
sounds = {
|
||||
error="__DATADIR__/sounds/Error.wav";
|
||||
self_log_in="__DATADIR__/sounds/LogIn.wav";
|
||||
self_log_out="__DATADIR__/sounds/LogOut.wav";
|
||||
user_log_in="__DATADIR__/sounds/ContactLogsIn.wav";
|
||||
user_log_out="__DATADIR__/sounds/ContactLogsOut.wav";
|
||||
call_incoming="__DATADIR__/sounds/IncomingCall.wav";
|
||||
call_outgoing="__DATADIR__/sounds/OutgoingCall.wav";
|
||||
generic_message="__DATADIR__/sounds/NewMessage.wav";
|
||||
transfer_pending="__DATADIR__/sounds/TransferPending.wav";
|
||||
transfer_completed="__DATADIR__/sounds/TransferComplete.wav";
|
||||
error="__DATADIR__/sounds/ToxicError.wav";
|
||||
user_log_in="__DATADIR__/sounds/ToxicContactOnline.wav";
|
||||
user_log_out="__DATADIR__/sounds/ToxicContactOffline.wav";
|
||||
call_incoming="__DATADIR__/sounds/ToxicIncomingCall.wav";
|
||||
call_outgoing="__DATADIR__/sounds/ToxicOutgoingCall.wav";
|
||||
generic_message="__DATADIR__/sounds/ToxicRecvMessage.wav";
|
||||
transfer_pending="__DATADIR__/sounds/ToxicTransferStart.wav";
|
||||
transfer_completed="__DATADIR__/sounds/ToxicTransferComplete.wav";
|
||||
};
|
||||
|
||||
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
|
||||
@ -72,5 +97,6 @@ keys = {
|
||||
page_bottom="Ctrl+H";
|
||||
peer_list_up="Ctrl+[";
|
||||
peer_list_down="Ctrl+]";
|
||||
toggle_peerlist="Ctrl+b";
|
||||
};
|
||||
|
||||
|
11
misc/toxic.desktop
Normal file
11
misc/toxic.desktop
Normal file
@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=Toxic
|
||||
Comment=A CLI based Tox client
|
||||
TryExec=toxic
|
||||
Exec=toxic
|
||||
Icon=utilities-terminal
|
||||
Categories=InstantMessaging;AudioVideo;Network;
|
||||
Terminal=true
|
||||
MimeType=x-scheme-handler/tox;
|
Binary file not shown.
Binary file not shown.
BIN
sounds/Error.wav
BIN
sounds/Error.wav
Binary file not shown.
Binary file not shown.
BIN
sounds/LogIn.wav
BIN
sounds/LogIn.wav
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
sounds/ToxicContactOffline.wav
Normal file
BIN
sounds/ToxicContactOffline.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicContactOnline.wav
Normal file
BIN
sounds/ToxicContactOnline.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicError.wav
Normal file
BIN
sounds/ToxicError.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicIncomingCall.wav
Normal file
BIN
sounds/ToxicIncomingCall.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicOutgoingCall.wav
Normal file
BIN
sounds/ToxicOutgoingCall.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicRecvMessage.wav
Normal file
BIN
sounds/ToxicRecvMessage.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicTransferComplete.wav
Normal file
BIN
sounds/ToxicTransferComplete.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicTransferStart.wav
Normal file
BIN
sounds/ToxicTransferStart.wav
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1,2 @@
|
||||
Tox's sounds are licensed under the "Creative Commons Attribution 3.0 Unported", all credit attributed to Adam Reid.
|
||||
ToxicError.wav, ToxicRecvMessage.wav, ToxicContactOffline.wav, ToxicIncomingCall.wav, ToxicTransferComplete.wav, ToxicContactOnline.wav, ToxicOutgoingCall.wav and ToxicTransferStart.wav
|
||||
are licensed under the "Creative Commons Attribution 3.0 Unported". All credit attributed to Jfreegman.
|
||||
|
734
src/audio_call.c
734
src/audio_call.c
File diff suppressed because it is too large
Load Diff
@ -20,12 +20,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _audio_h
|
||||
#define _audio_h
|
||||
#ifndef AUDIO_CALL_H
|
||||
#define AUDIO_CALL_H
|
||||
|
||||
#include <tox/toxav.h>
|
||||
|
||||
#include "device.h"
|
||||
#include "audio_device.h"
|
||||
|
||||
#define MAX_CALLS 10
|
||||
|
||||
typedef enum _AudioError {
|
||||
ae_None = 0,
|
||||
@ -34,12 +36,58 @@ typedef enum _AudioError {
|
||||
ae_StartingCoreAudio = 1 << 2
|
||||
} AudioError;
|
||||
|
||||
/* You will have to pass pointer to first member of 'windows'
|
||||
* declared in windows.c otherwise undefined behaviour will
|
||||
*/
|
||||
ToxAv *init_audio(ToxWindow *self, Tox *tox);
|
||||
void terminate_audio();
|
||||
#ifdef VIDEO
|
||||
typedef enum _VideoError {
|
||||
ve_None = 0,
|
||||
ve_StartingCaptureDevice = 1 << 0,
|
||||
ve_StartingOutputDevice = 1 << 1,
|
||||
ve_StartingCoreVideo = 1 << 2
|
||||
} VideoError;
|
||||
|
||||
#endif /* VIDEO */
|
||||
|
||||
typedef struct Call {
|
||||
pthread_t ttid; /* Transmission thread id */
|
||||
bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
|
||||
uint32_t in_idx, out_idx; /* Audio Index */
|
||||
#ifdef VIDEO
|
||||
uint32_t vin_idx, vout_idx; /* Video Index */
|
||||
#endif /* VIDEO */
|
||||
pthread_mutex_t mutex;
|
||||
} Call;
|
||||
|
||||
struct CallControl {
|
||||
AudioError audio_errors;
|
||||
#ifdef VIDEO
|
||||
VideoError video_errors;
|
||||
#endif /* VIDEO */
|
||||
|
||||
ToxAV *av;
|
||||
ToxWindow *prompt;
|
||||
|
||||
Call calls[MAX_CALLS];
|
||||
uint32_t call_state;
|
||||
bool pending_call;
|
||||
bool audio_enabled;
|
||||
bool video_enabled;
|
||||
|
||||
uint32_t audio_bit_rate;
|
||||
int32_t audio_frame_duration;
|
||||
uint32_t audio_sample_rate;
|
||||
uint8_t audio_channels;
|
||||
|
||||
uint32_t video_bit_rate;
|
||||
int32_t video_frame_duration;
|
||||
|
||||
} CallControl;
|
||||
|
||||
struct CallControl CallControl;
|
||||
|
||||
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
|
||||
ToxAV *init_audio(ToxWindow *self, Tox *tox);
|
||||
void terminate_audio();
|
||||
int start_transmission(ToxWindow *self, Call *call);
|
||||
int stop_transmission(Call *call, uint32_t friend_number);
|
||||
void stop_current_call(ToxWindow *self);
|
||||
|
||||
#endif /* _audio_h */
|
||||
#endif /* AUDIO_CALL_H */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* device.c
|
||||
/* audio_device.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
@ -20,9 +20,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "device.h"
|
||||
#include "audio_device.h"
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
#include "audio_call.h"
|
||||
#endif
|
||||
|
||||
@ -38,37 +38,37 @@
|
||||
/* compatibility with older versions of OpenAL */
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
#include <AL/alext.h>
|
||||
#endif
|
||||
#endif
|
||||
#endif /* ALC_ALL_DEVICES_SPECIFIER */
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define OPENAL_BUFS 5
|
||||
#define inline__ inline __attribute__((always_inline))
|
||||
|
||||
extern struct user_settings *user_settings_;
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
typedef struct _Device {
|
||||
typedef struct Device {
|
||||
ALCdevice *dhndl; /* Handle of device selected/opened */
|
||||
ALCcontext *ctx; /* Device context */
|
||||
DataHandleCallback cb; /* Use this to handle data from input device usually */
|
||||
void* cb_data; /* Data to be passed to callback */
|
||||
int32_t call_idx; /* ToxAv call index */
|
||||
|
||||
int32_t friend_number; /* ToxAV friend number */
|
||||
|
||||
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
|
||||
size_t ref_count;
|
||||
uint32_t ref_count;
|
||||
int32_t selection;
|
||||
_Bool enable_VAD;
|
||||
_Bool muted;
|
||||
bool enable_VAD;
|
||||
bool muted;
|
||||
pthread_mutex_t mutex[1];
|
||||
uint32_t sample_rate;
|
||||
uint32_t sample_rate;
|
||||
uint32_t frame_duration;
|
||||
int32_t sound_mode;
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
float VAD_treshold; /* 40 is usually recommended value */
|
||||
#endif
|
||||
} Device;
|
||||
@ -76,12 +76,12 @@ typedef struct _Device {
|
||||
const char *ddevice_names[2]; /* Default device */
|
||||
const char *devices_names[2][MAX_DEVICES]; /* Container of available devices */
|
||||
static int size[2]; /* Size of above containers */
|
||||
Device *running[2][MAX_DEVICES]; /* Running devices */
|
||||
Device *running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
|
||||
uint32_t primary_device[2]; /* Primary device */
|
||||
|
||||
#ifdef _AUDIO
|
||||
static ToxAv* av = NULL;
|
||||
#endif /* _AUDIO */
|
||||
#ifdef AUDIO
|
||||
static ToxAV* av = NULL;
|
||||
#endif /* AUDIO */
|
||||
|
||||
/* q_mutex */
|
||||
#define lock pthread_mutex_lock(&mutex)
|
||||
@ -89,66 +89,66 @@ static ToxAv* av = NULL;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
|
||||
_Bool thread_running = _True,
|
||||
thread_paused = _True; /* Thread control */
|
||||
bool thread_running = true,
|
||||
thread_paused = true; /* Thread control */
|
||||
|
||||
void* thread_poll(void*);
|
||||
/* Meet devices */
|
||||
#ifdef _AUDIO
|
||||
DeviceError init_devices(ToxAv* av_)
|
||||
#ifdef AUDIO
|
||||
DeviceError init_devices(ToxAV* av_)
|
||||
#else
|
||||
DeviceError init_devices()
|
||||
#endif /* _AUDIO */
|
||||
#endif /* AUDIO */
|
||||
{
|
||||
const char *stringed_device_list;
|
||||
|
||||
|
||||
|
||||
|
||||
size[input] = 0;
|
||||
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
|
||||
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
||||
|
||||
|
||||
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
|
||||
devices_names[input][size[input]] = stringed_device_list;
|
||||
devices_names[input][size[input]] = stringed_device_list;
|
||||
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
size[output] = 0;
|
||||
if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) {
|
||||
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||
|
||||
|
||||
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
|
||||
devices_names[output][size[output]] = stringed_device_list;
|
||||
devices_names[output][size[output]] = stringed_device_list;
|
||||
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Start poll thread
|
||||
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
|
||||
if (pthread_mutex_init(&mutex, NULL) != 0)
|
||||
return de_InternalError;
|
||||
|
||||
pthread_t thread_id;
|
||||
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
|
||||
return de_InternalError;
|
||||
|
||||
#ifdef _AUDIO
|
||||
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
|
||||
return de_InternalError;
|
||||
|
||||
#ifdef AUDIO
|
||||
av = av_;
|
||||
#endif /* _AUDIO */
|
||||
|
||||
#endif /* AUDIO */
|
||||
|
||||
return (DeviceError) de_None;
|
||||
}
|
||||
|
||||
DeviceError terminate_devices()
|
||||
{
|
||||
/* Cleanup if needed */
|
||||
lock;
|
||||
thread_running = false;
|
||||
unlock;
|
||||
|
||||
usleep(20000);
|
||||
|
||||
pthread_mutex_destroy(&mutex);
|
||||
|
||||
|
||||
if (pthread_mutex_destroy(&mutex) != 0)
|
||||
return (DeviceError) de_InternalError;
|
||||
|
||||
return (DeviceError) de_None;
|
||||
}
|
||||
|
||||
@ -156,35 +156,35 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
||||
{
|
||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||
lock;
|
||||
|
||||
|
||||
Device* device = running[type][device_idx];
|
||||
|
||||
if (!device) {
|
||||
|
||||
if (!device) {
|
||||
unlock;
|
||||
return de_DeviceNotActive;
|
||||
}
|
||||
|
||||
|
||||
device->muted = !device->muted;
|
||||
|
||||
|
||||
unlock;
|
||||
return de_None;
|
||||
}
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
|
||||
{
|
||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||
lock;
|
||||
|
||||
|
||||
Device* device = running[input][device_idx];
|
||||
|
||||
if (!device) {
|
||||
|
||||
if (!device) {
|
||||
unlock;
|
||||
return de_DeviceNotActive;
|
||||
}
|
||||
|
||||
|
||||
device->VAD_treshold = value;
|
||||
|
||||
|
||||
unlock;
|
||||
return de_None;
|
||||
}
|
||||
@ -195,7 +195,7 @@ DeviceError set_primary_device(DeviceType type, int32_t selection)
|
||||
{
|
||||
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
|
||||
primary_device[type] = selection;
|
||||
|
||||
|
||||
return de_None;
|
||||
}
|
||||
|
||||
@ -204,6 +204,10 @@ DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t
|
||||
return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels);
|
||||
}
|
||||
|
||||
void get_primary_device_name(DeviceType type, char *buf, int size)
|
||||
{
|
||||
memcpy(buf, ddevice_names[type], size);
|
||||
}
|
||||
|
||||
// TODO: generate buffers separately
|
||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
|
||||
@ -211,86 +215,88 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
|
||||
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
|
||||
|
||||
if (channels != 1 && channels != 2) return de_UnsupportedMode;
|
||||
|
||||
|
||||
lock;
|
||||
|
||||
const uint32_t frame_size = (sample_rate * frame_duration / 1000);
|
||||
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; i ++);
|
||||
|
||||
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i);
|
||||
|
||||
if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; }
|
||||
else *device_idx = i;
|
||||
|
||||
Device* device = running[type][*device_idx] = calloc(1, sizeof(Device));
|
||||
device->selection = selection;
|
||||
|
||||
device->sample_rate = sample_rate;
|
||||
device->frame_duration = frame_duration;
|
||||
device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
||||
|
||||
for (i = 0; i < *device_idx; i ++) { /* Check if any previous has the same selection */
|
||||
if ( running[type][i]->selection == selection ) {
|
||||
device->dhndl = running[type][i]->dhndl;
|
||||
if (type == output) {
|
||||
device->ctx = running[type][i]->ctx;
|
||||
memcpy(device->buffers, running[type][i]->buffers, sizeof(running[type][i]->buffers));
|
||||
device->source = running[type][i]->source;
|
||||
}
|
||||
device->ref_count++;
|
||||
pthread_mutex_init(device->mutex, NULL);
|
||||
|
||||
for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
|
||||
if ( running[type][i] && running[type][i]->selection == selection ) {
|
||||
// printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl);
|
||||
|
||||
running[type][*device_idx] = running[type][i];
|
||||
running[type][i]->ref_count ++;
|
||||
|
||||
unlock;
|
||||
return de_None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Device* device = running[type][*device_idx] = calloc(1, sizeof(Device));
|
||||
device->selection = selection;
|
||||
|
||||
device->sample_rate = sample_rate;
|
||||
device->frame_duration = frame_duration;
|
||||
device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
||||
|
||||
if (pthread_mutex_init(device->mutex, NULL) != 0) {
|
||||
free(device);
|
||||
unlock;
|
||||
return de_InternalError;
|
||||
}
|
||||
|
||||
if (type == input) {
|
||||
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
|
||||
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
|
||||
sample_rate, device->sound_mode, frame_size * 2);
|
||||
#ifdef _AUDIO
|
||||
device->VAD_treshold = user_settings_->VAD_treshold;
|
||||
#ifdef AUDIO
|
||||
device->VAD_treshold = user_settings->VAD_treshold;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
else {
|
||||
device->dhndl = alcOpenDevice(devices_names[type][selection]);
|
||||
if ( !device->dhndl ) {
|
||||
if ( !device->dhndl ) {
|
||||
free(device);
|
||||
running[type][*device_idx] = NULL;
|
||||
unlock;
|
||||
return de_FailedStart;
|
||||
}
|
||||
|
||||
|
||||
device->ctx = alcCreateContext(device->dhndl, NULL);
|
||||
alcMakeContextCurrent(device->ctx);
|
||||
|
||||
|
||||
alGenBuffers(OPENAL_BUFS, device->buffers);
|
||||
alGenSources((uint32_t)1, &device->source);
|
||||
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
||||
|
||||
|
||||
uint16_t zeros[frame_size];
|
||||
memset(zeros, 0, frame_size*2);
|
||||
|
||||
for ( i =0; i < OPENAL_BUFS; ++i) {
|
||||
|
||||
for ( i = 0; i < OPENAL_BUFS; ++i ) {
|
||||
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate);
|
||||
}
|
||||
|
||||
|
||||
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
|
||||
alSourcePlay(device->source);
|
||||
}
|
||||
|
||||
|
||||
if (alcGetError(device->dhndl) != AL_NO_ERROR) {
|
||||
free(device);
|
||||
running[type][*device_idx] = NULL;
|
||||
unlock;
|
||||
return de_FailedStart;
|
||||
}
|
||||
|
||||
|
||||
if (type == input) {
|
||||
alcCaptureStart(device->dhndl);
|
||||
thread_paused = _False;
|
||||
thread_paused = false;
|
||||
}
|
||||
|
||||
pthread_mutex_init(device->mutex, NULL);
|
||||
|
||||
unlock;
|
||||
return de_None;
|
||||
}
|
||||
@ -298,97 +304,97 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
|
||||
DeviceError close_device(DeviceType type, uint32_t device_idx)
|
||||
{
|
||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||
|
||||
|
||||
lock;
|
||||
Device* device = running[type][device_idx];
|
||||
|
||||
if (!device) {
|
||||
DeviceError rc = de_None;
|
||||
|
||||
if (!device) {
|
||||
unlock;
|
||||
return de_DeviceNotActive;
|
||||
}
|
||||
|
||||
if ( !(device->ref_count--) ) {
|
||||
running[type][device_idx] = NULL;
|
||||
unlock;
|
||||
|
||||
DeviceError rc = de_None;
|
||||
|
||||
|
||||
running[type][device_idx] = NULL;
|
||||
|
||||
if ( !device->ref_count ) {
|
||||
|
||||
// printf("Closed device ");
|
||||
|
||||
if (type == input) {
|
||||
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
|
||||
}
|
||||
else {
|
||||
else {
|
||||
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
|
||||
|
||||
|
||||
alDeleteSources(1, &device->source);
|
||||
alDeleteBuffers(OPENAL_BUFS, device->buffers);
|
||||
|
||||
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
|
||||
|
||||
alcMakeContextCurrent(NULL);
|
||||
if ( device->ctx ) alcDestroyContext(device->ctx);
|
||||
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
|
||||
}
|
||||
|
||||
|
||||
free(device);
|
||||
return rc;
|
||||
}
|
||||
|
||||
else device->ref_count--;
|
||||
|
||||
unlock;
|
||||
|
||||
return de_None;
|
||||
return rc;
|
||||
}
|
||||
|
||||
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD)
|
||||
DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD)
|
||||
{
|
||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
||||
return de_InvalidSelection;
|
||||
|
||||
|
||||
lock;
|
||||
running[input][device_idx]->cb = callback;
|
||||
running[input][device_idx]->cb_data = data;
|
||||
running[input][device_idx]->enable_VAD = enable_VAD;
|
||||
running[input][device_idx]->call_idx = call_idx;
|
||||
running[input][device_idx]->friend_number = friend_number;
|
||||
unlock;
|
||||
|
||||
|
||||
return de_None;
|
||||
}
|
||||
|
||||
inline__ DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels)
|
||||
inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels)
|
||||
{
|
||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||
|
||||
|
||||
Device* device = running[output][device_idx];
|
||||
|
||||
|
||||
if (!device || device->muted) return de_DeviceNotActive;
|
||||
|
||||
|
||||
pthread_mutex_lock(device->mutex);
|
||||
|
||||
|
||||
|
||||
|
||||
ALuint bufid;
|
||||
ALint processed, queued;
|
||||
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
|
||||
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
|
||||
|
||||
|
||||
if(processed) {
|
||||
ALuint bufids[processed];
|
||||
alSourceUnqueueBuffers(device->source, processed, bufids);
|
||||
alDeleteBuffers(processed - 1, bufids + 1);
|
||||
bufid = bufids[0];
|
||||
}
|
||||
}
|
||||
else if(queued < 16) alGenBuffers(1, &bufid);
|
||||
else {
|
||||
else {
|
||||
pthread_mutex_unlock(device->mutex);
|
||||
return de_Busy;
|
||||
}
|
||||
|
||||
|
||||
alBufferData(bufid, device->sound_mode, data, lenght * 2 * channels, device->sample_rate);
|
||||
|
||||
|
||||
alBufferData(bufid, device->sound_mode, data, length * 2 * channels, device->sample_rate);
|
||||
alSourceQueueBuffers(device->source, 1, &bufid);
|
||||
|
||||
|
||||
ALint state;
|
||||
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
|
||||
|
||||
|
||||
if(state != AL_PLAYING) alSourcePlay(device->source);
|
||||
|
||||
|
||||
|
||||
|
||||
pthread_mutex_unlock(device->mutex);
|
||||
return de_None;
|
||||
}
|
||||
@ -401,53 +407,59 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
|
||||
(void)arg;
|
||||
uint32_t i;
|
||||
int32_t sample = 0;
|
||||
|
||||
|
||||
while (thread_running)
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
lock;
|
||||
if (!thread_running) {
|
||||
unlock;
|
||||
break;
|
||||
}
|
||||
unlock;
|
||||
|
||||
if (thread_paused) usleep(10000); /* Wait for unpause. */
|
||||
else
|
||||
{
|
||||
for (i = 0; i < size[input]; i ++)
|
||||
for (i = 0; i < size[input]; ++i)
|
||||
{
|
||||
lock;
|
||||
if (running[input][i] != NULL)
|
||||
if (running[input][i] != NULL)
|
||||
{
|
||||
alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
|
||||
|
||||
|
||||
int f_size = (running[input][i]->sample_rate * running[input][i]->frame_duration / 1000);
|
||||
|
||||
if (sample < f_size) {
|
||||
|
||||
if (sample < f_size) {
|
||||
unlock;
|
||||
continue;
|
||||
}
|
||||
Device* device = running[input][i];
|
||||
|
||||
|
||||
int16_t frame[16000];
|
||||
alcCaptureSamples(device->dhndl, frame, f_size);
|
||||
|
||||
if ( device->muted
|
||||
#ifdef _AUDIO
|
||||
|| (device->enable_VAD && !toxav_has_activity(av, device->call_idx, frame, f_size, device->VAD_treshold))
|
||||
#endif /* _AUDIO */
|
||||
)
|
||||
{ unlock; continue; } /* Skip if no voice activity */
|
||||
|
||||
|
||||
if (device->muted) {
|
||||
unlock;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
|
||||
}
|
||||
}
|
||||
unlock;
|
||||
}
|
||||
usleep(5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void print_devices(ToxWindow* self, DeviceType type)
|
||||
{
|
||||
int i = 0;
|
||||
for ( ; i < size[type]; i ++)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size[type]; ++i)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]);
|
||||
|
||||
return;
|
||||
@ -460,8 +472,8 @@ DeviceError selection_valid(DeviceType type, int32_t selection)
|
||||
|
||||
void* get_device_callback_data(uint32_t device_idx)
|
||||
{
|
||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
||||
return NULL;
|
||||
|
||||
|
||||
return running[input][device_idx]->cb_data;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* device.h
|
||||
/* audio_device.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
@ -26,16 +26,14 @@
|
||||
* Read from running input device(s) via select()/callback combo.
|
||||
*/
|
||||
|
||||
#ifndef _device_h
|
||||
#define _device_h
|
||||
#ifndef AUDIO_DEVICE_H
|
||||
#define AUDIO_DEVICE_H
|
||||
|
||||
#define OPENAL_BUFS 5
|
||||
#define MAX_DEVICES 32
|
||||
#include <inttypes.h>
|
||||
#include "windows.h"
|
||||
|
||||
#define _True 1
|
||||
#define _False 0
|
||||
|
||||
typedef enum DeviceType {
|
||||
input,
|
||||
output,
|
||||
@ -57,22 +55,22 @@ typedef enum DeviceError {
|
||||
typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data);
|
||||
|
||||
|
||||
#ifdef _AUDIO
|
||||
DeviceError init_devices(ToxAv* av);
|
||||
#ifdef AUDIO
|
||||
DeviceError init_devices(ToxAV* av);
|
||||
#else
|
||||
DeviceError init_devices();
|
||||
#endif /* _AUDIO */
|
||||
#endif /* AUDIO */
|
||||
|
||||
DeviceError terminate_devices();
|
||||
|
||||
/* Callback handles ready data from INPUT device */
|
||||
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD);
|
||||
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
|
||||
void* get_device_callback_data(uint32_t device_idx);
|
||||
|
||||
/* toggle device mute */
|
||||
DeviceError device_mute(DeviceType type, uint32_t device_idx);
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
|
||||
#endif
|
||||
|
||||
@ -84,9 +82,10 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
|
||||
DeviceError close_device(DeviceType type, uint32_t device_idx);
|
||||
|
||||
/* Write data to device */
|
||||
DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels);
|
||||
DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels);
|
||||
|
||||
void print_devices(ToxWindow* self, DeviceType type);
|
||||
void get_primary_device_name(DeviceType type, char *buf, int size);
|
||||
|
||||
DeviceError selection_valid(DeviceType type, int32_t selection);
|
||||
#endif /* _device_h */
|
||||
#endif /* AUDIO_DEVICE_H */
|
@ -93,7 +93,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
|
||||
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
const char *L = (char *) list;
|
||||
@ -104,7 +104,9 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
|
||||
return -1;
|
||||
|
||||
bool dir_search = strncmp(ubuf, "/sendfile", strlen("/sendfile")) == 0;
|
||||
/* TODO: generalize this */
|
||||
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|
||||
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
|
||||
|
||||
/* isolate substring from space behind pos to pos */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
@ -112,7 +114,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
tmp[ctx->pos] = '\0';
|
||||
|
||||
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
|
||||
char *sub = malloc(strlen(ubuf) + 1);
|
||||
char *sub = calloc(1, strlen(ubuf) + 1);
|
||||
|
||||
if (sub == NULL)
|
||||
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
|
||||
@ -134,20 +136,20 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
}
|
||||
}
|
||||
|
||||
if (string_is_empty(sub)) {
|
||||
if (!sub[0]) {
|
||||
free(sub);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int s_len = strlen(sub);
|
||||
const char *str;
|
||||
int n_matches = 0;
|
||||
char matches[n_items][MAX_STR_SIZE];
|
||||
int i = 0;
|
||||
|
||||
/* put all list matches in matches array */
|
||||
for (i = 0; i < n_items; ++i) {
|
||||
str = &L[i * size];
|
||||
char str[MAX_CMDNAME_SIZE + 1];
|
||||
snprintf(str, sizeof(str), "%s", &L[i * size]);
|
||||
|
||||
if (strncasecmp(str, sub, s_len) == 0)
|
||||
strcpy(matches[n_matches++], str);
|
||||
@ -163,10 +165,11 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
|
||||
char match[MAX_STR_SIZE];
|
||||
get_str_match(self, match, matches, n_matches);
|
||||
size_t match_len = strlen(match);
|
||||
|
||||
if (dir_search) {
|
||||
if (n_matches == 1)
|
||||
endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/";
|
||||
endchrs = char_rfind(match, '.', match_len) ? "\"" : "/";
|
||||
else
|
||||
endchrs = "";
|
||||
} else if (n_matches > 1) {
|
||||
@ -175,23 +178,26 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
|
||||
/* put match in correct spot in buf and append endchars */
|
||||
int n_endchrs = strlen(endchrs);
|
||||
int m_len = strlen(match);
|
||||
int strt = ctx->pos - s_len;
|
||||
int diff = m_len - s_len + n_endchrs;
|
||||
int diff = match_len - s_len + n_endchrs;
|
||||
|
||||
if (ctx->len + diff >= MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
char tmpend[MAX_STR_SIZE];
|
||||
strcpy(tmpend, &ubuf[ctx->pos]);
|
||||
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
|
||||
|
||||
if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf))
|
||||
return -1;
|
||||
|
||||
strcpy(&ubuf[strt], match);
|
||||
strcpy(&ubuf[strt + m_len], endchrs);
|
||||
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
|
||||
strcpy(&ubuf[strt + match_len], endchrs);
|
||||
strcpy(&ubuf[strt + match_len + n_endchrs], tmpend);
|
||||
|
||||
/* convert to widechar and copy back to original buf */
|
||||
wchar_t newbuf[MAX_STR_SIZE];
|
||||
|
||||
if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1)
|
||||
if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1)
|
||||
return -1;
|
||||
|
||||
wcscpy(ctx->line, newbuf);
|
||||
@ -202,21 +208,21 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
return diff;
|
||||
}
|
||||
|
||||
/* transforms a sendfile tab complete contaning the shorthand "~/" into the full home directory.*/
|
||||
static void complt_home_dir(ToxWindow *self, char *path, int pathsize)
|
||||
/* transforms a tab complete starting with the shorthand "~" into the full home directory.*/
|
||||
static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
char homedir[MAX_STR_SIZE];
|
||||
char homedir[MAX_STR_SIZE] = {0};
|
||||
get_home_dir(homedir, sizeof(homedir));
|
||||
|
||||
char newline[MAX_STR_SIZE];
|
||||
snprintf(newline, sizeof(newline), "/sendfile \"%s%s", homedir, path + 1);
|
||||
snprintf(path, pathsize, "%s", &newline[11]);
|
||||
snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1);
|
||||
snprintf(path, pathsize, "%s", &newline[cmdlen]);
|
||||
|
||||
wchar_t wline[MAX_STR_SIZE];
|
||||
|
||||
if (mbs_to_wcs_buf(wline, newline, sizeof(wline)) == -1)
|
||||
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1)
|
||||
return;
|
||||
|
||||
int newlen = wcslen(wline);
|
||||
@ -229,39 +235,42 @@ static void complt_home_dir(ToxWindow *self, char *path, int pathsize)
|
||||
ctx->len = ctx->pos;
|
||||
}
|
||||
|
||||
/* attempts to match /sendfile "<incomplete-dir>" line to matching directories.
|
||||
/* attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||
|
||||
if only one match, auto-complete line.
|
||||
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
|
||||
#define MAX_DIRS 512
|
||||
|
||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line)
|
||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||
{
|
||||
char b_path[MAX_STR_SIZE];
|
||||
char b_name[MAX_STR_SIZE];
|
||||
const wchar_t *tmpline = &line[11]; /* start after "/sendfile \"" */
|
||||
char b_cmd[MAX_STR_SIZE];
|
||||
const wchar_t *tmpline = &line[wcslen(cmd) + 2]; /* start after "/command \"" */
|
||||
|
||||
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
|
||||
return -1;
|
||||
return -1;
|
||||
|
||||
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1)
|
||||
return -1;
|
||||
|
||||
if (b_path[0] == '~')
|
||||
complt_home_dir(self, b_path, sizeof(b_path));
|
||||
complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
|
||||
|
||||
int si = char_rfind(b_path, '/', strlen(b_path));
|
||||
|
||||
if (!b_path[0]) { /* list everything in pwd */
|
||||
b_path[0] = '.';
|
||||
b_path[1] = '\0';
|
||||
b_path[1] = '\0';
|
||||
} else if (!si && b_path[0] != '/') { /* look for matches in pwd */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), ".%s", b_path);
|
||||
strcpy(b_path, tmp);
|
||||
snprintf(b_path, sizeof(b_path), "%s", tmp);
|
||||
}
|
||||
|
||||
strcpy(b_name, &b_path[si + 1]);
|
||||
snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]);
|
||||
b_path[si + 1] = '\0';
|
||||
int b_name_len = strlen(b_name);
|
||||
|
||||
DIR *dp = opendir(b_path);
|
||||
|
||||
if (dp == NULL)
|
||||
@ -272,13 +281,15 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line)
|
||||
int dircount = 0;
|
||||
|
||||
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
|
||||
if (strncmp(entry->d_name, b_name, b_name_len) == 0
|
||||
if (strncmp(entry->d_name, b_name, b_name_len) == 0
|
||||
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
|
||||
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
|
||||
++dircount;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
|
||||
if (dircount == 0)
|
||||
return -1;
|
||||
|
||||
|
@ -20,8 +20,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _autocomplete_h
|
||||
#define _autocomplete_h
|
||||
#ifndef AUTOCOMPLETE_H
|
||||
#define AUTOCOMPLETE_H
|
||||
|
||||
/* looks for all instances in list that begin with the last entered word in line according to pos,
|
||||
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
@ -33,10 +33,10 @@
|
||||
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||
int complete_line(ToxWindow *self, const void *list, int n_items, int size);
|
||||
|
||||
/* attempts to match /sendfile "<incomplete-dir>" line to matching directories.
|
||||
/* attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||
|
||||
if only one match, auto-complete line.
|
||||
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
|
||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line);
|
||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd);
|
||||
|
||||
#endif /* #define _autocomplete_h */
|
||||
#endif /* #define AUTOCOMPLETE_H */
|
||||
|
205
src/avatars.c
Normal file
205
src/avatars.c
Normal file
@ -0,0 +1,205 @@
|
||||
/* avatars.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2015 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "misc_tools.h"
|
||||
#include "file_transfers.h"
|
||||
#include "friendlist.h"
|
||||
#include "avatars.h"
|
||||
|
||||
extern FriendsList Friends;
|
||||
|
||||
static struct Avatar {
|
||||
char name[TOX_MAX_FILENAME_LENGTH + 1];
|
||||
size_t name_len;
|
||||
char path[PATH_MAX + 1];
|
||||
size_t path_len;
|
||||
off_t size;
|
||||
} Avatar;
|
||||
|
||||
|
||||
static void avatar_clear(void)
|
||||
{
|
||||
memset(&Avatar, 0, sizeof(struct Avatar));
|
||||
}
|
||||
|
||||
/* Sends avatar to friendnum.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int avatar_send(Tox *m, uint32_t friendnum)
|
||||
{
|
||||
TOX_ERR_FILE_SEND err;
|
||||
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
|
||||
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
|
||||
if (Avatar.size == 0)
|
||||
return 0;
|
||||
|
||||
if (err != TOX_ERR_FILE_SEND_OK) {
|
||||
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
|
||||
|
||||
if (!ft)
|
||||
return -1;
|
||||
|
||||
ft->file = fopen(Avatar.path, "r");
|
||||
|
||||
if (ft->file == NULL)
|
||||
return -1;
|
||||
|
||||
snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
|
||||
ft->file_size = Avatar.size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sends avatar to all friends */
|
||||
static void avatar_send_all(Tox *m)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < Friends.max_idx; ++i) {
|
||||
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE)
|
||||
avatar_send(m, Friends.list[i].num);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets avatar to path and sends it to all online contacts.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int avatar_set(Tox *m, const char *path, size_t path_len)
|
||||
{
|
||||
if (path_len == 0 || path_len >= sizeof(Avatar.path))
|
||||
return -1;
|
||||
|
||||
FILE *fp = fopen(path, "rb");
|
||||
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
|
||||
char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
|
||||
if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
off_t size = file_size(path);
|
||||
|
||||
if (size == 0 || size > MAX_AVATAR_FILE_SIZE)
|
||||
return -1;
|
||||
|
||||
get_file_name(Avatar.name, sizeof(Avatar.name), path);
|
||||
Avatar.name_len = strlen(Avatar.name);
|
||||
snprintf(Avatar.path, sizeof(Avatar.path), "%s", path);
|
||||
Avatar.path_len = path_len;
|
||||
Avatar.size = size;
|
||||
|
||||
avatar_send_all(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unsets avatar and sends to all online contacts.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
void avatar_unset(Tox *m)
|
||||
{
|
||||
avatar_clear();
|
||||
avatar_send_all(m);
|
||||
}
|
||||
|
||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control)
|
||||
{
|
||||
switch (control) {
|
||||
case TOX_FILE_CONTROL_RESUME:
|
||||
if (ft->state == FILE_TRANSFER_PENDING) {
|
||||
ft->state = FILE_TRANSFER_STARTED;
|
||||
} else if (ft->state == FILE_TRANSFER_PAUSED) {
|
||||
ft->state = FILE_TRANSFER_STARTED;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOX_FILE_CONTROL_PAUSE:
|
||||
ft->state = FILE_TRANSFER_PAUSED;
|
||||
break;
|
||||
|
||||
case TOX_FILE_CONTROL_CANCEL:
|
||||
close_file_transfer(NULL, m, ft, -1, NULL, silent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length)
|
||||
{
|
||||
if (ft->state != FILE_TRANSFER_STARTED)
|
||||
return;
|
||||
|
||||
if (length == 0) {
|
||||
close_file_transfer(NULL, m, ft, -1, NULL, silent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ft->file == NULL) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ft->position != position) {
|
||||
if (fseek(ft->file, position, SEEK_SET) == -1) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
return;
|
||||
}
|
||||
|
||||
ft->position = position;
|
||||
}
|
||||
|
||||
uint8_t send_data[length];
|
||||
size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
|
||||
|
||||
if (send_length != length) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
return;
|
||||
}
|
||||
|
||||
TOX_ERR_FILE_SEND_CHUNK err;
|
||||
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
|
||||
|
||||
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
|
||||
fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
|
||||
|
||||
ft->position += send_length;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
}
|
52
src/avatars.h
Normal file
52
src/avatars.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* avatars.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AVATARS_H
|
||||
#define AVATARS_H
|
||||
|
||||
#define MAX_AVATAR_FILE_SIZE 65536
|
||||
|
||||
/* Sends avatar to friendnum.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int avatar_send(Tox *m, uint32_t friendnum);
|
||||
|
||||
/* Sets avatar to path and sends it to all online contacts.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int avatar_set(Tox *m, const char *path, size_t length);
|
||||
|
||||
/* Unsets avatar and sends to all online contacts.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
void avatar_unset(Tox *m);
|
||||
|
||||
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
|
||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control);
|
||||
|
||||
#endif /* AVATARS_H */
|
1038
src/chat.c
1038
src/chat.c
File diff suppressed because it is too large
Load Diff
@ -20,8 +20,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CHAT_H_6489PZ13
|
||||
#define CHAT_H_6489PZ13
|
||||
#ifndef CHAT_H
|
||||
#define CHAT_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
@ -32,4 +32,4 @@ void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
|
||||
void kill_chat_window(ToxWindow *self, Tox *m);
|
||||
ToxWindow new_chat(Tox *m, int32_t friendnum);
|
||||
|
||||
#endif /* end of include guard: CHAT_H_6489PZ13 */
|
||||
#endif /* end of include guard: CHAT_H */
|
||||
|
@ -31,14 +31,10 @@
|
||||
#include "line_info.h"
|
||||
#include "groupchat.h"
|
||||
#include "chat.h"
|
||||
#include "file_senders.h"
|
||||
#include "file_transfers.h"
|
||||
|
||||
extern ToxWindow *prompt;
|
||||
extern _Friends Friends;
|
||||
|
||||
extern FileSender file_senders[MAX_FILES];
|
||||
extern uint8_t max_file_senders_index;
|
||||
extern uint8_t num_active_file_senders;
|
||||
extern FriendsList Friends;
|
||||
|
||||
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
@ -47,51 +43,39 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
return;
|
||||
}
|
||||
|
||||
char msg[MAX_STR_SIZE];
|
||||
const char *inoutstr = argv[1];
|
||||
int filenum = atoi(argv[2]);
|
||||
long int idx = strtol(argv[2], NULL, 10);
|
||||
|
||||
if (filenum >= MAX_FILES || filenum < 0) {
|
||||
if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(inoutstr, "in") == 0) { /* cancel an incoming file transfer */
|
||||
if (!Friends.list[self->num].file_receiver[filenum].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
struct FileTransfer *ft = NULL;
|
||||
|
||||
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);
|
||||
chat_close_file_receiver(m, filenum, self->num, TOX_FILECONTROL_KILL);
|
||||
return;
|
||||
} else if (strcasecmp(inoutstr, "out") == 0) { /* cancel an outgoing file transfer */
|
||||
int i;
|
||||
bool match = false;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
if (file_senders[i].active && file_senders[i].filenum == filenum) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *filename = file_senders[i].filename;
|
||||
char msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' canceled.", filename);
|
||||
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, self->num);
|
||||
return;
|
||||
/* cancel an incoming file transfer */
|
||||
if (strcasecmp(inoutstr, "in") == 0) {
|
||||
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||
} else if (strcasecmp(inoutstr, "out") == 0) {
|
||||
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ft) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' aborted.", ft->file_name);
|
||||
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
|
||||
}
|
||||
|
||||
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -101,9 +85,9 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = atoi(argv[1]);
|
||||
long int groupnum = strtol(argv[1], NULL, 10);
|
||||
|
||||
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
|
||||
if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == LONG_MAX) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
|
||||
return;
|
||||
}
|
||||
@ -123,25 +107,36 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
return;
|
||||
}
|
||||
|
||||
const char *groupkey = Friends.list[self->num].groupchat_key;
|
||||
const char *groupkey = Friends.list[self->num].group_invite.key;
|
||||
uint16_t length = Friends.list[self->num].group_invite.length;
|
||||
uint8_t type = Friends.list[self->num].group_invite.type;
|
||||
|
||||
if (!Friends.list[self->num].groupchat_pending) {
|
||||
if (!Friends.list[self->num].group_invite.pending) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey);
|
||||
int groupnum = -1;
|
||||
|
||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||
groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length);
|
||||
/*#ifdef AUDIO
|
||||
else
|
||||
groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length,
|
||||
NULL, NULL);
|
||||
#endif*/
|
||||
|
||||
if (groupnum == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
||||
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||
tox_del_groupchat(m, groupnum);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -151,50 +146,76 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t filenum = atoi(argv[1]);
|
||||
long int idx = strtol(argv[1], NULL, 10);
|
||||
|
||||
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
|
||||
if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Friends.list[self->num].file_receiver[filenum].pending) {
|
||||
struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||
|
||||
if (!ft) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* prep progress bar line */
|
||||
char progline[MAX_STR_SIZE];
|
||||
prep_prog_line(progline);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||
Friends.list[self->num].file_receiver[filenum].line_id = self->chatwin->hst->line_end->id + 2;
|
||||
|
||||
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.");
|
||||
if (ft->state != FILE_TRANSFER_PENDING) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
Friends.list[self->num].file_receiver[filenum].pending = false;
|
||||
if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
|
||||
const char *msg = "File transfer failed: Invalid file path.";
|
||||
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||
return;
|
||||
}
|
||||
|
||||
TOX_ERR_FILE_CONTROL err;
|
||||
tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
|
||||
|
||||
if (err != TOX_ERR_FILE_CONTROL_OK)
|
||||
goto on_recv_error;
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
|
||||
|
||||
/* prep progress bar line */
|
||||
char progline[MAX_STR_SIZE];
|
||||
init_progress_bar(progline);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||
|
||||
ft->line_id = self->chatwin->hst->line_end->id + 2;
|
||||
ft->state = FILE_TRANSFER_STARTED;
|
||||
|
||||
return;
|
||||
|
||||
on_recv_error:
|
||||
switch (err) {
|
||||
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_NOT_FOUND:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_SENDQ:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
|
||||
return;
|
||||
|
||||
default:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (max_file_senders_index >= (MAX_FILES - 1)) {
|
||||
const char *errmsg = "Please wait for some of your outgoing file transfers to complete.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
return;
|
||||
}
|
||||
const char *errmsg = NULL;
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
|
||||
@ -224,47 +245,66 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
return;
|
||||
}
|
||||
|
||||
fseek(file_to_send, 0, SEEK_END);
|
||||
uint64_t filesize = ftell(file_to_send);
|
||||
fseek(file_to_send, 0, SEEK_SET);
|
||||
off_t filesize = file_size(path);
|
||||
|
||||
char filename[MAX_STR_SIZE] = {0};
|
||||
get_file_name(filename, sizeof(filename), path);
|
||||
int namelen = strlen(filename);
|
||||
int filenum = tox_new_file_sender(m, self->num, filesize, (const uint8_t *) filename, namelen);
|
||||
|
||||
if (filenum == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error sending file.");
|
||||
if (filesize == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
|
||||
fclose(file_to_send);
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
char file_name[TOX_MAX_FILENAME_LENGTH];
|
||||
size_t namelen = get_file_name(file_name, sizeof(file_name), path);
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
if (!file_senders[i].active) {
|
||||
memcpy(file_senders[i].filename, filename, namelen + 1);
|
||||
file_senders[i].active = true;
|
||||
file_senders[i].toxwin = self;
|
||||
file_senders[i].file = file_to_send;
|
||||
file_senders[i].filenum = filenum;
|
||||
file_senders[i].friendnum = self->num;
|
||||
file_senders[i].timestamp = get_unix_time();
|
||||
file_senders[i].size = filesize;
|
||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||
tox_file_data_size(m, self->num), file_to_send);
|
||||
TOX_ERR_FILE_SEND err;
|
||||
uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL,
|
||||
(uint8_t *) file_name, namelen, &err);
|
||||
|
||||
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);
|
||||
if (err != TOX_ERR_FILE_SEND_OK)
|
||||
goto on_send_error;
|
||||
|
||||
++num_active_file_senders;
|
||||
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
|
||||
|
||||
if (i == max_file_senders_index)
|
||||
++max_file_senders_index;
|
||||
|
||||
reset_file_sender_queue();
|
||||
return;
|
||||
}
|
||||
if (!ft) {
|
||||
err = TOX_ERR_FILE_SEND_TOO_MANY;
|
||||
goto on_send_error;
|
||||
}
|
||||
|
||||
memcpy(ft->file_name, file_name, namelen + 1);
|
||||
ft->file = file_to_send;
|
||||
ft->file_size = filesize;
|
||||
tox_file_get_file_id(m, self->num, filenum, ft->file_id, NULL);
|
||||
|
||||
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, file_name, sizestr);
|
||||
|
||||
return;
|
||||
|
||||
on_send_error:
|
||||
switch (err) {
|
||||
case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND:
|
||||
errmsg = "File transfer failed: Invalid friend.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED:
|
||||
errmsg = "File transfer failed: Friend is offline.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FILE_SEND_NAME_TOO_LONG:
|
||||
errmsg = "File transfer failed: Filename is too long.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FILE_SEND_TOO_MANY:
|
||||
errmsg = "File transfer failed: Too many concurrent file transfers.";
|
||||
break;
|
||||
|
||||
default:
|
||||
errmsg = "File transfer failed.";
|
||||
break;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
|
||||
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
|
||||
fclose(file_to_send);
|
||||
}
|
||||
|
@ -20,8 +20,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _chat_commands_h
|
||||
#define _chat_commands_h
|
||||
#ifndef CHAT_COMMANDS_H
|
||||
#define CHAT_COMMANDS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
@ -32,7 +32,7 @@ void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR
|
||||
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_reject(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
@ -41,6 +41,11 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
|
||||
void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* _AUDIO */
|
||||
#endif /* AUDIO */
|
||||
|
||||
#endif /* #define _chat_commands_h */
|
||||
#ifdef VIDEO
|
||||
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* VIDEO */
|
||||
|
||||
#endif /* #define CHAT_COMMANDS_H */
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include "toxic.h"
|
||||
#include "configdir.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
/* get the user's home directory */
|
||||
void get_home_dir(char *home, int size)
|
||||
@ -66,11 +67,11 @@ void get_home_dir(char *home, int size)
|
||||
*/
|
||||
char *get_user_config_dir(void)
|
||||
{
|
||||
char home[NSS_BUFLEN_PASSWD];
|
||||
char home[NSS_BUFLEN_PASSWD] = {0};
|
||||
get_home_dir(home, sizeof(home));
|
||||
|
||||
char *user_config_dir;
|
||||
size_t len;
|
||||
char *user_config_dir = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
# if defined(__APPLE__)
|
||||
len = strlen(home) + strlen("/Library/Application Support") + 1;
|
||||
@ -82,9 +83,9 @@ char *get_user_config_dir(void)
|
||||
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
|
||||
# else /* __APPLE__ */
|
||||
|
||||
const char *tmp;
|
||||
const char *tmp = getenv("XDG_CONFIG_HOME");
|
||||
|
||||
if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
|
||||
if (tmp == NULL) {
|
||||
len = strlen(home) + strlen("/.config") + 1;
|
||||
user_config_dir = malloc(len);
|
||||
|
||||
@ -98,6 +99,11 @@ char *get_user_config_dir(void)
|
||||
|
||||
# endif /* __APPLE__ */
|
||||
|
||||
if (!file_exists(user_config_dir)) {
|
||||
free(user_config_dir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return user_config_dir;
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _configdir_h
|
||||
#define _configdir_h
|
||||
#ifndef CONFIGDIR_H
|
||||
#define CONFIGDIR_H
|
||||
|
||||
#ifndef NSS_BUFLEN_PASSWD
|
||||
#define NSS_BUFLEN_PASSWD 4096
|
||||
@ -38,4 +38,4 @@ char *get_user_config_dir(void);
|
||||
void get_home_dir(char *home, int size);
|
||||
int create_user_config_dirs(char *path);
|
||||
|
||||
#endif /* #define _configdir_h */
|
||||
#endif /* #define CONFIGDIR_H */
|
||||
|
418
src/dns.c
418
src/dns.c
@ -1,418 +0,0 @@
|
||||
/* dns.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 <string.h>
|
||||
#include <sys/types.h> /* for u_char */
|
||||
#include <netinet/in.h>
|
||||
#include <resolv.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <arpa/nameser_compat.h>
|
||||
#else
|
||||
#include <arpa/nameser.h>
|
||||
#endif /* ifdef __APPLE__ */
|
||||
|
||||
#include <tox/toxdns.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "line_info.h"
|
||||
#include "dns.h"
|
||||
#include "global_commands.h"
|
||||
#include "misc_tools.h"
|
||||
#include "configdir.h"
|
||||
|
||||
#define DNS3_KEY_SIZE 32
|
||||
#define MAX_DNS_REQST_SIZE 255
|
||||
#define TOX_DNS3_TXT_PREFIX "v=tox3;id="
|
||||
|
||||
extern struct _Winthread Winthread;
|
||||
extern struct _dns3_servers dns3_servers;
|
||||
extern struct arg_opts arg_opts;
|
||||
|
||||
#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_SIZE];
|
||||
} dns3_servers_backup[] = {
|
||||
{
|
||||
"utox.org",
|
||||
{
|
||||
0xD3, 0x15, 0x4F, 0x65, 0xD2, 0x8A, 0x5B, 0x41, 0xA0, 0x5D, 0x4A, 0xC7, 0xE4, 0xB3, 0x9C, 0x6B,
|
||||
0x1C, 0x23, 0x3C, 0xC8, 0x57, 0xFB, 0x36, 0x5C, 0x56, 0xE8, 0x39, 0x27, 0x37, 0x46, 0x2A, 0x12
|
||||
}
|
||||
},
|
||||
{
|
||||
"toxme.se",
|
||||
{
|
||||
0x5D, 0x72, 0xC5, 0x17, 0xDF, 0x6A, 0xEC, 0x54, 0xF1, 0xE9, 0x77, 0xA6, 0xB6, 0xF2, 0x59, 0x14,
|
||||
0xEA, 0x4C, 0xF7, 0x27, 0x7A, 0x85, 0x02, 0x7C, 0xD9, 0xF5, 0x19, 0x6D, 0xF1, 0x7E, 0x0B, 0x13
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static struct _thread_data {
|
||||
ToxWindow *self;
|
||||
char id_bin[TOX_FRIEND_ADDRESS_SIZE];
|
||||
char addr[MAX_STR_SIZE];
|
||||
char msg[MAX_STR_SIZE];
|
||||
uint8_t busy;
|
||||
Tox *m;
|
||||
} t_data;
|
||||
|
||||
static struct _dns_thread {
|
||||
pthread_t tid;
|
||||
pthread_attr_t attr;
|
||||
} 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);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "User lookup failed: %s", errmsg);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void kill_dns_thread(void *dns_obj)
|
||||
{
|
||||
if (dns_obj)
|
||||
tox_dns3_kill(dns_obj);
|
||||
|
||||
memset(&t_data, 0, sizeof(struct _thread_data));
|
||||
pthread_attr_destroy(&dns_thread.attr);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
/* puts TXT from dns response in buf. Returns length of TXT on success, -1 on fail.*/
|
||||
static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char *buf)
|
||||
{
|
||||
uint8_t *ans_pt = answer + sizeof(HEADER);
|
||||
uint8_t *ans_end = answer + ans_len;
|
||||
char exp_ans[PACKETSZ];
|
||||
|
||||
int len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
|
||||
|
||||
if (len == -1)
|
||||
return dns_error(self, "dn_expand failed.");
|
||||
|
||||
ans_pt += len;
|
||||
|
||||
if (ans_pt > ans_end - 4)
|
||||
return dns_error(self, "DNS reply was too short.");
|
||||
|
||||
int type;
|
||||
GETSHORT(type, ans_pt);
|
||||
|
||||
if (type != T_TXT)
|
||||
return dns_error(self, "Broken DNS reply.");
|
||||
|
||||
|
||||
ans_pt += INT16SZ; /* class */
|
||||
uint32_t size = 0;
|
||||
|
||||
/* recurse through CNAME rr's */
|
||||
do {
|
||||
ans_pt += size;
|
||||
len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
|
||||
|
||||
if (len == -1)
|
||||
return dns_error(self, "Second dn_expand failed.");
|
||||
|
||||
ans_pt += len;
|
||||
|
||||
if (ans_pt > ans_end - 10)
|
||||
return dns_error(self, "DNS reply was too short.");
|
||||
|
||||
GETSHORT(type, ans_pt);
|
||||
ans_pt += INT16SZ;
|
||||
ans_pt += 4;
|
||||
GETSHORT(size, ans_pt);
|
||||
|
||||
if (ans_pt + size < answer || ans_pt + size > ans_end)
|
||||
return dns_error(self, "RR overflow.");
|
||||
|
||||
} while (type == T_CNAME);
|
||||
|
||||
if (type != T_TXT)
|
||||
return dns_error(self, "DNS response failed.");
|
||||
|
||||
uint32_t txt_len = *ans_pt;
|
||||
|
||||
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);
|
||||
|
||||
return txt_len;
|
||||
}
|
||||
|
||||
/* Takes address addr in the form "username@domain", puts the username in namebuf,
|
||||
and the domain in dombuf.
|
||||
|
||||
return length of username on success, -1 on failure */
|
||||
static int parse_addr(const char *addr, char *namebuf, char *dombuf)
|
||||
{
|
||||
char tmpaddr[MAX_STR_SIZE];
|
||||
char *tmpname, *tmpdom;
|
||||
|
||||
strcpy(tmpaddr, addr);
|
||||
tmpname = strtok(tmpaddr, "@");
|
||||
tmpdom = strtok(NULL, "");
|
||||
|
||||
if (tmpname == NULL || tmpdom == NULL)
|
||||
return -1;
|
||||
|
||||
str_to_lower(tmpdom);
|
||||
strcpy(namebuf, tmpname);
|
||||
strcpy(dombuf, tmpdom);
|
||||
|
||||
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 inputdomain[MAX_STR_SIZE];
|
||||
char name[MAX_STR_SIZE];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
char DNS_pubkey[DNS3_KEY_SIZE];
|
||||
char domain[MAX_DOMAIN_SIZE];
|
||||
|
||||
int match = get_domain_match(DNS_pubkey, domain, inputdomain);
|
||||
|
||||
if (match == -1) {
|
||||
dns_error(self, "Domain not found.");
|
||||
kill_dns_thread(NULL);
|
||||
}
|
||||
|
||||
void *dns_obj = tox_dns3_new((uint8_t *) DNS_pubkey);
|
||||
|
||||
if (dns_obj == NULL) {
|
||||
dns_error(self, "Core failed to create DNS object.");
|
||||
kill_dns_thread(NULL);
|
||||
}
|
||||
|
||||
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,
|
||||
(uint8_t *) name, namelen);
|
||||
|
||||
if (str_len == -1) {
|
||||
dns_error(self, "Core failed to generate DNS3 string.");
|
||||
kill_dns_thread(dns_obj);
|
||||
}
|
||||
|
||||
string[str_len] = '\0';
|
||||
|
||||
u_char answer[PACKETSZ];
|
||||
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, domain);
|
||||
int ans_len = res_query(d_string, C_IN, T_TXT, answer, sizeof(answer));
|
||||
|
||||
if (ans_len <= 0) {
|
||||
dns_error(self, "DNS query failed.");
|
||||
kill_dns_thread(dns_obj);
|
||||
}
|
||||
|
||||
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 + 1];
|
||||
int prfx_len = strlen(TOX_DNS3_TXT_PREFIX);
|
||||
|
||||
/* extract the encrypted ID from TXT response */
|
||||
if (strncmp(ans_id, TOX_DNS3_TXT_PREFIX, prfx_len) != 0) {
|
||||
dns_error(self, "Bad DNS3 TXT response.");
|
||||
kill_dns_thread(dns_obj);
|
||||
}
|
||||
|
||||
memcpy(encrypted_id, ans_id + prfx_len, ans_len - prfx_len);
|
||||
|
||||
if (tox_decrypt_dns3_TXT(dns_obj, (uint8_t *) t_data.id_bin, (uint8_t *) encrypted_id,
|
||||
strlen(encrypted_id), request_id) == -1) {
|
||||
dns_error(self, "Core failed to decrypt DNS response.");
|
||||
kill_dns_thread(dns_obj);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
kill_dns_thread(dns_obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
t_data.self = self;
|
||||
t_data.m = m;
|
||||
t_data.busy = 1;
|
||||
|
||||
if (pthread_attr_init(&dns_thread.attr) != 0)
|
||||
exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_ATTR);
|
||||
|
||||
if (pthread_attr_setdetachstate(&dns_thread.attr, PTHREAD_CREATE_DETACHED) != 0)
|
||||
exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_ATTR);
|
||||
|
||||
if (pthread_create(&dns_thread.tid, &dns_thread.attr, dns3_lookup_thread, NULL) != 0)
|
||||
exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_CREATE);
|
||||
}
|
@ -29,6 +29,7 @@
|
||||
#include "execute.h"
|
||||
#include "chat_commands.h"
|
||||
#include "global_commands.h"
|
||||
#include "group_commands.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
@ -41,25 +42,32 @@ struct cmd_func {
|
||||
static struct cmd_func global_commands[] = {
|
||||
{ "/accept", cmd_accept },
|
||||
{ "/add", cmd_add },
|
||||
{ "/avatar", cmd_avatar },
|
||||
{ "/clear", cmd_clear },
|
||||
{ "/connect", cmd_connect },
|
||||
{ "/decline", cmd_decline },
|
||||
{ "/exit", cmd_quit },
|
||||
{ "/groupchat", cmd_groupchat },
|
||||
{ "/group", cmd_groupchat },
|
||||
{ "/help", cmd_prompt_help },
|
||||
{ "/log", cmd_log },
|
||||
{ "/myid", cmd_myid },
|
||||
{ "/myqr", cmd_myqr },
|
||||
{ "/nick", cmd_nick },
|
||||
{ "/note", cmd_note },
|
||||
{ "/nospam", cmd_nospam },
|
||||
{ "/q", cmd_quit },
|
||||
{ "/quit", cmd_quit },
|
||||
{ "/requests", cmd_requests },
|
||||
{ "/status", cmd_status },
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
{ "/lsdev", cmd_list_devices },
|
||||
{ "/sdev", cmd_change_device },
|
||||
#endif /* _AUDIO */
|
||||
#endif /* AUDIO */
|
||||
#ifdef VIDEO
|
||||
{ "/lsvdev", cmd_list_video_devices },
|
||||
{ "/svdev" , cmd_change_video_device },
|
||||
#endif /* VIDEO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd_func chat_commands[] = {
|
||||
@ -68,16 +76,28 @@ static struct cmd_func chat_commands[] = {
|
||||
{ "/join", cmd_join_group },
|
||||
{ "/savefile", cmd_savefile },
|
||||
{ "/sendfile", cmd_sendfile },
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
{ "/call", cmd_call },
|
||||
{ "/answer", cmd_answer },
|
||||
{ "/reject", cmd_reject },
|
||||
{ "/hangup", cmd_hangup },
|
||||
{ "/sdev", cmd_ccur_device },
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
#endif /* _AUDIO */
|
||||
#endif /* AUDIO */
|
||||
#ifdef VIDEO
|
||||
{ "/video", cmd_video },
|
||||
#endif /* VIDEO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd_func group_commands[] = {
|
||||
{ "/title", cmd_set_title },
|
||||
|
||||
#ifdef AUDIO
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
#endif /* AUDIO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parses input command and puts args into arg array.
|
||||
@ -101,7 +121,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;
|
||||
@ -118,7 +138,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);
|
||||
@ -126,12 +146,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;
|
||||
@ -158,16 +178,18 @@ 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;
|
||||
|
||||
case GROUPCHAT_COMMAND_MODE:
|
||||
if (do_command(w, self, m, num_args, group_commands, args) == 0)
|
||||
return;
|
||||
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;
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||
|
@ -20,22 +20,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _execute_h
|
||||
#define _execute_h
|
||||
#ifndef EXECUTE_H
|
||||
#define EXECUTE_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define MAX_NUM_ARGS 4 /* Includes command */
|
||||
|
||||
#ifdef _AUDIO
|
||||
#define GLOBAL_NUM_COMMANDS 18
|
||||
#define CHAT_NUM_COMMANDS 12
|
||||
#else
|
||||
#define GLOBAL_NUM_COMMANDS 16
|
||||
#define CHAT_NUM_COMMANDS 5
|
||||
#endif /* _AUDIO */
|
||||
|
||||
enum {
|
||||
GLOBAL_COMMAND_MODE,
|
||||
CHAT_COMMAND_MODE,
|
||||
@ -44,4 +36,4 @@ enum {
|
||||
|
||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);
|
||||
|
||||
#endif /* #define _execute_h */
|
||||
#endif /* #define EXECUTE_H */
|
||||
|
@ -1,290 +0,0 @@
|
||||
/* file_senders.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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "friendlist.h"
|
||||
#include "file_senders.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
|
||||
FileSender file_senders[MAX_FILES];
|
||||
uint8_t max_file_senders_index;
|
||||
uint8_t num_active_file_senders;
|
||||
extern _Friends Friends;
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
Assumes progline is of size MAX_STR_SIZE */
|
||||
void prep_prog_line(char *progline)
|
||||
{
|
||||
strcpy(progline, "0.0 B/s [");
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_PROG_MARKS; ++i)
|
||||
strcat(progline, "-");
|
||||
|
||||
strcat(progline, "] 0%");
|
||||
}
|
||||
|
||||
/* prints a progress bar for file transfers.
|
||||
if friendnum is -1 we're sending the file, otherwise we're receiving. */
|
||||
void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_done)
|
||||
{
|
||||
double bps;
|
||||
uint32_t line_id;
|
||||
|
||||
if (friendnum < 0) {
|
||||
bps = file_senders[idx].bps;
|
||||
line_id = file_senders[idx].line_id;
|
||||
} else {
|
||||
bps = Friends.list[friendnum].file_receiver[idx].bps;
|
||||
line_id = Friends.list[friendnum].file_receiver[idx].line_id;
|
||||
}
|
||||
|
||||
char msg[MAX_STR_SIZE];
|
||||
bytes_convert_str(msg, sizeof(msg), bps);
|
||||
strcat(msg, "/s [");
|
||||
|
||||
int n = pct_done / (100 / NUM_PROG_MARKS);
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
strcat(msg, "#");
|
||||
|
||||
for (j = i; j < NUM_PROG_MARKS; ++j)
|
||||
strcat(msg, "-");
|
||||
|
||||
strcat(msg, "] ");
|
||||
|
||||
char pctstr[16];
|
||||
const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%";
|
||||
snprintf(pctstr, sizeof(pctstr), frmt, pct_done);
|
||||
strcat(msg, pctstr);
|
||||
|
||||
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;
|
||||
|
||||
for (j = max_file_senders_index; j > 0; --j) {
|
||||
if (file_senders[j - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (msg != NULL)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
|
||||
|
||||
if (CTRL > 0)
|
||||
tox_file_send_control(m, friendnum, 0, filenum, CTRL, 0, 0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void close_all_file_senders(Tox *m)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (file_senders[i].active) {
|
||||
fclose(file_senders[i].file);
|
||||
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenum,
|
||||
TOX_FILECONTROL_KILL, 0, 0);
|
||||
memset(&file_senders[i], 0, sizeof(FileSender));
|
||||
}
|
||||
|
||||
set_max_file_senders_index();
|
||||
}
|
||||
}
|
||||
|
||||
static void send_file_data(ToxWindow *self, Tox *m, int i, int32_t friendnum, int filenum, const char *filename)
|
||||
{
|
||||
FILE *fp = file_senders[i].file;
|
||||
|
||||
while (true) {
|
||||
if (tox_file_send_data(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece,
|
||||
file_senders[i].piecelen) == -1)
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_file_senders(Tox *m)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (!file_senders[i].active)
|
||||
continue;
|
||||
|
||||
if (file_senders[i].queue_pos > 0) {
|
||||
--file_senders[i].queue_pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
ToxWindow *self = file_senders[i].toxwin;
|
||||
char *filename = file_senders[i].filename;
|
||||
int filenum = file_senders[i].filenum;
|
||||
int32_t friendnum = file_senders[i].friendnum;
|
||||
|
||||
/* kill file transfer if chatwindow is closed */
|
||||
if (self->chatwin == NULL) {
|
||||
close_file_sender(self, m, i, NULL, TOX_FILECONTROL_KILL, filenum, friendnum);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If file transfer has timed out kill transfer and send kill control */
|
||||
if (timed_out(file_senders[i].timestamp, get_unix_time(), TIMEOUT_FILESENDER)) {
|
||||
char msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", 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);
|
||||
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;
|
||||
}
|
||||
|
||||
refresh_sender_prog(m);
|
||||
refresh_recv_prog(m);
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/* file_senders.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _filesenders_h
|
||||
#define _filesenders_h
|
||||
|
||||
#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 32
|
||||
#define TIMEOUT_FILESENDER 120
|
||||
#define NUM_PROG_MARKS 50 /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||
|
||||
typedef struct {
|
||||
FILE *file;
|
||||
ToxWindow *toxwin;
|
||||
int32_t friendnum;
|
||||
bool active;
|
||||
bool noconnection;
|
||||
bool finished;
|
||||
int filenum;
|
||||
char nextpiece[FILE_PIECE_SIZE];
|
||||
uint16_t piecelen;
|
||||
char filename[MAX_STR_SIZE];
|
||||
uint64_t timestamp;
|
||||
uint64_t last_progress;
|
||||
double bps;
|
||||
uint64_t size;
|
||||
uint32_t line_id;
|
||||
uint8_t queue_pos;
|
||||
} FileSender;
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
Assumes progline is of size MAX_STR_SIZE */
|
||||
void prep_prog_line(char *progline);
|
||||
|
||||
/* prints a progress bar for file transfers.
|
||||
if friendnum is -1 we're sending the file, otherwise we're receiving. */
|
||||
void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_remain);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
#endif /* #define _filesenders_h */
|
316
src/file_transfers.c
Normal file
316
src/file_transfers.c
Normal file
@ -0,0 +1,316 @@
|
||||
/* file_transfers.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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "friendlist.h"
|
||||
#include "file_transfers.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
|
||||
extern FriendsList Friends;
|
||||
|
||||
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||
#define NUM_PROG_MARKS 50
|
||||
|
||||
/* Checks for timed out file transfers and closes them. */
|
||||
#define CHECK_FILE_TIMEOUT_INTERAVAL 5
|
||||
void check_file_transfer_timeouts(Tox *m)
|
||||
{
|
||||
char msg[MAX_STR_SIZE];
|
||||
static uint64_t last_check = 0;
|
||||
|
||||
if (!timed_out(last_check, CHECK_FILE_TIMEOUT_INTERAVAL))
|
||||
return;
|
||||
|
||||
last_check = get_unix_time();
|
||||
|
||||
size_t i, j;
|
||||
|
||||
for (i = 0; i < Friends.max_idx; ++i) {
|
||||
if (!Friends.list[i].active)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_FILES; ++j) {
|
||||
struct FileTransfer *ft_send = &Friends.list[i].file_sender[j];
|
||||
|
||||
if (ft_send->state > FILE_TRANSFER_PAUSED) {
|
||||
if (timed_out(ft_send->last_keep_alive, TIMEOUT_FILESENDER)) {
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_send->file_name);
|
||||
close_file_transfer(ft_send->window, m, ft_send, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||
}
|
||||
}
|
||||
|
||||
struct FileTransfer *ft_recv = &Friends.list[i].file_receiver[j];
|
||||
|
||||
if (ft_recv->state > FILE_TRANSFER_PAUSED) {
|
||||
if (timed_out(ft_recv->last_keep_alive, TIMEOUT_FILESENDER)) {
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_recv->file_name);
|
||||
close_file_transfer(ft_recv->window, m, ft_recv, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
Assumes progline is of size MAX_STR_SIZE */
|
||||
void init_progress_bar(char *progline)
|
||||
{
|
||||
strcpy(progline, "0.0 B/s [");
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_PROG_MARKS; ++i)
|
||||
strcat(progline, "-");
|
||||
|
||||
strcat(progline, "] 0%");
|
||||
}
|
||||
|
||||
/* prints a progress bar for file transfers.
|
||||
if friendnum is -1 we're sending the file, otherwise we're receiving. */
|
||||
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
|
||||
{
|
||||
if (bps < 0 || pct_done < 0 || pct_done > 100)
|
||||
return;
|
||||
|
||||
char msg[MAX_STR_SIZE];
|
||||
bytes_convert_str(msg, sizeof(msg), bps);
|
||||
strcat(msg, "/s [");
|
||||
|
||||
int n = pct_done / (100 / NUM_PROG_MARKS);
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
strcat(msg, "#");
|
||||
|
||||
for (j = i; j < NUM_PROG_MARKS; ++j)
|
||||
strcat(msg, "-");
|
||||
|
||||
strcat(msg, "] ");
|
||||
|
||||
char pctstr[16];
|
||||
const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%";
|
||||
snprintf(pctstr, sizeof(pctstr), frmt, pct_done);
|
||||
strcat(msg, pctstr);
|
||||
|
||||
line_info_set(self, line_id, msg);
|
||||
}
|
||||
|
||||
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
|
||||
{
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||
return;
|
||||
|
||||
/* Timeout must be set to 1 second to show correct bytes per second */
|
||||
if (!timed_out(ft->last_line_progress, 1))
|
||||
return;
|
||||
|
||||
double remain = ft->file_size - ft->position;
|
||||
double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
|
||||
print_progress_bar(self, ft->bps, pct_done, ft->line_id);
|
||||
|
||||
ft->bps = 0;
|
||||
ft->last_line_progress = get_unix_time();
|
||||
}
|
||||
|
||||
/* refreshes active file transfer status bars. */
|
||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]);
|
||||
refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||
* Returns NULL if filenum is invalid.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
|
||||
|
||||
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum)
|
||||
return ft_send;
|
||||
|
||||
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
|
||||
|
||||
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum)
|
||||
return ft_recv;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||
FILE_TRANSFER_DIRECTION direction)
|
||||
{
|
||||
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
|
||||
return NULL;
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
|
||||
&Friends.list[friendnum].file_sender[i] :
|
||||
&Friends.list[friendnum].file_receiver[i];
|
||||
|
||||
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
|
||||
return ft;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns a pointer to an unused file sender.
|
||||
* Returns NULL if all file senders are in use.
|
||||
*/
|
||||
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
ft->window = window;
|
||||
ft->index = i;
|
||||
ft->friendnum = friendnum;
|
||||
ft->filenum = filenum;
|
||||
ft->file_type = type;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
ft->state = FILE_TRANSFER_PENDING;
|
||||
ft->direction = FILE_TRANSFER_SEND;
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns a pointer to an unused file receiver.
|
||||
* Returns NULL if all file receivers are in use.
|
||||
*/
|
||||
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i];
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
ft->window = window;
|
||||
ft->index = i;
|
||||
ft->friendnum = friendnum;
|
||||
ft->filenum = filenum;
|
||||
ft->file_type = type;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
ft->state = FILE_TRANSFER_PENDING;
|
||||
ft->direction = FILE_TRANSFER_RECV;
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initializes an unused file transfer and returns its pointer.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||
FILE_TRANSFER_DIRECTION direction, uint8_t type)
|
||||
{
|
||||
if (direction == FILE_TRANSFER_RECV)
|
||||
return new_file_receiver(window, friendnum, filenum, type);
|
||||
|
||||
if (direction == FILE_TRANSFER_SEND)
|
||||
return new_file_sender(window, friendnum, filenum, type);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Closes file transfer ft.
|
||||
*
|
||||
* Set CTRL to -1 if we don't want to send a control signal.
|
||||
* Set message or self to NULL if we don't want to display a message.
|
||||
*/
|
||||
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||
Notification sound_type)
|
||||
{
|
||||
if (!ft)
|
||||
return;
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||
return;
|
||||
|
||||
if (ft->file)
|
||||
fclose(ft->file);
|
||||
|
||||
if (CTRL >= 0)
|
||||
tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
|
||||
|
||||
if (message && self) {
|
||||
if (self->active_box != -1 && sound_type != silent)
|
||||
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
|
||||
else
|
||||
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
|
||||
}
|
||||
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
}
|
||||
|
||||
/* Kills all active file transfers for friendnum */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
}
|
||||
}
|
||||
|
||||
void kill_all_file_transfers(Tox *m)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < Friends.max_idx; ++i)
|
||||
kill_all_file_transfers_friend(m, Friends.list[i].num);
|
||||
}
|
115
src/file_transfers.h
Normal file
115
src/file_transfers.h
Normal file
@ -0,0 +1,115 @@
|
||||
/* file_transfers.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FILE_TRANSFERS_H
|
||||
#define FILE_TRANSFERS_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define KiB 1024
|
||||
#define MiB 1048576 /* 1024^2 */
|
||||
#define GiB 1073741824 /* 1024^3 */
|
||||
|
||||
#define MAX_FILES 32
|
||||
#define TIMEOUT_FILESENDER 120
|
||||
|
||||
typedef enum FILE_TRANSFER_STATE {
|
||||
FILE_TRANSFER_INACTIVE,
|
||||
FILE_TRANSFER_PAUSED,
|
||||
FILE_TRANSFER_PENDING,
|
||||
FILE_TRANSFER_STARTED,
|
||||
} FILE_TRANSFER_STATE;
|
||||
|
||||
typedef enum FILE_TRANSFER_DIRECTION {
|
||||
FILE_TRANSFER_SEND,
|
||||
FILE_TRANSFER_RECV
|
||||
} FILE_TRANSFER_DIRECTION;
|
||||
|
||||
struct FileTransfer {
|
||||
ToxWindow *window;
|
||||
FILE *file;
|
||||
FILE_TRANSFER_STATE state;
|
||||
FILE_TRANSFER_DIRECTION direction;
|
||||
uint8_t file_type;
|
||||
char file_name[TOX_MAX_FILENAME_LENGTH + 1];
|
||||
char file_path[PATH_MAX + 1]; /* Not used by senders */
|
||||
double bps;
|
||||
uint32_t filenum;
|
||||
uint32_t friendnum;
|
||||
size_t index;
|
||||
uint64_t file_size;
|
||||
uint64_t position;
|
||||
uint64_t last_line_progress; /* The last time we updated the progress bar */
|
||||
uint64_t last_keep_alive; /* The last time we sent or received data */
|
||||
uint32_t line_id;
|
||||
uint8_t file_id[TOX_FILE_ID_LENGTH];
|
||||
};
|
||||
|
||||
/* Checks for timed out file transfers and closes them. */
|
||||
void check_file_transfer_timeouts(Tox *m);
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
progline must be at lesat MAX_STR_SIZE bytes */
|
||||
void init_progress_bar(char *progline);
|
||||
|
||||
/* prints a progress bar for file transfers */
|
||||
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
|
||||
|
||||
/* refreshes active file transfer status bars. */
|
||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
|
||||
|
||||
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||
* Returns NULL if filenum is invalid.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum);
|
||||
|
||||
|
||||
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||
FILE_TRANSFER_DIRECTION direction);
|
||||
|
||||
/* Initializes an unused file transfer and returns its pointer.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||
FILE_TRANSFER_DIRECTION direction, uint8_t type);
|
||||
|
||||
/* Closes file transfer ft.
|
||||
*
|
||||
* Set CTRL to -1 if we don't want to send a control signal.
|
||||
* Set message or self to NULL if we don't want to display a message.
|
||||
*/
|
||||
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||
Notification sound_type);
|
||||
|
||||
/* Kills all active file transfers for friendnum */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
|
||||
|
||||
void kill_all_file_transfers(Tox *m);
|
||||
|
||||
#endif /* #define FILE_TRANSFERS_H */
|
573
src/friendlist.c
573
src/friendlist.c
File diff suppressed because it is too large
Load Diff
@ -20,27 +20,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FRIENDLIST_H_53I41IM
|
||||
#define FRIENDLIST_H_53I41IM
|
||||
#ifndef FRIENDLIST_H
|
||||
#define FRIENDLIST_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "file_senders.h"
|
||||
|
||||
struct FileReceiver {
|
||||
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;
|
||||
};
|
||||
#include "file_transfers.h"
|
||||
|
||||
struct LastOnline {
|
||||
uint64_t last_on;
|
||||
@ -48,51 +35,60 @@ struct LastOnline {
|
||||
char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */
|
||||
};
|
||||
|
||||
struct GroupChatInvite {
|
||||
char *key;
|
||||
uint16_t length;
|
||||
uint8_t type;
|
||||
bool pending;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char name[TOXIC_MAX_NAME_LENGTH];
|
||||
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
int namelength;
|
||||
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
||||
uint16_t statusmsg_len;
|
||||
char groupchat_key[TOX_CLIENT_ID_SIZE];
|
||||
bool groupchat_pending;
|
||||
char pub_key[TOX_CLIENT_ID_SIZE];
|
||||
int32_t num;
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
|
||||
size_t statusmsg_len;
|
||||
char pub_key[TOX_PUBLIC_KEY_SIZE];
|
||||
uint32_t num;
|
||||
int chatwin;
|
||||
bool active;
|
||||
bool online;
|
||||
uint8_t is_typing;
|
||||
TOX_CONNECTION connection_status;
|
||||
bool is_typing;
|
||||
bool logging_on; /* saves preference for friend irrespective of global settings */
|
||||
uint8_t status;
|
||||
|
||||
struct LastOnline last_online;
|
||||
struct FileReceiver file_receiver[MAX_FILES];
|
||||
uint8_t active_file_receivers;
|
||||
struct GroupChatInvite group_invite;
|
||||
|
||||
struct FileTransfer file_receiver[MAX_FILES];
|
||||
struct FileTransfer file_sender[MAX_FILES];
|
||||
} ToxicFriend;
|
||||
|
||||
typedef struct {
|
||||
char name[TOXIC_MAX_NAME_LENGTH];
|
||||
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
int namelength;
|
||||
char pub_key[TOX_CLIENT_ID_SIZE];
|
||||
int32_t num;
|
||||
char pub_key[TOX_PUBLIC_KEY_SIZE];
|
||||
uint32_t num;
|
||||
bool active;
|
||||
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;
|
||||
size_t num_friends;
|
||||
size_t num_online;
|
||||
size_t max_idx; /* 1 + the index of the last friend in list */
|
||||
uint32_t *index;
|
||||
ToxicFriend *list;
|
||||
} _Friends;
|
||||
} FriendsList;
|
||||
|
||||
ToxWindow new_friendlist(void);
|
||||
void disable_chatwin(int32_t f_num);
|
||||
void disable_chatwin(uint32_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);
|
||||
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
|
||||
|
||||
/* sorts friendlist_index first by connection status then alphabetically */
|
||||
void sort_friendlist_index(void);
|
||||
|
||||
#endif /* end of include guard: FRIENDLIST_H_53I41IM */
|
||||
#endif /* end of include guard: FRIENDLIST_H */
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
@ -30,15 +29,18 @@
|
||||
#include "friendlist.h"
|
||||
#include "log.h"
|
||||
#include "line_info.h"
|
||||
#include "dns.h"
|
||||
#include "groupchat.h"
|
||||
#include "prompt.h"
|
||||
#include "help.h"
|
||||
#include "term_mplex.h"
|
||||
#include "avatars.h"
|
||||
#include "name_lookup.h"
|
||||
#include "qr_code.h"
|
||||
|
||||
extern char *DATA_FILE;
|
||||
extern ToxWindow *prompt;
|
||||
extern _Friends Friends;
|
||||
extern _FriendRequests FriendRequests;
|
||||
extern FriendsList Friends;
|
||||
extern FriendRequests FrndRequests;
|
||||
|
||||
/* command functions */
|
||||
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -48,80 +50,89 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
return;
|
||||
}
|
||||
|
||||
int req = atoi(argv[1]);
|
||||
long int req = strtol(argv[1], NULL, 10);
|
||||
|
||||
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
|
||||
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) {
|
||||
if (!FrndRequests.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, FriendRequests.request[req].key);
|
||||
TOX_ERR_FRIEND_ADD err;
|
||||
uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err);
|
||||
|
||||
if (friendnum == -1)
|
||||
msg = "Failed to add friend.";
|
||||
else {
|
||||
msg = "Friend request accepted.";
|
||||
if (err != TOX_ERR_FRIEND_ADD_OK) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err);
|
||||
return;
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
|
||||
on_friendadded(m, friendnum, true);
|
||||
}
|
||||
|
||||
memset(&FriendRequests.request[req], 0, sizeof(struct _friend_request));
|
||||
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = FriendRequests.max_idx; i > 0; --i) {
|
||||
if (FriendRequests.request[i - 1].active)
|
||||
for (i = FrndRequests.max_idx; i > 0; --i) {
|
||||
if (FrndRequests.request[i - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
FriendRequests.max_idx = i;
|
||||
--FriendRequests.num_requests;
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
|
||||
FrndRequests.max_idx = i;
|
||||
--FrndRequests.num_requests;
|
||||
|
||||
}
|
||||
|
||||
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg)
|
||||
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg)
|
||||
{
|
||||
const char *errmsg;
|
||||
int32_t f_num = tox_add_friend(m, (uint8_t *) id_bin, (uint8_t *) msg, (uint16_t) strlen(msg));
|
||||
|
||||
switch (f_num) {
|
||||
case TOX_FAERR_TOOLONG:
|
||||
TOX_ERR_FRIEND_ADD err;
|
||||
uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_FRIEND_ADD_TOO_LONG:
|
||||
errmsg = "Message is too long.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_NOMESSAGE:
|
||||
case TOX_ERR_FRIEND_ADD_NO_MESSAGE:
|
||||
errmsg = "Please add a message to your request.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_OWNKEY:
|
||||
case TOX_ERR_FRIEND_ADD_OWN_KEY:
|
||||
errmsg = "That appears to be your own ID.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_ALREADYSENT:
|
||||
case TOX_ERR_FRIEND_ADD_ALREADY_SENT:
|
||||
errmsg = "Friend request has already been sent.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_UNKNOWN:
|
||||
errmsg = "Undefined error when adding friend.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_BADCHECKSUM:
|
||||
case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM:
|
||||
errmsg = "Bad checksum in address.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_SETNEWNOSPAM:
|
||||
case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM:
|
||||
errmsg = "Nospam was different.";
|
||||
break;
|
||||
|
||||
default:
|
||||
case TOX_ERR_FRIEND_ADD_MALLOC:
|
||||
errmsg = "Core memory allocation failed.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_OK:
|
||||
errmsg = "Friend request sent.";
|
||||
on_friendadded(m, f_num, true);
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_NULL:
|
||||
/* fallthrough */
|
||||
default:
|
||||
errmsg = "Faile to add friend: Unknown error.";
|
||||
break;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
@ -151,21 +162,23 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
snprintf(msg, sizeof(msg), "%s", tmp);
|
||||
} else {
|
||||
char selfname[TOX_MAX_NAME_LENGTH];
|
||||
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfname);
|
||||
tox_self_get_name(m, (uint8_t *) selfname);
|
||||
|
||||
size_t n_len = tox_self_get_name_size(m);
|
||||
selfname[n_len] = '\0';
|
||||
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
|
||||
}
|
||||
|
||||
char id_bin[TOX_FRIEND_ADDRESS_SIZE] = {0};
|
||||
char id_bin[TOX_ADDRESS_SIZE] = {0};
|
||||
uint16_t id_len = (uint16_t) strlen(id);
|
||||
|
||||
/* try to add tox ID */
|
||||
if (id_len == 2 * TOX_FRIEND_ADDRESS_SIZE) {
|
||||
if (id_len == 2 * TOX_ADDRESS_SIZE) {
|
||||
size_t i;
|
||||
char xx[3];
|
||||
uint32_t x;
|
||||
|
||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
|
||||
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
|
||||
xx[0] = id[2 * i];
|
||||
xx[1] = id[2 * i + 1];
|
||||
xx[2] = '\0';
|
||||
@ -179,17 +192,52 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
}
|
||||
|
||||
cmd_add_helper(self, m, id_bin, msg);
|
||||
} else { /* assume id is a username@domain address and do DNS lookup */
|
||||
dns3_lookup(self, m, id_bin, id, msg);
|
||||
} else { /* assume id is a username@domain address and do http name server lookup */
|
||||
name_lookup(self, m, id_bin, id, msg);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 2 || strlen(argv[1]) < 3) {
|
||||
avatar_unset(m);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argv[1][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* remove opening and closing quotes */
|
||||
char path[MAX_STR_SIZE];
|
||||
snprintf(path, sizeof(path), "%s", &argv[1][1]);
|
||||
int len = strlen(path) - 1;
|
||||
|
||||
if (len <= 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
|
||||
return;
|
||||
}
|
||||
|
||||
path[len] = '\0';
|
||||
char filename[MAX_STR_SIZE];
|
||||
get_file_name(filename, sizeof(filename), path);
|
||||
|
||||
if (avatar_set(m, path, len) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||
"Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.",
|
||||
MAX_AVATAR_FILE_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
|
||||
}
|
||||
|
||||
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
line_info_clear(self->chatwin->hst);
|
||||
wclear(window);
|
||||
endwin();
|
||||
refresh();
|
||||
force_refresh(window);
|
||||
}
|
||||
|
||||
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -200,17 +248,41 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
||||
}
|
||||
|
||||
const char *ip = argv[1];
|
||||
const char *port = argv[2];
|
||||
const char *key = argv[3];
|
||||
const char *port_str = argv[2];
|
||||
const char *ascii_key = argv[3];
|
||||
|
||||
if (atoi(port) == 0) {
|
||||
long int port = strtol(port_str, NULL, 10);
|
||||
|
||||
if (port <= 0 || port > MAX_PORT_RANGE) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
|
||||
return;
|
||||
}
|
||||
|
||||
char *binary_string = hex_string_to_bin(key);
|
||||
tox_bootstrap_from_address(m, ip, atoi(port), (uint8_t *) binary_string);
|
||||
free(binary_string);
|
||||
char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||
if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
|
||||
return;
|
||||
}
|
||||
|
||||
TOX_ERR_BOOTSTRAP err;
|
||||
tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err);
|
||||
tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_BOOTSTRAP_BAD_HOST:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP.");
|
||||
break;
|
||||
|
||||
case TOX_ERR_BOOTSTRAP_BAD_PORT:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port.");
|
||||
break;
|
||||
|
||||
case TOX_ERR_BOOTSTRAP_NULL:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -220,29 +292,29 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
||||
return;
|
||||
}
|
||||
|
||||
int req = atoi(argv[1]);
|
||||
long int req = strtol(argv[1], NULL, 10);
|
||||
|
||||
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
|
||||
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) {
|
||||
if (!FrndRequests.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));
|
||||
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = FriendRequests.max_idx; i > 0; --i) {
|
||||
if (FriendRequests.request[i - 1].active)
|
||||
for (i = FrndRequests.max_idx; i > 0; --i) {
|
||||
if (FrndRequests.request[i - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
FriendRequests.max_idx = i;
|
||||
--FriendRequests.num_requests;
|
||||
FrndRequests.max_idx = i;
|
||||
--FrndRequests.num_requests;
|
||||
}
|
||||
|
||||
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -252,14 +324,37 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = tox_add_groupchat(m);
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t type;
|
||||
|
||||
if (!strcasecmp(argv[1], "audio"))
|
||||
type = TOX_GROUPCHAT_TYPE_AV;
|
||||
else if (!strcasecmp(argv[1], "text"))
|
||||
type = TOX_GROUPCHAT_TYPE_TEXT;
|
||||
else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = -1;
|
||||
|
||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||
groupnum = tox_add_groupchat(m);
|
||||
/*#ifdef AUDIO
|
||||
else
|
||||
groupnum = toxav_add_av_groupchat(m, NULL, NULL);
|
||||
#endif*/
|
||||
|
||||
if (groupnum == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
||||
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||
tox_del_groupchat(m, groupnum);
|
||||
return;
|
||||
@ -275,9 +370,9 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
|
||||
if (argc == 0) {
|
||||
if (log->log_on)
|
||||
msg = "Logging for this window is ON. Type \"/log off\" to disable.";
|
||||
msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)";
|
||||
else
|
||||
msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
|
||||
msg = "Logging for this window is OFF; type \"/log on\" to enable.";
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
return;
|
||||
@ -286,19 +381,21 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
const char *swch = argv[1];
|
||||
|
||||
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
int log_ret = -1;
|
||||
|
||||
if (self->is_chat) {
|
||||
Friends.list[self->num].logging_on = true;
|
||||
log_enable(self->name, Friends.list[self->num].pub_key, log);
|
||||
log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
|
||||
} else if (self->is_prompt) {
|
||||
char myid[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(m, (uint8_t *) myid);
|
||||
log_enable(self->name, myid, log);
|
||||
log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT);
|
||||
} else if (self->is_groupchat) {
|
||||
log_enable(self->name, NULL, log);
|
||||
log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP);
|
||||
}
|
||||
|
||||
msg = "Logging enabled";
|
||||
msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
return;
|
||||
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
||||
@ -307,7 +404,7 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
|
||||
log_disable(log);
|
||||
|
||||
msg = "Logging disabled";
|
||||
msg = "Logging disabled.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
return;
|
||||
}
|
||||
@ -318,19 +415,57 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
|
||||
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0};
|
||||
char address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(m, (uint8_t *) address);
|
||||
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||
char bin_id[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) bin_id);
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
|
||||
char xx[3];
|
||||
snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff);
|
||||
strcat(id, xx);
|
||||
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
|
||||
}
|
||||
|
||||
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||
char bin_id[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) bin_id);
|
||||
|
||||
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||
return;
|
||||
}
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
tox_self_get_name(m, (uint8_t *) nick);
|
||||
size_t nick_len = tox_self_get_name_size(m);
|
||||
nick[nick_len] = '\0';
|
||||
|
||||
size_t data_file_len = strlen(DATA_FILE);
|
||||
char dir[data_file_len + 1];
|
||||
size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir);
|
||||
|
||||
char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1];
|
||||
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
|
||||
|
||||
FILE *output = fopen(qr_path, "wb");
|
||||
|
||||
if (output == NULL) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ID_to_QRcode(id_string, output) == -1) {
|
||||
fclose(output);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
|
||||
|
||||
fclose(output);
|
||||
}
|
||||
|
||||
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -341,7 +476,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
}
|
||||
|
||||
char nick[MAX_STR_SIZE];
|
||||
int len = 0;
|
||||
size_t len = 0;
|
||||
|
||||
if (argv[1][0] == '\"') { /* remove opening and closing quotes */
|
||||
snprintf(nick, sizeof(nick), "%s", &argv[1][1]);
|
||||
@ -360,7 +495,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
nick[len] = '\0';
|
||||
|
||||
tox_set_name(m, (uint8_t *) nick, (uint16_t) len);
|
||||
tox_self_set_name(m, (uint8_t *) nick, len, NULL);
|
||||
prompt_update_nick(prompt, nick);
|
||||
|
||||
store_data(m, DATA_FILE);
|
||||
@ -387,6 +522,15 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
prompt_update_statusmessage(prompt, m, msg);
|
||||
}
|
||||
|
||||
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
uint32_t nospam = rand(); /* should be random enough */
|
||||
tox_self_set_nospam(m, nospam);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your Tox ID has been changed to:");
|
||||
cmd_myid(window, self, m, 0, NULL);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Any services that relied on your old ID will need to be updated manually.");
|
||||
}
|
||||
|
||||
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
help_init_menu(self);
|
||||
@ -399,7 +543,7 @@ void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
|
||||
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (FriendRequests.num_requests == 0) {
|
||||
if (FrndRequests.num_requests == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
|
||||
return;
|
||||
}
|
||||
@ -407,64 +551,63 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
int i, j;
|
||||
int count = 0;
|
||||
|
||||
for (i = 0; i < FriendRequests.max_idx; ++i) {
|
||||
if (!FriendRequests.request[i].active)
|
||||
for (i = 0; i < FrndRequests.max_idx; ++i) {
|
||||
if (!FrndRequests.request[i].active)
|
||||
continue;
|
||||
|
||||
char id[TOX_CLIENT_ID_SIZE * 2 + 1] = {0};
|
||||
char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0};
|
||||
|
||||
for (j = 0; j < TOX_CLIENT_ID_SIZE; ++j) {
|
||||
for (j = 0; j < TOX_PUBLIC_KEY_SIZE; ++j) {
|
||||
char d[3];
|
||||
snprintf(d, sizeof(d), "%02X", FriendRequests.request[i].key[j] & 0xff);
|
||||
snprintf(d, sizeof(d), "%02X", FrndRequests.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);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg);
|
||||
|
||||
if (++count < FriendRequests.num_requests)
|
||||
if (++count < FrndRequests.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;
|
||||
const char *errmsg;
|
||||
|
||||
lock_status ();
|
||||
|
||||
if (argc >= 2) {
|
||||
have_note = true;
|
||||
} else if (argc < 1) {
|
||||
errmsg = "Require a status. Statuses are: online, busy and away.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
return;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
char status[MAX_STR_SIZE];
|
||||
snprintf(status, sizeof(status), "%s", argv[1]);
|
||||
str_to_lower(status);
|
||||
const char *status_str = argv[1];
|
||||
TOX_USER_STATUS status;
|
||||
|
||||
TOX_USERSTATUS status_kind;
|
||||
|
||||
if (!strcmp(status, "online"))
|
||||
status_kind = TOX_USERSTATUS_NONE;
|
||||
else if (!strcmp(status, "away"))
|
||||
status_kind = TOX_USERSTATUS_AWAY;
|
||||
else if (!strcmp(status, "busy"))
|
||||
status_kind = TOX_USERSTATUS_BUSY;
|
||||
if (!strcasecmp(status_str, "online"))
|
||||
status = TOX_USER_STATUS_NONE;
|
||||
else if (!strcasecmp(status_str, "away"))
|
||||
status = TOX_USER_STATUS_AWAY;
|
||||
else if (!strcasecmp(status_str, "busy"))
|
||||
status = TOX_USER_STATUS_BUSY;
|
||||
else {
|
||||
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
return;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
tox_set_user_status(m, status_kind);
|
||||
prompt_update_status(prompt, status_kind);
|
||||
tox_self_set_status(m, status);
|
||||
prompt_update_status(prompt, status);
|
||||
|
||||
if (have_note) {
|
||||
if (argv[2][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
|
||||
return;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* remove opening and closing quotes */
|
||||
@ -475,4 +618,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
|
||||
prompt_update_statusmessage(prompt, m, msg);
|
||||
}
|
||||
|
||||
finish:
|
||||
unlock_status ();
|
||||
}
|
||||
|
@ -20,32 +20,40 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _global_commands_h
|
||||
#define _global_commands_h
|
||||
#ifndef GLOBAL_COMMANDS_H
|
||||
#define GLOBAL_COMMANDS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_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]);
|
||||
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
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_nospam(WINDOW *window, ToxWindow *self, Tox *m, 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);
|
||||
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg);
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* _AUDIO */
|
||||
#endif /* AUDIO */
|
||||
|
||||
#endif /* #define _global_commands_h */
|
||||
#ifdef VIDEO
|
||||
void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* VIDEO */
|
||||
|
||||
#endif /* #define GLOBAL_COMMANDS_H */
|
||||
|
79
src/group_commands.c
Normal file
79
src/group_commands.c
Normal file
@ -0,0 +1,79 @@
|
||||
/* group_commands.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 <string.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "log.h"
|
||||
|
||||
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
char title[MAX_STR_SIZE];
|
||||
|
||||
if (argc < 1) {
|
||||
int tlen = tox_group_get_title(m, self->num, (uint8_t *) title, TOX_MAX_NAME_LENGTH);
|
||||
|
||||
if (tlen != -1) {
|
||||
title[tlen] = '\0';
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (argv[1][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* remove opening and closing quotes */
|
||||
snprintf(title, sizeof(title), "%s", &argv[1][1]);
|
||||
int len = strlen(title) - 1;
|
||||
title[len] = '\0';
|
||||
|
||||
if (tox_group_set_title(m, self->num, (uint8_t *) title, len) != 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title.");
|
||||
return;
|
||||
}
|
||||
|
||||
set_window_title(self, title, len);
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||
size_t sn_len = tox_self_get_name_size(m);
|
||||
selfnick[sn_len] = '\0';
|
||||
|
||||
line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
|
||||
|
||||
char tmp_event[MAX_STR_SIZE];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* dns.c
|
||||
/* group_commands.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
@ -20,13 +20,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/* Does DNS lookup for addr and puts resulting tox id in id_bin.
|
||||
Return 0 on success, -1 on failure. */
|
||||
#ifndef GROUP_COMMANDS_H
|
||||
#define GROUP_COMMANDS_H
|
||||
|
||||
#ifndef _dns_h
|
||||
#define _dns_h
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
/* 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);
|
||||
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
#endif /* #define _dns_h */
|
||||
#endif /* GROUP_COMMANDS_H */
|
528
src/groupchat.c
528
src/groupchat.c
@ -29,6 +29,21 @@
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <wchar.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef AUDIO
|
||||
#ifdef __APPLE__
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
/* compatibility with older versions of OpenAL */
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
#include <AL/alext.h>
|
||||
#endif /* ALC_ALL_DEVICES_SPECIFIER */
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* AUDIO */
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
@ -44,37 +59,94 @@
|
||||
#include "help.h"
|
||||
#include "notify.h"
|
||||
#include "autocomplete.h"
|
||||
#include "audio_device.h"
|
||||
|
||||
extern char *DATA_FILE;
|
||||
|
||||
static GroupChat groupchats[MAX_GROUPCHAT_NUM];
|
||||
static int max_groupchat_index = 0;
|
||||
|
||||
extern struct user_settings *user_settings_;
|
||||
extern struct user_settings *user_settings;
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
/* temporary until group chats have unique commands */
|
||||
extern const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
|
||||
#ifdef AUDIO
|
||||
#define AC_NUM_GROUP_COMMANDS 24
|
||||
#else
|
||||
#define AC_NUM_GROUP_COMMANDS 20
|
||||
#endif /* AUDIO */
|
||||
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
||||
/* Array of groupchat command names used for tab completion. */
|
||||
static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
{ "/accept" },
|
||||
{ "/add" },
|
||||
{ "/avatar" },
|
||||
{ "/clear" },
|
||||
{ "/close" },
|
||||
{ "/connect" },
|
||||
{ "/decline" },
|
||||
{ "/exit" },
|
||||
{ "/group" },
|
||||
{ "/help" },
|
||||
{ "/log" },
|
||||
{ "/myid" },
|
||||
{ "/myqr" },
|
||||
{ "/nick" },
|
||||
{ "/note" },
|
||||
{ "/nospam" },
|
||||
{ "/quit" },
|
||||
{ "/requests" },
|
||||
{ "/status" },
|
||||
{ "/title" },
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
{ "/lsdev" },
|
||||
{ "/sdev" },
|
||||
{ "/mute" },
|
||||
{ "/sense" },
|
||||
|
||||
#endif /* AUDIO */
|
||||
};
|
||||
|
||||
#ifdef AUDIO
|
||||
static int group_audio_open_out_device(int groupnum);
|
||||
static int group_audio_close_out_device(int groupnum);
|
||||
#endif /* AUDIO */
|
||||
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type)
|
||||
{
|
||||
if (groupnum > MAX_GROUPCHAT_NUM)
|
||||
return -1;
|
||||
|
||||
ToxWindow self = new_group_chat(m, groupnum);
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= max_groupchat_index; ++i) {
|
||||
if (!groupchats[i].active) {
|
||||
groupchats[i].chatwin = add_window(m, new_group_chat(m, groupnum));
|
||||
groupchats[i].chatwin = add_window(m, self);
|
||||
groupchats[i].active = true;
|
||||
groupchats[i].num_peers = 0;
|
||||
groupchats[i].type = type;
|
||||
groupchats[i].start_time = get_unix_time();
|
||||
|
||||
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
||||
groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
||||
groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t));
|
||||
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
|
||||
|
||||
if (groupchats[i].peer_names == NULL || groupchats[i].oldpeer_names == NULL
|
||||
|| groupchats[i].peer_name_lengths == NULL || groupchats[i].oldpeer_name_lengths == NULL)
|
||||
exit_toxic_err("failed in init_groupchat_win", FATALERR_MEMORY);
|
||||
|
||||
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
||||
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
|
||||
|
||||
#ifdef AUDIO
|
||||
if (type == TOX_GROUPCHAT_TYPE_AV)
|
||||
if (group_audio_open_out_device(i) == -1)
|
||||
fprintf(stderr, "Group Audio failed to init\n");
|
||||
#endif /* AUDIO */
|
||||
|
||||
set_active_window(groupchats[i].chatwin);
|
||||
|
||||
if (i == max_groupchat_index)
|
||||
@ -87,7 +159,7 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void kill_groupchat_window(ToxWindow *self)
|
||||
static void kill_groupchat_window(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
@ -97,15 +169,17 @@ 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);
|
||||
}
|
||||
|
||||
static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
||||
void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
||||
{
|
||||
tox_del_groupchat(m, groupnum);
|
||||
#ifdef AUDIO
|
||||
group_audio_close_out_device(groupnum);
|
||||
#endif
|
||||
|
||||
free(groupchats[groupnum].peer_names);
|
||||
free(groupchats[groupnum].oldpeer_names);
|
||||
@ -124,6 +198,45 @@ static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
||||
kill_groupchat_window(self);
|
||||
}
|
||||
|
||||
/* destroys and re-creates groupchat window with or without the peerlist */
|
||||
void redraw_groupchat_win(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
endwin();
|
||||
refresh();
|
||||
clear();
|
||||
|
||||
int x2, y2;
|
||||
getmaxyx(stdscr, y2, x2);
|
||||
y2 -= 2;
|
||||
|
||||
if (y2 <= 0 || x2 <= 0)
|
||||
return;
|
||||
|
||||
if (ctx->sidebar) {
|
||||
delwin(ctx->sidebar);
|
||||
ctx->sidebar = NULL;
|
||||
}
|
||||
|
||||
delwin(ctx->linewin);
|
||||
delwin(ctx->history);
|
||||
delwin(self->window);
|
||||
|
||||
self->window = newwin(y2, x2, 0, 0);
|
||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||
|
||||
if (self->show_peerlist) {
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||
} else {
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
||||
}
|
||||
|
||||
scrollok(ctx->history, 0);
|
||||
|
||||
}
|
||||
|
||||
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
||||
const char *msg, uint16_t len)
|
||||
{
|
||||
@ -133,20 +246,20 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
int n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
|
||||
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1); /* enforce client max name length */
|
||||
nick[n_len] = '\0';
|
||||
get_group_nick_truncate(m, nick, peernum, groupnum);
|
||||
|
||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||
uint16_t sn_len = tox_get_self_name(m, (uint8_t *) selfnick);
|
||||
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||
|
||||
size_t sn_len = tox_self_get_name_size(m);
|
||||
selfnick[sn_len] = '\0';
|
||||
|
||||
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
|
||||
|
||||
/* Only play sound if mentioned */
|
||||
if (strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH - 1)) {
|
||||
/* Only play sound if mentioned by someone else */
|
||||
if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) {
|
||||
sound_notify(self, generic_message, NT_WNDALERT_0, NULL);
|
||||
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg);
|
||||
else
|
||||
@ -154,7 +267,9 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
|
||||
|
||||
nick_clr = RED;
|
||||
}
|
||||
else sound_notify(self, silent, NT_WNDALERT_1, NULL);
|
||||
else {
|
||||
sound_notify(self, silent, NT_WNDALERT_1, NULL);
|
||||
}
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
@ -171,37 +286,60 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_group_nick_truncate(m, nick, peernum, groupnum);
|
||||
|
||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfnick);
|
||||
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||
|
||||
size_t n_len = tox_self_get_name_size(m);
|
||||
selfnick[n_len] = '\0';
|
||||
|
||||
if (strcasestr(action, selfnick)) {
|
||||
sound_notify(self, generic_message, NT_WNDALERT_0, NULL);
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
int n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
|
||||
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1); /* enforce client max name length */
|
||||
nick[n_len] = '\0';
|
||||
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
|
||||
else
|
||||
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action);
|
||||
}
|
||||
else sound_notify(self, silent, NT_WNDALERT_1, NULL);
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
|
||||
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
nick[n_len] = '\0';
|
||||
else {
|
||||
sound_notify(self, silent, NT_WNDALERT_1, NULL);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *title,
|
||||
uint8_t length)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (self->num != groupnum)
|
||||
return;
|
||||
|
||||
set_window_title(self, title, length);
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
/* don't announce title when we join the room */
|
||||
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
|
||||
return;
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_group_nick_truncate(m, nick, peernum, groupnum);
|
||||
line_info_add(self, timefrmt, nick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
|
||||
|
||||
char tmp_event[MAX_STR_SIZE];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||
write_to_log(tmp_event, nick, ctx->log, true);
|
||||
}
|
||||
|
||||
/* Puts two copies of peerlist/lengths in chat instance */
|
||||
static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], uint16_t lengths[], int npeers)
|
||||
{
|
||||
@ -223,31 +361,75 @@ static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], ui
|
||||
exit_toxic_err("failed in copy_peernames", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
uint16_t unknown_len = (uint16_t) strlen(UNKNOWN_NAME);
|
||||
uint16_t u_len = strlen(UNKNOWN_NAME);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < npeers; ++i) {
|
||||
if (string_is_empty((char *) peerlist[i])) {
|
||||
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
||||
groupchats[gnum].peer_name_lengths[i] = unknown_len;
|
||||
if (!lengths[i]) {
|
||||
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, u_len);
|
||||
groupchats[gnum].peer_names[i * N + u_len] = '\0';
|
||||
groupchats[gnum].peer_name_lengths[i] = u_len;
|
||||
} else {
|
||||
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], N);
|
||||
uint16_t n_len = lengths[i];
|
||||
|
||||
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
|
||||
uint16_t n_len = MIN(lengths[i], TOXIC_MAX_NAME_LENGTH - 1);
|
||||
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], n_len);
|
||||
groupchats[gnum].peer_names[i * N + n_len] = '\0';
|
||||
groupchats[gnum].peer_name_lengths[i] = n_len;
|
||||
filter_str((char *) &groupchats[gnum].peer_names[i * N], n_len);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
|
||||
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths,
|
||||
sizeof(uint16_t) * npeers);
|
||||
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths, sizeof(uint16_t) * npeers);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
||||
uint8_t change)
|
||||
struct group_add_thrd {
|
||||
Tox *m;
|
||||
ToxWindow *self;
|
||||
int peernum;
|
||||
int groupnum;
|
||||
uint64_t timestamp;
|
||||
pthread_t tid;
|
||||
pthread_attr_t attr;
|
||||
};
|
||||
|
||||
/* Waits GROUP_EVENT_WAIT seconds for a new peer to set their name before announcing them */
|
||||
void *group_add_wait(void *data)
|
||||
{
|
||||
struct group_add_thrd *thrd = (struct group_add_thrd *) data;
|
||||
ToxWindow *self = thrd->self;
|
||||
Tox *m = thrd->m;
|
||||
char peername[TOX_MAX_NAME_LENGTH];
|
||||
|
||||
/* keep polling for a name that differs from the default until we run out of time */
|
||||
while (true) {
|
||||
usleep(100000);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
get_group_nick_truncate(m, peername, thrd->peernum, thrd->groupnum);
|
||||
|
||||
if (strcmp(peername, DEFAULT_TOX_NAME) || timed_out(thrd->timestamp, GROUP_EVENT_WAIT)) {
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
}
|
||||
|
||||
const char *event = "has joined the room";
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_add(self, timefrmt, (char *) peername, NULL, CONNECTION, 0, GREEN, event);
|
||||
write_to_log(event, (char *) peername, self->chatwin->log, true);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
pthread_attr_destroy(&thrd->attr);
|
||||
free(thrd);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t change)
|
||||
{
|
||||
if (self->num != groupnum)
|
||||
return;
|
||||
@ -274,7 +456,12 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
||||
/* Update name/len lists */
|
||||
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
|
||||
uint16_t tmp_peerlens[num_peers];
|
||||
tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers);
|
||||
|
||||
if (tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers) == -1) {
|
||||
memset(tmp_peerlist, 0, sizeof(tmp_peerlist));
|
||||
memset(tmp_peerlens, 0, sizeof(tmp_peerlens));
|
||||
}
|
||||
|
||||
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
|
||||
|
||||
/* get current peername then sort namelist */
|
||||
@ -296,14 +483,38 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
||||
|
||||
switch (change) {
|
||||
case TOX_CHAT_CHANGE_PEER_ADD:
|
||||
event = "has joined the room";
|
||||
line_info_add(self, timefrmt, (char *) peername, NULL, CONNECTION, 0, GREEN, event);
|
||||
write_to_log(event, (char *) peername, ctx->log, true);
|
||||
if (!timed_out(groupchats[groupnum].start_time, GROUP_EVENT_WAIT))
|
||||
break;
|
||||
|
||||
struct group_add_thrd *thrd = malloc(sizeof(struct group_add_thrd));
|
||||
thrd->m = m;
|
||||
thrd->peernum = peernum;
|
||||
thrd->groupnum = groupnum;
|
||||
thrd->self = self;
|
||||
thrd->timestamp = get_unix_time();
|
||||
|
||||
if (pthread_attr_init(&thrd->attr) != 0) {
|
||||
free(thrd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pthread_attr_setdetachstate(&thrd->attr, PTHREAD_CREATE_DETACHED) != 0) {
|
||||
pthread_attr_destroy(&thrd->attr);
|
||||
free(thrd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pthread_create(&thrd->tid, &thrd->attr, group_add_wait, (void *) thrd) != 0) {
|
||||
pthread_attr_destroy(&thrd->attr);
|
||||
free(thrd);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TOX_CHAT_CHANGE_PEER_DEL:
|
||||
event = "has left the room";
|
||||
line_info_add(self, timefrmt, (char *) oldpeername, NULL, CONNECTION, 0, 0, event);
|
||||
line_info_add(self, timefrmt, (char *) oldpeername, NULL, DISCONNECTION, 0, RED, event);
|
||||
|
||||
if (groupchats[self->num].side_pos > 0)
|
||||
--groupchats[self->num].side_pos;
|
||||
@ -312,6 +523,13 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
||||
break;
|
||||
|
||||
case TOX_CHAT_CHANGE_PEER_NAME:
|
||||
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
|
||||
return;
|
||||
|
||||
/* ignore initial name change (TODO: this is a bad way to do this) */
|
||||
if (strcmp((char *) oldpeername, DEFAULT_TOX_NAME) == 0)
|
||||
return;
|
||||
|
||||
event = " is now known as ";
|
||||
line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, NAME_CHANGE, 0, 0, event);
|
||||
|
||||
@ -345,7 +563,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
getyx(self->window, y, x);
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= 0)
|
||||
if (x2 <= 0 || y2 <= 0)
|
||||
return;
|
||||
|
||||
if (self->help->active) {
|
||||
@ -368,29 +586,33 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
if (ctx->len > 0) {
|
||||
int diff;
|
||||
|
||||
if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e'))
|
||||
diff = complete_line(self, groupchats[self->num].peer_names,
|
||||
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
|
||||
else
|
||||
diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
|
||||
/* TODO: make this not suck */
|
||||
if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) {
|
||||
diff = complete_line(self, groupchats[self->num].peer_names, groupchats[self->num].num_peers,
|
||||
TOX_MAX_NAME_LENGTH);
|
||||
} else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) {
|
||||
diff = dir_match(self, m, ctx->line, L"/avatar");
|
||||
} else {
|
||||
diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE);
|
||||
}
|
||||
|
||||
if (diff != -1) {
|
||||
if (x + diff > x2 - 1) {
|
||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
||||
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
||||
}
|
||||
} else {
|
||||
sound_notify(self, error, 0, NULL);
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
} else {
|
||||
sound_notify(self, error, 0, NULL);
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
} else if (key == user_settings_->key_peer_list_down) { /* Scroll peerlist up and down one position */
|
||||
} else if (key == user_settings->key_peer_list_down) { /* Scroll peerlist up and down one position */
|
||||
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
|
||||
|
||||
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
|
||||
++groupchats[self->num].side_pos;
|
||||
} else if (key == user_settings_->key_peer_list_up) {
|
||||
} else if (key == user_settings->key_peer_list_up) {
|
||||
if (groupchats[self->num].side_pos > 0)
|
||||
--groupchats[self->num].side_pos;
|
||||
} else if (key == '\n') {
|
||||
@ -431,9 +653,15 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= 0 || y2 <= 0)
|
||||
return;
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_print(self);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
wclear(ctx->linewin);
|
||||
|
||||
curs_set(1);
|
||||
@ -442,41 +670,43 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
||||
|
||||
wclear(ctx->sidebar);
|
||||
|
||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
||||
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
|
||||
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
|
||||
|
||||
int num_peers = groupchats[self->num].num_peers;
|
||||
if (self->show_peerlist) {
|
||||
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
|
||||
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
|
||||
|
||||
wmove(ctx->sidebar, 0, 1);
|
||||
wattron(ctx->sidebar, A_BOLD);
|
||||
wprintw(ctx->sidebar, "Peers: %d\n", num_peers);
|
||||
wattroff(ctx->sidebar, A_BOLD);
|
||||
int num_peers = groupchats[self->num].num_peers;
|
||||
|
||||
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
||||
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
||||
wmove(ctx->sidebar, 0, 1);
|
||||
wattron(ctx->sidebar, A_BOLD);
|
||||
wprintw(ctx->sidebar, "Peers: %d\n", num_peers);
|
||||
wattroff(ctx->sidebar, A_BOLD);
|
||||
|
||||
int N = TOX_MAX_NAME_LENGTH;
|
||||
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
||||
int i;
|
||||
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
||||
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
||||
|
||||
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
||||
wmove(ctx->sidebar, i + 2, 1);
|
||||
int peer = i + groupchats[self->num].side_pos;
|
||||
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
||||
int i;
|
||||
|
||||
/* truncate nick to fit in side panel without modifying list */
|
||||
char tmpnck[TOX_MAX_NAME_LENGTH];
|
||||
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * N], SIDEBAR_WIDTH - 2);
|
||||
tmpnck[SIDEBAR_WIDTH - 2] = '\0';
|
||||
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
||||
wmove(ctx->sidebar, i + 2, 1);
|
||||
int peer = i + groupchats[self->num].side_pos;
|
||||
|
||||
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
||||
/* truncate nick to fit in side panel without modifying list */
|
||||
char tmpnck[TOX_MAX_NAME_LENGTH];
|
||||
int maxlen = SIDEBAR_WIDTH - 2;
|
||||
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * TOX_MAX_NAME_LENGTH], maxlen);
|
||||
tmpnck[maxlen] = '\0';
|
||||
|
||||
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
||||
}
|
||||
}
|
||||
|
||||
int y, x;
|
||||
getyx(self->window, y, x);
|
||||
(void) x;
|
||||
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
|
||||
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||
wmove(self->window, y + 1, new_x);
|
||||
|
||||
wrefresh(self->window);
|
||||
@ -490,6 +720,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= 0 || y2 <= 0)
|
||||
exit_toxic_err("failed in groupchat_onInit", FATALERR_CURSES);
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||
@ -504,8 +737,13 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
|
||||
|
||||
line_info_init(ctx->hst);
|
||||
|
||||
if (user_settings_->autolog == AUTOLOG_ON)
|
||||
log_enable(self->name, NULL, ctx->log);
|
||||
if (user_settings->autolog == AUTOLOG_ON) {
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||
}
|
||||
|
||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
||||
|
||||
@ -513,6 +751,124 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
}
|
||||
|
||||
|
||||
#ifdef AUDIO
|
||||
static int group_audio_open_out_device(int groupnum)
|
||||
{
|
||||
char dname[MAX_STR_SIZE];
|
||||
get_primary_device_name(output, dname, sizeof(dname));
|
||||
dname[MAX_STR_SIZE - 1] = '\0';
|
||||
|
||||
groupchats[groupnum].audio.dvhandle = alcOpenDevice(dname);
|
||||
|
||||
if (groupchats[groupnum].audio.dvhandle == NULL)
|
||||
return -1;
|
||||
|
||||
groupchats[groupnum].audio.dvctx = alcCreateContext(groupchats[groupnum].audio.dvhandle, NULL);
|
||||
alcMakeContextCurrent(groupchats[groupnum].audio.dvctx);
|
||||
alGenBuffers(OPENAL_BUFS, groupchats[groupnum].audio.buffers);
|
||||
alGenSources((uint32_t) 1, &groupchats[groupnum].audio.source);
|
||||
alSourcei(groupchats[groupnum].audio.source, AL_LOOPING, AL_FALSE);
|
||||
|
||||
if (alcGetError(groupchats[groupnum].audio.dvhandle) != AL_NO_ERROR) {
|
||||
group_audio_close_out_device(groupnum);
|
||||
groupchats[groupnum].audio.dvhandle = NULL;
|
||||
groupchats[groupnum].audio.dvctx = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
alSourceQueueBuffers(groupchats[groupnum].audio.source, OPENAL_BUFS, groupchats[groupnum].audio.buffers);
|
||||
alSourcePlay(groupchats[groupnum].audio.source);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int group_audio_close_out_device(int groupnum)
|
||||
{
|
||||
if (!groupchats[groupnum].audio.dvhandle)
|
||||
return -1;
|
||||
|
||||
if (!groupchats[groupnum].audio.dvctx)
|
||||
return -1;
|
||||
|
||||
if (alcGetCurrentContext() != groupchats[groupnum].audio.dvctx)
|
||||
alcMakeContextCurrent(groupchats[groupnum].audio.dvctx);
|
||||
|
||||
alDeleteSources((uint32_t) 1, &groupchats[groupnum].audio.source);
|
||||
alDeleteBuffers(OPENAL_BUFS, groupchats[groupnum].audio.buffers);
|
||||
|
||||
alcMakeContextCurrent(NULL);
|
||||
alcDestroyContext(groupchats[groupnum].audio.dvctx);
|
||||
|
||||
if (!alcCloseDevice(groupchats[groupnum].audio.dvhandle))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// static int group_audio_write(int peernum, int groupnum, const int16_t *pcm, unsigned int samples, uint8_t channels,
|
||||
// unsigned int sample_rate)
|
||||
// {
|
||||
// if (!pcm)
|
||||
// return -1;
|
||||
|
||||
// if (channels == 0 || channels > 2)
|
||||
// return -2;
|
||||
|
||||
// ALuint bufid;
|
||||
// ALint processed = 0, queued = 0;
|
||||
|
||||
// alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_PROCESSED, &processed);
|
||||
// alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_QUEUED, &queued);
|
||||
// fprintf(stderr, "source: %d, queued: %d, processed: %d\n", groupchats[groupnum].audio.source, queued, processed);
|
||||
|
||||
// if (processed) {
|
||||
// ALuint bufids[processed];
|
||||
// alSourceUnqueueBuffers(groupchats[groupnum].audio.source, processed, bufids);
|
||||
// alDeleteBuffers(processed - 1, bufids + 1);
|
||||
// bufid = bufids[0];
|
||||
// } else if (queued < 16) {
|
||||
// alGenBuffers(1, &bufid);
|
||||
// } else {
|
||||
// return -3;
|
||||
// }
|
||||
|
||||
// int length = samples * channels * sizeof(int16_t);
|
||||
|
||||
// alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, pcm, length, sample_rate);
|
||||
// alSourceQueueBuffers(groupchats[groupnum].audio.source, 1, &bufid);
|
||||
|
||||
// ALint state;
|
||||
// alGetSourcei(groupchats[groupnum].audio.source, AL_SOURCE_STATE, &state);
|
||||
|
||||
// if (state != AL_PLAYING)
|
||||
// alSourcePlay(groupchats[groupnum].audio.source);
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
static void groupchat_onWriteDevice(ToxWindow *self, Tox *m, uint32_t groupnum, int peernum, const int16_t *pcm,
|
||||
unsigned int samples, uint8_t channels, unsigned int sample_rate)
|
||||
{
|
||||
return;
|
||||
|
||||
// if (groupnum != self->num)
|
||||
// return;
|
||||
|
||||
// if (peernum < 0)
|
||||
// return;
|
||||
|
||||
// if (groupchats[groupnum].audio.dvhandle == NULL)
|
||||
// fprintf(stderr, "dvhandle is null)\n");
|
||||
|
||||
// if (groupchats[groupnum].audio.dvctx == NULL)
|
||||
// fprintf(stderr, "ctx is null\n");
|
||||
|
||||
// int ret = group_audio_write(peernum, groupnum, pcm, samples, channels, sample_rate);
|
||||
// fprintf(stderr, "write: %d\n", ret);
|
||||
}
|
||||
#endif /* AUDIO */
|
||||
|
||||
ToxWindow new_group_chat(Tox *m, int groupnum)
|
||||
{
|
||||
ToxWindow ret;
|
||||
@ -527,6 +883,11 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
|
||||
ret.onGroupMessage = &groupchat_onGroupMessage;
|
||||
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
|
||||
ret.onGroupAction = &groupchat_onGroupAction;
|
||||
ret.onGroupTitleChange = &groupchat_onGroupTitleChange;
|
||||
|
||||
#ifdef AUDIO
|
||||
ret.onWriteDevice = &groupchat_onWriteDevice;
|
||||
#endif
|
||||
|
||||
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
|
||||
|
||||
@ -540,6 +901,7 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
|
||||
ret.help = help;
|
||||
|
||||
ret.num = groupnum;
|
||||
ret.show_peerlist = true;
|
||||
ret.active_box = -1;
|
||||
|
||||
return ret;
|
||||
|
@ -20,29 +20,67 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _groupchat_h
|
||||
#define _groupchat_h
|
||||
#ifndef GROUPCHAT_H
|
||||
#define GROUPCHAT_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#ifdef AUDIO
|
||||
#include "audio_call.h"
|
||||
#endif
|
||||
|
||||
#ifdef AUDIO
|
||||
#ifdef __APPLE__
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
/* compatibility with older versions of OpenAL */
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
#include <AL/alext.h>
|
||||
#endif /* ALC_ALL_DEVICES_SPECIFIER */
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* AUDIO */
|
||||
|
||||
#define SIDEBAR_WIDTH 16
|
||||
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
||||
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
|
||||
#define GROUP_EVENT_WAIT 3
|
||||
|
||||
#ifdef AUDIO
|
||||
struct GAudio {
|
||||
ALCdevice *dvhandle; /* Handle of device selected/opened */
|
||||
ALCcontext *dvctx;
|
||||
ALuint source;
|
||||
ALuint buffers[OPENAL_BUFS];
|
||||
};
|
||||
#endif /* AUDIO */
|
||||
|
||||
typedef struct {
|
||||
int chatwin;
|
||||
bool active;
|
||||
uint8_t type;
|
||||
int num_peers;
|
||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||
uint64_t start_time;
|
||||
uint8_t *peer_names;
|
||||
uint8_t *oldpeer_names;
|
||||
uint16_t *peer_name_lengths;
|
||||
uint16_t *oldpeer_name_lengths;
|
||||
|
||||
#ifdef AUDIO
|
||||
struct GAudio audio;
|
||||
#endif
|
||||
} GroupChat;
|
||||
|
||||
void kill_groupchat_window(ToxWindow *self);
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum);
|
||||
void close_groupchat(ToxWindow *self, Tox *m, int groupnum);
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type);
|
||||
|
||||
/* destroys and re-creates groupchat window with or without the peerlist */
|
||||
void redraw_groupchat_win(ToxWindow *self);
|
||||
|
||||
ToxWindow new_group_chat(Tox *m, int groupnum);
|
||||
|
||||
#endif /* #define _groupchat_h */
|
||||
#endif /* #define GROUPCHAT_H */
|
||||
|
87
src/help.c
87
src/help.c
@ -27,7 +27,7 @@
|
||||
#include "help.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#define HELP_MENU_HEIGHT 8
|
||||
#define HELP_MENU_HEIGHT 9
|
||||
#define HELP_MENU_WIDTH 26
|
||||
|
||||
void help_init_menu(ToxWindow *self)
|
||||
@ -60,6 +60,9 @@ static void help_init_window(ToxWindow *self, int height, int width)
|
||||
int y2, x2;
|
||||
getmaxyx(stdscr, y2, x2);
|
||||
|
||||
if (y2 <= 0 || x2 <= 0)
|
||||
return;
|
||||
|
||||
height = MIN(height, y2);
|
||||
width = MIN(width, x2);
|
||||
|
||||
@ -86,6 +89,12 @@ static void help_draw_menu(ToxWindow *self)
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "hat commands\n");
|
||||
|
||||
wprintw(win, " g");
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "r");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "oup commands\n");
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, " f");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
@ -138,27 +147,39 @@ static void help_draw_global(ToxWindow *self)
|
||||
|
||||
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
|
||||
wprintw(win, " /accept <id> : Accept friend request\n");
|
||||
wprintw(win, " /avatar <path> : Set an avatar (leave path empty to unset)\n");
|
||||
wprintw(win, " /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");
|
||||
wprintw(win, " /nick <nick> : Set your nickname\n");
|
||||
wprintw(win, " /nospam : Change part of your Tox ID to stop spam\n");
|
||||
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
|
||||
wprintw(win, " /groupchat : Create a group chat\n");
|
||||
wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
|
||||
wprintw(win, " /myid : Print your Tox ID\n");
|
||||
wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n");
|
||||
wprintw(win, " /clear : Clear window history\n");
|
||||
wprintw(win, " /close : Close the current chat window\n");
|
||||
wprintw(win, " /quit or /exit : Exit Toxic\n");
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Audio:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, " /lsdev <type> : List devices where type: in|out\n");
|
||||
wprintw(win, " /sdev <type> <id> : Set active device\n");
|
||||
#endif /* _AUDIO */
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef VIDEO
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Video:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, " /lsvdev <type> : List video devices where type: in|out\n");
|
||||
wprintw(win, " /svdev <type> <id> : Set active video device\n");
|
||||
#endif /* VIDEO */
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
@ -182,7 +203,7 @@ static void help_draw_chat(ToxWindow *self)
|
||||
wprintw(win, " /savefile <id> : Receive a file\n");
|
||||
wprintw(win, " /cancel <type> <id> : Cancel file transfer where type: in|out\n");
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Audio:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
@ -193,8 +214,15 @@ static void help_draw_chat(ToxWindow *self)
|
||||
wprintw(win, " /hangup : Hangup active call\n");
|
||||
wprintw(win, " /sdev <type> <id> : Change active device\n");
|
||||
wprintw(win, " /mute <type> : Mute active device if in call\n");
|
||||
wprintw(win, " /sense <n> : VAD sensitivity treshold\n");
|
||||
#endif /* _AUDIO */
|
||||
wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef VIDEO
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Video:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
wprintw(win, " /video : Toggle video call\n");
|
||||
#endif /* VIDEO */
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
@ -212,11 +240,12 @@ static void help_draw_keys(ToxWindow *self)
|
||||
wprintw(win, "Key bindings:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, " Ctrl+O and Ctrl+P : Navigate through the tabs\n");
|
||||
wprintw(win, " Ctrl+O and Ctrl+P : Navigate through the tabs\n");
|
||||
wprintw(win, " Page Up and Page Down : Scroll window history one line\n");
|
||||
wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\n");
|
||||
wprintw(win, " Ctrl+H : Move to the bottom of window history\n");
|
||||
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n\n");
|
||||
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n");
|
||||
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n\n");
|
||||
wprintw(win, " (Note: Custom keybindings override these defaults.)\n\n");
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
@ -225,6 +254,24 @@ static void help_draw_keys(ToxWindow *self)
|
||||
wrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_group(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
|
||||
wmove(win, 1, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||
wprintw(win, "Group commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, " /title <msg> : Set group title (show current title if no msg)\n\n");
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_contacts(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
@ -256,30 +303,39 @@ void help_onKey(ToxWindow *self, wint_t key)
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
#ifdef _AUDIO
|
||||
#ifdef VIDEO
|
||||
help_init_window(self, 22, 80);
|
||||
#elif AUDIO
|
||||
help_init_window(self, 19, 80);
|
||||
#else
|
||||
help_init_window(self, 9, 80);
|
||||
help_init_window(self, 10, 80);
|
||||
#endif
|
||||
self->help->type = HELP_CHAT;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
#ifdef _AUDIO
|
||||
help_init_window(self, 23, 80);
|
||||
#ifdef VIDEO
|
||||
help_init_window(self, 30, 80);
|
||||
#elif AUDIO
|
||||
help_init_window(self, 26, 80);
|
||||
#else
|
||||
help_init_window(self, 19, 80);
|
||||
help_init_window(self, 22, 80);
|
||||
#endif
|
||||
self->help->type = HELP_GLOBAL;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
help_init_window(self, 6, 80);
|
||||
self->help->type = HELP_GROUP;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
help_init_window(self, 10, 80);
|
||||
self->help->type = HELP_CONTACTS;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
help_init_window(self, 12, 80);
|
||||
help_init_window(self, 13, 80);
|
||||
self->help->type = HELP_KEYS;
|
||||
break;
|
||||
|
||||
@ -316,6 +372,7 @@ void help_onDraw(ToxWindow *self)
|
||||
break;
|
||||
|
||||
case HELP_GROUP:
|
||||
help_draw_group(self);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _help_h
|
||||
#define _help_h
|
||||
#ifndef HELP_H
|
||||
#define HELP_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
@ -39,4 +39,4 @@ void help_onDraw(ToxWindow *self);
|
||||
void help_init_menu(ToxWindow *self);
|
||||
void help_onKey(ToxWindow *self, wint_t key);
|
||||
|
||||
#endif /* #define _help_h */
|
||||
#endif /* #define HELP_H */
|
||||
|
55
src/input.c
55
src/input.c
@ -32,6 +32,10 @@
|
||||
#include "toxic_strings.h"
|
||||
#include "line_info.h"
|
||||
#include "notify.h"
|
||||
#include "groupchat.h"
|
||||
#include "settings.h"
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
/* add a char to input field and buffer */
|
||||
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||
@ -42,12 +46,12 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_
|
||||
|
||||
/* this is the only place we need to do this check */
|
||||
if (cur_len == -1) {
|
||||
sound_notify(self, error, 0, NULL);
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (add_char_to_buf(ctx, key) == -1) {
|
||||
sound_notify(self, error, 0, NULL);
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -63,7 +67,7 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (del_char_buf_bck(ctx) == -1) {
|
||||
sound_notify(self, error, 0, NULL);
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -80,21 +84,32 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
|
||||
static void input_delete(ToxWindow *self)
|
||||
{
|
||||
if (del_char_buf_frnt(self->chatwin) == -1)
|
||||
sound_notify(self, error, 0, NULL);
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
|
||||
/* delete last typed word */
|
||||
static void input_del_word(ToxWindow *self, int x, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (del_word_buf(ctx) == -1) {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* deletes entire line before cursor from input field and buffer */
|
||||
static void input_discard(ToxWindow *self)
|
||||
{
|
||||
if (discard_buf(self->chatwin) == -1)
|
||||
sound_notify(self, error, 0, NULL);
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
|
||||
/* deletes entire line after cursor from input field and buffer */
|
||||
static void input_kill(ChatContext *ctx)
|
||||
{
|
||||
if (kill_buf(ctx) == -1)
|
||||
sound_notify(NULL, error, NT_ALWAYS, NULL);
|
||||
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
}
|
||||
|
||||
static void input_yank(ToxWindow *self, int x, int mx_x)
|
||||
@ -102,7 +117,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (yank_buf(ctx) == -1) {
|
||||
sound_notify(self, error, 0, NULL);
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -110,7 +125,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
|
||||
|
||||
if (x + yank_cols >= mx_x) {
|
||||
int rmdr = MAX(0, (x + yank_cols) - mx_x);
|
||||
int s_len = wcswidth(&ctx->line[ctx->start], rmdr);
|
||||
int s_len = MAX(0, wcswidth(&ctx->line[ctx->start], rmdr));
|
||||
ctx->start += s_len + 1;
|
||||
}
|
||||
}
|
||||
@ -122,7 +137,7 @@ static void input_mv_end(ToxWindow *self, int y, int mx_x)
|
||||
|
||||
ctx->pos = ctx->len;
|
||||
|
||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
||||
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
|
||||
}
|
||||
|
||||
@ -181,7 +196,7 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x)
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
fetch_hist_item(ctx, key);
|
||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
||||
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||
ctx->start = wlen < mx_x ? 0 : wlen - mx_x + 1;
|
||||
}
|
||||
|
||||
@ -213,6 +228,10 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||
input_yank(self, x, mx_x);
|
||||
break;
|
||||
|
||||
case T_KEY_C_W:
|
||||
input_del_word(self, x, mx_x);
|
||||
break;
|
||||
|
||||
case KEY_HOME:
|
||||
case T_KEY_C_A:
|
||||
input_mv_home(self);
|
||||
@ -236,10 +255,26 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||
input_history(self, key, mx_x);
|
||||
break;
|
||||
|
||||
case T_KEY_C_L:
|
||||
force_refresh(self->chatwin->history);
|
||||
break;
|
||||
|
||||
default:
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* TODO: this special case is ugly.
|
||||
maybe convert entire function to if/else and make them all customizable keys? */
|
||||
if (!match && key == user_settings->key_toggle_peerlist) {
|
||||
if (self->is_groupchat) {
|
||||
self->show_peerlist ^= 1;
|
||||
redraw_groupchat_win(self);
|
||||
}
|
||||
|
||||
match = true;
|
||||
}
|
||||
|
||||
|
||||
return match;
|
||||
}
|
||||
|
@ -20,8 +20,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _input_h
|
||||
#define _input_h
|
||||
#ifndef INPUT_H
|
||||
#define INPUT_H
|
||||
|
||||
/* add a char to input field and buffer for given chatcontext */
|
||||
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
|
||||
@ -30,4 +30,4 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_
|
||||
return true if key matches a function, false otherwise */
|
||||
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
|
||||
|
||||
#endif /* #define _input_h */
|
||||
#endif /* #define INPUT_H */
|
205
src/line_info.c
205
src/line_info.c
@ -31,8 +31,10 @@
|
||||
#include "groupchat.h"
|
||||
#include "settings.h"
|
||||
#include "notify.h"
|
||||
#include "message_queue.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
extern struct user_settings *user_settings_;
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
void line_info_init(struct history *hst)
|
||||
{
|
||||
@ -46,8 +48,8 @@ void line_info_init(struct history *hst)
|
||||
hst->queue_sz = 0;
|
||||
}
|
||||
|
||||
/* resets line_start (page end) */
|
||||
static void line_info_reset_start(ToxWindow *self, struct history *hst)
|
||||
/* resets line_start (moves to end of chat history) */
|
||||
void line_info_reset_start(ToxWindow *self, struct history *hst)
|
||||
{
|
||||
struct line_info *line = hst->line_end;
|
||||
|
||||
@ -57,7 +59,7 @@ static void line_info_reset_start(ToxWindow *self, struct history *hst)
|
||||
int y2, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
int side_offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;
|
||||
int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;
|
||||
int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
|
||||
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
|
||||
|
||||
@ -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,22 +111,13 @@ 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 */
|
||||
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
|
||||
static struct line_info *line_info_ret_queue(struct history *hst)
|
||||
{
|
||||
if (hst->queue_sz <= 0)
|
||||
return NULL;
|
||||
|
||||
struct line_info *ret = hst->queue[0];
|
||||
struct line_info *line = hst->queue[0];
|
||||
|
||||
int i;
|
||||
|
||||
@ -131,21 +126,27 @@ static struct line_info *line_info_ret_queue(struct history *hst)
|
||||
|
||||
--hst->queue_sz;
|
||||
|
||||
return ret;
|
||||
return line;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
uint8_t colour, const char *msg, ...)
|
||||
/* creates new line_info line and puts it in the queue. */
|
||||
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||
uint8_t bold, uint8_t colour, const char *msg, ...)
|
||||
{
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
struct history *hst = self->chatwin->hst;
|
||||
|
||||
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
|
||||
return;
|
||||
|
||||
struct line_info *new_line = calloc(1, sizeof(struct line_info));
|
||||
|
||||
if (new_line == NULL)
|
||||
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
|
||||
|
||||
char frmt_msg[MAX_STR_SIZE] = {0};
|
||||
char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0};
|
||||
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
@ -156,14 +157,33 @@ 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:
|
||||
/* fallthrough */
|
||||
case OUT_ACTION:
|
||||
len += strlen(user_settings->line_normal) + 2;
|
||||
break;
|
||||
|
||||
case IN_MSG:
|
||||
/* fallthrough */
|
||||
case OUT_MSG:
|
||||
len += strlen(user_settings->line_normal) + 3;
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
len += 3;
|
||||
len += strlen(user_settings->line_join) + 2;
|
||||
break;
|
||||
|
||||
case DISCONNECTION:
|
||||
len += strlen(user_settings->line_quit) + 2;
|
||||
break;
|
||||
|
||||
case SYS_MSG:
|
||||
break;
|
||||
|
||||
case NAME_CHANGE:
|
||||
len += strlen(user_settings->line_alert) + 1;
|
||||
break;
|
||||
|
||||
case PROMPT:
|
||||
++len;
|
||||
break;
|
||||
@ -185,9 +205,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) + 1;
|
||||
}
|
||||
|
||||
if (name1) {
|
||||
@ -204,12 +224,14 @@ 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() */
|
||||
static void line_info_check_queue(ToxWindow *self)
|
||||
static void line_info_check_queue(ToxWindow *self)
|
||||
{
|
||||
struct history *hst = self->chatwin->hst;
|
||||
struct line_info *line = line_info_ret_queue(hst);
|
||||
@ -217,7 +239,7 @@ static void line_info_check_queue(ToxWindow *self)
|
||||
if (line == NULL)
|
||||
return;
|
||||
|
||||
if (hst->start_id > user_settings_->history_size)
|
||||
if (hst->start_id > user_settings->history_size)
|
||||
line_info_root_fwd(hst);
|
||||
|
||||
line->id = hst->line_end->id + 1;
|
||||
@ -233,7 +255,7 @@ static void line_info_check_queue(ToxWindow *self)
|
||||
if (x2 <= SIDEBAR_WIDTH)
|
||||
return;
|
||||
|
||||
int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
|
||||
int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
|
||||
int lines = 1 + line->newlines + (line->len / (x2 - offst));
|
||||
int max_y = y2 - CHATBOX_HEIGHT;
|
||||
|
||||
@ -247,6 +269,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 +304,12 @@ void line_info_print(ToxWindow *self)
|
||||
|
||||
switch (type) {
|
||||
case OUT_MSG:
|
||||
/* fallthrough */
|
||||
case OUT_MSG_READ:
|
||||
/* fallthrough */
|
||||
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;
|
||||
@ -293,34 +320,66 @@ void line_info_print(ToxWindow *self)
|
||||
nameclr = CYAN;
|
||||
|
||||
wattron(win, COLOR_PAIR(nameclr));
|
||||
wprintw(win, "%s: ", line->name1);
|
||||
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
|
||||
wattroff(win, COLOR_PAIR(nameclr));
|
||||
|
||||
if (line->msg[0] == '>')
|
||||
wattron(win, COLOR_PAIR(GREEN));
|
||||
else if (line->msg[0] == '<')
|
||||
wattron(win, COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
wprintw(win, "%s", line->msg);
|
||||
|
||||
if (line->msg[0] == '>')
|
||||
wattroff(win, COLOR_PAIR(GREEN));
|
||||
else if (line->msg[0] == '<')
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
|
||||
if (type == OUT_MSG && timed_out(line->timestamp, 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:
|
||||
/* fallthrough */
|
||||
case OUT_ACTION:
|
||||
/* fallthrough */
|
||||
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 %s", user_settings->line_normal, line->name1, line->msg);
|
||||
wattroff(win, COLOR_PAIR(YELLOW));
|
||||
|
||||
if (type == OUT_ACTION && timed_out(line->timestamp, 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,13 +412,33 @@ 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));
|
||||
wprintw(win, "%s ", user_settings->line_join);
|
||||
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "* %s ", line->name1);
|
||||
wprintw(win, "%s ", line->name1);
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
wattroff(win, COLOR_PAIR(line->colour));
|
||||
|
||||
break;
|
||||
|
||||
case DISCONNECTION:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s ", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(win, COLOR_PAIR(line->colour));
|
||||
wprintw(win, "%s ", user_settings->line_quit);
|
||||
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "%s ", line->name1);
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
wattroff(win, COLOR_PAIR(line->colour));
|
||||
|
||||
@ -367,12 +446,13 @@ 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));
|
||||
wprintw(win, "%s ", user_settings->line_alert);
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "* %s", line->name1);
|
||||
wprintw(win, "%s", line->name1);
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, "%s", line->msg);
|
||||
@ -393,6 +473,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;
|
||||
@ -416,14 +497,14 @@ static void line_info_scroll_up(struct history *hst)
|
||||
{
|
||||
if (hst->line_start->prev)
|
||||
hst->line_start = hst->line_start->prev;
|
||||
else sound_notify(NULL, error, NT_ALWAYS, NULL);
|
||||
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
}
|
||||
|
||||
static void line_info_scroll_down(struct history *hst)
|
||||
{
|
||||
if (hst->line_start->next)
|
||||
hst->line_start = hst->line_start->next;
|
||||
else sound_notify(NULL, error, NT_ALWAYS, NULL);
|
||||
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
}
|
||||
|
||||
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
||||
@ -455,24 +536,24 @@ bool line_info_onKey(ToxWindow *self, wint_t key)
|
||||
struct history *hst = self->chatwin->hst;
|
||||
bool match = true;
|
||||
|
||||
if (key == user_settings_->key_half_page_up) {
|
||||
line_info_page_up(self, hst);
|
||||
}
|
||||
else if (key == user_settings_->key_half_page_down) {
|
||||
line_info_page_down(self, hst);
|
||||
}
|
||||
else if (key == user_settings_->key_scroll_line_up) {
|
||||
line_info_scroll_up(hst);
|
||||
}
|
||||
else if (key == user_settings_->key_scroll_line_down) {
|
||||
line_info_scroll_down(hst);
|
||||
}
|
||||
else if (key == user_settings_->key_page_bottom) {
|
||||
line_info_reset_start(self, hst);
|
||||
}
|
||||
else {
|
||||
match = false;
|
||||
}
|
||||
if (key == user_settings->key_half_page_up) {
|
||||
line_info_page_up(self, hst);
|
||||
}
|
||||
else if (key == user_settings->key_half_page_down) {
|
||||
line_info_page_down(self, hst);
|
||||
}
|
||||
else if (key == user_settings->key_scroll_line_up) {
|
||||
line_info_scroll_up(hst);
|
||||
}
|
||||
else if (key == user_settings->key_scroll_line_down) {
|
||||
line_info_scroll_down(hst);
|
||||
}
|
||||
else if (key == user_settings->key_page_bottom) {
|
||||
line_info_reset_start(self, hst);
|
||||
}
|
||||
else {
|
||||
match = false;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
@ -20,36 +20,43 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _line_info_h
|
||||
#define _line_info_h
|
||||
#ifndef LINE_INFO_H
|
||||
#define LINE_INFO_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
#define MAX_HISTORY 100000
|
||||
#define MIN_HISTORY 40
|
||||
#define MAX_QUEUE 128
|
||||
#define MAX_LINE_INFO_QUEUE 1024
|
||||
#define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* needs extra room for log loading */
|
||||
|
||||
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,
|
||||
DISCONNECTION,
|
||||
NAME_CHANGE,
|
||||
} LINE_TYPE;
|
||||
|
||||
struct line_info {
|
||||
char timestamp[TIME_STR_SIZE];
|
||||
char name1[TOXIC_MAX_NAME_LENGTH];
|
||||
char name2[TOXIC_MAX_NAME_LENGTH];
|
||||
char msg[TOX_MAX_MESSAGE_LENGTH];
|
||||
char timestr[TIME_STR_SIZE];
|
||||
char name1[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
char name2[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
char msg[MAX_LINE_INFO_MSG_SIZE];
|
||||
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;
|
||||
@ -63,14 +70,13 @@ struct history {
|
||||
struct line_info *line_end;
|
||||
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
|
||||
|
||||
struct line_info *queue[MAX_QUEUE];
|
||||
struct line_info *queue[MAX_LINE_INFO_QUEUE];
|
||||
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,
|
||||
uint8_t colour, const char *msg, ...);
|
||||
/* creates new line_info line and puts it in the queue. */
|
||||
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||
uint8_t bold, uint8_t colour, const char *msg, ...);
|
||||
|
||||
/* Prints a section of history starting at line_start */
|
||||
void line_info_print(ToxWindow *self);
|
||||
@ -84,7 +90,10 @@ void line_info_clear(struct history *hst);
|
||||
/* puts msg in specified line_info msg buffer */
|
||||
void line_info_set(ToxWindow *self, uint32_t id, char *msg);
|
||||
|
||||
/* resets line_start (moves to end of chat history) */
|
||||
void line_info_reset_start(ToxWindow *self, struct history *hst);
|
||||
|
||||
void line_info_init(struct history *hst);
|
||||
bool line_info_onKey(ToxWindow *self, wint_t key); /* returns true if key is a match */
|
||||
|
||||
#endif /* #define _line_info_h */
|
||||
#endif /* #define LINE_INFO_H */
|
||||
|
240
src/log.c
240
src/log.c
@ -23,6 +23,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "configdir.h"
|
||||
#include "toxic.h"
|
||||
@ -30,62 +31,92 @@
|
||||
#include "misc_tools.h"
|
||||
#include "log.h"
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
|
||||
extern struct user_settings *user_settings_;
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
|
||||
void init_logging_session(char *name, const char *key, struct chatlog *log)
|
||||
/* There are three types of logs: chat logs, groupchat logs, and prompt logs (see LOG_TYPE in log.h)
|
||||
A prompt log is in the format: LOGDIR/selfkey-home.log
|
||||
A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log
|
||||
A groupchat log is in the format: LOGDIR/selfkey-groupname-date[time].log
|
||||
|
||||
Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used.
|
||||
|
||||
Returns 0 on success, -1 if the path is too long */
|
||||
static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype)
|
||||
{
|
||||
if (!log->log_on)
|
||||
return;
|
||||
|
||||
if (!valid_nick(name))
|
||||
name = UNKNOWN_NAME;
|
||||
|
||||
const char *set_path = user_settings_->chatlogs_path;
|
||||
const char *namedash = logtype == LOG_PROMPT ? "" : "-";
|
||||
const char *set_path = user_settings->chatlogs_path;
|
||||
|
||||
char *user_config_dir = get_user_config_dir();
|
||||
int path_len = strlen(set_path) + strlen(name) ? *set_path
|
||||
: strlen(user_config_dir) + strlen(LOGDIR) + strlen(name);
|
||||
int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash);
|
||||
path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR);
|
||||
|
||||
/* use first 4 digits of key as log ident. If no key use a timestamp */
|
||||
char ident[32];
|
||||
/* first 6 digits of selfkey */
|
||||
char self_id[32];
|
||||
path_len += KEY_IDENT_DIGITS * 2;
|
||||
sprintf(&self_id[0], "%02X", selfkey[0] & 0xff);
|
||||
sprintf(&self_id[2], "%02X", selfkey[1] & 0xff);
|
||||
sprintf(&self_id[4], "%02X", selfkey[2] & 0xff);
|
||||
self_id[KEY_IDENT_DIGITS * 2] = '\0';
|
||||
|
||||
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';
|
||||
} else {
|
||||
strftime(ident, sizeof(ident), "%Y-%m-%d[%H:%M:%S]", get_time());
|
||||
path_len += strlen(ident) + 1;
|
||||
char other_id[32] = {0};
|
||||
|
||||
switch (logtype) {
|
||||
case LOG_CHAT:
|
||||
path_len += KEY_IDENT_DIGITS * 2;
|
||||
sprintf(&other_id[0], "%02X", otherkey[0] & 0xff);
|
||||
sprintf(&other_id[2], "%02X", otherkey[1] & 0xff);
|
||||
sprintf(&other_id[4], "%02X", otherkey[2] & 0xff);
|
||||
other_id[KEY_IDENT_DIGITS * 2] = '\0';
|
||||
break;
|
||||
|
||||
case LOG_GROUP:
|
||||
strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time());
|
||||
path_len += strlen(other_id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (path_len >= MAX_STR_SIZE) {
|
||||
log->log_on = false;
|
||||
if (path_len >= destsize) {
|
||||
free(user_config_dir);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char log_path[MAX_STR_SIZE];
|
||||
|
||||
if (*set_path)
|
||||
snprintf(log_path, sizeof(log_path), "%s%s-%s.log", set_path, name, ident);
|
||||
if (!string_is_empty(set_path))
|
||||
snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id);
|
||||
else
|
||||
snprintf(log_path, sizeof(log_path), "%s%s%s-%s.log", user_config_dir, LOGDIR, name, ident);
|
||||
snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id);
|
||||
|
||||
free(user_config_dir);
|
||||
|
||||
log->file = fopen(log_path, "a");
|
||||
|
||||
if (log->file == NULL) {
|
||||
log->log_on = false;
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Opens log file or creates a new one */
|
||||
static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
||||
{
|
||||
if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL))
|
||||
return -1;
|
||||
|
||||
char log_path[MAX_STR_SIZE];
|
||||
|
||||
if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1)
|
||||
return -1;
|
||||
|
||||
log->file = fopen(log_path, "a+");
|
||||
snprintf(log->path, sizeof(log->path), "%s", log_path);
|
||||
|
||||
if (log->file == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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)
|
||||
@ -103,33 +134,136 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
|
||||
else
|
||||
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
|
||||
|
||||
const char *t = user_settings_->time == TIME_12 ? "%Y/%m/%d [%I:%M:%S %p]" : "%Y/%m/%d [%H:%M:%S]";
|
||||
const char *t = user_settings->log_timestamp_format;
|
||||
char s[MAX_STR_SIZE];
|
||||
strftime(s, MAX_STR_SIZE, t, get_time());
|
||||
fprintf(log->file, "%s %s %s\n", s, name_frmt, msg);
|
||||
|
||||
uint64_t curtime = get_unix_time();
|
||||
|
||||
if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) {
|
||||
if (timed_out(log->lastwrite, LOG_FLUSH_LIMIT)) {
|
||||
fflush(log->file);
|
||||
log->lastwrite = curtime;
|
||||
log->lastwrite = get_unix_time();
|
||||
}
|
||||
}
|
||||
|
||||
void log_enable(char *name, const char *key, struct chatlog *log)
|
||||
{
|
||||
log->log_on = true;
|
||||
|
||||
if (log->file == NULL)
|
||||
init_logging_session(name, key, log);
|
||||
}
|
||||
|
||||
void log_disable(struct chatlog *log)
|
||||
{
|
||||
log->log_on = false;
|
||||
|
||||
if (log->file != NULL) {
|
||||
if (log->file != NULL)
|
||||
fclose(log->file);
|
||||
log->file = NULL;
|
||||
}
|
||||
|
||||
memset(log, 0, sizeof(struct chatlog));
|
||||
}
|
||||
|
||||
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
||||
{
|
||||
log->log_on = true;
|
||||
|
||||
if (log->file != NULL)
|
||||
return 0;
|
||||
|
||||
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
|
||||
log_disable(log);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Loads previous history from chat log */
|
||||
void load_chat_history(ToxWindow *self, struct chatlog *log)
|
||||
{
|
||||
if (log->file == NULL)
|
||||
return;
|
||||
|
||||
off_t sz = file_size(log->path);
|
||||
|
||||
if (sz <= 0)
|
||||
return;
|
||||
|
||||
char *hstbuf = malloc(sz + 1);
|
||||
|
||||
if (hstbuf == NULL)
|
||||
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
|
||||
|
||||
if (fseek(log->file, 0L, SEEK_SET) == -1) {
|
||||
free(hstbuf);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fread(hstbuf, sz, 1, log->file) != 1) {
|
||||
free(hstbuf);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
|
||||
return;
|
||||
}
|
||||
|
||||
hstbuf[sz] = '\0';
|
||||
|
||||
/* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
|
||||
int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
|
||||
int start, count = 0;
|
||||
|
||||
/* start at end and backtrace L lines or to the beginning of buffer */
|
||||
for (start = sz - 1; start >= 0 && count < L; --start) {
|
||||
if (hstbuf[start] == '\n')
|
||||
++count;
|
||||
}
|
||||
|
||||
const char *line = strtok(&hstbuf[start + 1], "\n");
|
||||
|
||||
if (line == NULL) {
|
||||
free(hstbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
while (line != NULL && count--) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
free(hstbuf);
|
||||
}
|
||||
|
||||
/* renames chatlog file replacing src with dest.
|
||||
Returns 0 on success or if no log exists, -1 on failure. */
|
||||
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum)
|
||||
{
|
||||
ToxWindow *toxwin = get_window_ptr(winnum);
|
||||
struct chatlog *log = NULL;
|
||||
bool log_on = false;
|
||||
|
||||
/* disable log if necessary and save its state */
|
||||
if (toxwin != NULL) {
|
||||
log = toxwin->chatwin->log;
|
||||
log_on = log->log_on;
|
||||
}
|
||||
|
||||
if (log_on)
|
||||
log_disable(log);
|
||||
|
||||
char newpath[MAX_STR_SIZE];
|
||||
char oldpath[MAX_STR_SIZE];
|
||||
|
||||
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1)
|
||||
goto on_error;
|
||||
|
||||
if (!file_exists(oldpath))
|
||||
return 0;
|
||||
|
||||
if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1)
|
||||
goto on_error;
|
||||
|
||||
if (rename(oldpath, newpath) != 0)
|
||||
goto on_error;
|
||||
|
||||
if (log_on)
|
||||
log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
if (log_on)
|
||||
log_enable(src, selfkey, otherkey, log, LOG_CHAT);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
32
src/log.h
32
src/log.h
@ -20,28 +20,40 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#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 */
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
struct chatlog {
|
||||
FILE *file;
|
||||
uint64_t lastwrite;
|
||||
int pos;
|
||||
char path[MAX_STR_SIZE];
|
||||
bool log_on; /* specific to current chat window */
|
||||
};
|
||||
|
||||
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
|
||||
void init_logging_session(char *name, const char *key, struct chatlog *log);
|
||||
enum {
|
||||
LOG_GROUP,
|
||||
LOG_PROMPT,
|
||||
LOG_CHAT,
|
||||
} LOG_TYPE;
|
||||
|
||||
/* formats/writes line to log file */
|
||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event);
|
||||
|
||||
/* enables logging for specified log and creates/fetches file if necessary */
|
||||
void log_enable(char *name, const char *key, struct chatlog *log);
|
||||
/* enables logging for specified log and creates/fetches file if necessary.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype);
|
||||
|
||||
/* disables logging for specified log and closes file */
|
||||
void log_disable(struct chatlog *log);
|
||||
|
||||
#endif /* #define _log_h */
|
||||
/* Loads previous history from chat log */
|
||||
void load_chat_history(ToxWindow *self, struct chatlog *log);
|
||||
|
||||
/* renames chatlog file replacing src with dest.
|
||||
Returns 0 on success or if no log exists, -1 on failure. */
|
||||
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
|
||||
|
||||
#endif /* #define LOG_H */
|
||||
|
154
src/message_queue.c
Normal file
154
src/message_queue.c
Normal file
@ -0,0 +1,154 @@
|
||||
/* 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, size_t 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];
|
||||
tox_self_get_name(m, (uint8_t *) selfname);
|
||||
|
||||
size_t len = tox_self_get_name_size(m);
|
||||
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 60
|
||||
|
||||
/* 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;
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL))
|
||||
return;
|
||||
|
||||
uint32_t receipt = 0;
|
||||
|
||||
TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
|
||||
receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
|
||||
|
||||
msg->last_send_try = get_unix_time();
|
||||
msg->receipt = receipt;
|
||||
return;
|
||||
}
|
51
src/message_queue.h
Normal file
51
src/message_queue.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MESSAGE_QUEUE_H
|
||||
#define MESSAGE_QUEUE_H
|
||||
|
||||
struct cqueue_msg {
|
||||
char message[MAX_STR_SIZE];
|
||||
size_t 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, size_t 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);
|
||||
|
||||
#endif /* #define MESSAGE_QUEUE_H */
|
218
src/misc_tools.c
218
src/misc_tools.c
@ -32,10 +32,10 @@
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "settings.h"
|
||||
#include "file_senders.h"
|
||||
#include "file_transfers.h"
|
||||
|
||||
extern ToxWindow *prompt;
|
||||
extern struct user_settings *user_settings_;
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
static uint64_t current_unix_time;
|
||||
|
||||
@ -54,6 +54,7 @@ void hst_to_net(uint8_t *num, uint16_t numbytes)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note: The time functions are not thread safe */
|
||||
void update_unix_time(void)
|
||||
{
|
||||
current_unix_time = (uint64_t) time(NULL);
|
||||
@ -65,9 +66,9 @@ uint64_t get_unix_time(void)
|
||||
}
|
||||
|
||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
|
||||
int timed_out(uint64_t timestamp, uint64_t timeout)
|
||||
{
|
||||
return timestamp + timeout <= curtime;
|
||||
return timestamp + timeout <= get_unix_time();
|
||||
}
|
||||
|
||||
/* Get the current local time */
|
||||
@ -82,12 +83,12 @@ struct tm *get_time(void)
|
||||
/*Puts the current time in buf in the format of [HH:mm:ss] */
|
||||
void get_time_str(char *buf, int bufsize)
|
||||
{
|
||||
if (user_settings_->timestamps == TIMESTAMPS_OFF) {
|
||||
if (user_settings->timestamps == TIMESTAMPS_OFF) {
|
||||
buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
const char *t = user_settings_->time == TIME_12 ? "[%-I:%M:%S] " : "[%H:%M:%S] ";
|
||||
const char *t = user_settings->timestamp_format;
|
||||
strftime(buf, bufsize, t, get_time());
|
||||
}
|
||||
|
||||
@ -109,20 +110,24 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
|
||||
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
|
||||
}
|
||||
|
||||
char *hex_string_to_bin(const char *hex_string)
|
||||
/*
|
||||
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
|
||||
* output_size must be exactly half of hex_len.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size)
|
||||
{
|
||||
size_t len = strlen(hex_string);
|
||||
char *val = malloc(len);
|
||||
if (output_size == 0 || hex_len != output_size * 2)
|
||||
return -1;
|
||||
|
||||
if (val == NULL)
|
||||
exit_toxic_err("failed in hex_string_to_bin", FATALERR_MEMORY);
|
||||
for (size_t i = 0; i < output_size; ++i) {
|
||||
sscanf(hex_string, "%2hhx", &output[i]);
|
||||
hex_string += 2;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; ++i, hex_string += 2)
|
||||
sscanf(hex_string, "%2hhx", &val[i]);
|
||||
|
||||
return val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hex_string_to_bytes(char *buf, int size, const char *keystr)
|
||||
@ -144,9 +149,30 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Converts a binary representation of a Tox ID into a string.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size)
|
||||
{
|
||||
if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1))
|
||||
return -1;
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < TOX_ADDRESS_SIZE; ++i)
|
||||
snprintf(&output[i*2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 1 if the string is empty, 0 otherwise */
|
||||
int string_is_empty(const char *string)
|
||||
{
|
||||
if (!string)
|
||||
return true;
|
||||
|
||||
return string[0] == '\0';
|
||||
}
|
||||
|
||||
@ -188,8 +214,9 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2)
|
||||
- cannot be empty
|
||||
- cannot start with a space
|
||||
- must not contain a forward slash (for logfile naming purposes)
|
||||
- must not contain contiguous spaces */
|
||||
int valid_nick(char *nick)
|
||||
- must not contain contiguous spaces
|
||||
- must not contain a newline or tab seqeunce */
|
||||
int valid_nick(const char *nick)
|
||||
{
|
||||
if (!nick[0] || nick[0] == ' ')
|
||||
return 0;
|
||||
@ -197,27 +224,43 @@ int valid_nick(char *nick)
|
||||
int i;
|
||||
|
||||
for (i = 0; nick[i]; ++i) {
|
||||
if (nick[i] == ' ' && nick[i + 1] == ' ')
|
||||
return 0;
|
||||
if ((nick[i] == ' ' && nick[i + 1] == ' ')
|
||||
|| nick[i] == '/'
|
||||
|| nick[i] == '\n'
|
||||
|| nick[i] == '\t'
|
||||
|| nick[i] == '\v'
|
||||
|| nick[i] == '\r')
|
||||
|
||||
if (nick[i] == '/')
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* gets base file name from path or original file name if no path is supplied */
|
||||
void get_file_name(char *namebuf, int bufsize, const char *pathname)
|
||||
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
|
||||
void filter_str(char *str, size_t len)
|
||||
{
|
||||
int idx = strlen(pathname) - 1;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0')
|
||||
str[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/* gets base file name from path or original file name if no path is supplied.
|
||||
* Returns the file name length
|
||||
*/
|
||||
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
|
||||
{
|
||||
int len = strlen(pathname) - 1;
|
||||
char *path = strdup(pathname);
|
||||
|
||||
if (path == NULL)
|
||||
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
|
||||
|
||||
while (idx >= 0 && pathname[idx] == '/')
|
||||
path[idx--] = '\0';
|
||||
while (len >= 0 && pathname[len] == '/')
|
||||
path[len--] = '\0';
|
||||
|
||||
char *finalname = strdup(path);
|
||||
|
||||
@ -234,6 +277,29 @@ void get_file_name(char *namebuf, int bufsize, const char *pathname)
|
||||
snprintf(namebuf, bufsize, "%s", finalname);
|
||||
free(finalname);
|
||||
free(path);
|
||||
|
||||
return strlen(namebuf);
|
||||
}
|
||||
|
||||
/* Gets the base directory of path and puts it in dir.
|
||||
* dir must have at least as much space as path_len + 1.
|
||||
*
|
||||
* Returns the length of the base directory.
|
||||
*/
|
||||
size_t get_base_dir(const char *path, size_t path_len, char *dir)
|
||||
{
|
||||
if (path_len == 0 || path == NULL)
|
||||
return 0;
|
||||
|
||||
size_t dir_len = char_rfind(path, '/', path_len);
|
||||
|
||||
if (dir_len != 0 && dir_len < path_len)
|
||||
++dir_len; /* Leave trailing slash */
|
||||
|
||||
memcpy(dir, path, dir_len);
|
||||
dir[dir_len] = '\0';
|
||||
|
||||
return dir_len;
|
||||
}
|
||||
|
||||
/* converts str to all lowercase */
|
||||
@ -246,12 +312,48 @@ void str_to_lower(char *str)
|
||||
}
|
||||
|
||||
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
|
||||
Returns nick len on success, -1 on failure */
|
||||
int get_nick_truncate(Tox *m, char *buf, int friendnum)
|
||||
if toxcore API call fails, put UNKNOWN_NAME in buf
|
||||
Returns nick len */
|
||||
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
|
||||
{
|
||||
int len = tox_get_name(m, friendnum, (uint8_t *) buf);
|
||||
size_t len = tox_friend_get_name_size(m, friendnum, NULL);
|
||||
|
||||
if (len == 0) {
|
||||
strcpy(buf, UNKNOWN_NAME);
|
||||
len = strlen(UNKNOWN_NAME);
|
||||
} else {
|
||||
tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL);
|
||||
}
|
||||
|
||||
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
buf[len] = '\0';
|
||||
filter_str(buf, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
/* same as get_nick_truncate but for groupchats */
|
||||
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum)
|
||||
{
|
||||
int len = tox_group_peername(m, groupnum, peernum, (uint8_t *) buf);
|
||||
|
||||
if (len == -1) {
|
||||
strcpy(buf, UNKNOWN_NAME);
|
||||
len = strlen(UNKNOWN_NAME);
|
||||
}
|
||||
|
||||
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
buf[len] = '\0';
|
||||
filter_str(buf, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
/* copies data to msg buffer.
|
||||
returns length of msg, which will be no larger than size-1 */
|
||||
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length)
|
||||
{
|
||||
size_t len = MIN(length, size - 1);
|
||||
memcpy(msg, data, len);
|
||||
msg[len] = '\0';
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -269,8 +371,8 @@ int char_find(int idx, const char *s, char ch)
|
||||
return i;
|
||||
}
|
||||
|
||||
/* returns index of the last instance of ch in s starting at len
|
||||
returns 0 if char not found (skips 0th index) */
|
||||
/* returns index of the last instance of ch in s starting at len.
|
||||
returns 0 if char not found (skips 0th index). */
|
||||
int char_rfind(const char *s, char ch, int len)
|
||||
{
|
||||
int i = 0;
|
||||
@ -306,8 +408,56 @@ 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 *fp)
|
||||
bool file_exists(const char *path)
|
||||
{
|
||||
struct stat s;
|
||||
return stat(fp, &s) == 0;
|
||||
return stat(path, &s) == 0;
|
||||
}
|
||||
|
||||
/* returns file size. If file doesn't exist returns 0. */
|
||||
off_t file_size(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(path, &st) == -1)
|
||||
return 0;
|
||||
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
/* compares the first size bytes of fp to signature.
|
||||
Returns 0 if they are the same, 1 if they differ, and -1 on error.
|
||||
|
||||
On success this function will seek back to the beginning of fp */
|
||||
int check_file_signature(const char *signature, size_t size, FILE *fp)
|
||||
{
|
||||
char buf[size];
|
||||
|
||||
if (fread(buf, size, 1, fp) != 1)
|
||||
return -1;
|
||||
|
||||
int ret = memcmp(signature, buf, size);
|
||||
|
||||
if (fseek(fp, 0L, SEEK_SET) == -1)
|
||||
return -1;
|
||||
|
||||
return ret == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
/* sets window title in tab bar. */
|
||||
void set_window_title(ToxWindow *self, const char *title, int len)
|
||||
{
|
||||
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
|
||||
if (self->is_groupchat) /* keep groupnumber in title */
|
||||
snprintf(cpy, sizeof(cpy), "%d %s", self->num, title);
|
||||
else
|
||||
snprintf(cpy, sizeof(cpy), "%s", title);
|
||||
|
||||
if (len > MAX_WINDOW_NAME_LENGTH) {
|
||||
strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "...");
|
||||
cpy[MAX_WINDOW_NAME_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
snprintf(self->name, sizeof(self->name), "%s", cpy);
|
||||
}
|
||||
|
@ -19,8 +19,10 @@
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef _misc_tools_h
|
||||
#define _misc_tools_h
|
||||
#ifndef MISC_TOOLS_H
|
||||
#define MISC_TOOLS_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
@ -39,22 +41,35 @@
|
||||
|
||||
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);
|
||||
/*
|
||||
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
|
||||
* output_size must be exactly half of hex_len.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size);
|
||||
|
||||
/* 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 */
|
||||
/* Converts a binary representation of a Tox ID into a string.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size);
|
||||
|
||||
/* get the current unix time (not thread safe) */
|
||||
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] (not thread safe) */
|
||||
void get_time_str(char *buf, int bufsize);
|
||||
|
||||
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
|
||||
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs);
|
||||
|
||||
/* get the current local time */
|
||||
/* get the current local time (not thread safe) */
|
||||
struct tm *get_time(void);
|
||||
|
||||
/* updates current unix time (should be run once per do_toxic loop) */
|
||||
@ -73,7 +88,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
|
||||
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
||||
|
||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||
int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
||||
int timed_out(uint64_t timestamp, uint64_t timeout);
|
||||
|
||||
/* Colours the window tab according to type. Beeps if is_beep is true */
|
||||
void alert_window(ToxWindow *self, int type, bool is_beep);
|
||||
@ -85,18 +100,37 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2);
|
||||
- cannot be empty
|
||||
- cannot start with a space
|
||||
- must not contain a forward slash (for logfile naming purposes)
|
||||
- must not contain contiguous spaces */
|
||||
int valid_nick(char *nick);
|
||||
- must not contain contiguous spaces
|
||||
- must not contain a newline or tab seqeunce */
|
||||
int valid_nick(const char *nick);
|
||||
|
||||
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
|
||||
void filter_str(char *str, size_t len);
|
||||
|
||||
/* gets base file name from path or original file name if no path is supplied */
|
||||
void get_file_name(char *namebuf, int bufsize, const char *pathname);
|
||||
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname);
|
||||
|
||||
/* Gets the base directory of path and puts it in dir.
|
||||
* dir must have at least as much space as path_len.
|
||||
*
|
||||
* Returns the length of the base directory on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
size_t get_base_dir(const char *path, size_t path_len, char *dir);
|
||||
|
||||
/* converts str to all lowercase */
|
||||
void str_to_lower(char *str);
|
||||
|
||||
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
|
||||
Returns nick len on success, -1 on failure */
|
||||
int get_nick_truncate(Tox *m, char *buf, int friendnum);
|
||||
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
|
||||
|
||||
/* same as get_nick_truncate but for groupchats */
|
||||
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum);
|
||||
|
||||
/* copies data to msg buffer.
|
||||
returns length of msg, which will be no larger than size-1 */
|
||||
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length);
|
||||
|
||||
/* returns index of the first instance of ch in s starting at idx.
|
||||
returns length of s if char not found */
|
||||
@ -110,6 +144,18 @@ int char_rfind(const char *s, char ch, int len);
|
||||
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 *fp);
|
||||
bool file_exists(const char *path);
|
||||
|
||||
#endif /* #define _misc_tools_h */
|
||||
/* returns file size. If file doesn't exist returns 0. */
|
||||
off_t file_size(const char *path);
|
||||
|
||||
/* compares the first size bytes of fp and signature.
|
||||
Returns 0 if they are the same, 1 if they differ, and -1 on error.
|
||||
|
||||
On success this function will seek back to the beginning of fp */
|
||||
int check_file_signature(const char *signature, size_t size, FILE *fp);
|
||||
|
||||
/* sets window title in tab bar. */
|
||||
void set_window_title(ToxWindow *self, const char *title, int len);
|
||||
|
||||
#endif /* #define MISC_TOOLS_H */
|
||||
|
461
src/name_lookup.c
Normal file
461
src/name_lookup.c
Normal file
@ -0,0 +1,461 @@
|
||||
/* name_lookup.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2015 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 <string.h>
|
||||
#include <sys/types.h> /* for u_char */
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "line_info.h"
|
||||
#include "global_commands.h"
|
||||
#include "misc_tools.h"
|
||||
#include "configdir.h"
|
||||
|
||||
extern struct arg_opts arg_opts;
|
||||
extern struct Winthread Winthread;;
|
||||
|
||||
#define NAMESERVER_API_PATH "api"
|
||||
#define SERVER_KEY_SIZE 32
|
||||
#define MAX_SERVERS 50
|
||||
#define MAX_DOMAIN_SIZE 32
|
||||
#define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3
|
||||
|
||||
/* List based on Mozilla's recommended configurations for modern browsers */
|
||||
#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"
|
||||
|
||||
struct Nameservers {
|
||||
int lines;
|
||||
char names[MAX_SERVERS][MAX_DOMAIN_SIZE];
|
||||
char keys[MAX_SERVERS][SERVER_KEY_SIZE];
|
||||
} Nameservers;
|
||||
|
||||
static struct thread_data {
|
||||
Tox *m;
|
||||
ToxWindow *self;
|
||||
char id_bin[TOX_ADDRESS_SIZE];
|
||||
char addr[MAX_STR_SIZE];
|
||||
char msg[MAX_STR_SIZE];
|
||||
bool busy;
|
||||
bool disabled;
|
||||
} t_data;
|
||||
|
||||
static struct lookup_thread {
|
||||
pthread_t tid;
|
||||
pthread_attr_t attr;
|
||||
} lookup_thread;
|
||||
|
||||
static int lookup_error(ToxWindow *self, const char *errmsg, ...)
|
||||
{
|
||||
char frmt_msg[MAX_STR_SIZE];
|
||||
|
||||
va_list args;
|
||||
va_start(args, errmsg);
|
||||
vsnprintf(frmt_msg, sizeof(frmt_msg), errmsg, args);
|
||||
va_end(args);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void kill_lookup_thread(void)
|
||||
{
|
||||
memset(&t_data, 0, sizeof(struct thread_data));
|
||||
pthread_attr_destroy(&lookup_thread.attr);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
/* Attempts to load the nameserver list pointed at by path into the Nameservers structure.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* -1 is reserved.
|
||||
* Returns -2 if the supplied path does not exist.
|
||||
* Returns -3 if the list does not contain any valid entries.
|
||||
*/
|
||||
static int load_nameserver_list(const char *path)
|
||||
{
|
||||
FILE *fp = fopen(path, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
return -2;
|
||||
|
||||
char line[MAX_SERVER_LINE];
|
||||
|
||||
while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) {
|
||||
int linelen = strlen(line);
|
||||
|
||||
if (linelen < SERVER_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) != SERVER_KEY_SIZE * 2)
|
||||
continue;
|
||||
|
||||
snprintf(Nameservers.names[Nameservers.lines], sizeof(Nameservers.names[Nameservers.lines]), "%s", name);
|
||||
int res = hex_string_to_bytes(Nameservers.keys[Nameservers.lines], SERVER_KEY_SIZE, keystr);
|
||||
|
||||
if (res == -1)
|
||||
continue;
|
||||
|
||||
++Nameservers.lines;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (Nameservers.lines < 1)
|
||||
return -3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Takes address addr in the form "username@domain", puts the username in namebuf,
|
||||
* and the domain in dombuf.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure
|
||||
*/
|
||||
static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz)
|
||||
{
|
||||
if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH)))
|
||||
return -1;
|
||||
|
||||
char tmpaddr[MAX_STR_SIZE];
|
||||
char *tmpname = NULL;
|
||||
char *tmpdom = NULL;
|
||||
|
||||
snprintf(tmpaddr, sizeof(tmpaddr), "%s", addr);
|
||||
tmpname = strtok(tmpaddr, "@");
|
||||
tmpdom = strtok(NULL, "");
|
||||
|
||||
if (tmpname == NULL || tmpdom == NULL)
|
||||
return -1;
|
||||
|
||||
str_to_lower(tmpdom);
|
||||
snprintf(namebuf, namebuf_sz, "%s", tmpname);
|
||||
snprintf(dombuf, dombuf_sz, "%s", tmpdom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* matches input domain name with domains in list and obtains key.
|
||||
* Turns out_domain into the full domain we need to make a POST request.
|
||||
*
|
||||
* Return true on match.
|
||||
* Returns false on no match.
|
||||
*/
|
||||
static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_size, const char *inputdomain)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < Nameservers.lines; ++i) {
|
||||
if (strcmp(Nameservers.names[i], inputdomain) == 0) {
|
||||
memcpy(pubkey, Nameservers.keys[i], SERVER_KEY_SIZE);
|
||||
snprintf(out_domain, out_domain_size, "https://%s/%s", Nameservers.names[i], NAMESERVER_API_PATH);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define MAX_RECV_LOOKUP_DATA_SIZE 1024
|
||||
|
||||
/* Holds raw data received from name server */
|
||||
struct Recv_Data {
|
||||
char data[MAX_RECV_LOOKUP_DATA_SIZE];
|
||||
size_t size;
|
||||
};
|
||||
|
||||
size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer)
|
||||
{
|
||||
struct Recv_Data *recv_data = (struct Recv_Data *) user_pointer;
|
||||
size_t real_size = size * nmemb;
|
||||
|
||||
if (real_size >= MAX_RECV_LOOKUP_DATA_SIZE)
|
||||
return 0;
|
||||
|
||||
memcpy(&recv_data->data, data, real_size);
|
||||
recv_data->size = real_size;
|
||||
recv_data->data[real_size] = '\0';
|
||||
|
||||
return real_size;
|
||||
}
|
||||
|
||||
/* Converts Tox ID string contained in recv_data to binary format and puts it in thread's ID buffer.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
#define ID_PREFIX "\"tox_id\": \""
|
||||
static int process_response(struct Recv_Data *recv_data)
|
||||
{
|
||||
size_t prefix_size = strlen(ID_PREFIX);
|
||||
|
||||
if (recv_data->size < TOX_ADDRESS_SIZE * 2 + prefix_size)
|
||||
return -1;
|
||||
|
||||
const char *IDstart = strstr(recv_data->data, ID_PREFIX);
|
||||
|
||||
if (IDstart == NULL)
|
||||
return -1;
|
||||
|
||||
if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size)
|
||||
return -1;
|
||||
|
||||
char ID_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||
memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2);
|
||||
ID_string[TOX_ADDRESS_SIZE * 2] = 0;
|
||||
|
||||
if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets proxy info for given CURL handler.
|
||||
*
|
||||
* Returns 0 on success or if no proxy is set by the client.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
static int set_lookup_proxy(ToxWindow *self, CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
|
||||
{
|
||||
if (proxy_type == TOX_PROXY_TYPE_NONE)
|
||||
return 0;
|
||||
|
||||
if (proxy_address == NULL || port == 0) {
|
||||
lookup_error(self, "Unknown proxy error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
|
||||
|
||||
if (ret != CURLE_OK) {
|
||||
lookup_error(self, "Failed to set proxy port (libcurl error %d)", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP;
|
||||
|
||||
ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type);
|
||||
|
||||
if (ret != CURLE_OK) {
|
||||
lookup_error(self, "Failed to set proxy type (libcurl error %d)", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
|
||||
|
||||
if (ret != CURLE_OK) {
|
||||
lookup_error(self, "Failed to set proxy (libcurl error %d)", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *lookup_thread_func(void *data)
|
||||
{
|
||||
ToxWindow *self = t_data.self;
|
||||
|
||||
char input_domain[MAX_STR_SIZE];
|
||||
char name[MAX_STR_SIZE];
|
||||
|
||||
if (parse_addr(t_data.addr, name, sizeof(name), input_domain, sizeof(input_domain)) == -1) {
|
||||
lookup_error(self, "Input must be a 76 character Tox ID or an address in the form: username@domain");
|
||||
kill_lookup_thread();
|
||||
}
|
||||
|
||||
char nameserver_key[SERVER_KEY_SIZE];
|
||||
char real_domain[MAX_DOMAIN_SIZE];
|
||||
|
||||
if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) {
|
||||
if (!strcasecmp(input_domain, "utox.org"))
|
||||
lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic");
|
||||
else
|
||||
lookup_error(self, "Name server domain not found.");
|
||||
|
||||
kill_lookup_thread();
|
||||
}
|
||||
|
||||
CURL *c_handle = curl_easy_init();
|
||||
|
||||
if (!c_handle) {
|
||||
lookup_error(self, "curl handler error");
|
||||
kill_lookup_thread();
|
||||
}
|
||||
|
||||
struct Recv_Data recv_data;
|
||||
memset(&recv_data, 0, sizeof(struct Recv_Data));
|
||||
|
||||
char post_data[MAX_STR_SIZE];
|
||||
snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name);
|
||||
|
||||
struct curl_slist *headers = NULL;
|
||||
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
headers = curl_slist_append(headers, "charsets: utf-8");
|
||||
|
||||
curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(c_handle, CURLOPT_URL, real_domain);
|
||||
curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, write_lookup_data);
|
||||
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, (void *) &recv_data);
|
||||
curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||||
curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data);
|
||||
|
||||
if (set_lookup_proxy(self, c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type) == -1)
|
||||
goto on_exit;
|
||||
|
||||
int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL);
|
||||
|
||||
if (ret != CURLE_OK) {
|
||||
lookup_error(self, "TLS could not be enabled (libcurl error %d)", ret);
|
||||
goto on_exit;
|
||||
}
|
||||
|
||||
ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
|
||||
|
||||
if (ret != CURLE_OK) {
|
||||
lookup_error(self, "TLSv1.2 could not be set (libcurl error %d)", ret);
|
||||
goto on_exit;
|
||||
}
|
||||
|
||||
ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST);
|
||||
|
||||
if (ret != CURLE_OK) {
|
||||
lookup_error(self, "Failed to set TLS cipher list (libcurl error %d)", ret);
|
||||
goto on_exit;
|
||||
}
|
||||
|
||||
ret = curl_easy_perform(c_handle);
|
||||
|
||||
if (ret != CURLE_OK) {
|
||||
/* If system doesn't support any of the specified ciphers suites, fall back to default */
|
||||
if (ret == CURLE_SSL_CIPHER) {
|
||||
curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL);
|
||||
ret = curl_easy_perform(c_handle);
|
||||
}
|
||||
|
||||
if (ret != CURLE_OK) {
|
||||
lookup_error(self, "HTTPS lookup error (libcurl error %d)", ret);
|
||||
goto on_exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (process_response(&recv_data) == -1) {
|
||||
lookup_error(self, "Bad response.");
|
||||
goto on_exit;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
on_exit:
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(c_handle);
|
||||
kill_lookup_thread();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message)
|
||||
{
|
||||
if (t_data.disabled) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (t_data.busy) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish.");
|
||||
return;
|
||||
}
|
||||
|
||||
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", message);
|
||||
t_data.self = self;
|
||||
t_data.m = m;
|
||||
t_data.busy = true;
|
||||
|
||||
if (pthread_attr_init(&lookup_thread.attr) != 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init");
|
||||
memset(&t_data, 0, sizeof(struct thread_data));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set");
|
||||
pthread_attr_destroy(&lookup_thread.attr);
|
||||
memset(&t_data, 0, sizeof(struct thread_data));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init");
|
||||
pthread_attr_destroy(&lookup_thread.attr);
|
||||
memset(&t_data, 0, sizeof(struct thread_data));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initializes http based name lookups. Note: This function must be called only once before additional
|
||||
* threads are spawned.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 if curl failed to init.
|
||||
* Returns -2 if the nameserver list cannot be found.
|
||||
* Returns -3 if the nameserver list does not contain any valid entries.
|
||||
*/
|
||||
int name_lookup_init(void)
|
||||
{
|
||||
if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
|
||||
t_data.disabled = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *path = arg_opts.nameserver_path[0] ? arg_opts.nameserver_path : PACKAGE_DATADIR "/nameservers";
|
||||
int ret = load_nameserver_list(path);
|
||||
|
||||
if (ret != 0) {
|
||||
t_data.disabled = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void name_lookup_cleanup(void)
|
||||
{
|
||||
curl_global_cleanup();
|
||||
}
|
37
src/name_lookup.h
Normal file
37
src/name_lookup.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* name_lookup.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NAME_LOOKUP
|
||||
#define NAME_LOOKUP
|
||||
|
||||
/* Initializes http based name lookups. Note: This function must be called only once before additional
|
||||
* threads are spawned.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int name_lookup_init(void);
|
||||
void name_lookup_cleanup(void);
|
||||
|
||||
int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message);
|
||||
|
||||
#endif /* NAME_LOOKUP */
|
590
src/notify.c
590
src/notify.c
File diff suppressed because it is too large
Load Diff
20
src/notify.h
20
src/notify.h
@ -1,5 +1,5 @@
|
||||
/* notify.h
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
@ -20,8 +20,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _notify_h
|
||||
#define _notify_h
|
||||
#ifndef NOTIFY_H
|
||||
#define NOTIFY_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "windows.h"
|
||||
@ -29,7 +29,7 @@
|
||||
typedef enum _Notification
|
||||
{
|
||||
silent = -1,
|
||||
error,
|
||||
notif_error,
|
||||
self_log_in,
|
||||
self_log_out,
|
||||
user_log_in,
|
||||
@ -49,14 +49,14 @@ typedef enum _Flags {
|
||||
NT_LOOP = 1 << 2, /* Loop sound. If this setting active, notify() will return id of the sound
|
||||
* so it could be stopped. It will return 0 if error or NT_NATIVE flag is set and play \a instead
|
||||
*/
|
||||
NT_RESTOL = 1 << 3, /* Respect tolerance. Usually used to stop flood at toxic startup
|
||||
NT_RESTOL = 1 << 3, /* Respect tolerance. Usually used to stop flood at toxic startup
|
||||
* Only works if login_cooldown is true when calling init_notify()
|
||||
*/
|
||||
NT_NOTIFWND = 1 << 4, /* Pop notify window. NOTE: only works(/WILL WORK) if libnotify is present */
|
||||
NT_WNDALERT_0 = 1 << 5, /* Alert toxic */
|
||||
NT_WNDALERT_1 = 1 << 6, /* Alert toxic */
|
||||
NT_WNDALERT_2 = 1 << 7, /* Alert toxic */
|
||||
|
||||
|
||||
NT_ALWAYS = 1 << 8, /* Force sound to play */
|
||||
} Flags;
|
||||
|
||||
@ -68,13 +68,13 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id);
|
||||
|
||||
void stop_sound(int id);
|
||||
|
||||
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, char* title, const char* format, ...);
|
||||
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...);
|
||||
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...);
|
||||
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...);
|
||||
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...);
|
||||
|
||||
#ifdef _SOUND_NOTIFY
|
||||
#ifdef SOUND_NOTIFY
|
||||
int set_sound(Notification sound, const char* value);
|
||||
#endif /* _SOUND_NOTIFY */
|
||||
#endif /* SOUND_NOTIFY */
|
||||
|
||||
#endif /* _notify_h */
|
||||
#endif /* NOTIFY_H */
|
||||
|
54
src/osx_video.h
Normal file
54
src/osx_video.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* osx_video.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OSX_VIDEO_H
|
||||
#define OSX_VIDEO_H
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#endif /* __OBJC__ */
|
||||
|
||||
#define RELEASE_CHK(func, obj) if ((obj))\
|
||||
func((obj));
|
||||
|
||||
void bgrtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height);
|
||||
|
||||
#ifdef __OBJC__
|
||||
@interface OSXVideo : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
|
||||
- (instancetype)initWithDeviceNames:(char **)device_names AmtDevices:(int *)size;
|
||||
@end
|
||||
#endif /* __OBJC__ */
|
||||
|
||||
int osx_video_init(char **device_names, int *size);
|
||||
void osx_video_release();
|
||||
/* Start device */
|
||||
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height);
|
||||
/* Stop device */
|
||||
void osx_video_close_device(uint32_t device_idx);
|
||||
/* Read data from device */
|
||||
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height);
|
||||
|
||||
|
||||
#endif /* OSX_VIDEO_H */
|
308
src/osx_video.m
Normal file
308
src/osx_video.m
Normal file
@ -0,0 +1,308 @@
|
||||
/* osx_video.m
|
||||
*
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef __OBJC__
|
||||
#include "osx_video.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* Helper video format functions
|
||||
*/
|
||||
static uint8_t rgb_to_y(int r, int g, int b)
|
||||
{
|
||||
int y = ((9798 * r + 19235 * g + 3736 * b) >> 15);
|
||||
return y>255? 255 : y<0 ? 0 : y;
|
||||
}
|
||||
|
||||
static uint8_t rgb_to_u(int r, int g, int b)
|
||||
{
|
||||
int u = ((-5538 * r + -10846 * g + 16351 * b) >> 15) + 128;
|
||||
return u>255? 255 : u<0 ? 0 : u;
|
||||
}
|
||||
|
||||
static uint8_t rgb_to_v(int r, int g, int b)
|
||||
{
|
||||
int v = ((16351 * r + -13697 * g + -2664 * b) >> 15) + 128;
|
||||
return v>255? 255 : v<0 ? 0 : v;
|
||||
}
|
||||
|
||||
void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height)
|
||||
{
|
||||
uint16_t x, y;
|
||||
uint8_t *p;
|
||||
uint8_t r, g, b;
|
||||
|
||||
for(y = 0; y != height; y += 2) {
|
||||
p = rgb;
|
||||
for(x = 0; x != width; x++) {
|
||||
b = *rgb++;
|
||||
g = *rgb++;
|
||||
r = *rgb++;
|
||||
rgb++;
|
||||
|
||||
*plane_y++ = rgb_to_y(r, g, b);
|
||||
}
|
||||
|
||||
for(x = 0; x != width / 2; x++) {
|
||||
b = *rgb++;
|
||||
g = *rgb++;
|
||||
r = *rgb++;
|
||||
rgb++;
|
||||
|
||||
*plane_y++ = rgb_to_y(r, g, b);
|
||||
|
||||
b = *rgb++;
|
||||
g = *rgb++;
|
||||
r = *rgb++;
|
||||
rgb++;
|
||||
|
||||
*plane_y++ = rgb_to_y(r, g, b);
|
||||
|
||||
b = ((int)b + (int)*(rgb - 8) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
|
||||
g = ((int)g + (int)*(rgb - 7) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
|
||||
r = ((int)r + (int)*(rgb - 6) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
|
||||
p++;
|
||||
|
||||
*plane_u++ = rgb_to_u(r, g, b);
|
||||
*plane_v++ = rgb_to_v(r, g, b);
|
||||
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* End of helper video format functions
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Implementation for OSXVideo
|
||||
*/
|
||||
@implementation OSXVideo {
|
||||
dispatch_queue_t _processingQueue;
|
||||
AVCaptureSession *_session;
|
||||
AVCaptureVideoDataOutput *_linkerVideo;
|
||||
|
||||
CVImageBufferRef _currentFrame;
|
||||
pthread_mutex_t _frameLock;
|
||||
BOOL _shouldMangleDimensions;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDeviceNames: (char **)device_names AmtDevices: (int *)size {
|
||||
_session = [[AVCaptureSession alloc] init];
|
||||
|
||||
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
|
||||
int i;
|
||||
for (i = 0; i < [devices count]; ++i) {
|
||||
AVCaptureDevice *device = [devices objectAtIndex:i];
|
||||
char *video_input_name;
|
||||
NSString *localizedName = [device localizedName];
|
||||
video_input_name = (char*)malloc(strlen([localizedName cStringUsingEncoding:NSUTF8StringEncoding]) + 1);
|
||||
strcpy(video_input_name, (char*)[localizedName cStringUsingEncoding:NSUTF8StringEncoding]);
|
||||
device_names[i] = video_input_name;
|
||||
}
|
||||
|
||||
if ( i <= 0 )
|
||||
return nil;
|
||||
*size = i;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
pthread_mutex_destroy(&_frameLock);
|
||||
[_session release];
|
||||
[_linkerVideo release];
|
||||
dispatch_release(_processingQueue);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (int)openVideoDeviceIndex: (uint32_t)device_idx Width: (uint16_t *)width Height: (uint16_t *)height {
|
||||
pthread_mutex_init(&_frameLock, NULL);
|
||||
pthread_mutex_lock(&_frameLock);
|
||||
_processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL);
|
||||
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
|
||||
AVCaptureDevice *device = [devices objectAtIndex:device_idx];
|
||||
NSError *error = NULL;
|
||||
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
|
||||
|
||||
if ( error != NULL ) {
|
||||
[input release];
|
||||
return -1;
|
||||
}
|
||||
|
||||
[_session beginConfiguration];
|
||||
[_session addInput:input];
|
||||
//_session.sessionPreset = AVCaptureSessionPreset640x480;
|
||||
//*width = 640;
|
||||
//*height = 480;
|
||||
_shouldMangleDimensions = YES;
|
||||
[_session commitConfiguration];
|
||||
[input release];
|
||||
[device release];
|
||||
|
||||
/* Obtain device resolution */
|
||||
AVCaptureInputPort *port = [input.ports objectAtIndex:0];
|
||||
CMFormatDescriptionRef format_description = port.formatDescription;
|
||||
if ( format_description ) {
|
||||
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description);
|
||||
*width = dimensions.width;
|
||||
*height = dimensions.height;
|
||||
} else {
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
}
|
||||
|
||||
_linkerVideo = [[AVCaptureVideoDataOutput alloc] init];
|
||||
[_linkerVideo setSampleBufferDelegate:self queue:_processingQueue];
|
||||
// TODO possibly get a better pixel format
|
||||
if (_shouldMangleDimensions) {
|
||||
[_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
|
||||
(id)kCVPixelBufferWidthKey: @640,
|
||||
(id)kCVPixelBufferHeightKey: @480}];
|
||||
} else {
|
||||
[_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
|
||||
}
|
||||
[_session addOutput:_linkerVideo];
|
||||
[_session startRunning];
|
||||
|
||||
pthread_mutex_unlock(&_frameLock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)closeVideoDeviceIndex: (uint32_t)device_idx {
|
||||
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
|
||||
AVCaptureDevice *device = [devices objectAtIndex:device_idx];
|
||||
NSError *error = NULL;
|
||||
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
|
||||
[_session stopRunning];
|
||||
[_session removeOutput:_linkerVideo];
|
||||
[_session removeInput:input];
|
||||
[_linkerVideo release];
|
||||
}
|
||||
|
||||
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
|
||||
pthread_mutex_lock(&_frameLock);
|
||||
CVImageBufferRef img = CMSampleBufferGetImageBuffer(sampleBuffer);
|
||||
if (!img) {
|
||||
NSLog(@"Toxic WARNING: Bad sampleBuffer from AVfoundation!");
|
||||
} else {
|
||||
CVPixelBufferUnlockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
|
||||
RELEASE_CHK(CFRelease, _currentFrame);
|
||||
|
||||
_currentFrame = (CVImageBufferRef)CFRetain(img);
|
||||
// we're not going to do anything to it, so it's safe to lock it always
|
||||
CVPixelBufferLockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
|
||||
}
|
||||
pthread_mutex_unlock(&_frameLock);
|
||||
}
|
||||
|
||||
- (int)getVideoFrameY: (uint8_t *)y U: (uint8_t *)u V: (uint8_t *)v Width: (uint16_t *)width Height: (uint16_t *)height {
|
||||
if (!_currentFrame) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&_frameLock);
|
||||
CFRetain(_currentFrame);
|
||||
|
||||
CFTypeID imageType = CFGetTypeID(_currentFrame);
|
||||
if (imageType == CVPixelBufferGetTypeID()) {
|
||||
// TODO maybe handle other formats
|
||||
bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height);
|
||||
} else if (imageType == CVOpenGLBufferGetTypeID()) {
|
||||
// OpenGL pbuffer
|
||||
} else if (imageType == CVOpenGLTextureGetTypeID()) {
|
||||
// OpenGL Texture (Do we need to handle these?)
|
||||
}
|
||||
|
||||
CVPixelBufferRelease(_currentFrame);
|
||||
pthread_mutex_unlock(&_frameLock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@end
|
||||
/*
|
||||
* End of implementation for OSXVideo
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* C-interface for OSXVideo
|
||||
*/
|
||||
static OSXVideo* _OSXVideo = nil;
|
||||
|
||||
int osx_video_init(char **device_names, int *size)
|
||||
{
|
||||
_OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size];
|
||||
|
||||
if ( _OSXVideo == nil )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osx_video_release()
|
||||
{
|
||||
[_OSXVideo release];
|
||||
_OSXVideo = nil;
|
||||
}
|
||||
|
||||
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height)
|
||||
{
|
||||
if ( _OSXVideo == nil )
|
||||
return -1;
|
||||
|
||||
return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height];
|
||||
}
|
||||
|
||||
void osx_video_close_device(uint32_t device_idx)
|
||||
{
|
||||
[_OSXVideo closeVideoDeviceIndex: device_idx];
|
||||
}
|
||||
|
||||
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height)
|
||||
{
|
||||
if ( _OSXVideo == nil )
|
||||
return -1;
|
||||
|
||||
return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height];
|
||||
}
|
||||
/*
|
||||
* End of C-interface for OSXVideo
|
||||
*/
|
||||
|
||||
#endif /* __OBJC__ */
|
231
src/prompt.c
231
src/prompt.c
@ -31,6 +31,7 @@
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "prompt.h"
|
||||
#include "friendlist.h"
|
||||
#include "execute.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic_strings.h"
|
||||
@ -43,39 +44,57 @@
|
||||
#include "autocomplete.h"
|
||||
|
||||
extern ToxWindow *prompt;
|
||||
extern struct user_settings *user_settings_;
|
||||
extern struct _Winthread Winthread;
|
||||
extern struct user_settings *user_settings;
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
_FriendRequests FriendRequests;
|
||||
extern FriendsList Friends;
|
||||
FriendRequests FrndRequests;
|
||||
#ifdef VIDEO
|
||||
#define AC_NUM_GLOB_COMMANDS 22
|
||||
#elif AUDIO
|
||||
#define AC_NUM_GLOB_COMMANDS 20
|
||||
#else
|
||||
#define AC_NUM_GLOB_COMMANDS 18
|
||||
#endif
|
||||
|
||||
/* Array of global command names used for tab completion. */
|
||||
const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
{ "/accept" },
|
||||
{ "/add" },
|
||||
{ "/avatar" },
|
||||
{ "/clear" },
|
||||
{ "/close" }, /* rm /close when groupchats gets its own list */
|
||||
{ "/connect" },
|
||||
{ "/decline" },
|
||||
{ "/exit" },
|
||||
{ "/groupchat" },
|
||||
{ "/group" },
|
||||
{ "/help" },
|
||||
{ "/log" },
|
||||
{ "/myid" },
|
||||
{ "/myqr" },
|
||||
{ "/nick" },
|
||||
{ "/note" },
|
||||
{ "/nospam" },
|
||||
{ "/quit" },
|
||||
{ "/requests" },
|
||||
{ "/status" },
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
|
||||
{ "/lsdev" },
|
||||
{ "/sdev" },
|
||||
|
||||
#endif /* _AUDIO */
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef VIDEO
|
||||
|
||||
{ "/lsvdev" },
|
||||
{ "/svdev" },
|
||||
|
||||
#endif /* VIDEO */
|
||||
|
||||
};
|
||||
|
||||
void kill_prompt_window(ToxWindow *self)
|
||||
void kill_prompt_window(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
StatusBar *statusbar = self->stb;
|
||||
@ -88,7 +107,6 @@ void kill_prompt_window(ToxWindow *self)
|
||||
delwin(statusbar->topline);
|
||||
|
||||
free(ctx->log);
|
||||
free(ctx->hst);
|
||||
free(ctx);
|
||||
free(self->help);
|
||||
free(statusbar);
|
||||
@ -96,6 +114,13 @@ void kill_prompt_window(ToxWindow *self)
|
||||
del_window(self);
|
||||
}
|
||||
|
||||
/* callback: Updates own connection status in prompt statusbar */
|
||||
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata)
|
||||
{
|
||||
StatusBar *statusbar = prompt->stb;
|
||||
statusbar->connection = connection_status;
|
||||
}
|
||||
|
||||
/* Updates own nick in prompt statusbar */
|
||||
void prompt_update_nick(ToxWindow *prompt, const char *nick)
|
||||
{
|
||||
@ -109,44 +134,42 @@ void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusms
|
||||
{
|
||||
StatusBar *statusbar = prompt->stb;
|
||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||
int len = strlen(statusbar->statusmsg);
|
||||
size_t len = strlen(statusbar->statusmsg);
|
||||
statusbar->statusmsg_len = len;
|
||||
tox_set_status_message(m, (uint8_t *) statusmsg, (uint64_t) len);
|
||||
|
||||
TOX_ERR_SET_INFO err;
|
||||
tox_self_set_status_message(m, (uint8_t *) statusmsg, len, &err);
|
||||
|
||||
if (err != TOX_ERR_SET_INFO_OK)
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err);
|
||||
}
|
||||
|
||||
/* Updates own status in prompt statusbar */
|
||||
void prompt_update_status(ToxWindow *prompt, uint8_t status)
|
||||
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status)
|
||||
{
|
||||
StatusBar *statusbar = prompt->stb;
|
||||
statusbar->status = status;
|
||||
}
|
||||
|
||||
/* Updates own connection status in prompt statusbar */
|
||||
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
|
||||
{
|
||||
StatusBar *statusbar = prompt->stb;
|
||||
statusbar->is_online = is_connected;
|
||||
}
|
||||
|
||||
/* Adds friend request to pending friend requests.
|
||||
Returns request number on success, -1 if queue is full. */
|
||||
static int add_friend_request(const char *public_key, const char *data)
|
||||
{
|
||||
if (FriendRequests.max_idx >= MAX_FRIEND_REQUESTS)
|
||||
if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS)
|
||||
return -1;
|
||||
|
||||
int i;
|
||||
|
||||
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);
|
||||
for (i = 0; i <= FrndRequests.max_idx; ++i) {
|
||||
if (!FrndRequests.request[i].active) {
|
||||
FrndRequests.request[i].active = true;
|
||||
memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE);
|
||||
snprintf(FrndRequests.request[i].msg, sizeof(FrndRequests.request[i].msg), "%s", data);
|
||||
|
||||
if (i == FriendRequests.max_idx)
|
||||
++FriendRequests.max_idx;
|
||||
if (i == FrndRequests.max_idx)
|
||||
++FrndRequests.max_idx;
|
||||
|
||||
++FriendRequests.num_requests;
|
||||
++FrndRequests.num_requests;
|
||||
|
||||
return i;
|
||||
}
|
||||
@ -163,7 +186,7 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
getyx(self->window, y, x);
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= 0)
|
||||
if (x2 <= 0 || y2 <= 0)
|
||||
return;
|
||||
|
||||
/* ignore non-menu related input if active */
|
||||
@ -184,18 +207,30 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
|
||||
if (key == '\t') { /* TAB key: auto-completes command */
|
||||
if (ctx->len > 1 && ctx->line[0] == '/') {
|
||||
int diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
|
||||
int diff = -1;
|
||||
|
||||
if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0)
|
||||
diff = dir_match(self, m, ctx->line, L"/avatar");
|
||||
else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0){
|
||||
const char status_cmd_list[3][8] = {
|
||||
{"online"},
|
||||
{"away"},
|
||||
{"busy"},
|
||||
};
|
||||
diff = complete_line(self, status_cmd_list, 3, 8);
|
||||
} else
|
||||
diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
|
||||
|
||||
if (diff != -1) {
|
||||
if (x + diff > x2 - 1) {
|
||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
||||
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
||||
}
|
||||
} else {
|
||||
sound_notify(self, error, 0, NULL);
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
} else {
|
||||
sound_notify(self, error, 0, NULL);
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
} else if (key == '\n') {
|
||||
rm_trailing_spaces_buf(ctx);
|
||||
@ -222,9 +257,15 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (y2 <= 0 || x2 <= 0)
|
||||
return;
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_print(self);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
wclear(ctx->linewin);
|
||||
|
||||
curs_set(1);
|
||||
@ -233,33 +274,35 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
||||
|
||||
StatusBar *statusbar = self->stb;
|
||||
|
||||
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
|
||||
wmove(statusbar->topline, 0, 0);
|
||||
|
||||
if (statusbar->is_online) {
|
||||
int colour = WHITE;
|
||||
const char *status_text = "Unknown";
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
TOX_CONNECTION connection = statusbar->connection;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
switch (statusbar->status) {
|
||||
case TOX_USERSTATUS_NONE:
|
||||
if (connection != TOX_CONNECTION_NONE) {
|
||||
int colour = MAGENTA;
|
||||
const char *status_text = "ERROR";
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
TOX_USER_STATUS status = statusbar->status;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
switch (status) {
|
||||
case TOX_USER_STATUS_NONE:
|
||||
status_text = "Online";
|
||||
colour = GREEN;
|
||||
break;
|
||||
|
||||
case TOX_USERSTATUS_AWAY:
|
||||
case TOX_USER_STATUS_AWAY:
|
||||
status_text = "Away";
|
||||
colour = YELLOW;
|
||||
break;
|
||||
|
||||
case TOX_USERSTATUS_BUSY:
|
||||
case TOX_USER_STATUS_BUSY:
|
||||
status_text = "Busy";
|
||||
colour = RED;
|
||||
break;
|
||||
|
||||
case TOX_USERSTATUS_INVALID:
|
||||
status_text = "ERROR";
|
||||
colour = MAGENTA;
|
||||
break;
|
||||
}
|
||||
|
||||
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||
@ -267,25 +310,30 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
} else {
|
||||
wprintw(statusbar->topline, " [Offline]");
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
wprintw(statusbar->topline, " %s ", statusbar->nick);
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
}
|
||||
|
||||
/* Reset statusbar->statusmsg on window resize */
|
||||
if (x2 != self->x) {
|
||||
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {0};
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
tox_get_self_status_message(m, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
size_t slen = tox_self_get_status_message_size(m);
|
||||
tox_self_get_status_message (m, (uint8_t*) statusmsg);
|
||||
statusmsg[slen] = '\0';
|
||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
}
|
||||
|
||||
self->x = x2;
|
||||
@ -293,6 +341,8 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
/* Truncate note if it doesn't fit in statusbar */
|
||||
uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
|
||||
if (statusbar->statusmsg_len > maxlen) {
|
||||
statusbar->statusmsg[maxlen - 3] = '\0';
|
||||
strcat(statusbar->statusmsg, "...");
|
||||
@ -300,7 +350,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
}
|
||||
|
||||
if (statusbar->statusmsg[0])
|
||||
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
|
||||
wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
||||
|
||||
@ -308,7 +360,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
getyx(self->window, y, x);
|
||||
(void) x;
|
||||
|
||||
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
|
||||
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||
wmove(self->window, y + 1, new_x);
|
||||
|
||||
wrefresh(self->window);
|
||||
@ -317,11 +369,8 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
help_onDraw(self);
|
||||
}
|
||||
|
||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum , uint8_t status)
|
||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status)
|
||||
{
|
||||
if (friendnum < 0)
|
||||
return;
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
|
||||
@ -334,24 +383,25 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
const char *msg;
|
||||
|
||||
if (status == 1) {
|
||||
if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) {
|
||||
msg = "has come online";
|
||||
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
|
||||
write_to_log(msg, nick, ctx->log, true);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
|
||||
box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
|
||||
"%s has come online", nick );
|
||||
else
|
||||
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
|
||||
"Toxic", "%s has come online", nick );
|
||||
} else {
|
||||
}
|
||||
else if (connection_status == TOX_CONNECTION_NONE) {
|
||||
msg = "has gone offline";
|
||||
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, RED, msg);
|
||||
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
|
||||
write_to_log(msg, nick, ctx->log, true);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
|
||||
box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
|
||||
"%s has gone offline", nick );
|
||||
else
|
||||
box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
|
||||
@ -359,8 +409,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum
|
||||
}
|
||||
}
|
||||
|
||||
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data,
|
||||
uint16_t length)
|
||||
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, size_t length)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
@ -386,30 +435,33 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
|
||||
{
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (y2 <= 0 || x2 <= 0)
|
||||
exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES);
|
||||
|
||||
(void) y2;
|
||||
|
||||
/* Init statusbar info */
|
||||
StatusBar *statusbar = self->stb;
|
||||
statusbar->status = TOX_USERSTATUS_NONE;
|
||||
statusbar->is_online = false;
|
||||
statusbar->status = TOX_USER_STATUS_NONE;
|
||||
statusbar->connection = TOX_CONNECTION_NONE;
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
char statusmsg[MAX_STR_SIZE];
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
|
||||
uint16_t n_len = tox_get_self_name(m, (uint8_t *) nick);
|
||||
uint16_t s_len = tox_get_self_status_message(m, (uint8_t *) statusmsg, MAX_STR_SIZE);
|
||||
uint8_t status = tox_get_self_user_status(m);
|
||||
size_t n_len = tox_self_get_name_size(m);
|
||||
tox_self_get_name(m, (uint8_t *) nick);
|
||||
|
||||
size_t s_len = tox_self_get_status_message_size(m);
|
||||
tox_self_get_status_message(m, (uint8_t *) statusmsg);
|
||||
|
||||
TOX_USER_STATUS status = tox_self_get_status(m);
|
||||
|
||||
nick[n_len] = '\0';
|
||||
statusmsg[s_len] = '\0';
|
||||
|
||||
/* load prev status message or show toxic version if it has never been set */
|
||||
char ver[strlen(TOXICVER) + 1];
|
||||
strcpy(ver, TOXICVER);
|
||||
const char *toxic_ver = strtok(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);
|
||||
if (s_len == 0 || !strncmp(statusmsg, "Toxing on Toxic", strlen("Toxing on Toxic"))) {
|
||||
snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic");
|
||||
s_len = strlen(statusmsg);
|
||||
statusmsg[s_len] = '\0';
|
||||
}
|
||||
@ -428,7 +480,7 @@ static void print_welcome_msg(ToxWindow *self)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_ _/ _ \\ \\/ /_ _/ ___|");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " | || | | \\ / | | | ");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " | || |_| / \\ | | |___ ");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_| \\___/_/\\_\\___\\____|");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_| \\___/_/\\_\\___\\____| v." TOXICVER);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
|
||||
const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging client.";
|
||||
@ -444,6 +496,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||
int y2, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (y2 <= 0 || x2 <= 0)
|
||||
exit_toxic_err("failed in prompt_onInit", FATALERR_CURSES);
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||
@ -456,16 +511,19 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||
|
||||
line_info_init(ctx->hst);
|
||||
|
||||
if (user_settings_->autolog == AUTOLOG_ON) {
|
||||
char myid[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(m, (uint8_t *) myid);
|
||||
log_enable(self->name, myid, ctx->log);
|
||||
if (user_settings->autolog == AUTOLOG_ON) {
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||
}
|
||||
|
||||
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)
|
||||
@ -473,6 +531,7 @@ ToxWindow new_prompt(void)
|
||||
ToxWindow ret;
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
|
||||
ret.num = -1;
|
||||
ret.active = true;
|
||||
ret.is_prompt = true;
|
||||
|
||||
@ -496,6 +555,6 @@ ToxWindow new_prompt(void)
|
||||
ret.help = help;
|
||||
|
||||
ret.active_box = -1;
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
26
src/prompt.h
26
src/prompt.h
@ -20,39 +20,37 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PROMPT_H_UZYGWFFL
|
||||
#define PROMPT_H_UZYGWFFL
|
||||
#ifndef PROMPT_H
|
||||
#define PROMPT_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#ifdef _AUDIO
|
||||
#define AC_NUM_GLOB_COMMANDS 18
|
||||
#else
|
||||
#define AC_NUM_GLOB_COMMANDS 16
|
||||
#endif /* _AUDIO */
|
||||
|
||||
#define MAX_FRIEND_REQUESTS 32
|
||||
|
||||
struct _friend_request {
|
||||
struct friend_request {
|
||||
bool active;
|
||||
char msg[MAX_STR_SIZE];
|
||||
uint8_t key[TOX_CLIENT_ID_SIZE];
|
||||
uint8_t key[TOX_PUBLIC_KEY_SIZE];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int max_idx;
|
||||
int num_requests;
|
||||
struct _friend_request request[MAX_FRIEND_REQUESTS];
|
||||
} _FriendRequests;
|
||||
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, Tox *m, const char *statusmsg);
|
||||
void prompt_update_status(ToxWindow *prompt, uint8_t status);
|
||||
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status);
|
||||
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
|
||||
void kill_prompt_window(ToxWindow *self);
|
||||
|
||||
#endif /* end of include guard: PROMPT_H_UZYGWFFL */
|
||||
/* callback: Updates own connection status in prompt statusbar */
|
||||
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata);
|
||||
|
||||
#endif /* end of include guard: PROMPT_H */
|
||||
|
89
src/qr_code.c
Normal file
89
src/qr_code.c
Normal file
@ -0,0 +1,89 @@
|
||||
/* qr_obj_code.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2015 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 <qrencode.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "qr_code.h"
|
||||
|
||||
#define BORDER_LEN 1
|
||||
#define CHAR_1 "\342\226\210"
|
||||
#define CHAR_2 "\342\226\204"
|
||||
#define CHAR_3 "\342\226\200"
|
||||
|
||||
/* Converts a tox ID string into a QRcode and prints it to the given file stream.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int ID_to_QRcode(const char *tox_id, FILE *fp)
|
||||
{
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
|
||||
QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
|
||||
|
||||
if (qr_obj == NULL)
|
||||
return -1;
|
||||
|
||||
size_t width = qr_obj->width;
|
||||
size_t i, j;
|
||||
|
||||
for (i = 0; i < width + BORDER_LEN * 2; ++i)
|
||||
fprintf(fp, "%s", CHAR_1);
|
||||
|
||||
fprintf(fp, "\n");
|
||||
|
||||
for (i = 0; i < width; i += 2) {
|
||||
for (j = 0; j < BORDER_LEN; ++j)
|
||||
fprintf(fp, "%s", CHAR_1);
|
||||
|
||||
const unsigned char *row_1 = qr_obj->data + width * i;
|
||||
const unsigned char *row_2 = row_1 + width;
|
||||
|
||||
for (j = 0; j < width; ++j) {
|
||||
bool x = row_1[j] & 1;
|
||||
bool y = (i + 1) < width ? (row_2[j] & 1) : false;
|
||||
|
||||
if (x && y)
|
||||
fprintf(fp, " ");
|
||||
else if (x)
|
||||
fprintf(fp, "%s", CHAR_2);
|
||||
else if (y)
|
||||
fprintf(fp, "%s", CHAR_3);
|
||||
else
|
||||
fprintf(fp, "%s", CHAR_1);
|
||||
}
|
||||
|
||||
for (j = 0; j < BORDER_LEN; ++j)
|
||||
fprintf(fp, "%s", CHAR_1);
|
||||
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
|
||||
QRcode_free(qr_obj);
|
||||
|
||||
return 0;
|
||||
}
|
35
src/qr_code.h
Normal file
35
src/qr_code.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* qr_code.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QR_CODE
|
||||
#define QR_CODE
|
||||
|
||||
#define QRCODE_FILENAME_EXT ".QRcode"
|
||||
|
||||
/* Converts a tox ID string into a QRcode and prints it to the given file stream.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int ID_to_QRcode(const char *tox_id, FILE *fp);
|
||||
|
||||
#endif /* QR_CODE */
|
294
src/settings.c
294
src/settings.c
@ -31,9 +31,9 @@
|
||||
#include "notify.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#ifdef _AUDIO
|
||||
#include "device.h"
|
||||
#endif /* _AUDIO */
|
||||
#ifdef AUDIO
|
||||
#include "audio_device.h"
|
||||
#endif /* AUDIO */
|
||||
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
@ -44,96 +44,136 @@
|
||||
|
||||
#define NO_SOUND "silent"
|
||||
|
||||
static struct _ui_strings {
|
||||
static struct ui_strings {
|
||||
const char* self;
|
||||
const char* timestamps;
|
||||
const char* time_format;
|
||||
const char* timestamp_format;
|
||||
const char* log_timestamp_format;
|
||||
const char* alerts;
|
||||
const char* native_colors;
|
||||
const char* autolog;
|
||||
const char* time_format;
|
||||
const char* history_size;
|
||||
const char* show_typing_self;
|
||||
const char* show_typing_other;
|
||||
const char* show_welcome_msg;
|
||||
|
||||
const char* line_join;
|
||||
const char* line_quit;
|
||||
const char* line_alert;
|
||||
const char* line_normal;
|
||||
|
||||
const char* mplex_away;
|
||||
const char* mplex_away_note;
|
||||
} ui_strings = {
|
||||
"ui",
|
||||
"timestamps",
|
||||
"time_format",
|
||||
"timestamp_format",
|
||||
"log_timestamp_format",
|
||||
"alerts",
|
||||
"native_colors",
|
||||
"autolog",
|
||||
"time_format",
|
||||
"history_size",
|
||||
"show_typing_self",
|
||||
"show_typing_other",
|
||||
"show_welcome_msg",
|
||||
"line_join",
|
||||
"line_quit",
|
||||
"line_alert",
|
||||
"line_normal",
|
||||
"mplex_away",
|
||||
"mplex_away_note",
|
||||
};
|
||||
|
||||
static void ui_defaults(struct user_settings* settings)
|
||||
static void ui_defaults(struct user_settings* settings)
|
||||
{
|
||||
settings->timestamps = TIMESTAMPS_ON;
|
||||
settings->time = TIME_24;
|
||||
snprintf(settings->timestamp_format, sizeof(settings->timestamp_format), "%s", TIMESTAMP_DEFAULT);
|
||||
snprintf(settings->log_timestamp_format, sizeof(settings->log_timestamp_format), "%s", LOG_TIMESTAMP_DEFAULT);
|
||||
|
||||
settings->autolog = AUTOLOG_OFF;
|
||||
settings->alerts = ALERTS_ENABLED;
|
||||
settings->colour_theme = DFLT_COLS;
|
||||
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;
|
||||
|
||||
snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN);
|
||||
snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT);
|
||||
snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT);
|
||||
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
|
||||
|
||||
settings->mplex_away = MPLEX_ON;
|
||||
snprintf (settings->mplex_away_note,
|
||||
sizeof (settings->mplex_away_note),
|
||||
"%s",
|
||||
MPLEX_AWAY_NOTE);
|
||||
}
|
||||
|
||||
static const struct _keys_strings {
|
||||
const char* self;
|
||||
const char* next_tab;
|
||||
const char* prev_tab;
|
||||
const char* scroll_line_up;
|
||||
const char* scroll_line_down;
|
||||
const char* half_page_up;
|
||||
const char* half_page_down;
|
||||
const char* page_bottom;
|
||||
const char* peer_list_up;
|
||||
const char* peer_list_down;
|
||||
static const struct keys_strings {
|
||||
const char* self;
|
||||
const char* next_tab;
|
||||
const char* prev_tab;
|
||||
const char* scroll_line_up;
|
||||
const char* scroll_line_down;
|
||||
const char* half_page_up;
|
||||
const char* half_page_down;
|
||||
const char* page_bottom;
|
||||
const char* peer_list_up;
|
||||
const char* peer_list_down;
|
||||
const char* toggle_peerlist;
|
||||
} key_strings = {
|
||||
"keys",
|
||||
"next_tab",
|
||||
"prev_tab",
|
||||
"scroll_line_up",
|
||||
"scroll_line_down",
|
||||
"half_page_up",
|
||||
"half_page_down",
|
||||
"page_bottom",
|
||||
"peer_list_up",
|
||||
"peer_list_down"
|
||||
"keys",
|
||||
"next_tab",
|
||||
"prev_tab",
|
||||
"scroll_line_up",
|
||||
"scroll_line_down",
|
||||
"half_page_up",
|
||||
"half_page_down",
|
||||
"page_bottom",
|
||||
"peer_list_up",
|
||||
"peer_list_down",
|
||||
"toggle_peerlist",
|
||||
};
|
||||
|
||||
/* defines from toxic.h */
|
||||
static void key_defaults(struct user_settings* settings)
|
||||
{
|
||||
settings->key_next_tab = T_KEY_NEXT;
|
||||
settings->key_prev_tab = T_KEY_PREV;
|
||||
settings->key_scroll_line_up = KEY_PPAGE;
|
||||
settings->key_scroll_line_down = KEY_NPAGE;
|
||||
settings->key_half_page_up = T_KEY_C_F;
|
||||
settings->key_half_page_down = T_KEY_C_V;
|
||||
settings->key_page_bottom = T_KEY_C_H;
|
||||
settings->key_peer_list_up = T_KEY_C_LB;
|
||||
settings->key_peer_list_down = T_KEY_C_RB;
|
||||
settings->key_next_tab = T_KEY_NEXT;
|
||||
settings->key_prev_tab = T_KEY_PREV;
|
||||
settings->key_scroll_line_up = KEY_PPAGE;
|
||||
settings->key_scroll_line_down = KEY_NPAGE;
|
||||
settings->key_half_page_up = T_KEY_C_F;
|
||||
settings->key_half_page_down = T_KEY_C_V;
|
||||
settings->key_page_bottom = T_KEY_C_H;
|
||||
settings->key_peer_list_up = T_KEY_C_LB;
|
||||
settings->key_peer_list_down = T_KEY_C_RB;
|
||||
settings->key_toggle_peerlist = T_KEY_C_B;
|
||||
}
|
||||
|
||||
static const struct _tox_strings {
|
||||
static const struct tox_strings {
|
||||
const char* self;
|
||||
const char* download_path;
|
||||
const char* chatlogs_path;
|
||||
const char* avatar_path;
|
||||
} tox_strings = {
|
||||
"tox",
|
||||
"download_path",
|
||||
"chatlogs_path",
|
||||
"avatar_path",
|
||||
};
|
||||
|
||||
static void tox_defaults(struct user_settings* settings)
|
||||
{
|
||||
strcpy(settings->download_path, "");
|
||||
strcpy(settings->chatlogs_path, "");
|
||||
strcpy(settings->avatar_path, "");
|
||||
}
|
||||
|
||||
#ifdef _AUDIO
|
||||
static const struct _audio_strings {
|
||||
#ifdef AUDIO
|
||||
static const struct audio_strings {
|
||||
const char* self;
|
||||
const char* input_device;
|
||||
const char* output_device;
|
||||
@ -153,10 +193,10 @@ static void audio_defaults(struct user_settings* settings)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _SOUND_NOTIFY
|
||||
static const struct _sound_strings {
|
||||
#ifdef SOUND_NOTIFY
|
||||
static const struct sound_strings {
|
||||
const char* self;
|
||||
const char* error;
|
||||
const char* notif_error;
|
||||
const char* self_log_in;
|
||||
const char* self_log_out;
|
||||
const char* user_log_in;
|
||||
@ -168,7 +208,7 @@ static const struct _sound_strings {
|
||||
const char* transfer_completed;
|
||||
} sound_strings = {
|
||||
"sounds",
|
||||
"error",
|
||||
"notif_error",
|
||||
"self_log_in",
|
||||
"self_log_out",
|
||||
"user_log_in",
|
||||
@ -185,11 +225,11 @@ static int key_parse(const char** bind){
|
||||
int len = strlen(*bind);
|
||||
|
||||
if (len > 5) {
|
||||
if(strncasecmp(*bind, "ctrl+", 5) == 0)
|
||||
if(strncasecmp(*bind, "ctrl+", 5) == 0)
|
||||
return toupper(bind[0][5]) - 'A' + 1;
|
||||
}
|
||||
|
||||
if (strncasecmp(*bind, "tab", 3) == 0)
|
||||
if (strncasecmp(*bind, "tab", 3) == 0)
|
||||
return T_KEY_TAB;
|
||||
|
||||
if (strncasecmp(*bind, "page", 4) == 0)
|
||||
@ -203,13 +243,13 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
config_t cfg[1];
|
||||
config_setting_t *setting;
|
||||
const char *str = NULL;
|
||||
|
||||
|
||||
/* Load default settings */
|
||||
ui_defaults(s);
|
||||
tox_defaults(s);
|
||||
key_defaults(s);
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
audio_defaults(s);
|
||||
#endif
|
||||
|
||||
@ -244,14 +284,49 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
/* ui */
|
||||
if ((setting = config_lookup(cfg, ui_strings.self)) != NULL) {
|
||||
config_setting_lookup_bool(setting, ui_strings.timestamps, &s->timestamps);
|
||||
|
||||
int time = 24;
|
||||
if ( config_setting_lookup_int(setting, ui_strings.time_format, &time) ) {
|
||||
if (time == 12) {
|
||||
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p");
|
||||
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]");
|
||||
}
|
||||
}
|
||||
|
||||
if ( config_setting_lookup_string(setting, ui_strings.timestamp_format, &str) ) {
|
||||
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", str);
|
||||
}
|
||||
|
||||
if ( config_setting_lookup_string(setting, ui_strings.log_timestamp_format, &str) ) {
|
||||
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", str);
|
||||
}
|
||||
|
||||
config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts);
|
||||
config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog);
|
||||
config_setting_lookup_bool(setting, ui_strings.native_colors, &s->colour_theme);
|
||||
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_int(setting, ui_strings.time_format, &s->time);
|
||||
s->time = s->time == TIME_24 || s->time == TIME_12 ? s->time : TIME_24; /* Check defaults */
|
||||
config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg);
|
||||
|
||||
if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) {
|
||||
snprintf(s->line_join, sizeof(s->line_join), "%s", str);
|
||||
}
|
||||
if ( config_setting_lookup_string(setting, ui_strings.line_quit, &str) ) {
|
||||
snprintf(s->line_quit, sizeof(s->line_quit), "%s", str);
|
||||
}
|
||||
if ( config_setting_lookup_string(setting, ui_strings.line_alert, &str) ) {
|
||||
snprintf(s->line_alert, sizeof(s->line_alert), "%s", str);
|
||||
}
|
||||
if ( config_setting_lookup_string(setting, ui_strings.line_normal, &str) ) {
|
||||
snprintf(s->line_normal, sizeof(s->line_normal), "%s", str);
|
||||
}
|
||||
|
||||
config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away);
|
||||
|
||||
if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) {
|
||||
snprintf (s->mplex_away_note, sizeof (s->mplex_away_note), "%s", str);
|
||||
}
|
||||
}
|
||||
|
||||
/* paths */
|
||||
@ -271,112 +346,117 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
snprintf(s->chatlogs_path, sizeof(s->chatlogs_path), "%s", str);
|
||||
int len = strlen(s->chatlogs_path);
|
||||
|
||||
if (len >= sizeof(s->chatlogs_path) - 2)
|
||||
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], "/");
|
||||
}
|
||||
|
||||
if ( config_setting_lookup_string(setting, tox_strings.avatar_path, &str) ) {
|
||||
snprintf(s->avatar_path, sizeof(s->avatar_path), "%s", str);
|
||||
int len = strlen(str);
|
||||
|
||||
if (len >= sizeof(s->avatar_path))
|
||||
s->avatar_path[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* keys */
|
||||
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);
|
||||
}
|
||||
/* keys */
|
||||
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.toggle_peerlist, &tmp))
|
||||
s->key_toggle_peerlist = key_parse(&tmp);
|
||||
}
|
||||
|
||||
#ifdef _AUDIO
|
||||
#ifdef AUDIO
|
||||
if ((setting = config_lookup(cfg, audio_strings.self)) != NULL) {
|
||||
config_setting_lookup_int(setting, audio_strings.input_device, &s->audio_in_dev);
|
||||
s->audio_in_dev = s->audio_in_dev < 0 || s->audio_in_dev > MAX_DEVICES ? 0 : s->audio_in_dev;
|
||||
|
||||
|
||||
config_setting_lookup_int(setting, audio_strings.output_device, &s->audio_out_dev);
|
||||
s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev;
|
||||
|
||||
|
||||
config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _SOUND_NOTIFY
|
||||
#ifdef SOUND_NOTIFY
|
||||
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 ( (config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) ||
|
||||
!set_sound(notif_error, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(error, PACKAGE_DATADIR "/sounds/Error.wav");
|
||||
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
|
||||
}
|
||||
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.user_log_in, &str) ||
|
||||
!set_sound(user_log_in, str) ) {
|
||||
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 (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(self_log_in, PACKAGE_DATADIR "/sounds/LogIn.wav");
|
||||
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.user_log_out, &str) ||
|
||||
!set_sound(user_log_out, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ContactLogsOut.wav");
|
||||
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.self_log_out, &str) ||
|
||||
!set_sound(self_log_out, str) ) {
|
||||
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 (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/IncomingCall.wav");
|
||||
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav");
|
||||
}
|
||||
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) ||
|
||||
!set_sound(call_outgoing, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/OutgoingCall.wav");
|
||||
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav");
|
||||
}
|
||||
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.generic_message, &str) ||
|
||||
!set_sound(generic_message, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(generic_message, PACKAGE_DATADIR "/sounds/NewMessage.wav");
|
||||
set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav");
|
||||
}
|
||||
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) ||
|
||||
!set_sound(transfer_pending, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/TransferPending.wav");
|
||||
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
|
||||
}
|
||||
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) ||
|
||||
!set_sound(transfer_completed, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/TransferComplete.wav");
|
||||
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
|
||||
}
|
||||
}
|
||||
else {
|
||||
set_sound(error, PACKAGE_DATADIR "/sounds/Error.wav");
|
||||
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ContactLogsIn.wav");
|
||||
set_sound(self_log_in, PACKAGE_DATADIR "/sounds/LogIn.wav");
|
||||
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ContactLogsOut.wav");
|
||||
set_sound(self_log_out, PACKAGE_DATADIR "/sounds/LogOut.wav");
|
||||
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/IncomingCall.wav");
|
||||
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/OutgoingCall.wav");
|
||||
set_sound(generic_message, PACKAGE_DATADIR "/sounds/NewMessage.wav");
|
||||
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/TransferPending.wav");
|
||||
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/TransferComplete.wav");
|
||||
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
|
||||
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
|
||||
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
|
||||
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav");
|
||||
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav");
|
||||
set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav");
|
||||
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
|
||||
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -20,35 +20,55 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _settings_h
|
||||
#define _settings_h
|
||||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <tox/tox.h>
|
||||
|
||||
/* Represents line_* hints max strlen */
|
||||
#define LINE_HINT_MAX 3
|
||||
|
||||
/* holds user setting values */
|
||||
struct user_settings {
|
||||
int autolog; /* boolean */
|
||||
int alerts; /* boolean */
|
||||
int time; /* 12 or 24 */
|
||||
|
||||
int timestamps; /* boolean */
|
||||
char timestamp_format[TIME_STR_SIZE];
|
||||
char log_timestamp_format[TIME_STR_SIZE];
|
||||
|
||||
int colour_theme; /* boolean (0 for default toxic colours) */
|
||||
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 line_join[LINE_HINT_MAX + 1];
|
||||
char line_quit[LINE_HINT_MAX + 1];
|
||||
char line_alert[LINE_HINT_MAX + 1];
|
||||
char line_normal[LINE_HINT_MAX + 1];
|
||||
|
||||
char download_path[PATH_MAX];
|
||||
char chatlogs_path[PATH_MAX];
|
||||
char avatar_path[PATH_MAX];
|
||||
|
||||
int key_next_tab; /* character code */
|
||||
int key_prev_tab; /* character code */
|
||||
int key_scroll_line_up;
|
||||
int key_scroll_line_down;
|
||||
int key_half_page_up;
|
||||
int key_half_page_down;
|
||||
int key_page_bottom;
|
||||
int key_peer_list_up;
|
||||
int key_peer_list_down;
|
||||
#ifdef _AUDIO
|
||||
int key_next_tab;
|
||||
int key_prev_tab;
|
||||
int key_scroll_line_up;
|
||||
int key_scroll_line_down;
|
||||
int key_half_page_up;
|
||||
int key_half_page_down;
|
||||
int key_page_bottom;
|
||||
int key_peer_list_up;
|
||||
int key_peer_list_down;
|
||||
int key_toggle_peerlist;
|
||||
|
||||
int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */
|
||||
char mplex_away_note [TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
|
||||
#ifdef AUDIO
|
||||
int audio_in_dev;
|
||||
int audio_out_dev;
|
||||
double VAD_treshold;
|
||||
@ -59,9 +79,6 @@ enum {
|
||||
AUTOLOG_OFF = 0,
|
||||
AUTOLOG_ON = 1,
|
||||
|
||||
TIME_24 = 24,
|
||||
TIME_12 = 12,
|
||||
|
||||
TIMESTAMPS_OFF = 0,
|
||||
TIMESTAMPS_ON = 1,
|
||||
|
||||
@ -74,8 +91,22 @@ enum {
|
||||
SHOW_TYPING_OFF = 0,
|
||||
SHOW_TYPING_ON = 1,
|
||||
|
||||
SHOW_WELCOME_MSG_OFF = 0,
|
||||
SHOW_WELCOME_MSG_ON = 1,
|
||||
|
||||
DFLT_HST_SIZE = 700,
|
||||
|
||||
MPLEX_OFF = 0,
|
||||
MPLEX_ON = 1,
|
||||
} settings_values;
|
||||
|
||||
#define LINE_JOIN "-->"
|
||||
#define LINE_QUIT "<--"
|
||||
#define LINE_ALERT "-!-"
|
||||
#define LINE_NORMAL "---"
|
||||
#define TIMESTAMP_DEFAULT "%H:%M:%S"
|
||||
#define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]"
|
||||
#define MPLEX_AWAY_NOTE "Detached from screen"
|
||||
|
||||
int settings_load(struct user_settings *s, const char *patharg);
|
||||
#endif /* #define _settings_h */
|
||||
#endif /* #define SETTINGS_H */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user