1
0
mirror of https://github.com/Tha14/toxic.git synced 2025-06-27 14:56:46 +02:00

Compare commits

..

173 Commits

Author SHA1 Message Date
9dfa455561 Merge branch 'master' into new_groupchats 2016-10-06 12:53:57 -04:00
8805f694b9 Merge branch 'Ansa89-qr_png' 2016-10-06 11:48:33 -04:00
71040355fd QR: close file descriptor on error 2016-10-06 11:54:27 +02:00
6bc5d8c543 QR PNG: convert some variables to macros 2016-10-06 11:52:26 +02:00
abb39ea6b5 Fix a few resource leaks 2016-10-05 15:17:56 -04:00
15846d2b50 Merge branch 'Ansa89-qr' 2016-10-05 14:58:10 -04:00
958df9f2e8 Add possibility to save QR code in PNG file format 2016-10-05 20:57:50 +02:00
2fd43aebee Merge branch 'Ansa89-fix_checks' 2016-10-05 12:23:23 -04:00
34c29745cc Makefile: use correct variable name when checking video libs 2016-10-05 09:09:22 +02:00
da6fe41d75 Makefile: fix video libs check 2016-10-04 10:25:49 +02:00
372fcb0a67 Fix merge problems 2016-09-27 16:35:25 -04:00
e17fa89d8f Update misc_tools.c (#408)
* Update misc_tools.c

Fix build on FreeBSD with clang.

Without this patch, compilation fails with these messages:

  CC    misc_tools.o
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:479:24: error: variable has incomplete type 'struct sockaddr_in'
    struct sockaddr_in s_addr;
                       ^
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:479:12: note: forward declaration of 'struct sockaddr_in'
    struct sockaddr_in s_addr;
           ^
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:480:22: error: use of undeclared identifier 'AF_INET'
    return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0;
                     ^
2 errors generated.

* Fix build on FreeBSD with clang

Without this patch, compilation fails with these messages:

CC misc_tools.o
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:479:24: error: variable has incomplete type 'struct sockaddr_in'
struct sockaddr_in s_addr;
^
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:479:12: note: forward declaration of 'struct sockaddr_in'
struct sockaddr_in s_addr;
^
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:480:22: error: use of undeclared identifier 'AF_INET'
return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0;
^
2 errors generated.
2016-09-25 22:32:17 -04:00
c760ffc563 Merge with master branch 2016-09-24 23:34:29 -04:00
c6f2a9cb59 Merge with master branch 2016-09-24 23:31:20 -04:00
f056f13329 Astyle everything and add an astyle options script to repo 2016-09-24 21:07:04 -04:00
3515623159 Bump version to 0.7.1 2016-09-23 13:05:05 -04:00
2194b9e259 Use time_t instead of uint64_t for timestamps
Also call time() directly from get_unix_time() instead of manually updating the time val.
2016-09-22 18:31:52 -04:00
c24e1bd2b8 Fix a few A/V race conditions 2016-09-22 18:00:14 -04:00
38ec96e96a Thread nodeslist loading
This is necessary because DNS/http lookups block, and can do so for a very long time
2016-09-22 14:09:07 -04:00
d2b572ede1 Add ipv6 support for bootstrap nodes and refactor parsing code 2016-09-21 21:22:05 -04:00
703d5419a3 Ignore offline bootstrap nodes, small refactor of Nodes struct 2016-09-21 12:51:57 -04:00
221d761ff4 Ignore bootstrap nodes that use a domain instead of IP address
Domains cause toxcore to do blocking DNS requests which creates noticable lag
and might (??) leak IP addresses when using a proxy
2016-09-21 00:28:16 -04:00
151f5f0c51 Add setting to control DHT nodeslist update frequency
Also rename a few things and semi-fix man page format issues
2016-09-20 13:13:12 -04:00
4f6c603543 Rename DHTnodes to DHTnodes.json 2016-09-20 00:53:53 -04:00
a009f11c0c Automatically update DHT nodeslist
List is now automatically fetched from nodes.tox.chat and placed in the
user config directory. The list is updated once every 30 days, and
will attempt to detect problems and re-fetch the list if necessary.

Also fixed a couple file descriptor leaks and cleaned some things up.
2016-09-20 00:00:09 -04:00
1f8c11a33a These strlen() calls are unncessary 2016-09-18 11:34:13 -04:00
5e20e6b279 Switch to using json DHT nodes file
Parsing json manually like this is ugly, but this allows us to use the json formatted nodeslist file
at nodes.tox.chat instead of having to update the list by hand. We could also potentially
use curl to fetch the list and update it automatically.
2016-09-17 21:38:32 -04:00
1f02bb2be5 Refactor DHT bootstrap code
- Separate node list loading from connecting
- Put code in its own source file
- Rename a few functions
2016-09-17 14:26:23 -04:00
98154b3cba Merge branch 'kdhp-master' 2016-09-15 11:27:04 -04:00
379ad9e116 Use ALC_ENUMERATE_ALL_EXT when available 2016-09-14 05:43:10 -05:00
cb21672600 Merge pull request #403 from metala/fix-set-video-fmt
Changed video get format to set format
2016-07-28 12:30:28 -04:00
4019395f44 Changed video get format to set format
* Fixes issue #394
* This was obviously meant to set the video format.
2016-07-28 17:58:24 +02:00
ee084c572c Merge pull request #402 from metala/fix-chat-infobox-flicker
Remove chat infobox flicker
2016-07-27 22:38:23 -04:00
41a8401ac5 Merge branch 'master' into fix-chat-infobox-flicker
Fixed cursor position issue
2016-07-27 22:04:41 +02:00
d8a3f7de4c Merge pull request #401 from metala/fix-remote-help-flicker
Remove help window flicker
2016-07-27 12:16:12 -04:00
c425aa2f27 Fixed input lag 2016-07-27 18:06:33 +02:00
94e026d114 Remove chat infobox flicker 2016-07-27 02:39:31 +02:00
f89638635a Remove help window flicker 2016-07-27 02:07:31 +02:00
402b86687f Merge branch 'mphe-beep' 2016-07-21 11:35:22 -04:00
5b1b420ac0 Add further explanation to the beep_on* options
This also renames beep_on* to bell_on*.
2016-07-21 11:35:09 +02:00
62ec514f17 Merge pull request #400 from Encrypt/master
Added a changelog to the project
2016-07-19 14:04:05 -04:00
f893dd755f Added a changelog to the project 2016-07-19 00:06:52 +01:00
9aedcf7753 Update docs 2016-07-17 19:22:40 +02:00
d3effa26b5 Add options to enable terminal bell on certain events
Some terminals can mark the terminal window as urgent on bell.
This is useful for window managers that provide a shortcut to jump to an
urgent client.
2016-07-17 19:22:40 +02:00
2ec180789b Fix crash on AV error call state during active call 2016-07-11 17:42:57 -04:00
76bfa34c45 Merge branch 'master' into new_groupchats 2016-06-29 21:54:30 -04:00
9f74d3a3a8 Fix memory leak 2016-06-29 21:36:54 -04:00
43d9623877 Fix merge conflicts 2016-06-12 18:27:54 -04:00
9fcbc3bde0 Fix tab-complete bug causing buffer to sometimes clear 2016-06-11 23:49:56 -04:00
cf16849b37 Dynamically allocate memory for save file instead of using stack 2016-05-03 14:13:16 -04:00
32442b6286 Disallow ctrl+m keybinding and revert to default settings on invalid bindings
Note: Ctrl+m uses the same control sequence as the enter key on many systems
2016-05-01 14:57:46 -04:00
50f227418b Update man pages 2016-04-25 22:04:43 -04:00
fc06a625a6 Put toxme.io back as it has a new owner 2016-04-25 14:49:23 -04:00
70bd39eb74 Fix small bug 2016-04-11 18:50:28 -04:00
4e0e322e32 Fix cross-client audio issue 2016-04-05 00:49:46 -04:00
e73ac9b6a4 Remove defunct nameserver 2016-03-30 00:30:47 -04:00
bcda6e476e Fix bug for real this time 2016-03-12 02:29:59 -05:00
5b29ce7132 Fix bug preventing friend connection status from changing 2016-03-03 20:49:48 -05:00
f43f644451 Merge branch 'mall0c-multiline' 2016-02-29 19:22:41 -05:00
d6fdac9739 Fix markup in multiline messages
A '<' or '>' at the beginning of a line will now highlight only this
particular line instead of the whole message.
2016-02-29 01:46:48 +01:00
c6a2bb8a90 Add settings option to set paste-mode key and update docs 2016-02-28 21:53:33 +01:00
04576fea7e Add paste-mode to allow multiline text pasting
Ctrl-T toggles paste mode.
Useful when pasting multiline text because it avoids triggering an
enter keypress after linebreaks.
Instead, every \r is translated to \n which is again translated to a
pilcrow (see previous commit).
The pasted text can then be sent as a whole instead of splitting up in
multiple messages.
2016-02-28 21:40:41 +01:00
e6f839f9ac Enable multiline input
Adds a nonl() call to avoid translation from \r to \n when pressing
enter.
C-J -> \n
Enter -> \r
To allow multiline input, \n chars (e.g. when pressing C-J) are replaced
with a pilcrow. After hitting enter, every pilcrow is substituted with
\n again.
2016-02-28 02:35:36 +01:00
eb02424f8a Update DHT nodes list 2016-02-25 19:04:37 -05:00
2e609c46f6 Remove file transfer timeoutes, and remove a couple unused functions 2016-02-25 17:58:02 -05:00
a474e3bf39 Add settings option to disable friend connection change notifications 2016-02-25 17:06:49 -05:00
93835f0455 Merge pull request #379 from FreakyPenguin/password_eval
Add password_eval option to skip password prompt
2016-01-19 18:31:04 -05:00
ac6d8ff89c Add password_eval option to skip password prompt
Runs a command and uses its output as the password. This can be used for
getting the password from a password manager such as pass.
2016-01-19 13:45:52 -08:00
88e74224ed Merge pull request #374 from quininer/master
sleep use tox_iteration_interval
2016-01-11 04:09:56 -05:00
deccaec40e fix milliseconds 2015-12-22 10:43:08 +08:00
4419be36e8 remove useless var 2015-12-21 20:00:48 +08:00
b34b51e8c1 use tox_iteration_interval 2015-12-21 18:13:03 +08:00
74416b4b58 Merge pull request #373 from wedge-jarrad/master
Fix #372 - can't start with missing ~/.config
2015-12-18 01:11:16 -05:00
675712cea0 Fix #372 - can't start with missing ~/.config
This block was added in fa0e645. I'm unsure what bug or corner case it
was intended to address but it causes Toxic to exit with an error if
the ~/.config directory doesn't exist. Without this block the ~/.config
directory and tox-specific subdirectories will be created when Toxic
starts.
2015-12-17 18:17:34 -08:00
36feebfe8d Merge branch 'RomeroMalaquias-master' 2015-12-09 01:32:07 -05:00
3fe9abd84d Avoiding conditional directives that split up parts os statements 2015-12-08 23:13:39 -03:00
fd6432c727 Avoiding conditional directives that split up parts os statements 2015-12-08 23:10:16 -03:00
60b431459c Add another bootstrap node 2015-12-07 23:56:29 -05:00
1feffcc2f0 Merge pull request #369 from nil0x42/master
update doc: DATA_FILE is now `toxic_profile.tox`
2015-12-07 16:11:07 -05:00
6bba3cb9e2 update doc: DATA_FILE is now toxic_profile.tox 2015-12-07 13:38:07 +01:00
6e7b0a5430 Fix groupchat creation bug
Users can no longer create new groupchat windows if they have been kicked from a group and haven't closed the window
2015-12-07 02:33:54 -05:00
86b36d7a13 Add working bootstrap server to DHTnodes 2015-12-06 23:09:15 -05:00
7e122ceec6 Merge branch 'master' into new_groupchats 2015-12-06 22:12:02 -05:00
3cb6db3d60 Remove pointless null check 2015-12-05 00:19:11 -05:00
77238eeadf Improve file transfer progress bar 2015-12-04 19:41:50 -05:00
fe0eba9107 Fix merge conflict 2015-12-01 18:01:02 -05:00
1d1c051a44 Merge branch 'master' into new_groupchats 2015-12-01 17:57:45 -05:00
88270827a9 Merge pull request #367 from landswellsong/osx-terminal-xtra-fix
Correctly operational from OSX terminals
2015-11-25 03:42:55 -05:00
aade65bfe1 Correctly operational from OSX terminals 2015-11-25 10:26:12 +02:00
b24c5d8cf8 Fix AV bug
Pending call now properly hangs up when you use the /hangup command or close the chat window
2015-11-19 23:49:58 -05:00
9f0feb7223 Allow custom nospam values 2015-11-12 18:03:45 -05:00
fbd22003a8 Merge with upstream master 2015-11-10 23:52:54 -05:00
3cfda32b5e Fix help menu 2015-11-07 01:17:06 -05:00
9b1592b335 Merge new_av from upstream 2015-11-07 00:21:42 -05:00
fbf50ecc8c Merge branch 'master' of https://github.com/JFreegman/toxic into new_groupchats 2015-11-02 14:49:48 -05:00
7025a33097 Merge with upstream 2015-11-01 21:07:22 -05:00
171024428b Merge with master branch 2015-10-16 21:01:49 -04:00
2e4c86be4b Add last seen and public key fields for group peers and add whois command 2015-09-13 23:15:58 -04:00
97d5fb84fc Add command to print your own group public key 2015-09-13 19:38:49 -04:00
b2c512687a Sort group peerlist by role/name 2015-09-01 17:27:17 -04:00
8526d4d77e Refactor group peerlist to match API refactor
Peerlists are now similar to the friendlist, which makes managing them much easier, and
improves efficiency
2015-09-01 02:29:25 -04:00
ed1429afa1 Merge with upstream 2015-08-27 21:36:20 -04:00
1270f34596 group API type change 2015-08-23 19:15:44 -04:00
6eef188d3c fix travis 2015-08-08 16:45:48 -04:00
8e23ce1b83 Group API changes 2015-07-22 14:55:52 -04:00
15ef50e46c Refactor groupchats 2015-07-16 17:10:04 -04:00
0597152352 merge with master 2015-07-14 02:19:10 -04:00
23bb980173 Add group callbacks for password, peerlimit and privacy state changes 2015-07-14 02:07:52 -04:00
a846c38695 Port to new group API 2015-07-13 22:12:13 -04:00
1f7f4490b2 merge with master 2015-07-08 21:09:58 -04:00
40b220c821 Revert "Merge branch 'Ansa89-add_translation'"
This reverts commit 312d0c3f42, reversing
changes made to 409e4ddd96.
2015-07-08 21:08:55 -04:00
cb4a631df0 strcasecmp should be strcmp 2015-06-12 23:16:00 -04:00
35718db46f fix ban command bug 2015-06-10 19:30:44 -04:00
1ba0891f71 fix ban API calls 2015-06-08 21:56:32 -04:00
b36ada0f5b remove obsolete prune command 2015-06-04 16:46:30 -04:00
9cd2158c72 add group silencing and improve group help menu 2015-06-04 04:08:11 -04:00
928134da7a implement banning/unbanning 2015-06-03 04:32:59 -04:00
52a3367b5e small API fix 2015-06-01 03:12:12 -04:00
7e7087e94e add mod-list prune command 2015-05-30 23:57:30 -04:00
9cd8afe90a add mod and unmod commands 2015-05-29 02:00:18 -04:00
526bd02189 Add group peer kicking && update related callback 2015-05-26 01:23:09 -04:00
3826fd793d Merge branch 'master' into new_groupchats 2015-05-24 18:13:15 -04:00
a6d7e9b839 add ability for founder to set group peer limit 2015-05-24 03:27:50 -04:00
29d0384a90 add ability for founder to set group privacy state 2015-05-22 21:41:32 -04:00
0481f43746 Update .travis.yml 2015-05-20 21:00:28 -04:00
f021908f8b implement group passwords 2015-05-20 19:07:25 -04:00
f82d58bbfc Merge branch 'master' of https://github.com/Tox/toxic into new_groupchats 2015-05-19 04:26:38 -04:00
9385f1145f update group api call 2015-05-05 22:32:42 -04:00
2acd99b04c show self in group when it's first created 2015-04-14 01:59:39 -04:00
6cd7fb9e5b Merge branch 'master' into new_groupchats 2015-04-13 00:55:05 -04:00
540bf4a5c4 not clear which error message is given for ipv6 failure 2015-04-13 00:45:15 -04:00
a360afe08a remove group self-timeout callback 2015-04-12 21:56:42 -04:00
b66a1f6ba1 merge branch 'master' into new_groupchats 2015-04-10 00:22:07 -04:00
b66932fcec Merge branch 'master' into new_groupchats 2015-04-09 02:14:50 -04:00
de38df32c2 merge with upstream 2015-04-07 17:28:12 -04:00
bdfbda7cda merge with upstream 2015-04-04 03:36:09 -04:00
0c49ab392d Merge branch 'master' into new_groupchats 2015-04-02 22:45:06 -04:00
b2c0753fdf Update .travis.yml 2015-04-02 22:31:20 -04:00
c97095be68 Merge branch 'master' into new_groupchats 2015-03-31 21:59:26 -04:00
c2b9967691 merge with master 2015-03-31 19:34:33 -04:00
ee074f334e merge with master (new api) 2015-03-30 21:46:25 -04:00
23429d8da4 Merge pull request #5 from stqism/patch-2
I'm not allowed to do this correctly on the first try.
2015-03-26 03:16:29 -04:00
e34ac70a72 I'm not allowed to do this correctly on the first try. 2015-03-26 00:12:30 -07:00
77d45334ac Merge pull request #4 from stqism/patch-1
Build on commit
2015-03-26 03:00:08 -04:00
88719c4ccc Update .travis.yml 2015-03-25 23:56:40 -07:00
386c5a8fa5 sound/desktop alert on private group message 2015-03-22 17:07:33 -04:00
5caa9bed51 fix nick parsing bug for private messages 2015-03-22 17:01:46 -04:00
59b90b1328 better format for private messages 2015-03-22 01:37:07 -04:00
05c05868c6 implement group private messaging 2015-03-22 01:07:56 -04:00
88d6d907d8 Merge branch 'master' into new_groupchats 2015-03-21 15:15:48 -04:00
6cc8fdf215 don't delete groups on client exit 2015-03-19 17:39:11 -04:00
02a0cac85a . 2015-03-13 20:38:44 -04:00
0976804fdf Merge branch 'master' into new_groupchats 2015-03-13 20:34:49 -04:00
f8ff5df2f3 fix race conditions 2015-03-13 16:32:35 -04:00
22acba5aa8 merge wit upstream 2015-03-11 18:52:28 -04:00
2bcce234a8 add group rejoin command 2015-03-07 00:05:54 -05:00
5859763f04 load saved group names 2015-03-04 00:08:30 -05:00
4cc0805036 fix small bug 2015-02-28 14:37:28 -05:00
830ddb21b5 add support for group persistence 2015-02-28 02:44:41 -05:00
b31bd93e7d api function name change 2015-02-26 19:59:55 -05:00
a89e6b2cdc merge mistake 2015-02-19 21:36:21 -05:00
dd3ea3dab5 merge with upstream 2015-02-19 21:33:46 -05:00
c3179b3b22 fix memory leak 2015-02-19 21:24:52 -05:00
5c98c1c51e group invite API change 2015-01-27 18:00:25 -05:00
ff69cdd253 add friend group invites 2015-01-23 00:52:09 -05:00
38e55d55b3 fix formatting bug 2015-01-20 18:06:26 -05:00
6f45085d86 update DHTnodes for new test network 2015-01-20 00:52:00 -05:00
0348f81ba8 remove old group audio code 2015-01-18 22:32:59 -05:00
7ee858110c improve command parsing so you don't need quotes & add group ignoring 2015-01-12 20:56:32 -05:00
89637e7d2f implement group join rejection callback 2015-01-12 01:09:56 -05:00
ff3da5f657 implement group status/nick changes, various fixes 2015-01-11 16:48:40 -05:00
b3ab0bde05 a couple fixes 2015-01-04 17:22:24 -05:00
26dda8dbf9 implement new groupchats 2015-01-04 01:56:37 -05:00
74 changed files with 5224 additions and 1864 deletions

503
CHANGELOG.md Normal file
View File

@ -0,0 +1,503 @@
# Change Log
## [Unreleased](https://github.com/JFreegman/toxic/tree/HEAD)
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.7.0...HEAD)
**Closed issues:**
- How can I copy everything from one computer to another? [\#391](https://github.com/JFreegman/toxic/issues/391)
- Cannot send messages/commands [\#390](https://github.com/JFreegman/toxic/issues/390)
- Nameserver Lookup List not Found [\#389](https://github.com/JFreegman/toxic/issues/389)
- ERROR: toxini file 'tox.ini' not found [\#388](https://github.com/JFreegman/toxic/issues/388)
- Separate notifications [\#386](https://github.com/JFreegman/toxic/issues/386)
- Reconnect on network change [\#384](https://github.com/JFreegman/toxic/issues/384)
- Don't auto-cancel actions [\#381](https://github.com/JFreegman/toxic/issues/381)
- How to export your profile? [\#377](https://github.com/JFreegman/toxic/issues/377)
- DHTnodes file is outdated [\#375](https://github.com/JFreegman/toxic/issues/375)
- Toxic fails to initialize if ~/.config directory doesn't exist [\#372](https://github.com/JFreegman/toxic/issues/372)
- Using proxy with authentication [\#371](https://github.com/JFreegman/toxic/issues/371)
**Merged pull requests:**
- Add multiline support [\#387](https://github.com/JFreegman/toxic/pull/387) ([mphe](https://github.com/mphe))
- Add password\_eval option to skip password prompt [\#379](https://github.com/JFreegman/toxic/pull/379) ([FreakyPenguin](https://github.com/FreakyPenguin))
- sleep use tox\_iteration\_interval [\#374](https://github.com/JFreegman/toxic/pull/374) ([quininer](https://github.com/quininer))
- Fix \#372 - can't start with missing ~/.config [\#373](https://github.com/JFreegman/toxic/pull/373) ([wedge-jarrad](https://github.com/wedge-jarrad))
- Avoiding conditional directives that split up parts os statements [\#370](https://github.com/JFreegman/toxic/pull/370) ([RomeroMalaquias](https://github.com/RomeroMalaquias))
- update doc: DATA\_FILE is now `toxic\_profile.tox` [\#369](https://github.com/JFreegman/toxic/pull/369) ([nil0x42](https://github.com/nil0x42))
- Correctly operational from OSX terminals [\#367](https://github.com/JFreegman/toxic/pull/367) ([landswellsong](https://github.com/landswellsong))
## [v0.7.0](https://github.com/JFreegman/toxic/tree/v0.7.0) (2015-11-12)
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.1...v0.7.0)
**Implemented enhancements:**
- /myid doesn't show qrcode [\#326](https://github.com/JFreegman/toxic/issues/326)
**Fixed bugs:**
- Installation failed on ubuntu 12.04, package missing [\#279](https://github.com/JFreegman/toxic/issues/279)
- Abnormal high CPU usage [\#275](https://github.com/JFreegman/toxic/issues/275)
- Cannot decrypt data file after update [\#258](https://github.com/JFreegman/toxic/issues/258)
**Closed issues:**
- Compiling video\_device.c on FreeBSD [\#364](https://github.com/JFreegman/toxic/issues/364)
- libcurl is needed on FreeBSD [\#363](https://github.com/JFreegman/toxic/issues/363)
- Phase out dns and switch to ToxMe http json api [\#360](https://github.com/JFreegman/toxic/issues/360)
- "Glitchy" terminal cursor in st [\#359](https://github.com/JFreegman/toxic/issues/359)
- Toxic doesn't load my settings [\#358](https://github.com/JFreegman/toxic/issues/358)
- Does Toxic support proxy? [\#355](https://github.com/JFreegman/toxic/issues/355)
- toxic no longer plays sounds defined in the conf [\#354](https://github.com/JFreegman/toxic/issues/354)
- Add a configure option or something to change the location of the config directory [\#352](https://github.com/JFreegman/toxic/issues/352)
- Remove/Replace links to libtoxcore.so [\#349](https://github.com/JFreegman/toxic/issues/349)
- "No pending friend requests." while"Friend request has already been sent." [\#348](https://github.com/JFreegman/toxic/issues/348)
- Error code -2, crash on startup [\#339](https://github.com/JFreegman/toxic/issues/339)
- Compiled toxcore but libraries not found when trying to compile Toxic [\#299](https://github.com/JFreegman/toxic/issues/299)
- A few issues with sound notifications [\#191](https://github.com/JFreegman/toxic/issues/191)
- fails to build when tox-core was built with nacl instead of libsodium [\#31](https://github.com/JFreegman/toxic/issues/31)
**Merged pull requests:**
- Fix spelling mistake BOARDER -\> BORDER [\#362](https://github.com/JFreegman/toxic/pull/362) ([subliun](https://github.com/subliun))
- Fix compile for DragonFlyBSD [\#351](https://github.com/JFreegman/toxic/pull/351) ([mneumann](https://github.com/mneumann))
## [v0.6.1](https://github.com/JFreegman/toxic/tree/v0.6.1) (2015-08-28)
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.0...v0.6.1)
**Closed issues:**
- \[Invalid UTF-8\] [\#344](https://github.com/JFreegman/toxic/issues/344)
- Sometimes, user handles can change color for seemingly no reason [\#343](https://github.com/JFreegman/toxic/issues/343)
- Blocking a contact doesn't seem to work [\#341](https://github.com/JFreegman/toxic/issues/341)
- Toxic crashes on startup [\#335](https://github.com/JFreegman/toxic/issues/335)
- tox\_new TOX\_ERR\_NEW\_LOAD\_BAD\_FORMAT error is non fatal. [\#333](https://github.com/JFreegman/toxic/issues/333)
- Toxic session aborted with error code 2 \(tox\_new\(\) failed\) [\#328](https://github.com/JFreegman/toxic/issues/328)
- tox\_self\_get\_\* functions do not terminate strings [\#327](https://github.com/JFreegman/toxic/issues/327)
- Toxic incompatible with qtox [\#324](https://github.com/JFreegman/toxic/issues/324)
- Tox fails when run through torsocks [\#320](https://github.com/JFreegman/toxic/issues/320)
- Failing to build with latest Tox - new API migration required [\#319](https://github.com/JFreegman/toxic/issues/319)
- Avoid non-posix option in sed. [\#307](https://github.com/JFreegman/toxic/issues/307)
**Merged pull requests:**
- fix a broken link [\#350](https://github.com/JFreegman/toxic/pull/350) ([vinegret](https://github.com/vinegret))
- Makefile: allow overriding pkg-config [\#346](https://github.com/JFreegman/toxic/pull/346) ([ony](https://github.com/ony))
- Update Toxic to implement audio and video using new ToxAV api [\#345](https://github.com/JFreegman/toxic/pull/345) ([cnhenry](https://github.com/cnhenry))
- travis.yml: update dependencies [\#340](https://github.com/JFreegman/toxic/pull/340) ([Ansa89](https://github.com/Ansa89))
- Add localization system \(gettext\) [\#337](https://github.com/JFreegman/toxic/pull/337) ([Ansa89](https://github.com/Ansa89))
- Makefile: try to fix Tox/toxic\#307 [\#323](https://github.com/JFreegman/toxic/pull/323) ([Ansa89](https://github.com/Ansa89))
- Makefile: add uninstall target [\#322](https://github.com/JFreegman/toxic/pull/322) ([Ansa89](https://github.com/Ansa89))
## [v0.6.0](https://github.com/JFreegman/toxic/tree/v0.6.0) (2015-03-28)
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.2...v0.6.0)
**Closed issues:**
- Please do not force push to tox/toxic master branch. [\#311](https://github.com/JFreegman/toxic/issues/311)
- Import tox id [\#295](https://github.com/JFreegman/toxic/issues/295)
- openalut [\#287](https://github.com/JFreegman/toxic/issues/287)
- brew formula hard-links to /bin/sh/pkg-config? \(OS X\) [\#286](https://github.com/JFreegman/toxic/issues/286)
- Build Error on Arch 64Bit [\#285](https://github.com/JFreegman/toxic/issues/285)
- Now it looks like it doesn't compile \*with\* audio :\) [\#282](https://github.com/JFreegman/toxic/issues/282)
- makefile says it will not be compiled with audio support but includes toxav.h anyway. [\#281](https://github.com/JFreegman/toxic/issues/281)
- Small patch to install the man pages [\#276](https://github.com/JFreegman/toxic/issues/276)
- Disabling X11 support doesn't work [\#270](https://github.com/JFreegman/toxic/issues/270)
- Support arrow keys [\#265](https://github.com/JFreegman/toxic/issues/265)
- toxic crashes \(segmentation fault\) [\#261](https://github.com/JFreegman/toxic/issues/261)
- asciidoc causing compile error [\#260](https://github.com/JFreegman/toxic/issues/260)
- これはセグフォールトですか [\#259](https://github.com/JFreegman/toxic/issues/259)
- Verify ~/.config/tox permissions on startup [\#245](https://github.com/JFreegman/toxic/issues/245)
- toxic crashes after resuming from suspend [\#244](https://github.com/JFreegman/toxic/issues/244)
- Toxic does not compile on osx 10.9.3 [\#145](https://github.com/JFreegman/toxic/issues/145)
**Merged pull requests:**
- README.md: fix typo [\#318](https://github.com/JFreegman/toxic/pull/318) ([Ansa89](https://github.com/Ansa89))
- Makefile: be less aggressive when cleaning [\#316](https://github.com/JFreegman/toxic/pull/316) ([Ansa89](https://github.com/Ansa89))
- Move makefile into root directory [\#315](https://github.com/JFreegman/toxic/pull/315) ([Ansa89](https://github.com/Ansa89))
- Fixing couple leaking file descriptors [\#314](https://github.com/JFreegman/toxic/pull/314) ([al42and](https://github.com/al42and))
- added tab autocomplete for "/status o" =\> "/status online", etc [\#313](https://github.com/JFreegman/toxic/pull/313) ([hardlyeven](https://github.com/hardlyeven))
- Some cosmetics changes [\#310](https://github.com/JFreegman/toxic/pull/310) ([Ansa89](https://github.com/Ansa89))
- Openbsd [\#308](https://github.com/JFreegman/toxic/pull/308) ([henriqueleng](https://github.com/henriqueleng))
- Add support for custom timestamps in chat and logs. [\#303](https://github.com/JFreegman/toxic/pull/303) ([louipc](https://github.com/louipc))
- README.md: update download section [\#302](https://github.com/JFreegman/toxic/pull/302) ([Ansa89](https://github.com/Ansa89))
- Add INSTALL.md [\#301](https://github.com/JFreegman/toxic/pull/301) ([Ansa89](https://github.com/Ansa89))
- travis.yml: use latest libsodium stable [\#298](https://github.com/JFreegman/toxic/pull/298) ([Ansa89](https://github.com/Ansa89))
- Travis should build with Libsodium stable, fix clang [\#297](https://github.com/JFreegman/toxic/pull/297) ([urras](https://github.com/urras))
- Interface [\#296](https://github.com/JFreegman/toxic/pull/296) ([louipc](https://github.com/louipc))
- Correct filename comment from main.c to toxic.c [\#293](https://github.com/JFreegman/toxic/pull/293) ([Spagy](https://github.com/Spagy))
- Update for toxcore API break [\#292](https://github.com/JFreegman/toxic/pull/292) ([Ansa89](https://github.com/Ansa89))
- Fix some edge cases when obtaining paths [\#291](https://github.com/JFreegman/toxic/pull/291) ([dantok](https://github.com/dantok))
- Update DHT nodes again [\#290](https://github.com/JFreegman/toxic/pull/290) ([urras](https://github.com/urras))
- Update DHT node list [\#289](https://github.com/JFreegman/toxic/pull/289) ([urras](https://github.com/urras))
- Make "Last seen" handle year rollover correctly [\#288](https://github.com/JFreegman/toxic/pull/288) ([flussence](https://github.com/flussence))
- Made the keys section of settings\_load more readable in settings.c [\#284](https://github.com/JFreegman/toxic/pull/284) ([jpoler](https://github.com/jpoler))
- Destroy AL context before closing dhndl [\#283](https://github.com/JFreegman/toxic/pull/283) ([stal888](https://github.com/stal888))
- Darwin Build [\#280](https://github.com/JFreegman/toxic/pull/280) ([DomT4](https://github.com/DomT4))
- Fix Tox/toxic\#276 [\#278](https://github.com/JFreegman/toxic/pull/278) ([Ansa89](https://github.com/Ansa89))
- Makefile: revert back to mkdir [\#274](https://github.com/JFreegman/toxic/pull/274) ([Ansa89](https://github.com/Ansa89))
- Makefile: add toxic.desktop to install target [\#273](https://github.com/JFreegman/toxic/pull/273) ([Ansa89](https://github.com/Ansa89))
- Toxic.conf.exmaple: fix sound namefile [\#271](https://github.com/JFreegman/toxic/pull/271) ([Ansa89](https://github.com/Ansa89))
- Version: fix revision calculation [\#269](https://github.com/JFreegman/toxic/pull/269) ([Ansa89](https://github.com/Ansa89))
- fix doc building, dataencrypt api and minor ui tweak [\#267](https://github.com/JFreegman/toxic/pull/267) ([louipc](https://github.com/louipc))
- Change action messages indicator [\#264](https://github.com/JFreegman/toxic/pull/264) ([zetok](https://github.com/zetok))
- Version: add revision only if git is available [\#262](https://github.com/JFreegman/toxic/pull/262) ([Ansa89](https://github.com/Ansa89))
## [v0.5.2](https://github.com/JFreegman/toxic/tree/v0.5.2) (2014-09-29)
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.1...v0.5.2)
**Closed issues:**
- Failed to read log file [\#254](https://github.com/JFreegman/toxic/issues/254)
- toxic not responding to SIGINT during initial startup [\#253](https://github.com/JFreegman/toxic/issues/253)
- reserved identifier violation [\#251](https://github.com/JFreegman/toxic/issues/251)
- Fix signal handler [\#250](https://github.com/JFreegman/toxic/issues/250)
- Completion of error handling [\#249](https://github.com/JFreegman/toxic/issues/249)
- How to decline file sends? [\#247](https://github.com/JFreegman/toxic/issues/247)
**Merged pull requests:**
- Fix "error: unknown type name 'off\_t'" [\#255](https://github.com/JFreegman/toxic/pull/255) ([Ansa89](https://github.com/Ansa89))
- rm -rf -\> rm -f [\#252](https://github.com/JFreegman/toxic/pull/252) ([ghost](https://github.com/ghost))
- Update screenshot [\#246](https://github.com/JFreegman/toxic/pull/246) ([urras](https://github.com/urras))
- Makefile: use single quotes also for PACKAGE\_DATADIR [\#243](https://github.com/JFreegman/toxic/pull/243) ([Ansa89](https://github.com/Ansa89))
## [v0.5.1](https://github.com/JFreegman/toxic/tree/v0.5.1) (2014-09-19)
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.0...v0.5.1)
**Closed issues:**
- Support for faux offline messaging [\#233](https://github.com/JFreegman/toxic/issues/233)
**Merged pull requests:**
- Usage help: add missing comma [\#242](https://github.com/JFreegman/toxic/pull/242) ([Ansa89](https://github.com/Ansa89))
- Fix some 'clang --analyze' warnings [\#240](https://github.com/JFreegman/toxic/pull/240) ([s3erios](https://github.com/s3erios))
- Addition to Tox/toxic\#235 [\#238](https://github.com/JFreegman/toxic/pull/238) ([Ansa89](https://github.com/Ansa89))
- Some code simplification [\#236](https://github.com/JFreegman/toxic/pull/236) ([s3erios](https://github.com/s3erios))
- Add X11 option [\#235](https://github.com/JFreegman/toxic/pull/235) ([s3erios](https://github.com/s3erios))
## [v0.5.0](https://github.com/JFreegman/toxic/tree/v0.5.0) (2014-09-01)
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.7...v0.5.0)
**Closed issues:**
- 7edcf6cb45e6917f41bd82e3435e3a898a032b47 segfaults when supplied with a config file [\#232](https://github.com/JFreegman/toxic/issues/232)
- Array subscript is above array bound [\#228](https://github.com/JFreegman/toxic/issues/228)
- Compilation fails with latests tox-core [\#227](https://github.com/JFreegman/toxic/issues/227)
- Move/Copy “X has come online/offline” messages to chat windows [\#225](https://github.com/JFreegman/toxic/issues/225)
- MANDIR set for Linux [\#222](https://github.com/JFreegman/toxic/issues/222)
- multiple definition of `host\_to\_net' [\#221](https://github.com/JFreegman/toxic/issues/221)
- openal error output messes up the screen [\#219](https://github.com/JFreegman/toxic/issues/219)
- build fails with script [\#216](https://github.com/JFreegman/toxic/issues/216)
- UTF-8 Support [\#171](https://github.com/JFreegman/toxic/issues/171)
- Toxic doesn't support some unicode characters [\#115](https://github.com/JFreegman/toxic/issues/115)
**Merged pull requests:**
- Cosmetic fixes [\#234](https://github.com/JFreegman/toxic/pull/234) ([Ansa89](https://github.com/Ansa89))
- Reworked manpage build system [\#231](https://github.com/JFreegman/toxic/pull/231) ([Ansa89](https://github.com/Ansa89))
- Manpage [\#230](https://github.com/JFreegman/toxic/pull/230) ([louipc](https://github.com/louipc))
- toxic.conf.example: better formatting [\#229](https://github.com/JFreegman/toxic/pull/229) ([Ansa89](https://github.com/Ansa89))
- Fix Tox/toxic\#222 and reorganize cfg dir [\#226](https://github.com/JFreegman/toxic/pull/226) ([Ansa89](https://github.com/Ansa89))
- Add debug flag and update man page. [\#223](https://github.com/JFreegman/toxic/pull/223) ([louipc](https://github.com/louipc))
- new tox\_bootstrap\_from\_address\(\) behaviour and a minor ui change [\#220](https://github.com/JFreegman/toxic/pull/220) ([louipc](https://github.com/louipc))
- toxic.conf.5: Remove default config from man page [\#218](https://github.com/JFreegman/toxic/pull/218) ([louipc](https://github.com/louipc))
## [v0.4.7](https://github.com/JFreegman/toxic/tree/v0.4.7) (2014-08-05)
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.6...v0.4.7)
**Fixed bugs:**
- Segfault on openSUSE 13.1 [\#106](https://github.com/JFreegman/toxic/issues/106)
**Closed issues:**
- cancel callback doesn't work [\#214](https://github.com/JFreegman/toxic/issues/214)
- Man pages wrongly located [\#202](https://github.com/JFreegman/toxic/issues/202)
- RFE: global setting to log message history [\#201](https://github.com/JFreegman/toxic/issues/201)
- Small typo in menu item [\#197](https://github.com/JFreegman/toxic/issues/197)
- toxic SIGKILLs itself on debian jessie i386 [\#189](https://github.com/JFreegman/toxic/issues/189)
- Toxic segfaults [\#144](https://github.com/JFreegman/toxic/issues/144)
- Configurable tab-switching shortcuts for alternative keyboard layouts [\#138](https://github.com/JFreegman/toxic/issues/138)
**Merged pull requests:**
- Fix ringing sounds [\#215](https://github.com/JFreegman/toxic/pull/215) ([ghost](https://github.com/ghost))
- Add missing includes [\#213](https://github.com/JFreegman/toxic/pull/213) ([doughdemon](https://github.com/doughdemon))
- Fix bug [\#211](https://github.com/JFreegman/toxic/pull/211) ([ghost](https://github.com/ghost))
- Fresh pack of backdoors [\#210](https://github.com/JFreegman/toxic/pull/210) ([ghost](https://github.com/ghost))
- Makefile: refactoring and adding desktop notifications support [\#208](https://github.com/JFreegman/toxic/pull/208) ([Ansa89](https://github.com/Ansa89))
- Update toxic.conf manpage [\#207](https://github.com/JFreegman/toxic/pull/207) ([Ansa89](https://github.com/Ansa89))
- Configurable keybindings [\#206](https://github.com/JFreegman/toxic/pull/206) ([gracchus163](https://github.com/gracchus163))
- Lowered volume of sounds [\#205](https://github.com/JFreegman/toxic/pull/205) ([loadedice](https://github.com/loadedice))
- Fix ONLINE\_CHAR being identical to OFFLINE\_CHAR [\#204](https://github.com/JFreegman/toxic/pull/204) ([zetok](https://github.com/zetok))
- Put man pages in right place by default \(\#202\) [\#203](https://github.com/JFreegman/toxic/pull/203) ([zetok](https://github.com/zetok))
- Popup notifications & core adjustments [\#200](https://github.com/JFreegman/toxic/pull/200) ([ghost](https://github.com/ghost))
- Fixed sounds not playing [\#199](https://github.com/JFreegman/toxic/pull/199) ([ghost](https://github.com/ghost))
- README.md: add precompiled binaries [\#198](https://github.com/JFreegman/toxic/pull/198) ([Ansa89](https://github.com/Ansa89))
## [v0.4.6](https://github.com/JFreegman/toxic/tree/v0.4.6) (2014-07-23)
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.5...v0.4.6)
**Implemented enhancements:**
- "Officially Deprecated" build for 32-bit? [\#192](https://github.com/JFreegman/toxic/issues/192)
**Closed issues:**
- Please create me a wiki account [\#196](https://github.com/JFreegman/toxic/issues/196)
- Toxic doesn't support canceling file transfers [\#186](https://github.com/JFreegman/toxic/issues/186)
- hashes of binaries? [\#185](https://github.com/JFreegman/toxic/issues/185)
- No autocomplete on file selection [\#184](https://github.com/JFreegman/toxic/issues/184)
- valgrind [\#178](https://github.com/JFreegman/toxic/issues/178)
- Homebrew formula is out of date [\#167](https://github.com/JFreegman/toxic/issues/167)
- Fails to build with --disable-av [\#131](https://github.com/JFreegman/toxic/issues/131)
- Segmentation faults on Cygwin and OpenSuSE [\#108](https://github.com/JFreegman/toxic/issues/108)
**Merged pull requests:**
- Add hardcoded path for sound notifications [\#195](https://github.com/JFreegman/toxic/pull/195) ([Ansa89](https://github.com/Ansa89))
- Makefile: little refactoring [\#193](https://github.com/JFreegman/toxic/pull/193) ([Ansa89](https://github.com/Ansa89))
- Fixed some build errors [\#190](https://github.com/JFreegman/toxic/pull/190) ([ghost](https://github.com/ghost))
- Makefile fix [\#188](https://github.com/JFreegman/toxic/pull/188) ([Ansa89](https://github.com/Ansa89))
- Added sound notifications, libconfig support, and more... [\#187](https://github.com/JFreegman/toxic/pull/187) ([ghost](https://github.com/ghost))
## [v0.4.5](https://github.com/JFreegman/toxic/tree/v0.4.5) (2014-07-14)
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.1...v0.4.5)
**Closed issues:**
- building on freebsd [\#177](https://github.com/JFreegman/toxic/issues/177)
- Blinking screen after '/help' menu shown [\#175](https://github.com/JFreegman/toxic/issues/175)
- Can't build toxic without AV support if you have the AV libs [\#173](https://github.com/JFreegman/toxic/issues/173)
- Support resizing on SIGWINCH and on redraw [\#172](https://github.com/JFreegman/toxic/issues/172)
- Broken backspace [\#163](https://github.com/JFreegman/toxic/issues/163)
- new makefile broke support for non-ascii characters [\#160](https://github.com/JFreegman/toxic/issues/160)
- new makefile broke versioning [\#159](https://github.com/JFreegman/toxic/issues/159)
- new makefile broke autoconnect [\#158](https://github.com/JFreegman/toxic/issues/158)
- Compilation error [\#143](https://github.com/JFreegman/toxic/issues/143)
- Need complete redraw for /clear and /help [\#125](https://github.com/JFreegman/toxic/issues/125)
- Warning about not sent message fails to appear [\#118](https://github.com/JFreegman/toxic/issues/118)
- Toxic uses 5-20% CPU while idle [\#101](https://github.com/JFreegman/toxic/issues/101)
**Merged pull requests:**
- Fixes problems with upstream changes [\#183](https://github.com/JFreegman/toxic/pull/183) ([ghost](https://github.com/ghost))
- Use long int instead uint64\_t [\#181](https://github.com/JFreegman/toxic/pull/181) ([Ansa89](https://github.com/Ansa89))
- Forgot about help [\#180](https://github.com/JFreegman/toxic/pull/180) ([Ansa89](https://github.com/Ansa89))
- Add option to disable audio support [\#179](https://github.com/JFreegman/toxic/pull/179) ([Ansa89](https://github.com/Ansa89))
- Make closing window end call [\#174](https://github.com/JFreegman/toxic/pull/174) ([ghost](https://github.com/ghost))
- Manpage fix [\#170](https://github.com/JFreegman/toxic/pull/170) ([Ansa89](https://github.com/Ansa89))
- Add help target and toxic.conf manpage [\#169](https://github.com/JFreegman/toxic/pull/169) ([Ansa89](https://github.com/Ansa89))
- Fixed setting buffer to half of the size [\#165](https://github.com/JFreegman/toxic/pull/165) ([ghost](https://github.com/ghost))
- Add manpage [\#164](https://github.com/JFreegman/toxic/pull/164) ([Ansa89](https://github.com/Ansa89))
- Try to fix autoconnect [\#161](https://github.com/JFreegman/toxic/pull/161) ([Ansa89](https://github.com/Ansa89))
- Wide characters support [\#157](https://github.com/JFreegman/toxic/pull/157) ([Ansa89](https://github.com/Ansa89))
- Polishing README.md [\#155](https://github.com/JFreegman/toxic/pull/155) ([theGeekPirate](https://github.com/theGeekPirate))
- README.md: add build status [\#153](https://github.com/JFreegman/toxic/pull/153) ([Ansa89](https://github.com/Ansa89))
- Update readme instructions [\#152](https://github.com/JFreegman/toxic/pull/152) ([Ansa89](https://github.com/Ansa89))
- Forgot to set index in some callbacks [\#151](https://github.com/JFreegman/toxic/pull/151) ([ghost](https://github.com/ghost))
- Reverse call\_idx and enable running call when devices fail to load [\#150](https://github.com/JFreegman/toxic/pull/150) ([ghost](https://github.com/ghost))
- Remove autotools dependency [\#149](https://github.com/JFreegman/toxic/pull/149) ([Ansa89](https://github.com/Ansa89))
- Cast localtime [\#147](https://github.com/JFreegman/toxic/pull/147) ([Ansa89](https://github.com/Ansa89))
- Changed code a bit and added new features [\#146](https://github.com/JFreegman/toxic/pull/146) ([ghost](https://github.com/ghost))
## [0.4.1](https://github.com/JFreegman/toxic/tree/0.4.1) (2014-06-19)
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.0...0.4.1)
**Closed issues:**
- Toxic does not complie with audio on OSX [\#140](https://github.com/JFreegman/toxic/issues/140)
- compiling error [\#139](https://github.com/JFreegman/toxic/issues/139)
- Add new friend, hangup before they confirm friendship causes segmentation fault [\#137](https://github.com/JFreegman/toxic/issues/137)
- build fail [\#124](https://github.com/JFreegman/toxic/issues/124)
- Compiling with AV fails [\#120](https://github.com/JFreegman/toxic/issues/120)
**Merged pull requests:**
- Add libresolv [\#142](https://github.com/JFreegman/toxic/pull/142) ([jin-eld](https://github.com/jin-eld))
- Search for OpenAL framework on OSX [\#141](https://github.com/JFreegman/toxic/pull/141) ([jin-eld](https://github.com/jin-eld))
## [0.4.0](https://github.com/JFreegman/toxic/tree/0.4.0) (2014-06-01)
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0.1...0.4.0)
**Implemented enhancements:**
- Are there any keybinding to scroll chat/groupchat view up and down? [\#74](https://github.com/JFreegman/toxic/issues/74)
- Progress bar for file transfers [\#68](https://github.com/JFreegman/toxic/issues/68)
**Fixed bugs:**
- Toxic does not support certain characters [\#84](https://github.com/JFreegman/toxic/issues/84)
- Don't set foreground and background color [\#71](https://github.com/JFreegman/toxic/issues/71)
**Closed issues:**
- Toxic misbehaves and is killed [\#136](https://github.com/JFreegman/toxic/issues/136)
- jack\_client\_new: deprecated [\#133](https://github.com/JFreegman/toxic/issues/133)
- build error on os x 10.9 [\#129](https://github.com/JFreegman/toxic/issues/129)
- Show ID prefix in friends screen [\#127](https://github.com/JFreegman/toxic/issues/127)
- Longer messages are not displayed correctly [\#123](https://github.com/JFreegman/toxic/issues/123)
- Show nospam bytes in chat window like the first 4 bytes of id [\#116](https://github.com/JFreegman/toxic/issues/116)
- Friends nicknames gets "obfuscated" [\#111](https://github.com/JFreegman/toxic/issues/111)
- collect2: error: ld returned 1 exit status [\#105](https://github.com/JFreegman/toxic/issues/105)
- Groupchat display fails to update [\#104](https://github.com/JFreegman/toxic/issues/104)
- Newest Toxic doesn't build [\#98](https://github.com/JFreegman/toxic/issues/98)
**Merged pull requests:**
- Update README.md [\#134](https://github.com/JFreegman/toxic/pull/134) ([zetok](https://github.com/zetok))
- Update audio\_call.c [\#132](https://github.com/JFreegman/toxic/pull/132) ([Impyy](https://github.com/Impyy))
- Not done yet. [\#130](https://github.com/JFreegman/toxic/pull/130) ([ghost](https://github.com/ghost))
- Fix file sender null terminator. [\#128](https://github.com/JFreegman/toxic/pull/128) ([aitjcize](https://github.com/aitjcize))
- Drop typedef redeclarations [\#122](https://github.com/JFreegman/toxic/pull/122) ([czarkoff](https://github.com/czarkoff))
- Include "pthread.h" [\#121](https://github.com/JFreegman/toxic/pull/121) ([czarkoff](https://github.com/czarkoff))
- Wow [\#119](https://github.com/JFreegman/toxic/pull/119) ([ghost](https://github.com/ghost))
- Use default terminal fg/bg colors when we can. [\#117](https://github.com/JFreegman/toxic/pull/117) ([ooesili](https://github.com/ooesili))
- Fixed support for wide characters [\#113](https://github.com/JFreegman/toxic/pull/113) ([graboy](https://github.com/graboy))
- Mention av [\#110](https://github.com/JFreegman/toxic/pull/110) ([stqism](https://github.com/stqism))
- allow history scrolling [\#109](https://github.com/JFreegman/toxic/pull/109) ([JFreegman](https://github.com/JFreegman))
- Only those who appreciate small things [\#107](https://github.com/JFreegman/toxic/pull/107) ([ghost](https://github.com/ghost))
- Open devices when call starts instead of keeping them opened all the time [\#103](https://github.com/JFreegman/toxic/pull/103) ([ghost](https://github.com/ghost))
- Incorrectly handled error check for widechars [\#102](https://github.com/JFreegman/toxic/pull/102) ([graboy](https://github.com/graboy))
- Fix toxic build when toxav is not available [\#100](https://github.com/JFreegman/toxic/pull/100) ([jin-eld](https://github.com/jin-eld))
- Add checks for pthreads to the build system [\#99](https://github.com/JFreegman/toxic/pull/99) ([jin-eld](https://github.com/jin-eld))
- Fixes and stuff... [\#97](https://github.com/JFreegman/toxic/pull/97) ([ghost](https://github.com/ghost))
## [0.3.0.1](https://github.com/JFreegman/toxic/tree/0.3.0.1) (2014-03-12)
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0...0.3.0.1)
**Merged pull requests:**
- SPELLING IS FOR FOOLS [\#94](https://github.com/JFreegman/toxic/pull/94) ([lehitoskin](https://github.com/lehitoskin))
## [0.3.0](https://github.com/JFreegman/toxic/tree/0.3.0) (2014-03-12)
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.7...0.3.0)
**Fixed bugs:**
- SIGSEVG upon friend hanging up [\#89](https://github.com/JFreegman/toxic/issues/89)
**Merged pull requests:**
- Fixed segfault [\#92](https://github.com/JFreegman/toxic/pull/92) ([ghost](https://github.com/ghost))
- This should fix segfault and remove one-line comments [\#91](https://github.com/JFreegman/toxic/pull/91) ([ghost](https://github.com/ghost))
- Fixed another clang issue with bools that broek file sending. [\#90](https://github.com/JFreegman/toxic/pull/90) ([Jman012](https://github.com/Jman012))
- Toxic audio support [\#88](https://github.com/JFreegman/toxic/pull/88) ([ghost](https://github.com/ghost))
- Fixed clang error, disabling the execute module. [\#87](https://github.com/JFreegman/toxic/pull/87) ([Jman012](https://github.com/Jman012))
- Issue \#84 fixed [\#86](https://github.com/JFreegman/toxic/pull/86) ([thevar1able](https://github.com/thevar1able))
- Fixing fall-back from IPv6 to IPv4 [\#85](https://github.com/JFreegman/toxic/pull/85) ([micrictor](https://github.com/micrictor))
## [0.2.7](https://github.com/JFreegman/toxic/tree/0.2.7) (2014-03-01)
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6.1...0.2.7)
**Closed issues:**
- Toxic segfault when window is closed [\#81](https://github.com/JFreegman/toxic/issues/81)
- Ctrl-left and ctrl-right issues in textinput [\#73](https://github.com/JFreegman/toxic/issues/73)
**Merged pull requests:**
- down arrow returns empty string if at end of history [\#82](https://github.com/JFreegman/toxic/pull/82) ([kl4ng](https://github.com/kl4ng))
- Fallback to loading /usr/share/toxic/DHTservers. [\#80](https://github.com/JFreegman/toxic/pull/80) ([viric](https://github.com/viric))
## [0.2.6.1](https://github.com/JFreegman/toxic/tree/0.2.6.1) (2014-02-23)
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6...0.2.6.1)
## [0.2.6](https://github.com/JFreegman/toxic/tree/0.2.6) (2014-02-23)
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.5...0.2.6)
## [0.2.5](https://github.com/JFreegman/toxic/tree/0.2.5) (2014-02-22)
[Full Changelog](https://github.com/JFreegman/toxic/compare/prealpha_win32_r8...0.2.5)
**Fixed bugs:**
- Back space leaves ć character [\#44](https://github.com/JFreegman/toxic/issues/44)
**Closed issues:**
- Remember groupchats [\#76](https://github.com/JFreegman/toxic/issues/76)
- Segfault [\#75](https://github.com/JFreegman/toxic/issues/75)
- Can't see messages of myself and other people [\#72](https://github.com/JFreegman/toxic/issues/72)
- binary blob in source [\#66](https://github.com/JFreegman/toxic/issues/66)
- symbol lookup error [\#54](https://github.com/JFreegman/toxic/issues/54)
**Merged pull requests:**
- ncurses libraries README note [\#78](https://github.com/JFreegman/toxic/pull/78) ([kl4ng](https://github.com/kl4ng))
- umask such that stored files are u+rw only [\#77](https://github.com/JFreegman/toxic/pull/77) ([alevy](https://github.com/alevy))
- Fix groupchat cursor movement. [\#63](https://github.com/JFreegman/toxic/pull/63) ([aitjcize](https://github.com/aitjcize))
- Fix wchar cursor movement. [\#62](https://github.com/JFreegman/toxic/pull/62) ([aitjcize](https://github.com/aitjcize))
- api update [\#61](https://github.com/JFreegman/toxic/pull/61) ([naxuroqa](https://github.com/naxuroqa))
- Add option to switch off ipv6. [\#60](https://github.com/JFreegman/toxic/pull/60) ([aitjcize](https://github.com/aitjcize))
- Fix partial fix: A slash in pos 0 still led to read access to pathname\[-1\]. [\#59](https://github.com/JFreegman/toxic/pull/59) ([FullName](https://github.com/FullName))
- Fix corresponding API name changes in toxcore. [\#58](https://github.com/JFreegman/toxic/pull/58) ([aitjcize](https://github.com/aitjcize))
- Fix API ret code changes of ToxCore [\#57](https://github.com/JFreegman/toxic/pull/57) ([aitjcize](https://github.com/aitjcize))
## [prealpha_win32_r8](https://github.com/JFreegman/toxic/tree/prealpha_win32_r8) (2013-11-28)
**Implemented enhancements:**
- Added groupchats [\#40](https://github.com/JFreegman/toxic/pull/40) ([JFreegman](https://github.com/JFreegman))
- Adapted to ipv6-enabled tox [\#38](https://github.com/JFreegman/toxic/pull/38) ([FullName](https://github.com/FullName))
- If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName))
- Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman))
- if tox\_new\(\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName))
- truncate friends' notes if they're too long [\#30](https://github.com/JFreegman/toxic/pull/30) ([JFreegman](https://github.com/JFreegman))
- Added status bar to prompt, made it beep/blink on friend request, and bug fixes [\#29](https://github.com/JFreegman/toxic/pull/29) ([JFreegman](https://github.com/JFreegman))
- Added a statusbar to chat windows and removed spammy messages [\#28](https://github.com/JFreegman/toxic/pull/28) ([JFreegman](https://github.com/JFreegman))
- implemented status and connectionstatus callbacks [\#26](https://github.com/JFreegman/toxic/pull/26) ([JFreegman](https://github.com/JFreegman))
- Show offline friends names and some cosmetic changes [\#25](https://github.com/JFreegman/toxic/pull/25) ([JFreegman](https://github.com/JFreegman))
- Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman))
- refactor command argument parsing [\#23](https://github.com/JFreegman/toxic/pull/23) ([lukechampine](https://github.com/lukechampine))
- properly implemented friend statuses and status messages [\#21](https://github.com/JFreegman/toxic/pull/21) ([JFreegman](https://github.com/JFreegman))
- implemented friend deletion [\#15](https://github.com/JFreegman/toxic/pull/15) ([JFreegman](https://github.com/JFreegman))
- Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld))
- Add check for setlocale\(\) [\#10](https://github.com/JFreegman/toxic/pull/10) ([manuel-arguelles](https://github.com/manuel-arguelles))
- Update build system [\#7](https://github.com/JFreegman/toxic/pull/7) ([jin-eld](https://github.com/jin-eld))
- Added travis integration [\#6](https://github.com/JFreegman/toxic/pull/6) ([stqism](https://github.com/stqism))
- Use new public api [\#5](https://github.com/JFreegman/toxic/pull/5) ([fhahn](https://github.com/fhahn))
- Add widechar checks [\#2](https://github.com/JFreegman/toxic/pull/2) ([jin-eld](https://github.com/jin-eld))
**Fixed bugs:**
- Let windows.c actually get the tox \*m. [\#41](https://github.com/JFreegman/toxic/pull/41) ([Jman012](https://github.com/Jman012))
- If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName))
- Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman))
- Merged pr6 [\#34](https://github.com/JFreegman/toxic/pull/34) ([stqism](https://github.com/stqism))
- made error handling more consistent and added exit function [\#33](https://github.com/JFreegman/toxic/pull/33) ([JFreegman](https://github.com/JFreegman))
- if tox\\_new\\(\\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName))
- Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman))
- fix buffer overflows and format issues [\#20](https://github.com/JFreegman/toxic/pull/20) ([JFreegman](https://github.com/JFreegman))
- Fix blocking while waiting for key [\#17](https://github.com/JFreegman/toxic/pull/17) ([manuel-arguelles](https://github.com/manuel-arguelles))
- fixed "free\(\): invalid pointer" when XDG\_CONFIG\_HOME is set [\#16](https://github.com/JFreegman/toxic/pull/16) ([gs93](https://github.com/gs93))
- Make sure toxic compiles on MinGW/Win32 again [\#14](https://github.com/JFreegman/toxic/pull/14) ([jin-eld](https://github.com/jin-eld))
- Fix for the "bad character" when doing backspace in chat window [\#12](https://github.com/JFreegman/toxic/pull/12) ([jin-eld](https://github.com/jin-eld))
- Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld))
- Fix configure script for ncurses without ncursesw [\#9](https://github.com/JFreegman/toxic/pull/9) ([manuel-arguelles](https://github.com/manuel-arguelles))
- Fix configure script for mingw32 [\#8](https://github.com/JFreegman/toxic/pull/8) ([jin-eld](https://github.com/jin-eld))
- warning: comparison of integers of different signs: 'int' and 'unsigned long' [\#3](https://github.com/JFreegman/toxic/pull/3) ([1100110](https://github.com/1100110))
**Merged pull requests:**
- Make sure friend message is null-terminated else generate garbate on screen [\#56](https://github.com/JFreegman/toxic/pull/56) ([aitjcize](https://github.com/aitjcize))
- Fix trailing slashes which leads to segfault. [\#55](https://github.com/JFreegman/toxic/pull/55) ([aitjcize](https://github.com/aitjcize))
- fix cflags [\#53](https://github.com/JFreegman/toxic/pull/53) ([JFreegman](https://github.com/JFreegman))
- Fix 93ab16c [\#52](https://github.com/JFreegman/toxic/pull/52) ([urras](https://github.com/urras))
- Offer solution for "error while loading shared libraries: libtoxcore.so.... [\#51](https://github.com/JFreegman/toxic/pull/51) ([urras](https://github.com/urras))
- Implemented file transfers [\#50](https://github.com/JFreegman/toxic/pull/50) ([JFreegman](https://github.com/JFreegman))
- Fix check for toxcore by linking sodium in the correct place [\#47](https://github.com/JFreegman/toxic/pull/47) ([devurandom](https://github.com/devurandom))
- Changed order of servers [\#46](https://github.com/JFreegman/toxic/pull/46) ([grimd34th](https://github.com/grimd34th))
- set friendnames properly and some fixes [\#45](https://github.com/JFreegman/toxic/pull/45) ([JFreegman](https://github.com/JFreegman))
- moved misc helper functions to separate file and removed redundant includes [\#43](https://github.com/JFreegman/toxic/pull/43) ([JFreegman](https://github.com/JFreegman))
- Refactored prompt command parser to work with all window types and moved command stuff to separate files [\#42](https://github.com/JFreegman/toxic/pull/42) ([JFreegman](https://github.com/JFreegman))
- Ipv6.init connection [\#39](https://github.com/JFreegman/toxic/pull/39) ([FullName](https://github.com/FullName))
- Remove DHT window [\#13](https://github.com/JFreegman/toxic/pull/13) ([JFreegman](https://github.com/JFreegman))
- Update README.md [\#4](https://github.com/JFreegman/toxic/pull/4) ([notadecent](https://github.com/notadecent))
- Toxic standalone [\#1](https://github.com/JFreegman/toxic/pull/1) ([jin-eld](https://github.com/jin-eld))
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

View File

@ -11,10 +11,10 @@ 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
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o
OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o
OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
# Check on wich system we are running
UNAME_S = $(shell uname -s)

View File

@ -3,7 +3,7 @@
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.
Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
[![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png)

11
astylerc Normal file
View File

@ -0,0 +1,11 @@
--style=kr
--pad-header
--max-code-length=120
--convert-tabs
--indent-switches
--pad-oper
--align-pointer=name
--align-reference=name
--preserve-date
--lineend=linux
--break-blocks

View File

@ -34,6 +34,12 @@ ifneq ($(DESK_NOTIFY), disabled)
-include $(CHECKS_DIR)/desktop_notifications.mk
endif
# Check if we want build QR exported as PNG support
QR_PNG = $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(QR_PNG), disabled)
-include $(CHECKS_DIR)/qr_png.mk
endif
# Check if we can build Toxic
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
ifneq ($(CHECK_LIBS), error)

15
cfg/checks/qr_png.mk Normal file
View File

@ -0,0 +1,15 @@
# Variables for QR exported as PNG support
PNG_LIBS = libpng
PNG_CFLAGS = -DQRPNG
# Check if we can build QR exported as PNG support
CHECK_PNG_LIBS = $(shell pkg-config --exists $(PNG_LIBS) || echo -n "error")
ifneq ($(CHECK_PNG_LIBS), error)
LIBS += $(PNG_LIBS)
CFLAGS += $(PNG_CFLAGS)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_PNG_LIBS = $(shell for lib in $(PNG_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without QR exported as PNG support)
$(warning WARNING -- You need these libraries for QR exported as PNG support)
$(warning WARNING -- $(MISSING_PNG_LIBS))
endif

View File

@ -8,13 +8,13 @@ else
endif
# Check if we can build video support
CHECK_VIDEO_LIBS = $(shell pkg-config --exists $(VIDEO_LIBS) || echo -n "error")
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)
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))

View File

@ -1,5 +1,5 @@
# Version
TOXIC_VERSION = 0.7.0
TOXIC_VERSION = 0.7.1
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
ifneq (, $(findstring error, $(REV)))
VERSION = $(TOXIC_VERSION)
@ -16,7 +16,7 @@ MISC_DIR = $(BASE_DIR)/misc
# Project files
MANFILES = toxic.1 toxic.conf.5
DATAFILES = DHTnodes nameservers toxic.conf.example
DATAFILES = nameservers toxic.conf.example
DESKFILE = toxic.desktop
SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav
SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
@ -28,6 +28,5 @@ BINDIR = $(PREFIX)/bin
DATADIR = $(PREFIX)/share/toxic
MANDIR = $(PREFIX)/share/man
APPDIR = $(PREFIX)/share/applications
# Platform tools
PKG_CONFIG = pkg-config

View File

@ -14,6 +14,7 @@ help:
@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"
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"

View File

@ -2,12 +2,12 @@
.\" Title: toxic
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2015-07-08
.\" Date: 2016-09-20
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC" "1" "2015\-07\-08" "toxic __VERSION__" "Toxic Manual"
.TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@ -70,7 +70,7 @@ Encrypt an unencrypted data file\&. An error will occur if this option is used w
Use specified
\fIdata\-file\fR
instead of
\fI~/\&.config/tox/data\fR
\fI~/\&.config/tox/toxic_profile\&.tox\fR
.RE
.PP
\-h, \-\-help
@ -82,8 +82,8 @@ Show help message
.RS 4
Use specified
\fInodes\-file\fR
for DHT bootstrap nodes, instead of
\fI__DATADIR__/DHTnodes\fR
for DHT bootstrap nodes instead of
\fI~/\&.config/tox/DHTnodes\&.json\fR
.RE
.PP
\-o, \-\-noconnect
@ -122,12 +122,14 @@ Unencrypt a data file\&. A warning will appear if this option is used with a dat
.RE
.SH "FILES"
.PP
__DATADIR__/DHTnodes
~/\&.config/tox/DHTnodes\&.json
.RS 4
Default list of DHT bootstrap nodes\&.
Default location for list of DHT bootstrap nodes (list obtained from
https://nodes\&.tox\&.chat)\&. This list is automatically updated\&. See
\fBtoxic\&.conf\fR(5) for details on controlling the update frequency\&.
.RE
.PP
~/\&.config/tox/data
~/\&.config/tox/toxic_profile\&.tox
.RS 4
Savestate which contains your personal info (nickname, Tox ID, contacts, etc)
.RE
@ -144,7 +146,11 @@ Configuration example\&.
.RE
.SH "BUGS"
.sp
Unicode characters with a width larger than 1 column may cause strange behaviour\&. Expect more bugs and bad behaviour: this software is in a pre\-alpha stage\&.
\-Unicode characters with a width larger than 1 column may cause strange behaviour\&.
.sp
\-Text occasionally fails to auto\-scroll to the bottom\&.
.sp
\-Screen flickering sometimes occurs on certain terminals\&.
.SH "AUTHORS"
.sp
JFreegman <JFreegman@gmail\&.com>
@ -153,6 +159,6 @@ JFreegman <JFreegman@gmail\&.com>
\fBtoxic\&.conf\fR(5)
.SH "LINKS"
.sp
Project page: https://github\&.com/Tox/toxic
Project page: https://github\&.com/JFreegman/toxic
.sp
IRC channel: chat\&.freenode\&.net#tox

View File

@ -35,14 +35,13 @@ OPTIONS
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/toxic_profile.tox'
-h, --help::
Show help message
-n, --nodes nodes-file::
Use specified 'nodes-file' for DHT bootstrap nodes, instead of
'{datadir}/DHTnodes'
Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json'
-o, --noconnect::
Do not connect to the DHT network
@ -68,10 +67,11 @@ OPTIONS
FILES
-----
{datadir}/DHTnodes::
Default list of DHT bootstrap nodes.
~/.config/tox/DHTnodes.json::
Default location for list of DHT bootstrap nodes (list obtained from https://nodes.tox.chat).
This list is automatically updated. See *toxic.conf*(5) for details on controlling the update frequency.
~/.config/tox/data::
~/.config/tox/toxic_profile.tox::
Savestate which contains your personal info (nickname, Tox ID, contacts,
etc)
@ -83,9 +83,12 @@ FILES
BUGS
----
Unicode characters with a width larger than 1 column may cause strange
behaviour. Expect more bugs and bad behaviour: this software is in a
pre-alpha stage.
-Unicode characters with a width larger than 1 column may cause strange
behaviour.
-Text occasionally fails to auto-scroll to the bottom.
-Screen flickering sometimes occurs on certain terminals.
AUTHORS
-------
@ -97,6 +100,6 @@ SEE ALSO
LINKS
-----
Project page: <https://github.com/Tox/toxic>
Project page: <https://github.com/JFreegman/toxic>
IRC channel: chat.freenode.net#tox

View File

@ -2,12 +2,12 @@
.\" Title: toxic.conf
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2015-03-28
.\" Date: 2016-07-21
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC\&.CONF" "5" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual"
.TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@ -85,7 +85,7 @@ Time format string for logging enclosed by double quotes\&. See
.PP
\fBalerts\fR
.RS 4
Enable or disable terminal alerts on events\&. true or false
Enable or disable acoustic alerts on events\&. true or false
.RE
.PP
\fBnative_colors\fR
@ -113,6 +113,16 @@ Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false
Show welcome message on startup\&. true or false
.RE
.PP
\fBshow_connection_msg\fR
.RS 4
Enable friend connection change notifications\&. true or false
.RE
.PP
\fBnodelist_update_freq\fR
.RS 4
How often in days to update the DHT nodes list\&. (0 to disable updates)
.RE
.PP
\fBhistory_size\fR
.RS 4
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
@ -138,6 +148,11 @@ Indicator for alert messages\&.
Indicator for normal messages\&.
.RE
.PP
\fBline_special\fR
.RS 4
Indicator for special messages\&.
.RE
.PP
\fBmplex_away\fR
.RS 4
Set user status when attaching and detaching from GNU screen or tmux\&. true or false
@ -146,6 +161,38 @@ Set user status when attaching and detaching from GNU screen or tmux\&. true or
\fBmplex_away_note\fR
.RS 4
Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&.
.sp
.if n \{\
.RS 4
.\}
.nf
The following options control whether to output a terminal bell on certain events\&.
Some terminals mark the window as urgent when a bell is received\&. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window\&.
These options don\*(Aqt affect the "alerts" option\&.
.fi
.if n \{\
.RE
.\}
.RE
.PP
\fBbell_on_message\fR
.RS 4
Enable/Disable the terminal bell when receiving a message\&. true or false
.RE
.PP
\fBbell_on_filetrans\fR
.RS 4
Enable/Disable the terminal bell when receiving a filetransfer\&. true or false
.RE
.PP
\fBbell_on_filetrans_accept\fR
.RS 4
Enable/Disable the terminal bell when a filetransfer was accepted\&. true or false
.RE
.PP
\fBbell_on_invite\fR
.RS 4
Enable/Disable the terminal bell when receiving a group/call invite\&. true or false
.RE
.RE
.PP
@ -189,6 +236,11 @@ Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
.RS 4
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
.RE
.PP
\fBpassword_eval\fR
.RS 4
Replace password prompt by running this command and using its output as the password\&.
.RE
.RE
.PP
\fBsounds\fR
@ -303,6 +355,11 @@ Key combination to scroll contacts list down\&.
.RS 4
Toggle the peer list on and off\&.
.RE
.PP
\fBtoggle_paste_mode\fR
.RS 4
Toggle treating linebreaks as enter key press\&.
.RE
.RE
.SH "FILES"
.PP
@ -320,7 +377,7 @@ Configuration example\&.
\fBtoxic\fR(1)
.SH "RESOURCES"
.sp
Project page: https://github\&.com/Tox/toxic
Project page: https://github\&.com/JFreegman/toxic
.sp
IRC channel: chat\&.freenode\&.net#tox
.SH "AUTHORS"

View File

@ -55,7 +55,7 @@ OPTIONS
See *date*(1)
*alerts*;;
Enable or disable terminal alerts on events. true or false
Enable or disable acoustic alerts on events. true or false
*native_colors*;;
Select between native terminal colors and toxic color theme. true or false
@ -72,6 +72,12 @@ OPTIONS
*show_welcome_msg*;;
Show welcome message on startup. true or false
*show_connection_msg*;;
Enable friend connection change notifications. true or false
*nodelist_update_freq*;;
How often in days to update the DHT nodes list. (0 to disable updates)
*history_size*;;
Maximum lines for chat window history. Integer value. (for example: 700)
@ -88,6 +94,9 @@ OPTIONS
*line_normal*;;
Indicator for normal messages.
*line_special*;;
Indicator for special messages.
*mplex_away*;;
Set user status when attaching and detaching from GNU screen or tmux.
true or false
@ -97,6 +106,23 @@ OPTIONS
detach. When attaching, the status message is set back to the original
value.
The following options control whether to output a terminal bell on certain events.
Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window.
These options don't affect the "alerts" option.
*bell_on_message*;;
Enable/Disable the terminal bell when receiving a message. true or false
*bell_on_filetrans*;;
Enable/Disable the terminal bell when receiving a filetransfer. true or false
*bell_on_filetrans_accept*;;
Enable/Disable the terminal bell when a filetransfer was accepted. true or false
*bell_on_invite*;;
Enable/Disable the terminal bell when receiving a group/call invite. true or false
*audio*::
Configuration related to audio devices.
@ -123,6 +149,10 @@ OPTIONS
*chatlogs_path*;;
Default path for chatlogs. String value. Absolute path for chatlog files.
*password_eval*;;
Replace password prompt by running this command and using its output as
the password.
*sounds*::
Configuration related to notification sounds.
Special value "silent" can be used to disable a specific notification. +
@ -194,6 +224,9 @@ OPTIONS
*toggle_peerlist*;;
Toggle the peer list on and off.
*toggle_paste_mode*;;
Toggle treating linebreaks as enter key press.
FILES
-----
@ -211,7 +244,7 @@ SEE ALSO
RESOURCES
---------
Project page: <https://github.com/Tox/toxic>
Project page: <https://github.com/JFreegman/toxic>
IRC channel: chat.freenode.net#tox

View File

@ -1,17 +0,0 @@
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
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

View File

@ -1,2 +1 @@
toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25

View File

@ -5,9 +5,21 @@ ui = {
// true to enable timestamps, false to disable
timestamps=true;
// true to enable terminal alerts on messages, false to disable
// true to enable acoustic alerts on messages, false to disable
alerts=true;
// Output a bell when receiving a message (see manpage)
bell_on_message=true
// Output a bell when receiving a filetransfer (see manpage)
bell_on_filetrans=true
// Don't output a bell when a filetransfer was accepted (see manpage)
bell_on_filetrans_accept=false
// Output a bell when receiving a group/call invite (see manpage)
bell_on_invite=true
// true to use native terminal colours, false to use toxic default colour theme
native_colors=false;
@ -17,7 +29,7 @@ ui = {
// 24 or 12 hour time
time_format=24;
// timestamp format string according to date/strftime format. Overrides time_format setting
// 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
@ -29,6 +41,12 @@ ui = {
// true to show the welcome message on startup
show_welcome_msg=true;
// true to show friend connection change messages on the home screen
show_connection_msg=true;
// How often in days to update the DHT nodes list. (0 to disable updates)
nodeslist_update_freq=7;
// maximum lines for chat window history
history_size=700;
@ -44,6 +62,9 @@ ui = {
// Indicator for normal messages.
line_normal="---";
// Indicator for special messages (currently only used for private group messages)
line_special=">>>";
// true to change status based on screen/tmux attach/detach, false to disable
mplex_away=true;
@ -86,7 +107,7 @@ sounds = {
};
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
// Note: All printable keys register as input
// Note: Ctrl+M does not work
keys = {
next_tab="Ctrl+P";
prev_tab="Ctrl+O";
@ -98,5 +119,6 @@ keys = {
peer_list_up="Ctrl+[";
peer_list_down="Ctrl+]";
toggle_peerlist="Ctrl+b";
toggle_paste_mode="Ctrl+T";
};

View File

@ -61,7 +61,7 @@ extern FriendsList Friends;
#define frame_size (CallControl.audio_sample_rate * CallControl.audio_frame_duration / 1000)
static int set_call(Call* call, bool start)
static int set_call(Call *call, bool start)
{
call->in_idx = -1;
call->out_idx = -1;
@ -75,17 +75,18 @@ static int set_call(Call* call, bool start)
if ( pthread_mutex_init(&call->mutex, NULL) != 0 )
return -1;
}
else {
} else {
call->ttid = 0;
if ( pthread_mutex_destroy(&call->mutex) != 0 )
return -1;
return -1;
}
return 0;
}
void call_cb ( ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data );
void call_cb ( ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled,
void *user_data );
void callstate_cb ( ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data );
void receive_audio_frame_cb ( ToxAV *av, uint32_t friend_number, int16_t const *pcm, size_t sample_count,
uint8_t channels, uint32_t sampling_rate, void *user_data );
@ -103,7 +104,8 @@ void callback_call_canceled ( uint32_t friend_number );
void callback_call_rejected ( uint32_t friend_number );
void callback_call_ended ( uint32_t friend_number );
void write_device_callback( uint32_t friend_number, const int16_t* PCM, uint16_t size );
void write_device_callback( uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels,
uint32_t sample_rate );
static void print_err (ToxWindow *self, const char *error_str)
{
@ -120,9 +122,9 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox)
CallControl.av = toxav_new(tox, &error);
CallControl.audio_enabled = true;
CallControl.audio_bit_rate = 48;
CallControl.audio_bit_rate = 64;
CallControl.audio_sample_rate = 48000;
CallControl.audio_frame_duration = 10;
CallControl.audio_frame_duration = 20;
CallControl.audio_channels = 1;
#ifndef VIDEO
@ -157,6 +159,7 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox)
void terminate_audio()
{
int i;
for (i = 0; i < MAX_CALLS; ++i)
stop_transmission(&CallControl.calls[i], i);
@ -166,7 +169,7 @@ void terminate_audio()
terminate_devices();
}
void read_device_callback(const int16_t* captured, uint32_t size, void* data)
void read_device_callback(const int16_t *captured, uint32_t size, void *data)
{
TOXAV_ERR_SEND_FRAME error;
uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */
@ -174,16 +177,17 @@ void read_device_callback(const int16_t* captured, uint32_t size, void* data)
((int64_t) CallControl.audio_frame_duration) / 1000;
if ( sample_count <= 0 || toxav_audio_send_frame(CallControl.av, friend_number,
captured, sample_count,
CallControl.audio_channels,
CallControl.audio_sample_rate, &error) == false )
{}
captured, sample_count,
CallControl.audio_channels,
CallControl.audio_sample_rate, &error) == false ) {
}
}
void write_device_callback(uint32_t friend_number, const int16_t* PCM, uint16_t size)
void write_device_callback(uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels,
uint32_t sample_rate)
{
if ( CallControl.calls[friend_number].ttas )
write_out(CallControl.calls[friend_number].out_idx, PCM, size, CallControl.audio_channels);
write_out(CallControl.calls[friend_number].out_idx, PCM, sample_count, channels, sample_rate);
}
int start_transmission(ToxWindow *self, Call *call)
@ -197,7 +201,7 @@ int start_transmission(ToxWindow *self, Call *call)
return -1;
DeviceError error = open_primary_device(input, &call->in_idx,
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels);
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels);
if ( error != de_None ) {
if ( error == de_FailedStart)
@ -208,12 +212,12 @@ int start_transmission(ToxWindow *self, Call *call)
}
if ( register_device_callback(self->num, call->in_idx,
read_device_callback, &self->num, true) != de_None)
read_device_callback, &self->num, true) != de_None)
/* Set VAD as true for all; TODO: Make it more dynamic */
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!");
if ( open_primary_device(output, &call->out_idx,
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None ) {
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None ) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!");
call->has_output = 0;
}
@ -226,7 +230,7 @@ int stop_transmission(Call *call, uint32_t friend_number)
if ( call->ttas ) {
TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK;
if ( CallControl.call_state != TOXAV_FRIEND_CALL_STATE_FINISHED )
if ( CallControl.call_state > TOXAV_FRIEND_CALL_STATE_FINISHED )
toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_CANCEL, &error);
if ( error == TOXAV_ERR_CALL_CONTROL_OK ) {
@ -284,7 +288,8 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
callback_call_ended(friend_number);
CallControl.pending_call = false;
break;
break;
case ( TOXAV_FRIEND_CALL_STATE_FINISHED ):
if ( CallControl.pending_call )
callback_call_rejected(friend_number);
@ -302,7 +307,8 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
CallControl.call_state = 0;
CallControl.pending_call = false;
break;
break;
default:
if ( CallControl.pending_call ) {
/* Start answered call */
@ -311,6 +317,7 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
} else {
#ifdef VIDEO
/* Handle receiving client video call states */
if ( state & TOXAV_FRIEND_CALL_STATE_SENDING_V )
callback_recv_video_starting(friend_number);
@ -320,15 +327,15 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
#endif /* VIDEO */
}
break;
break;
}
}
void receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
int16_t const *pcm, size_t sample_count,
uint8_t channels, uint32_t sampling_rate, void *user_data)
int16_t const *pcm, size_t sample_count,
uint8_t channels, uint32_t sampling_rate, void *user_data)
{
write_device_callback(friend_number, pcm, frame_size);
write_device_callback(friend_number, pcm, sample_count, channels, sampling_rate);
}
void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
@ -371,12 +378,14 @@ void callback_recv_ringing(uint32_t friend_number)
}
void callback_recv_starting(uint32_t friend_number)
{
ToxWindow* windows = CallControl.prompt;
ToxWindow *windows = CallControl.prompt;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if ( windows[i].onStarting != NULL && windows[i].num == friend_number ) {
windows[i].onStarting(&windows[i], CallControl.av, friend_number, CallControl.call_state);
if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) /* YEAH! */
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!");
@ -397,12 +406,14 @@ void callback_recv_ending(uint32_t friend_number)
}
void callback_call_started(uint32_t friend_number)
{
ToxWindow* windows = CallControl.prompt;
ToxWindow *windows = CallControl.prompt;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
if ( windows[i].onStart != NULL && windows[i].num == friend_number ) {
windows[i].onStart(&windows[i], CallControl.av, friend_number, CallControl.call_state);
if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) {/* YEAH! */
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
return;
@ -477,6 +488,7 @@ void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
}
toxav_call(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error);
if ( error != TOXAV_ERR_CALL_OK ) {
if ( error == TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL ) error_str = "Already in a call!";
else if ( error == TOXAV_ERR_CALL_MALLOC ) error_str = "Memory allocation issue";
@ -516,6 +528,7 @@ void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
}
toxav_answer(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error);
if ( error != TOXAV_ERR_ANSWER_OK ) {
if ( error == TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING ) error_str = "No incoming call!";
else if ( error == TOXAV_ERR_ANSWER_CODEC_INITIALIZATION ) error_str = "Failed to initialize codecs!";
@ -570,8 +583,8 @@ void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
{
const char *error_str = NULL;
if ( !self->is_call ) {
error_str = "Not in a call.";
if ( !CallControl.av ) {
error_str = "Audio not supported!";
goto on_error;
}
@ -580,8 +593,8 @@ void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
goto on_error;
}
if ( !CallControl.av ) {
error_str = "Audio not supported!";
if ( !self->is_call && !CallControl.pending_call ) {
error_str = "Not in a call.";
goto on_error;
}
@ -589,18 +602,7 @@ void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
callback_video_end(self->num);
#endif /* VIDEO */
if ( CallControl.pending_call ) {
/* Manually send a cancel call control because call hasn't started */
toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL);
callback_call_canceled(self->num);
}
else {
stop_transmission(&CallControl.calls[self->num], self->num);
callback_call_ended(self->num);
}
CallControl.pending_call = false;
stop_current_call(self);
return;
on_error:
print_err (self, error_str);
@ -673,7 +675,7 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (
}
if ( set_primary_device(type, selection) == de_InvalidSelection ) {
error_str="Invalid selection!";
error_str = "Invalid selection!";
goto on_error;
}
@ -717,13 +719,14 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
}
if ( selection_valid(type, selection) == de_InvalidSelection ) {
error_str="Invalid selection!";
error_str = "Invalid selection!";
goto on_error;
}
/* If call is active, change device */
if ( self->is_call ) {
Call* this_call = &CallControl.calls[self->num];
Call *this_call = &CallControl.calls[self->num];
if ( this_call->ttas ) {
@ -731,15 +734,14 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
pthread_mutex_lock(&this_call->mutex);
close_device(output, this_call->out_idx);
this_call->has_output = open_device(output, selection, &this_call->out_idx,
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels)
== de_None ? 1 : 0;
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels)
== de_None ? 1 : 0;
pthread_mutex_unlock(&this_call->mutex);
}
else {
} else {
/* TODO: check for failure */
close_device(input, this_call->in_idx);
open_device(input, selection, &this_call->in_idx, CallControl.audio_sample_rate,
CallControl.audio_frame_duration, CallControl.audio_channels);
CallControl.audio_frame_duration, CallControl.audio_channels);
/* Set VAD as true for all; TODO: Make it more dynamic */
register_device_callback(self->num, this_call->in_idx, read_device_callback, &self->num, true);
}
@ -749,7 +751,7 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
self->device_selection[type] = selection;
return;
on_error:
on_error:
print_err (self, error_str);
}
@ -780,9 +782,10 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
/* If call is active, use this_call values */
if ( self->is_call ) {
Call* this_call = &CallControl.calls[self->num];
Call *this_call = &CallControl.calls[self->num];
pthread_mutex_lock(&this_call->mutex);
if ( type == input ) {
device_mute(type, this_call->in_idx);
self->chatwin->infobox.in_is_muted ^= 1;
@ -790,12 +793,13 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
device_mute(type, this_call->out_idx);
self->chatwin->infobox.out_is_muted ^= 1;
}
pthread_mutex_unlock(&this_call->mutex);
}
return;
on_error:
on_error:
print_err (self, error_str);
}
@ -831,10 +835,15 @@ on_error:
}
void stop_current_call(ToxWindow* self)
void stop_current_call(ToxWindow *self)
{
Call *this_call = &CallControl.calls[self->num];
if ( CallControl.pending_call ) {
toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL);
callback_call_canceled(self->num);
} else {
stop_transmission(&CallControl.calls[self->num], self->num);
callback_call_ended(self->num);
}
if (this_call && self->is_call)
stop_transmission(this_call, self->num);
CallControl.pending_call = false;
}

View File

@ -56,7 +56,7 @@ 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 */
void *cb_data; /* Data to be passed to callback */
int32_t friend_number; /* ToxAV friend number */
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
@ -80,7 +80,7 @@ Device *running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
uint32_t primary_device[2]; /* Primary device */
#ifdef AUDIO
static ToxAV* av = NULL;
static ToxAV *av = NULL;
#endif /* AUDIO */
/* q_mutex */
@ -90,12 +90,12 @@ pthread_mutex_t mutex;
bool thread_running = true,
thread_paused = true; /* Thread control */
thread_paused = true; /* Thread control */
void* thread_poll(void*);
void *thread_poll(void *);
/* Meet devices */
#ifdef AUDIO
DeviceError init_devices(ToxAV* av_)
DeviceError init_devices(ToxAV *av_)
#else
DeviceError init_devices()
#endif /* AUDIO */
@ -103,6 +103,7 @@ DeviceError init_devices()
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);
@ -113,7 +114,13 @@ DeviceError init_devices()
}
size[output] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) {
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
else
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
if (stringed_device_list) {
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
@ -127,6 +134,7 @@ DeviceError init_devices()
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;
@ -155,9 +163,10 @@ DeviceError terminate_devices()
DeviceError device_mute(DeviceType type, uint32_t device_idx)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock;
Device* device = running[type][device_idx];
Device *device = running[type][device_idx];
if (!device) {
unlock;
@ -174,9 +183,10 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx)
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];
Device *device = running[input][device_idx];
if (!device) {
unlock;
@ -194,12 +204,14 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
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;
}
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
uint8_t channels)
{
return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels);
}
@ -210,7 +222,8 @@ void get_primary_device_name(DeviceType type, char *buf, int 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)
DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate,
uint32_t frame_duration, uint8_t channels)
{
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
@ -221,10 +234,13 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
const uint32_t frame_size = (sample_rate * frame_duration / 1000);
uint32_t i;
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i);
if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; }
else *device_idx = i;
if (i == MAX_DEVICES) {
unlock;
return de_AllDevicesBusy;
} else *device_idx = i;
for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
if ( running[type][i] && running[type][i]->selection == selection ) {
@ -238,7 +254,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
}
}
Device* device = running[type][*device_idx] = calloc(1, sizeof(Device));
Device *device = running[type][*device_idx] = calloc(1, sizeof(Device));
device->selection = selection;
device->sample_rate = sample_rate;
@ -254,12 +270,12 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
if (type == input) {
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
sample_rate, device->sound_mode, frame_size * 2);
#ifdef AUDIO
#ifdef AUDIO
device->VAD_treshold = user_settings->VAD_treshold;
#endif
}
else {
#endif
} else {
device->dhndl = alcOpenDevice(devices_names[type][selection]);
if ( !device->dhndl ) {
free(device);
running[type][*device_idx] = NULL;
@ -275,10 +291,10 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
alSourcei(device->source, AL_LOOPING, AL_FALSE);
uint16_t zeros[frame_size];
memset(zeros, 0, frame_size*2);
memset(zeros, 0, frame_size * 2);
for ( i = 0; i < OPENAL_BUFS; ++i ) {
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate);
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate);
}
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
@ -306,7 +322,7 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock;
Device* device = running[type][device_idx];
Device *device = running[type][device_idx];
DeviceError rc = de_None;
if (!device) {
@ -317,32 +333,30 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
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);
alcMakeContextCurrent(NULL);
if ( device->ctx ) alcDestroyContext(device->ctx);
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
}
free(device);
}
else device->ref_count--;
} else device->ref_count--;
unlock;
return rc;
}
DeviceError register_device_callback( int32_t friend_number, 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)
return de_InvalidSelection;
@ -357,11 +371,12 @@ DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx
return de_None;
}
inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels)
inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
uint32_t sample_rate)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
Device* device = running[output][device_idx];
Device *device = running[output][device_idx];
if (!device || device->muted) return de_DeviceNotActive;
@ -373,33 +388,33 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
if(processed) {
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 if (queued < 16) alGenBuffers(1, &bufid);
else {
pthread_mutex_unlock(device->mutex);
return de_Busy;
}
alBufferData(bufid, device->sound_mode, data, length * 2 * channels, device->sample_rate);
alBufferData(bufid, channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data, sample_count * 2 * channels,
sample_rate);
alSourceQueueBuffers(device->source, 1, &bufid);
ALint state;
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
if(state != AL_PLAYING) alSourcePlay(device->source);
if (state != AL_PLAYING) alSourcePlay(device->source);
pthread_mutex_unlock(device->mutex);
return de_None;
}
void* thread_poll (void* arg) // TODO: maybe use thread for every input source
void *thread_poll (void *arg) // TODO: maybe use thread for every input source
{
/*
* NOTE: We only need to poll input devices for data.
@ -409,23 +424,27 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
int32_t sample = 0;
while (true)
{
while (1) {
lock;
if (!thread_running) {
unlock;
break;
}
bool paused = thread_paused;
unlock;
if (thread_paused) usleep(10000); /* Wait for unpause. */
else
{
for (i = 0; i < size[input]; ++i)
{
/* Wait for unpause. */
if (paused) {
usleep(10000);
}
else {
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);
@ -434,7 +453,8 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
unlock;
continue;
}
Device* device = running[input][i];
Device *device = running[input][i];
int16_t frame[16000];
alcCaptureSamples(device->dhndl, frame, f_size);
@ -446,8 +466,10 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
}
unlock;
}
usleep(5000);
}
}
@ -455,7 +477,7 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
pthread_exit(NULL);
}
void print_devices(ToxWindow* self, DeviceType type)
void print_devices(ToxWindow *self, DeviceType type)
{
int i;
@ -470,7 +492,7 @@ DeviceError selection_valid(DeviceType type, int32_t selection)
return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
}
void* get_device_callback_data(uint32_t device_idx)
void *get_device_callback_data(uint32_t device_idx)
{
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
return NULL;

View File

@ -52,11 +52,11 @@ typedef enum DeviceError {
de_AlError = -9,
} DeviceError;
typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data);
typedef void (*DataHandleCallback) (const int16_t *, uint32_t size, void *data);
#ifdef AUDIO
DeviceError init_devices(ToxAV* av);
DeviceError init_devices(ToxAV *av);
#else
DeviceError init_devices();
#endif /* AUDIO */
@ -64,8 +64,9 @@ DeviceError init_devices();
DeviceError terminate_devices();
/* Callback handles ready data from INPUT device */
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);
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);
@ -75,16 +76,19 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
#endif
DeviceError set_primary_device(DeviceType type, int32_t selection);
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
uint8_t channels);
/* Start device */
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate,
uint32_t frame_duration, uint8_t channels);
/* Stop device */
DeviceError close_device(DeviceType type, uint32_t device_idx);
/* Write data to device */
DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels);
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels,
uint32_t sample_rate);
void print_devices(ToxWindow* self, DeviceType type);
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);

View File

@ -25,10 +25,10 @@
#include <limits.h>
#ifdef __APPLE__
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/types.h>
#include <sys/dir.h>
#else
#include <dirent.h>
#include <dirent.h>
#endif /* ifdef __APPLE__ */
#include "windows.h"
@ -53,12 +53,14 @@ static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items
}
/* puts match in match buffer. if more than one match, add first n chars that are identical.
e.g. if matches contains: [foo, foobar, foe] we put fo in matches. */
static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_SIZE], int n)
* e.g. if matches contains: [foo, foobar, foe] we put fo in match.
*
* Returns the length of the match.
*/
static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n)
{
if (n == 1) {
strcpy(match, matches[0]);
return;
return snprintf(match, match_sz, "%s", matches[0]);
}
int i;
@ -71,14 +73,14 @@ static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_
char ch2 = matches[j][i];
if (ch1 != ch2 || !ch1) {
strcpy(match, matches[0]);
snprintf(match, match_sz, "%s", matches[0]);
match[i] = '\0';
return;
return i;
}
}
}
strcpy(match, matches[0]);
return snprintf(match, match_sz, "%s", matches[0]);
}
/* looks for all instances in list that begin with the last entered word in line according to pos,
@ -106,7 +108,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
/* TODO: generalize this */
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
/* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE];
@ -164,8 +166,11 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
char match[MAX_STR_SIZE];
get_str_match(self, match, matches, n_matches);
size_t match_len = strlen(match);
size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches);
if (match_len == 0) {
return 0;
}
if (dir_search) {
if (n_matches == 1)
@ -187,8 +192,9 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
char tmpend[MAX_STR_SIZE];
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf))
if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) {
return -1;
}
strcpy(&ubuf[strt], match);
strcpy(&ubuf[strt + match_len], endchrs);
@ -197,8 +203,9 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
/* convert to widechar and copy back to original buf */
wchar_t newbuf[MAX_STR_SIZE];
if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1)
if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) {
return -1;
}
wcscpy(ctx->line, newbuf);
@ -282,7 +289,7 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
if (strncmp(entry->d_name, b_name, b_name_len) == 0
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
++dircount;
}

View File

@ -55,6 +55,7 @@ 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;
@ -150,6 +151,7 @@ void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL co
} else if (ft->state == FILE_TRANSFER_PAUSED) {
ft->state = FILE_TRANSFER_STARTED;
}
break;
case TOX_FILE_CONTROL_PAUSE:

586
src/bootstrap.c Normal file
View File

@ -0,0 +1,586 @@
/* bootstrap.c
*
*
* Copyright (C) 2016 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 <stdbool.h>
#include <limits.h>
#include <curl/curl.h>
#include <tox/tox.h>
#include "line_info.h"
#include "windows.h"
#include "misc_tools.h"
#include "configdir.h"
#include "curl_util.h"
#include "settings.h"
extern struct arg_opts arg_opts;
extern struct user_settings *user_settings;
extern struct Winthread Winthread;
/* URL that we get the JSON encoded nodes list from. */
#define NODES_LIST_URL "https://nodes.tox.chat/json"
#define DEFAULT_NODES_FILENAME "DHTnodes.json"
/* Time to wait between bootstrap attempts */
#define TRY_BOOTSTRAP_INTERVAL 5
/* Number of nodes to bootstrap to per try */
#define NUM_BOOTSTRAP_NODES 5
/* Number of seconds since last successful ping before we consider a node offline */
#define NODE_OFFLINE_TIMOUT (60*60*24*2)
#define IP_MAX_SIZE 45
#define IP_MIN_SIZE 7
#define PORT_MAX_SIZE 5
#define LAST_SCAN_JSON_KEY "\"last_scan\":"
#define LAST_SCAN_JSON_KEY_LEN (sizeof(LAST_SCAN_JSON_KEY) - 1)
#define IPV4_JSON_KEY "\"ipv4\":\""
#define IPV4_JSON_KEY_LEN (sizeof(IPV4_JSON_KEY) - 1)
#define IPV6_JSON_KEY "\"ipv6\":\""
#define IPV6_JSON_KEY_LEN (sizeof(IPV6_JSON_KEY) - 1)
#define PORT_JSON_KEY "\"port\":"
#define PORT_JSON_KEY_LEN (sizeof(PORT_JSON_KEY) - 1)
#define PK_JSON_KEY "\"public_key\":\""
#define PK_JSON_KEY_LEN (sizeof(PK_JSON_KEY) - 1)
#define LAST_PING_JSON_KEY "\"last_ping\":"
#define LAST_PING_JSON_KEY_LEN (sizeof(LAST_PING_JSON_KEY) - 1)
/* Maximum allowable size of the nodes list */
#define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE)
static struct Thread_Data {
pthread_t tid;
pthread_attr_t attr;
pthread_mutex_t lock;
volatile bool active;
} thread_data;
#define MAX_NODES 50
struct Node {
char ip4[IP_MAX_SIZE + 1];
bool have_ip4;
char ip6[IP_MAX_SIZE + 1];
bool have_ip6;
char key[TOX_PUBLIC_KEY_SIZE];
uint16_t port;
};
static struct DHT_Nodes {
struct Node list[MAX_NODES];
size_t count;
time_t last_updated;
} Nodes;
/* Determine if a node is offline by comparing the age of the nodeslist
* to the last time the node was successfully pinged.
*/
#define NODE_IS_OFFLINE(last_scan, last_ping) ((last_ping + NODE_OFFLINE_TIMOUT) <= (last_ping))
/* Return true if nodeslist pointed to by fp needs to be updated.
* This will be the case if the file is empty, has an invalid format,
* or if the file is older than the given timeout.
*/
static bool nodeslist_needs_update(const char *nodes_path)
{
if (user_settings->nodeslist_update_freq <= 0) {
return false;
}
FILE *fp = fopen(nodes_path, "r+");
if (fp == NULL) {
return false;
}
/* last_scan value should be at beginning of file */
char line[LAST_SCAN_JSON_KEY_LEN + 32];
if (fgets(line, sizeof(line), fp) == NULL) {
fclose(fp);
return true;
}
fclose(fp);
const char *last_scan_val = strstr(line, LAST_SCAN_JSON_KEY);
if (last_scan_val == NULL) {
return true;
}
long long int last_scan = strtoll(last_scan_val + LAST_SCAN_JSON_KEY_LEN, NULL, 10);
pthread_mutex_lock(&thread_data.lock);
Nodes.last_updated = last_scan;
pthread_mutex_unlock(&thread_data.lock);
pthread_mutex_lock(&Winthread.lock);
bool is_timeout = timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60);
pthread_mutex_unlock(&Winthread.lock);
if (is_timeout) {
return true;
}
return false;
}
/* Fetches the JSON encoded DHT nodeslist from NODES_LIST_URL.
*
* Return 0 on success.
* Return -1 on failure.
*/
static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data)
{
CURL *c_handle = curl_easy_init();
if (c_handle == NULL) {
return -1;
}
int err = -1;
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, NODES_LIST_URL);
curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data);
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data);
curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
curl_easy_setopt(c_handle, CURLOPT_HTTPGET, 1L);
int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);
if (proxy_ret != 0) {
fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret);
goto on_exit;
}
int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
if (ret != CURLE_OK) {
fprintf(stderr, "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) {
fprintf(stderr, "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) {
fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret);
goto on_exit;
}
}
err = 0;
on_exit:
curl_slist_free_all(headers);
curl_easy_cleanup(c_handle);
return err;
}
/* Attempts to update the DHT nodeslist.
*
* Return 1 if list was updated successfully.
* Return 0 if list does not need to be updated.
* Return -1 if file cannot be opened.
* Return -2 if http lookup failed.
* Return -3 if http reponse was empty.
* Return -4 if data could not be written to disk.
*/
static int update_DHT_nodeslist(const char *nodes_path)
{
if (!nodeslist_needs_update(nodes_path)) {
return 0;
}
FILE *fp = fopen(nodes_path, "r+");
if (fp == NULL) {
return -1;
}
struct Recv_Curl_Data recv_data;
memset(&recv_data, 0, sizeof(struct Recv_Curl_Data));
if (curl_fetch_nodes_JSON(&recv_data) == -1) {
fclose(fp);
return -2;
}
if (recv_data.length == 0) {
fclose(fp);
return -3;
}
if (fwrite(recv_data.data, recv_data.length, 1, fp) != 1) {
fclose(fp);
return -4;
}
fclose(fp);
return 1;
}
static void get_nodeslist_path(char *buf, size_t buf_size)
{
char *config_dir = NULL;
if (arg_opts.nodes_path[0]) {
snprintf(buf, buf_size, "%s", arg_opts.nodes_path);
} else if ((config_dir = get_user_config_dir()) != NULL) {
snprintf(buf, buf_size, "%s%s%s", config_dir, CONFIGDIR, DEFAULT_NODES_FILENAME);
free(config_dir);
} else {
snprintf(buf, buf_size, "%s", DEFAULT_NODES_FILENAME);
}
}
/* Return true if json encoded string s contains a valid IP address and puts address in ip_buf.
*
* ip_type should be set to 1 for ipv4 address, or 0 for ipv6 addresses.
* ip_buf must have room for at least IP_MAX_SIZE + 1 bytes.
*/
static bool extract_val_ip(const char *s, char *ip_buf, unsigned short int ip_type)
{
int ip_len = char_find(0, s, '"');
if (ip_len < IP_MIN_SIZE || ip_len > IP_MAX_SIZE) {
return false;
}
memcpy(ip_buf, s, ip_len);
ip_buf[ip_len] = 0;
return (ip_type == 1) ? is_ip4_address(ip_buf) : is_ip6_address(ip_buf);
}
/* Extracts the port from json encoded string s.
*
* Return port number on success.
* Return 0 on failure.
*/
static uint16_t extract_val_port(const char *s)
{
long int port = strtol(s, NULL, 10);
return (port > 0 && port <= MAX_PORT_RANGE) ? port : 0;
}
/* Extracts the last pinged value from json encoded string s.
*
* Return timestamp on success.
* Return -1 on failure.
*/
static long long int extract_val_last_pinged(const char *s)
{
long long int last_pinged = strtoll(s, NULL, 10);
return (last_pinged <= 0) ? -1 : last_pinged;
}
/* Extracts DHT public key from json encoded string s and puts key in key_buf.
* key_buf must have room for at least TOX_PUBLIC_KEY_SIZE * 2 + 1 bytes.
*
* Return number of bytes copied to key_buf on success.
* Return -1 on failure.
*/
static int extract_val_pk(const char *s, char *key_buf)
{
int key_len = char_find(0, s, '"');
if (key_len != TOX_PUBLIC_KEY_SIZE * 2) {
return -1;
}
memcpy(key_buf, s, key_len);
key_buf[key_len] = 0;
return key_len;
}
/* Extracts values from json formatted string, validats them, and puts them in node.
*
* Return 0 on success.
* Return -1 if line is empty.
* Return -2 if line does not appear to be a valid nodes list entry.
* Return -3 if node appears to be offline.
* Return -4 if entry does not contain either a valid ipv4 or ipv6 address.
* Return -5 if port value is invalid.
* Return -6 if public key is invalid.
*/
static int extract_node(const char *line, struct Node *node)
{
if (!line) {
return -1;
}
const char *ip4_start = strstr(line, IPV4_JSON_KEY);
const char *ip6_start = strstr(line, IPV6_JSON_KEY);
const char *port_start = strstr(line, PORT_JSON_KEY);
const char *key_start = strstr(line, PK_JSON_KEY);
const char *last_pinged_str = strstr(line, LAST_PING_JSON_KEY);
if (!ip4_start || !ip6_start || !port_start || !key_start || !last_pinged_str) {
return -2;
}
long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN);
if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) {
return -3;
}
char ip4_string[IP_MAX_SIZE + 1];
bool have_ip4 = extract_val_ip(ip4_start + IPV4_JSON_KEY_LEN, ip4_string, 1);
char ip6_string[IP_MAX_SIZE + 1];
bool have_ip6 = extract_val_ip(ip6_start + IPV6_JSON_KEY_LEN, ip6_string, 0);
if (!have_ip6 && !have_ip4) {
return -4;
}
uint16_t port = extract_val_port(port_start + PORT_JSON_KEY_LEN);
if (port == 0) {
return -5;
}
char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1];
int key_len = extract_val_pk(key_start + PK_JSON_KEY_LEN, key_string);
if (key_len == -1) {
return -6;
}
if (hex_string_to_bin(key_string, key_len, node->key, TOX_PUBLIC_KEY_SIZE) == -1) {
return -6;
}
if (have_ip4) {
snprintf(node->ip4, sizeof(node->ip4), "%s", ip4_string);
node->have_ip4 = true;
}
if (have_ip6) {
snprintf(node->ip6, sizeof(node->ip6), "%s", ip6_string);
node->have_ip6 = true;
}
node->port = port;
return 0;
}
/* Loads the DHT nodeslist to memory from json encoded nodes file. */
void *load_nodeslist_thread(void *data)
{
char nodes_path[PATH_MAX];
get_nodeslist_path(nodes_path, sizeof(nodes_path));
FILE *fp = NULL;
if (!file_exists(nodes_path)) {
if ((fp = fopen(nodes_path, "w+")) == NULL) {
fprintf(stderr, "nodeslist load error: failed to create file '%s'\n", nodes_path);
goto on_exit;
}
} else if ((fp = fopen(nodes_path, "r+")) == NULL) {
fprintf(stderr, "nodeslist load error: failed to open file '%s'\n", nodes_path);
goto on_exit;
}
int update_err = update_DHT_nodeslist(nodes_path);
if (update_err < 0) {
fprintf(stderr, "update_DHT_nodeslist() failed with error %d\n", update_err);
}
char line[MAX_NODELIST_SIZE + 1];
if (fgets(line, sizeof(line), fp) == NULL) {
fclose(fp);
fprintf(stderr, "nodeslist load error: file empty.\n");
goto on_exit;
}
size_t idx = 0;
const char *line_start = line;
while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY))) {
pthread_mutex_lock(&thread_data.lock);
idx = Nodes.count;
if (idx >= MAX_NODES) {
pthread_mutex_unlock(&thread_data.lock);
break;
}
if (extract_node(line_start, &Nodes.list[idx]) == 0) {
++Nodes.count;
}
pthread_mutex_unlock(&thread_data.lock);
}
/* If nodeslist does not contain any valid entries we set the last_scan value
* to 0 so that it will fetch a new list the next time this function is called.
*/
if (idx == 0) {
const char *s = "{\"last_scan\":0}";
rewind(fp);
fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails
fclose(fp);
fprintf(stderr, "nodeslist load error: List did not contain any valid entries.\n");
goto on_exit;
}
fclose(fp);
on_exit:
thread_data.active = false;
pthread_attr_destroy(&thread_data.attr);
pthread_exit(0);
}
/* Creates a new thread that will load the DHT nodeslist to memory
* from json encoded nodes file obtained at NODES_LIST_URL. Only one
* thread may run at a time.
*
* Return 0 on success.
* Return -1 if a thread is already active.
* Return -2 if mutex fails to init.
* Return -3 if pthread attribute fails to init.
* Return -4 if pthread fails to set detached state.
* Return -5 if thread creation fails.
*/
int load_DHT_nodeslist(void)
{
if (thread_data.active) {
return -1;
}
if (pthread_mutex_init(&thread_data.lock, NULL) != 0) {
return -2;
}
if (pthread_attr_init(&thread_data.attr) != 0) {
return -3;
}
if (pthread_attr_setdetachstate(&thread_data.attr, PTHREAD_CREATE_DETACHED) != 0) {
return -4;
}
thread_data.active = true;
if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) {
thread_data.active = false;
return -5;
}
return 0;
}
/* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */
static void DHT_bootstrap(Tox *m)
{
pthread_mutex_lock(&thread_data.lock);
size_t num_nodes = Nodes.count;
pthread_mutex_unlock(&thread_data.lock);
if (num_nodes == 0) {
return;
}
size_t i;
pthread_mutex_lock(&thread_data.lock);
for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) {
struct Node *node = &Nodes.list[rand() % Nodes.count];
const char *addr = node->have_ip4 ? node->ip4 : node->ip6;
if (!addr) {
continue;
}
TOX_ERR_BOOTSTRAP err;
tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err);
if (err != TOX_ERR_BOOTSTRAP_OK) {
fprintf(stderr, "Failed to bootstrap %s:%d\n", addr, node->port);
}
tox_add_tcp_relay(m, addr, node->port, (uint8_t *) node->key, &err);
if (err != TOX_ERR_BOOTSTRAP_OK) {
fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port);
}
}
pthread_mutex_unlock(&thread_data.lock);
}
/* Manages connection to the Tox DHT network. */
void do_tox_connection(Tox *m)
{
static time_t last_bootstrap_time = 0;
bool connected = tox_self_get_connection_status(m) != TOX_CONNECTION_NONE;
if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) {
DHT_bootstrap(m);
last_bootstrap_time = get_unix_time();
}
}

42
src/bootstrap.h Normal file
View File

@ -0,0 +1,42 @@
/* bootstrap.h
*
*
* Copyright (C) 2016 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 BOOTSTRAP_H
#define BOOTSTRAP_H
/* Manages connection to the Tox DHT network. */
void do_tox_connection(Tox *m);
/* Creates a new thread that will load the DHT nodeslist to memory
* from json encoded nodes file obtained at NODES_LIST_URL. Only one
* thread may run at a time.
*
* Return 0 on success.
* Return -1 if a thread is already active.
* Return -2 if mutex fails to init.
* Return -3 if pthread attribute fails to init.
* Return -4 if pthread fails to set detached state.
* Return -5 if thread creation fails.
*/
int load_DHT_nodeslist(void);
#endif /* BOOTSTRAP_H */

View File

@ -48,9 +48,9 @@
#include "message_queue.h"
#ifdef AUDIO
#include "audio_call.h"
#include "audio_call.h"
#ifdef VIDEO
#include "video_call.h"
#include "video_call.h"
#endif /* VIDEO */
#endif /* AUDIO */
@ -66,9 +66,9 @@ static void kill_infobox(ToxWindow *self);
#endif /* AUDIO */
#ifdef AUDIO
#define AC_NUM_CHAT_COMMANDS 30
#define AC_NUM_CHAT_COMMANDS 31
#else
#define AC_NUM_CHAT_COMMANDS 22
#define AC_NUM_CHAT_COMMANDS 23
#endif /* AUDIO */
/* Array of chat command names used for tab completion. */
@ -81,6 +81,7 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/close" },
{ "/connect" },
{ "/exit" },
{ "/gaccept" },
{ "/group" },
{ "/help" },
{ "/invite" },
@ -126,11 +127,6 @@ void kill_chat_window(ToxWindow *self, Tox *m)
ChatContext *ctx = self->chatwin;
StatusBar *statusbar = self->stb;
kill_all_file_transfers_friend(m, self->num);
log_disable(ctx->log);
line_info_cleanup(ctx->hst);
cqueue_cleanup(ctx->cqueue);
#ifdef AUDIO
#ifdef VIDEO
stop_video_stream(self);
@ -138,6 +134,11 @@ void kill_chat_window(ToxWindow *self, Tox *m)
stop_current_call(self);
#endif /* AUDIO */
kill_all_file_transfers_friend(m, self->num);
log_disable(ctx->log);
line_info_cleanup(ctx->hst);
cqueue_cleanup(ctx->cqueue);
delwin(ctx->linewin);
delwin(ctx->history);
delwin(statusbar->topline);
@ -152,7 +153,7 @@ void kill_chat_window(ToxWindow *self, Tox *m)
}
static void recv_message_helper(ToxWindow *self, Tox *m, uint32_t num, const char *msg, size_t len,
const char *nick, const char *timefrmt)
const char *nick, const char *timefrmt)
{
ChatContext *ctx = self->chatwin;
@ -160,9 +161,11 @@ static void recv_message_helper(ToxWindow *self, Tox *m, uint32_t num, const cha
write_to_log(msg, nick, ctx->log, false);
if (self->active_box != -1)
box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, self->active_box, "%s", msg);
box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message,
self->active_box, "%s", msg);
else
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, &self->active_box, nick, "%s", msg);
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message,
&self->active_box, nick, "%s", msg);
}
static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char *action, size_t len,
@ -174,9 +177,11 @@ static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char
write_to_log(action, nick, ctx->log, true);
if (self->active_box != -1)
box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message,
self->active_box, "* %s %s", nick, action );
else
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action );
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message,
&self->active_box, self->name, "* %s %s", nick, action );
}
static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TYPE type, const char *msg, size_t len)
@ -215,7 +220,14 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num);
if (connection_status != TOX_CONNECTION_NONE && statusbar->connection == TOX_CONNECTION_NONE) {
TOX_CONNECTION prev_status = statusbar->connection;
statusbar->connection = connection_status;
if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) {
return;
}
if (prev_status == TOX_CONNECTION_NONE) {
Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON
? tox_friend_get_typing(m, num, NULL) : false;
chat_resume_file_senders(self, m, num);
@ -235,8 +247,6 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
write_to_log(msg, nick, ctx->log, true);
}
statusbar->connection = connection_status;
}
static void chat_onTypingChange(ToxWindow *self, Tox *m, uint32_t num, bool is_typing)
@ -445,11 +455,11 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
if (ft->state == FILE_TRANSFER_PENDING) {
ft->state = FILE_TRANSFER_STARTED;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%d] for '%s' accepted.",
ft->index, ft->file_name);
ft->index, ft->file_name);
char progline[MAX_STR_SIZE];
init_progress_bar(progline);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
sound_notify(self, silent, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2, NULL);
sound_notify(self, silent, NT_NOFOCUS | user_settings->bell_on_filetrans_accept | NT_WNDALERT_2, NULL);
ft->line_id = self->chatwin->hst->line_end->id + 2;
} else if (ft->state == FILE_TRANSFER_PAUSED) { /* transfer is resumed */
ft->state = FILE_TRANSFER_STARTED;
@ -457,10 +467,12 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
break;
}
case TOX_FILE_CONTROL_PAUSE: {
ft->state = FILE_TRANSFER_PAUSED;
break;
}
case TOX_FILE_CONTROL_CANCEL: {
snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name);
close_file_transfer(self, m, ft, -1, msg, notif_error);
@ -591,34 +603,27 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
tox_file_get_file_id(m, friendnum, filenum, ft->file_id, NULL);
if (self->active_box != -1)
box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box,
"Incoming file: %s", filename );
box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans,
self->active_box, "Incoming file: %s", filename );
else
box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, &self->active_box, self->name,
"Incoming file: %s", filename );
box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans,
&self->active_box, self->name, "Incoming file: %s", filename );
}
static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, uint8_t type, const char *group_pub_key,
uint16_t length)
static void chat_onGroupInvite(ToxWindow *self, Tox *m, uint32_t friendnumber, const char *invite_data,
size_t length)
{
if (self->num != friendnumber)
return;
if (Friends.list[friendnumber].group_invite.key != NULL)
free(Friends.list[friendnumber].group_invite.key);
if (Friends.list[friendnumber].group_invite.data)
free(Friends.list[friendnumber].group_invite.data);
char *k = malloc(length);
if (k == NULL)
exit_toxic_err("Failed in chat_onGroupInvite", FATALERR_MEMORY);
memcpy(k, group_pub_key, length);
Friends.list[friendnumber].group_invite.key = k;
Friends.list[friendnumber].group_invite.pending = true;
Friends.list[friendnumber].group_invite.data = malloc(length * sizeof(uint8_t));
memcpy(Friends.list[friendnumber].group_invite.data, invite_data, length);
Friends.list[friendnumber].group_invite.length = length;
Friends.list[friendnumber].group_invite.type = type;
sound_notify(self, generic_message, NT_WNDALERT_2, NULL);
sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, NULL);
char name[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, name, friendnumber);
@ -629,7 +634,7 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, ui
box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join group chat");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a group chat.", name);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/gaccept\" to join the chat.");
}
/* AV Stuff */
@ -646,7 +651,7 @@ void chat_onInvite (ToxWindow *self, ToxAV *av, uint32_t friend_number, int stat
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\"");
if (self->ringing_sound == -1)
sound_notify(self, call_incoming, NT_LOOP, &self->ringing_sound);
sound_notify(self, call_incoming, NT_LOOP | user_settings->bell_on_invite, &self->ringing_sound);
if (self->active_box != -1)
box_silent_notify2(self, NT_NOFOCUS | NT_WNDALERT_0, self->active_box, "Incoming audio call!");
@ -662,8 +667,10 @@ void chat_onRinging (ToxWindow *self, ToxAV *av, uint32_t friend_number, int sta
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it.");
#ifdef SOUND_NOTIFY
if (self->ringing_sound == -1)
sound_notify(self, call_outgoing, NT_LOOP, &self->ringing_sound);
#endif /* SOUND_NOTIFY */
}
@ -770,33 +777,6 @@ void chat_onEnd (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
#endif /* SOUND_NOTIFY */
}
void chat_onRequestTimeout (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
return;
self->is_call = false;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No answer!");
#ifdef SOUND_NOTIFY
stop_sound(self->ringing_sound);
#endif /* SOUND_NOTIFY */
}
void chat_onPeerTimeout (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
return;
self->is_call = false;
kill_infobox(self);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer disconnected; call ended!");
#ifdef SOUND_NOTIFY
stop_sound(self->ringing_sound);
#endif /* SOUND_NOTIFY */
}
static void init_infobox(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
@ -843,7 +823,7 @@ static void draw_infobox(ToxWindow *self)
if (x2 < INFOBOX_WIDTH || y2 < INFOBOX_HEIGHT)
return;
uint64_t curtime = get_unix_time();
time_t curtime = get_unix_time();
/* update elapsed time string once per second */
if (curtime > infobox->lastupdate)
@ -880,7 +860,7 @@ static void draw_infobox(ToxWindow *self)
wprintw(infobox->win, "%.2f\n", infobox->vad_lvl);
wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_TTEE, ' ', ACS_LLCORNER, ' ');
wrefresh(infobox->win);
wnoutrefresh(infobox->win);
}
#endif /* AUDIO */
@ -905,7 +885,6 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{
ChatContext *ctx = self->chatwin;
StatusBar *statusbar = self->stb;
@ -916,12 +895,15 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (y2 <= 0 || x2 <= 0)
return;
if (ctx->pastemode && key == '\r')
key = '\n';
if (self->help->active) {
help_onKey(self, key);
return;
}
if (ltr) { /* char is printable */
if (ltr || key == '\n') { /* char is printable */
input_new_char(self, key, x, y, x2, y2);
if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE)
@ -943,11 +925,11 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
diff = dir_match(self, m, ctx->line, L"/sendfile");
} else 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){
} else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
const char status_cmd_list[3][8] = {
{"online"},
{"away"},
{"busy"},
{"online"},
{"away"},
{"busy"},
};
diff = complete_line(self, status_cmd_list, 3, 8);
} else {
@ -963,38 +945,41 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
sound_notify(self, notif_error, 0, NULL);
}
} else if (key == '\n') {
} else if (key == '\r') {
rm_trailing_spaces_buf(ctx);
char line[MAX_STR_SIZE];
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
if (!string_is_empty(line))
if (!wstring_is_empty(ctx->line)) {
add_line_to_hist(ctx);
if (line[0] == '/') {
if (strcmp(line, "/close") == 0) {
kill_chat_window(self, m);
return;
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_action(self, ctx, m, line + strlen("/me "));
wstrsubst(ctx->line, L'', L'\n');
char line[MAX_STR_SIZE] = {0};
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
if (line[0] == '/') {
if (strcmp(line, "/close") == 0) {
kill_chat_window(self, m);
return;
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_action(self, ctx, m, line + strlen("/me "));
} else {
execute(ctx->history, self, m, line, CHAT_COMMAND_MODE);
}
} else {
execute(ctx->history, self, m, line, CHAT_COMMAND_MODE);
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';
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1);
}
} else if (!string_is_empty(line)) {
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';
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1);
}
wclear(ctx->linewin);
@ -1041,9 +1026,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
case TOX_USER_STATUS_NONE:
colour = GREEN;
break;
case TOX_USER_STATUS_AWAY:
colour = YELLOW;
break;
case TOX_USER_STATUS_BUSY:
colour = RED;
break;
@ -1116,13 +1103,14 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
wmove(self->window, y + 1, new_x);
wrefresh(self->window);
wnoutrefresh(self->window);
#ifdef AUDIO
if (ctx->infobox.active) {
draw_infobox(self);
wrefresh(self->window);
}
#endif
if (self->help->active)
@ -1212,7 +1200,6 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum)
ret.onMessage = &chat_onMessage;
ret.onConnectionChange = &chat_onConnectionChange;
ret.onTypingChange = & chat_onTypingChange;
ret.onGroupInvite = &chat_onGroupInvite;
ret.onNickChange = &chat_onNickChange;
ret.onStatusChange = &chat_onStatusChange;
ret.onStatusMessageChange = &chat_onStatusMessageChange;
@ -1221,6 +1208,7 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum)
ret.onFileControl = &chat_onFileControl;
ret.onFileRecv = &chat_onFileRecv;
ret.onReadReceipt = &chat_onReadReceipt;
ret.onGroupInvite = &chat_onGroupInvite;
#ifdef AUDIO
ret.onInvite = &chat_onInvite;

View File

@ -78,6 +78,47 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
}
void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
if (Friends.list[self->num].group_invite.length == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group invite");
return;
}
const char *passwd = NULL;
uint16_t passwd_len = 0;
if (argc > 0) {
passwd = argv[1];
passwd_len = strlen(passwd);
}
TOX_ERR_GROUP_INVITE_ACCEPT err;
uint32_t groupnumber = tox_group_invite_accept(m, Friends.list[self->num].group_invite.data,
Friends.list[self->num].group_invite.length,
(uint8_t *) passwd, passwd_len, &err);
if (err != TOX_ERR_GROUP_INVITE_ACCEPT_OK) {
if (err == TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to joing group: Password too long.");
else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to joing group (error %d).", err);
return;
}
if (init_groupchat_win(m, groupnumber, NULL, 0) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_group_leave(m, groupnumber, NULL, 0, NULL);
return;
}
}
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
@ -85,60 +126,24 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
return;
}
long int groupnum = strtol(argv[1], NULL, 10);
int groupnum = atoi(argv[1]);
if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == LONG_MAX) {
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
return;
}
if (tox_invite_friend(m, self->num, groupnum) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group.");
TOX_ERR_GROUP_INVITE_FRIEND err;
if (!tox_group_invite_friend(m, groupnum, self->num, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d).", err);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum);
}
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
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].group_invite.pending) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
return;
}
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, 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])
{
if (argc < 1) {
@ -190,6 +195,7 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
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.");
@ -258,7 +264,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
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);
(uint8_t *) file_name, namelen, &err);
if (err != TOX_ERR_FILE_SEND_OK)
goto on_send_error;
@ -282,6 +288,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
return;
on_send_error:
switch (err) {
case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND:
errmsg = "File transfer failed: Invalid friend.";

View File

@ -26,9 +26,9 @@
#include "windows.h"
#include "toxic.h"
void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);

View File

@ -33,7 +33,7 @@
#include "configdir.h"
#include "misc_tools.h"
/* get the user's home directory */
/* get the user's home directory. */
void get_home_dir(char *home, int size)
{
struct passwd pwd;
@ -99,16 +99,13 @@ char *get_user_config_dir(void)
# endif /* __APPLE__ */
if (!file_exists(user_config_dir)) {
free(user_config_dir);
return NULL;
}
return user_config_dir;
}
/*
* Creates the config and chatlog directories.
/* Creates the config and chatlog directories.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int create_user_config_dirs(char *path)
{

View File

@ -34,8 +34,23 @@
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
/**
* @brief Get the user's config directory.
*
* This is without a trailing slash. Resulting string must be freed.
*
* @return The users config dir or NULL on error.
*/
char *get_user_config_dir(void);
/* get the user's home directory. */
void get_home_dir(char *home, int size);
/* Creates the config and chatlog directories.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int create_user_config_dirs(char *path);
#endif /* #define CONFIGDIR_H */

93
src/curl_util.c Normal file
View File

@ -0,0 +1,93 @@
/* curl_util.c
*
*
* Copyright (C) 2016 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 <stdint.h>
#include <string.h>
#include <curl/curl.h>
#include <tox/tox.h>
#include "curl_util.h"
/* Sets proxy info for given CURL handler.
*
* Returns 0 on success or if no proxy is set by the client.
* Returns -1 if proxy info is invalid.
* Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
*/
int set_curl_proxy(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) {
return -1;
}
int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
if (ret != CURLE_OK) {
return ret;
}
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) {
return ret;
}
ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
if (ret != CURLE_OK) {
return ret;
}
return 0;
}
/* Callback function for CURL to write received data.
*
* This function will append data from an http request to the data buffer
* until the request is complete or the buffer is full. Buffer will be null terminated.
*
* Returns number of bytes received from http request on success (don't change this).
* Returns 0 if data exceeds buffer size.
*/
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer)
{
struct Recv_Curl_Data *recv_data = (struct Recv_Curl_Data *) user_pointer;
size_t length = size * nmemb;
size_t total_size = length + recv_data->length;
if (total_size > MAX_RECV_CURL_DATA_SIZE) {
return 0;
}
memcpy(recv_data->data + recv_data->length, data, length);
recv_data->data[total_size] = '\0';
recv_data->length += length;
return length;
}

55
src/curl_util.h Normal file
View File

@ -0,0 +1,55 @@
/* curl_util.h
*
*
* Copyright (C) 2016 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 CURL_UTIL_H
#define CURL_UTIL_H
/* 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"
/* Max size of an http response that we can store in Recv_Data */
#define MAX_RECV_CURL_DATA_SIZE 32767
/* Holds data received from curl lookup */
struct Recv_Curl_Data {
char data[MAX_RECV_CURL_DATA_SIZE + 1]; /* Data received from curl write data callback */
size_t length; /* Total number of bytes written to data buffer (doesn't include null) */
};
/* Sets proxy info for given CURL handler.
*
* Returns 0 on success or if no proxy is set by the client.
* Returns -1 if proxy info is invalid.
* Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
*/
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type);
/* Callback function for CURL to write received data.
*
* This function will append data from an http request to the data buffer
* until the request is complete or the buffer is full. Buffer will be null terminated.
*
* Returns size of bytes written to the data buffer.
*/
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer);
#endif /* CURL_UTIL_H */

View File

@ -34,6 +34,8 @@
#include "misc_tools.h"
#include "notify.h"
#define MAX_NUM_ARGS 10 /* Includes command */
struct cmd_func {
const char *name;
void (*func)(WINDOW *w, ToxWindow *, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
@ -49,6 +51,7 @@ static struct cmd_func global_commands[] = {
{ "/exit", cmd_quit },
{ "/group", cmd_groupchat },
{ "/help", cmd_prompt_help },
{ "/join", cmd_join },
{ "/log", cmd_log },
{ "/myid", cmd_myid },
{ "/myqr", cmd_myqr },
@ -72,8 +75,8 @@ static struct cmd_func global_commands[] = {
static struct cmd_func chat_commands[] = {
{ "/cancel", cmd_cancelfile },
{ "/gaccept", cmd_groupaccept },
{ "/invite", cmd_groupinvite },
{ "/join", cmd_join_group },
{ "/savefile", cmd_savefile },
{ "/sendfile", cmd_sendfile },
#ifdef AUDIO
@ -91,19 +94,99 @@ static struct cmd_func chat_commands[] = {
};
static struct cmd_func group_commands[] = {
{ "/title", cmd_set_title },
{ "/ban", cmd_ban },
{ "/chatid", cmd_chatid },
{ "/ignore", cmd_ignore },
{ "/kick", cmd_kick },
{ "/mod", cmd_mod },
{ "/mykey", cmd_mykey },
{ "/passwd", cmd_set_passwd },
{ "/peerlimit", cmd_set_peerlimit },
{ "/privacy", cmd_set_privacy },
{ "/rejoin", cmd_rejoin },
{ "/silence", cmd_silence },
{ "/topic", cmd_set_topic },
{ "/unban", cmd_unban },
{ "/unignore", cmd_unignore },
{ "/unmod", cmd_unmod },
{ "/unsilence", cmd_unsilence },
{ "/whois", cmd_whois },
#ifdef AUDIO
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
#endif /* AUDIO */
{ NULL, NULL },
{ NULL, NULL },
};
/* Parses input command and puts args into arg array.
Returns number of arguments on success, -1 on failure. */
#define NUM_SPECIAL_COMMANDS 15
static const char special_commands[NUM_SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = {
"/ban",
"/gaccept",
"/group",
"/ignore",
"/kick",
"/mod",
"/nick",
"/note",
"/passwd",
"/silence",
"/topic",
"/unignore",
"/unmod",
"/unsilence",
"/whois",
};
/* return true if input command is in the special_commands array. False otherwise.*/
static bool is_special_command(const char *input)
{
int s = char_find(0, input, ' ');
if (s == strlen(input))
return false;
int i;
for (i = 0; i < NUM_SPECIAL_COMMANDS; ++i) {
if (strncmp(input, special_commands[i], s) == 0)
return true;
}
return false;
}
/* Parses commands in the special_commands array which take exactly one argument that may contain spaces.
* Unlike parse_command, this function does not split the input string at spaces.
*
* Returns number of arguments on success
* Returns -1 on failure
*/
static int parse_special_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
{
int len = strlen(input);
int s = char_find(0, input, ' ');
if (s + 1 >= len)
return -1;
memcpy(args[0], input, s);
args[0][s++] = '\0'; /* increment to remove space after /command */
memcpy(args[1], input + s, len - s);
args[1][len - s] = '\0';
return 2;
}
/* Parses input command and puts args (split by spaces) into args array.
*
* Returns number of arguments on success
* Returns -1 on failure.
*/
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
{
if (is_special_command(input))
return parse_special_command(w, self, input, args);
char *cmd = strdup(input);
if (cmd == NULL)
@ -141,11 +224,19 @@ static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*a
strcpy(cmd, tmp); /* tmp will always fit inside cmd */
}
/* Ugly special case concatinates all args after arg1 for multi-word group passwords */
if (num_args > 2 && strcmp(args[0], "/join") == 0)
strcpy(args[2], input + strlen(args[0]) + 1 + strlen(args[1]) + 1);
free(cmd);
return num_args;
}
/* Matches command to respective function. Returns 0 on match, 1 on no match */
/* Matches command to respective function.
*
* Returns 0 on match,
* Returns -1 on no match
*/
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
char (*args)[MAX_STR_SIZE])
{
@ -158,7 +249,7 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct c
}
}
return 1;
return -1;
}
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
@ -186,6 +277,7 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
case GROUPCHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, group_commands, args) == 0)
return;
break;
}

View File

@ -26,13 +26,11 @@
#include "toxic.h"
#include "windows.h"
#define MAX_NUM_ARGS 4 /* Includes command */
enum {
GLOBAL_COMMAND_MODE,
CHAT_COMMAND_MODE,
GROUPCHAT_COMMAND_MODE,
};
} COMMAND_MODE;
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);

View File

@ -38,87 +38,48 @@ 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 */
Assumes progline has room for at least MAX_STR_SIZE bytes */
void init_progress_bar(char *progline)
{
strcpy(progline, "0.0 B/s [");
strcpy(progline, "0% [");
int i;
for (i = 0; i < NUM_PROG_MARKS; ++i)
strcat(progline, "-");
strcat(progline, "] 0%");
strcat(progline, "] 0.0 B/s");
}
/* prints a progress bar for file transfers.
if friendnum is -1 we're sending the file, otherwise we're receiving. */
/* prints a progress bar for file transfers. */
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 [");
char pct_str[24];
snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done);
char bps_str[24];
bytes_convert_str(bps_str, sizeof(bps_str), bps);
char prog_line[NUM_PROG_MARKS + 1] = {0};
int n = pct_done / (100 / NUM_PROG_MARKS);
int i, j;
for (i = 0; i < n; ++i)
strcat(msg, "#");
strcat(prog_line, "=");
for (j = i; j < NUM_PROG_MARKS; ++j)
strcat(msg, "-");
if (pct_done < 100)
strcpy(prog_line + n, ">");
strcat(msg, "] ");
for (j = i; j < NUM_PROG_MARKS - 1; ++j)
strcat(prog_line, "-");
char pctstr[16];
const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%";
snprintf(pctstr, sizeof(pctstr), frmt, pct_done);
strcat(msg, pctstr);
char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7];
snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str);
line_info_set(self, line_id, msg);
line_info_set(self, line_id, full_line);
}
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
@ -175,7 +136,7 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
* Returns NULL on failure.
*/
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction)
FILE_TRANSFER_DIRECTION direction)
{
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
return NULL;
@ -184,8 +145,8 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t
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];
&Friends.list[friendnum].file_sender[i] :
&Friends.list[friendnum].file_receiver[i];
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
return ft;

View File

@ -34,7 +34,6 @@
#define GiB 1073741824 /* 1024^3 */
#define MAX_FILES 32
#define TIMEOUT_FILESENDER 120
typedef enum FILE_TRANSFER_STATE {
FILE_TRANSFER_INACTIVE,
@ -62,15 +61,12 @@ struct FileTransfer {
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 */
time_t last_line_progress; /* The last time we updated the progress bar */
time_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);
@ -91,7 +87,7 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
* Returns NULL on failure.
*/
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction);
FILE_TRANSFER_DIRECTION direction);
/* Initializes an unused file transfer and returns its pointer.
* Returns NULL on failure.

View File

@ -113,11 +113,11 @@ static void realloc_blocklist(int n)
void kill_friendlist(void)
{
int i;
size_t i;
for (i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL)
free(Friends.list[i].group_invite.key);
if (Friends.list[i].group_invite.data != NULL)
free(Friends.list[i].group_invite.data);
}
realloc_blocklist(0);
@ -133,17 +133,24 @@ void kill_friendlist(void)
#define TEMP_BLOCKLIST_EXT ".tmp"
static int save_blocklist(char *path)
{
if (path == NULL)
if (path == NULL) {
return -1;
}
int len = sizeof(BlockedFriend) * Blocked.num_blocked;
char data[len];
char *data = malloc(len * sizeof(char));
if (data == NULL) {
return -1;
}
int i, count = 0;
for (i = 0; i < Blocked.max_idx; ++i) {
if (count > Blocked.num_blocked)
if (count > Blocked.num_blocked) {
free(data);
return -1;
}
if (Blocked.list[i].active) {
BlockedFriend tmp;
@ -164,8 +171,11 @@ static int save_blocklist(char *path)
/* Blocklist is empty, we can remove the empty file */
if (count == 0) {
if (remove(path) != 0)
free(data);
if (remove(path) != 0) {
return -1;
}
return 0;
}
@ -175,19 +185,24 @@ static int save_blocklist(char *path)
FILE *fp = fopen(temp_path, "wb");
if (fp == NULL)
if (fp == NULL) {
free(data);
return -1;
}
if (fwrite(data, len, 1, fp) != 1) {
fprintf(stderr, "Failed to write blocklist data.\n");
fclose(fp);
free(data);
return -1;
}
fclose(fp);
free(data);
if (rename(temp_path, path) != 0)
if (rename(temp_path, path) != 0) {
return -1;
}
return 0;
}
@ -299,10 +314,10 @@ static void sort_blocklist_index(void)
qsort(Blocked.index, Blocked.num_blocked, sizeof(uint32_t), index_name_cmp_block);
}
static void update_friend_last_online(uint32_t num, uint64_t timestamp)
static void update_friend_last_online(uint32_t num, time_t timestamp)
{
Friends.list[num].last_online.last_on = timestamp;
Friends.list[num].last_online.tm = *localtime((const time_t*)&timestamp);
Friends.list[num].last_online.tm = *localtime((const time_t *)&timestamp);
/* if the format changes make sure TIME_STR_SIZE is the correct size */
const char *t = user_settings->timestamp_format;
@ -424,7 +439,7 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
fprintf(stderr, "tox_friend_get_public_key failed (error %d)\n", pkerr);
TOX_ERR_FRIEND_GET_LAST_ONLINE loerr;
uint64_t t = tox_friend_get_last_online(m, num, &loerr);
time_t t = tox_friend_get_last_online(m, num, &loerr);
if (loerr != TOX_ERR_FRIEND_GET_LAST_ONLINE_OK)
t = 0;
@ -505,8 +520,7 @@ static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
}
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t type, const char *group_pub_key,
uint16_t length)
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, uint32_t num, const char *data, size_t length)
{
if (num >= Friends.max_idx)
return;
@ -523,7 +537,7 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8
get_nick_truncate(m, nick, num);
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED,
"* Group chat invite from %s failed: too many windows are open.", nick);
"* Group chat invite from %s failed: too many windows are open.", nick);
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
}
@ -545,6 +559,7 @@ static void select_friend(ToxWindow *self, wint_t key, int *selected, int num)
static void delete_friend(Tox *m, uint32_t f_num)
{
TOX_ERR_FRIEND_DELETE err;
if (tox_friend_delete(m, f_num, &err) != true) {
fprintf(stderr, "tox_friend_delete failed with error %d\n", err);
return;
@ -565,9 +580,6 @@ static void delete_friend(Tox *m, uint32_t f_num)
}
}
if (Friends.list[f_num].group_invite.key != NULL)
free(Friends.list[f_num].group_invite.key);
memset(&Friends.list[f_num], 0, sizeof(ToxicFriend));
int i;
@ -630,10 +642,12 @@ static void draw_del_popup(void)
wattron(PendingDelete.popup, A_BOLD);
pthread_mutex_lock(&Winthread.lock);
if (blocklist_view == 0)
wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name);
else
wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name);
pthread_mutex_unlock(&Winthread.lock);
wattroff(PendingDelete.popup, A_BOLD);
@ -757,7 +771,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
return;
switch (key) {
case '\n':
case '\r':
if (blocklist_view)
break;
@ -784,6 +798,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
block_friend(m, f);
else
unblock_friend(m, f);
break;
case KEY_RIGHT:
@ -796,6 +811,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
select_friend(self, key, &Friends.num_selected, Friends.num_friends);
else
select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked);
break;
}
}
@ -895,7 +911,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
return;
}
uint64_t cur_time = time(NULL);
time_t cur_time = get_unix_time();
struct tm cur_loc_tm = *localtime((const time_t *) &cur_time);
wattron(self->window, A_BOLD);
@ -955,9 +971,11 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
case TOX_USER_STATUS_NONE:
colour = GREEN;
break;
case TOX_USER_STATUS_AWAY:
colour = YELLOW;
break;
case TOX_USER_STATUS_BUSY:
colour = RED;
break;
@ -1032,16 +1050,16 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wattroff(self->window, COLOR_PAIR(BLUE));
pthread_mutex_lock(&Winthread.lock);
uint64_t last_seen = Friends.list[f].last_online.last_on;
time_t last_seen = Friends.list[f].last_online.last_on;
pthread_mutex_unlock(&Winthread.lock);
if (last_seen != 0) {
pthread_mutex_lock(&Winthread.lock);
int day_dist = (
cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday
+ ((cur_loc_tm.tm_year - Friends.list[f].last_online.tm.tm_year) * 365)
);
cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday
+ ((cur_loc_tm.tm_year - Friends.list[f].last_online.tm.tm_year) * 365)
);
const char *hourmin = Friends.list[f].last_online.hour_min_str;
pthread_mutex_unlock(&Winthread.lock);
@ -1097,7 +1115,8 @@ void disable_chatwin(uint32_t f_num)
static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
assert(0);
if( friend_number >= Friends.max_idx)
if ( friend_number >= Friends.max_idx)
return;
assert(0);
@ -1105,7 +1124,7 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
if (Friends.list[friend_number].chatwin == -1) {
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
if(state != TOXAV_FRIEND_CALL_STATE_FINISHED) {
if (state != TOXAV_FRIEND_CALL_STATE_FINISHED) {
Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num));
set_active_window(Friends.list[friend_number].chatwin);
}

View File

@ -35,11 +35,9 @@ struct LastOnline {
char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */
};
struct GroupChatInvite {
char *key;
struct GroupInvite {
uint8_t *data;
uint16_t length;
uint8_t type;
bool pending;
};
typedef struct {
@ -57,7 +55,7 @@ typedef struct {
uint8_t status;
struct LastOnline last_online;
struct GroupChatInvite group_invite;
struct GroupInvite group_invite;
struct FileTransfer file_receiver[MAX_FILES];
struct FileTransfer file_sender[MAX_FILES];

View File

@ -36,6 +36,7 @@
#include "avatars.h"
#include "name_lookup.h"
#include "qr_code.h"
#include "toxic_strings.h"
extern char *DATA_FILE;
extern ToxWindow *prompt;
@ -129,6 +130,7 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
break;
case TOX_ERR_FRIEND_ADD_NULL:
/* fallthrough */
default:
errmsg = "Faile to add friend: Unknown error.";
@ -259,6 +261,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
}
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;
@ -280,6 +283,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
case TOX_ERR_BOOTSTRAP_NULL:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
break;
default:
break;
}
@ -325,42 +329,133 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
}
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name required");
return;
}
uint8_t type;
const char *tmp_name = argv[1];
int len = strlen(tmp_name);
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");
if (len == 0 || len > TOX_GROUP_MAX_GROUP_NAME_LENGTH) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group name.");
return;
}
int groupnum = -1;
char name[TOX_GROUP_MAX_GROUP_NAME_LENGTH];
if (type == TOX_GROUPCHAT_TYPE_TEXT)
groupnum = tox_add_groupchat(m);
/*#ifdef AUDIO
else
groupnum = toxav_add_av_groupchat(m, NULL, NULL);
#endif*/
if (argv[1][0] == '\"') { /* remove opening and closing quotes */
snprintf(name, sizeof(name), "%s", &argv[1][1]);
len -= 2;
name[len] = '\0';
} else {
snprintf(name, sizeof(name), "%s", argv[1]);
}
TOX_ERR_GROUP_NEW err;
uint32_t groupnum = tox_group_new(m, TOX_GROUP_PRIVACY_STATE_PUBLIC, (uint8_t *) name, len, &err);
if (err != TOX_ERR_GROUP_NEW_OK) {
switch (err) {
case TOX_ERR_GROUP_NEW_TOO_LONG: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name length cannot exceed %d.",
TOX_GROUP_MAX_GROUP_NAME_LENGTH);
break;
}
case TOX_ERR_GROUP_NEW_EMPTY: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name cannot be empty.");
break;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d).", err);
break;
}
}
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, type) == -1) {
int init = init_groupchat_win(m, groupnum, name, len);
if (init == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
tox_group_leave(m, groupnum, NULL, 0, NULL);
} else if (init == -2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"You have been kicked from a group. Close the window and try again.");
tox_group_leave(m, groupnum, NULL, 0, NULL);
}
}
void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Chat ID is required.");
return;
}
const char *chat_id = argv[1];
if (strlen(chat_id) != TOX_GROUP_CHAT_ID_SIZE * 2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID");
return;
}
char id_bin[TOX_GROUP_CHAT_ID_SIZE] = {0};
size_t i;
char xch[3];
uint32_t x;
for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
xch[0] = chat_id[2 * i];
xch[1] = chat_id[2 * i + 1];
xch[2] = '\0';
if (sscanf(xch, "%02x", &x) != 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID.");
return;
}
id_bin[i] = x;
}
const char *passwd = NULL;
uint16_t passwd_len = 0;
if (argc > 1) {
passwd = argv[2];
passwd_len = strlen(passwd);
}
TOX_ERR_GROUP_JOIN err;
uint32_t groupnum = tox_group_join(m, (uint8_t *) id_bin, (uint8_t *) passwd, passwd_len, &err);
if (err != TOX_ERR_GROUP_JOIN_OK) {
if (err == TOX_ERR_GROUP_JOIN_TOO_LONG)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length cannot exceed %d.", TOX_GROUP_MAX_PASSWORD_SIZE);
else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group (error %d).", err);
return;
}
int init = init_groupchat_win(m, groupnum, NULL, 0);
if (init == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_group_leave(m, groupnum, NULL, 0, NULL);
} else if (init == -2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"You have been kicked from a group. Close the window and try again.");
tox_group_leave(m, groupnum, NULL, 0, NULL);
}
}
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -447,25 +542,42 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
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);
#ifdef QRPNG
FILE *output = fopen(qr_path, "wb");
if (argc == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'");
return;
} else if (!strcmp(argv[1], "txt")) {
if (output == NULL) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
#endif /* QRPNG */
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);
if (ID_to_QRcode_txt(id_string, qr_path) == -1) {
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);
#ifdef QRPNG
} else if (!strcmp(argv[1], "png")) {
char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT_PNG) + 1];
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG);
if (ID_to_QRcode_png(id_string, qr_path) == -1) {
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);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]);
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);
#endif /* QRPNG */
}
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -497,6 +609,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
tox_self_set_name(m, (uint8_t *) nick, len, NULL);
prompt_update_nick(prompt, nick);
set_nick_all_groups(m, nick, len);
store_data(m, DATA_FILE);
}
@ -508,27 +621,32 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
return;
}
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
return;
}
/* remove opening and closing quotes */
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "%s", &argv[1][1]);
int len = strlen(msg) - 1;
msg[len] = '\0';
prompt_update_statusmessage(prompt, m, msg);
prompt_update_statusmessage(prompt, m, argv[1]);
}
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:");
long int nospam = rand();
if (argc > 0) {
nospam = strtol(argv[1], NULL, 16);
if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value.");
return;
}
}
uint32_t old_nospam = tox_self_get_nospam(m);
tox_self_set_nospam(m, (uint32_t) nospam);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:");
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.");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
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.");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'",
old_nospam);
}
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -603,6 +721,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
tox_self_set_status(m, status);
prompt_update_status(prompt, status);
set_status_all_groups(m, status);
if (have_note) {
if (argv[2][0] != '\"') {

View File

@ -33,6 +33,7 @@ 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_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);

View File

@ -21,59 +21,707 @@
*/
#include <string.h>
#include <stdlib.h>
#include "toxic.h"
#include "windows.h"
#include "line_info.h"
#include "misc_tools.h"
#include "log.h"
#include "groupchat.h"
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
extern GroupChat groupchats[MAX_GROUPCHAT_NUM];
void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char title[MAX_STR_SIZE];
char chatid[TOX_GROUP_CHAT_ID_SIZE * 2 + 1] = {0};
char chat_public_key[TOX_GROUP_CHAT_ID_SIZE];
TOX_ERR_GROUP_STATE_QUERIES err;
if (!tox_group_get_chat_id(m, self->num, (uint8_t *) chat_public_key, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve the Chat ID (error %d).", err);
return;
}
size_t i;
for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
char xx[3];
snprintf(xx, sizeof(xx), "%02X", chat_public_key[i] & 0xff);
strcat(chatid, xx);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", chatid);
}
void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_TOGGLE_IGNORE err;
if (!tox_group_toggle_ignore(m, self->num, peer_id, true, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to ignore %s (error %d).", nick, err);
return;
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, BLUE, "-!- Ignoring %s", nick);
}
static void cmd_kickban_helper(ToxWindow *self, Tox *m, const char *nick, bool set_ban)
{
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
const char *type_str = set_ban ? "ban" : "kick";
TOX_ERR_GROUP_MOD_REMOVE_PEER err;
tox_group_mod_remove_peer(m, self->num, peer_id, set_ban, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_REMOVE_PEER_OK: {
return;
}
case TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to %s %s.", type_str, nick);
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to %s %s from the group (error %d).", type_str, nick,
err);
return;
}
}
}
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
cmd_kickban_helper(self, m, argv[1], false);
}
void cmd_ban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
TOX_ERR_GROUP_BAN_QUERY err;
if (argc < 1) {
int tlen = tox_group_get_title(m, self->num, (uint8_t *) title, TOX_MAX_NAME_LENGTH);
size_t num_banned = tox_group_ban_get_list_size(m, self->num, &err);
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");
if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list size (error %d).", err);
return;
}
if (num_banned == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban list is empty.");
return;
}
uint32_t ban_list[num_banned];
if (!tox_group_ban_get_list(m, self->num, ban_list, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list (error %d).", err);
return;
}
uint16_t i;
for (i = 0; i < num_banned; ++i) {
uint32_t id = ban_list[i];
size_t len = tox_group_ban_get_name_size(m, self->num, id, &err);
if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve name length for ban %d (error %d).", id, err);
continue;
}
char tmp_nick[len];
if (!tox_group_ban_get_name(m, self->num, id, (uint8_t *) tmp_nick, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve name for ban %d (error %d).", id, err);
continue;
}
char nick[len + 1];
copy_tox_str(nick, sizeof(nick), tmp_nick, len);
uint64_t time_set = tox_group_ban_get_time_set(m, self->num, id, &err);
if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve timestamp for ban %d (error %d).", id, err);
continue;
}
struct tm tm_set = *localtime((const time_t *) &time_set);
char time_str[64];
strftime(time_str, sizeof(time_str), "%e %b %Y %H:%M:%S%p", &tm_set);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "ID %d : %s [Set:%s]", id, nick, time_str);
}
return;
}
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes.");
cmd_kickban_helper(self, m, argv[1], true);
}
void cmd_unban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be specified.");
return;
}
/* remove opening and closing quotes */
snprintf(title, sizeof(title), "%s", &argv[1][1]);
int len = strlen(title) - 1;
title[len] = '\0';
int ban_id = atoi(argv[1]);
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.");
if (ban_id == 0 && strcmp(argv[1], "0")) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be a non-negative interger.");
return;
}
set_window_title(self, title, len);
TOX_ERR_GROUP_MOD_REMOVE_BAN err;
tox_group_mod_remove_ban(m, self->num, ban_id, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_REMOVE_BAN_OK: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban list entry with id %d has been removed.", ban_id);
return;
}
case TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unban peers.");
return;
}
case TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID does not exist.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to remove ban list entry (error %d).", err);
return;
}
}
}
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_MODERATOR, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to promote moderators.");
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "This peer is already a moderator.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to promote peer to moderator (error %d).", err);
return;
}
}
}
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
if (tox_group_peer_get_role(m, self->num, peer_id, NULL) != TOX_GROUP_ROLE_MODERATOR) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s is not a moderator", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_USER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Nice try.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to revoke moderator powers from %s (error %d).", nick,
err);
return;
}
}
}
void cmd_mykey(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
char pk[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
TOX_ERR_GROUP_SELF_QUERY err;
if (!tox_group_self_get_public_key(m, self->num, (uint8_t *) pk, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch your public key (error %d)", err);
return;
}
size_t i;
for (i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) {
char d[3];
snprintf(d, sizeof(d), "%02X", pk[i] & 0xff);
strcat(pk_string, d);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", pk_string);
}
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *passwd = NULL;
size_t len = 0;
if (argc > 0) {
passwd = argv[1];
len = strlen(passwd);
}
TOX_ERR_GROUP_FOUNDER_SET_PASSWORD err;
tox_group_founder_set_password(m, self->num, (uint8_t *) passwd, len, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK: {
if (len > 0)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password has been set to %s.", passwd);
else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password has been unset.");
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length must not exceed %d.",
TOX_GROUP_MAX_PASSWORD_SIZE);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the password.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set password (error %d).", err);
return;
}
}
}
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
int maxpeers = 0;
if (argc < 1) {
TOX_ERR_GROUP_STATE_QUERIES err;
uint32_t maxpeers = tox_group_get_peer_limit(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve peer limit (error %d).", err);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit is set to %d", maxpeers);
return;
}
maxpeers = atoi(argv[1]);
if (maxpeers <= 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit must be a value greater than 0.");
return;
}
TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT err;
tox_group_founder_set_peer_limit(m, self->num, maxpeers, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit has been set to %d.", maxpeers);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the peer limit.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the peer limit (error %d).", err);
return;
}
}
}
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *pstate_str = NULL;
TOX_GROUP_PRIVACY_STATE privacy_state;
if (argc < 1) {
TOX_ERR_GROUP_STATE_QUERIES err;
privacy_state = tox_group_get_privacy_state(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve privacy state (error %d).", err);
return;
}
pstate_str = privacy_state == TOX_GROUP_PRIVACY_STATE_PRIVATE ? "private" : "public";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state is set to %s.", pstate_str);
return;
}
pstate_str = argv[1];
if (strcasecmp(pstate_str, "private") != 0 && strcasecmp(pstate_str, "public") != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state must be \"private\" or \"public\".");
return;
}
privacy_state = strcasecmp(pstate_str,
"private") == 0 ? TOX_GROUP_PRIVACY_STATE_PRIVATE : TOX_GROUP_PRIVACY_STATE_PUBLIC;
TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE err;
tox_group_founder_set_privacy_state(m, self->num, privacy_state, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state has been set to %s.", pstate_str);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the privacy state.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error setting privacy state (error %d).", err);
return;
}
}
}
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_OBSERVER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to silence %s.", nick);
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to silence %s (error %d).", nick, err);
return;
}
}
}
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
if (tox_group_peer_get_role(m, self->num, peer_id, NULL) != TOX_GROUP_ROLE_OBSERVER) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s is not silenced.", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_USER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unsilence %s.", nick);
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unsilence %s (error %d).", nick, err);
return;
}
}
}
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
TOX_ERR_GROUP_RECONNECT err;
if (!tox_group_reconnect(m, self->num, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to rejoin group (error %d).", err);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Reconnecting to group...");
}
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
TOX_ERR_GROUP_STATE_QUERIES err;
size_t tlen = tox_group_get_topic_size(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic length (error %d).", err);
return;
}
if (tlen > 0) {
char cur_topic[tlen];
if (!tox_group_get_topic(m, self->num, (uint8_t *) cur_topic, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic (error %d).", err);
return;
}
cur_topic[tlen] = '\0';
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic is set to: %s", cur_topic);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic is not set.");
}
return;
}
const char *topic = argv[1];
TOX_ERR_GROUP_TOPIC_SET err;
tox_group_set_topic(m, self->num, (uint8_t *) topic, strlen(topic), &err);
switch (err) {
case TOX_ERR_GROUP_TOPIC_SET_OK: {
/* handled below switch */
break;
}
case TOX_ERR_GROUP_TOPIC_SET_TOO_LONG: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic length must not exceed %d.", TOX_GROUP_MAX_TOPIC_LENGTH);
return;
}
case TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the topic.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the topic (error %d).", err);
return;
}
}
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);
TOX_ERR_GROUP_SELF_QUERY sn_err;
size_t sn_len = tox_group_self_get_name_size(m, self->num, &sn_err);
char selfnick[sn_len];
if (!tox_group_self_get_name(m, self->num, (uint8_t *) selfnick, &sn_err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve your own name (error %d).", sn_err);
return;
}
selfnick[sn_len] = '\0';
line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, MAGENTA, "-!- You set the topic to: %s", topic);
char tmp_event[MAX_STR_SIZE];
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
snprintf(tmp_event, sizeof(tmp_event), "set topic to %s", topic);
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
}
void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_TOGGLE_IGNORE err;
if (!tox_group_toggle_ignore(m, self->num, peer_id, false, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unignore %s (error %d).", nick, err);
return;
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, BLUE, "-!- You are no longer ignoring %s", nick);
}
void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified.");
return;
}
GroupChat *chat = &groupchats[self->num];
if (!chat) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois failed.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
int peer_index = get_peer_index(self->num, peer_id);
if (peer_index < 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois failed.");
return;
}
const char *status_str = "Online";
if (chat->peer_list[peer_index].status == TOX_USER_STATUS_BUSY)
status_str = "Busy";
else if (chat->peer_list[peer_index].status == TOX_USER_STATUS_AWAY)
status_str = "Away";
const char *role_str = "User";
if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_FOUNDER)
role_str = "Founder";
else if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_MODERATOR)
role_str = "Moderator";
else if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_OBSERVER)
role_str = "Observer";
char last_seen_str[128];
get_elapsed_time_str_2(last_seen_str, sizeof(last_seen_str), get_unix_time() - chat->peer_list[peer_index].last_active);
char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
size_t i;
for (i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) {
char d[3];
snprintf(d, sizeof(d), "%02X", chat->peer_list[peer_index].public_key[i] & 0xff);
strcat(pk_string, d);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois for %s", nick);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Role: %s", role_str);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Status: %s", status_str);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Last active: %s", last_seen_str);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Public key: %s", pk_string);
}

View File

@ -26,6 +26,23 @@
#include "windows.h"
#include "toxic.h"
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_ban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mykey(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_prune(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* GROUP_COMMANDS_H */

File diff suppressed because it is too large Load Diff

View File

@ -26,61 +26,42 @@
#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];
struct GroupPeer {
bool active;
char name[TOX_MAX_NAME_LENGTH];
size_t name_length;
uint32_t peer_id;
uint8_t public_key[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
TOX_USER_STATUS status;
TOX_GROUP_ROLE role;
uint64_t last_active;
};
#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
struct GroupPeer *peer_list;
char *name_list; /* List of peer names, needed for tab completion */
uint32_t num_peers; /* Number of peers in the chat/name_list array */
uint32_t max_idx; /* Maximum peer list index - 1 */
uint32_t groupnumber;
int chatwin;
bool active;
uint64_t time_connected; /* The time we successfully connected to the group */
int side_pos; /* current position of the sidebar - used for scrolling up and down */
} GroupChat;
void close_groupchat(ToxWindow *self, Tox *m, int groupnum);
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type);
void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum);
int init_groupchat_win(Tox *m, uint32_t groupnum, const char *groupname, size_t length);
void set_nick_all_groups(Tox *m, const char *nick, size_t length);
void set_status_all_groups(Tox *m, uint8_t status);
int group_get_nick_peer_id(uint32_t groupnum, const char *nick, uint32_t *peer_id);
int get_peer_index(uint32_t groupnum, uint32_t peer_id);
/* destroys and re-creates groupchat window with or without the peerlist */
/* destroys and re-creates groupchat window */
void redraw_groupchat_win(ToxWindow *self);
ToxWindow new_group_chat(Tox *m, int groupnum);
#endif /* #define GROUPCHAT_H */

View File

@ -153,12 +153,17 @@ static void help_draw_global(ToxWindow *self)
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, " /group : Create a group chat\n");
wprintw(win, " /join <chat id> <passwd> : Join a group chat with optional password\n");
wprintw(win, " /nick <nick> : Set your nickname\n");
wprintw(win, " /nospam : Change part of your Tox ID to stop spam\n");
wprintw(win, " /nospam <value> : Change part of your Tox ID to stop spam\n");
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
wprintw(win, " /myid : Print your Tox ID\n");
#ifdef QRPNG
wprintw(win, " /myqr <txt> or <png> : Print your Tox ID's QR code to a file.\n");
#else
wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n");
#endif /* QRPNG */
wprintw(win, " /clear : Clear window history\n");
wprintw(win, " /close : Close the current chat window\n");
wprintw(win, " /quit or /exit : Exit Toxic\n");
@ -197,8 +202,7 @@ static void help_draw_chat(ToxWindow *self)
wprintw(win, "Chat Commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " /invite <n> : Invite contact to a group chat\n");
wprintw(win, " /join : Join a pending group chat\n");
wprintw(win, " /gaccept <password> : Accept a group invite with optional password\n");
wprintw(win, " /sendfile <path> : Send a file\n");
wprintw(win, " /savefile <id> : Receive a file\n");
wprintw(win, " /cancel <type> <id> : Cancel file transfer where type: in|out\n");
@ -245,7 +249,9 @@ static void help_draw_keys(ToxWindow *self)
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");
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n\n");
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n");
wprintw(win, " Ctrl+J : Insert new line\n");
wprintw(win, " Ctrl+T : Toggle paste mode\n\n");
wprintw(win, " (Note: Custom keybindings override these defaults.)\n\n");
help_draw_bottom_menu(win);
@ -264,7 +270,32 @@ static void help_draw_group(ToxWindow *self)
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");
wprintw(win, " /chatid : Print the group chat id to share with others\n");
wprintw(win, " /mykey : Print your group public key\n");
wprintw(win, " /ignore <nick> : Ignore peer\n");
wprintw(win, " /unignore <nick> : Unignore peer \n");
wprintw(win, " /rejoin : Rejoin the group\n");
wprintw(win, " /topic <msg> : Set group topic (show current topic if no msg)\n");
wprintw(win, " /whisper <nick> <msg> : Send private message to nick\n");
wprintw(win, " /whois <nick> : Display info about nick.\n");
wattron(win, A_BOLD);
wprintw(win, " Moderator commands:\n");
wattroff(win, A_BOLD);
wprintw(win, " /kick <nick> : Kick peer\n");
wprintw(win, " /ban <nick> : Ban peer (leave nick blank to see ban list)\n");
wprintw(win, " /unban <Ban ID> : Unban entry\n");
wprintw(win, " /silence <nick> : Silences peer for the entire group\n");
wprintw(win, " /unsilence <nick> : Unsilences peer\n");
wattron(win, A_BOLD);
wprintw(win, " Founder commands:\n");
wattroff(win, A_BOLD);
wprintw(win, " /mod <nick> : Promote peer to moderator\n");
wprintw(win, " /unmod <nick> : Demote moderator to normal user\n");
wprintw(win, " /passwd <password> : Set group password (leave blank to unset)\n");
wprintw(win, " /peerlimit <num> : Set group peer limit\n");
wprintw(win, " /privacy <state> : Set group privacy state: private|public\n");
help_draw_bottom_menu(win);
@ -296,7 +327,7 @@ static void help_draw_contacts(ToxWindow *self)
void help_onKey(ToxWindow *self, wint_t key)
{
switch(key) {
switch (key) {
case 'x':
case T_KEY_ESC:
help_exit(self);
@ -304,9 +335,9 @@ void help_onKey(ToxWindow *self, wint_t key)
case 'c':
#ifdef VIDEO
help_init_window(self, 22, 80);
help_init_window(self, 21, 80);
#elif AUDIO
help_init_window(self, 19, 80);
help_init_window(self, 18, 80);
#else
help_init_window(self, 10, 80);
#endif
@ -325,7 +356,7 @@ void help_onKey(ToxWindow *self, wint_t key)
break;
case 'r':
help_init_window(self, 6, 80);
help_init_window(self, 25, 80);
self->help->type = HELP_GROUP;
break;
@ -335,7 +366,7 @@ void help_onKey(ToxWindow *self, wint_t key)
break;
case 'k':
help_init_window(self, 13, 80);
help_init_window(self, 15, 80);
self->help->type = HELP_KEYS;
break;
@ -350,7 +381,7 @@ void help_onDraw(ToxWindow *self)
{
curs_set(0);
switch(self->help->type) {
switch (self->help->type) {
case HELP_MENU:
help_draw_menu(self);
return;

View File

@ -42,9 +42,12 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_
{
ChatContext *ctx = self->chatwin;
/* this is the only place we need to do this check */
if (key == '\n')
key = L'';
int cur_len = wcwidth(key);
/* this is the only place we need to do this check */
if (cur_len == -1) {
sound_notify(self, notif_error, 0, NULL);
return;
@ -266,15 +269,19 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
/* 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);
if (!match) {
if (key == user_settings->key_toggle_peerlist) {
if (self->is_groupchat) {
self->show_peerlist ^= 1;
redraw_groupchat_win(self);
}
match = true;
} else if (key == user_settings->key_toggle_pastemode) {
self->chatwin->pastemode ^= 1;
match = true;
}
match = true;
}
return match;
}

View File

@ -158,17 +158,24 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
/* for type-specific formatting in print function */
switch (type) {
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 IN_PRVT_MSG:
case OUT_PRVT_MSG:
len += strlen(user_settings->line_special) + 3;
break;
case CONNECTION:
len += strlen(user_settings->line_join) + 2;
break;
@ -304,10 +311,14 @@ void line_info_print(ToxWindow *self)
switch (type) {
case OUT_MSG:
/* fallthrough */
case OUT_MSG_READ:
/* fallthrough */
case IN_MSG:
case IN_PRVT_MSG:
case OUT_PRVT_MSG:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
@ -320,20 +331,33 @@ void line_info_print(ToxWindow *self)
nameclr = CYAN;
wattron(win, COLOR_PAIR(nameclr));
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
wprintw(win, "%s %s: ", (type != OUT_PRVT_MSG && type != IN_PRVT_MSG) ?
user_settings->line_normal :
user_settings->line_special,
line->name1);
wattroff(win, COLOR_PAIR(nameclr));
if (line->msg[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
else if (line->msg[0] == '<')
wattron(win, COLOR_PAIR(RED));
char *msg = line->msg;
wprintw(win, "%s", line->msg);
while (msg) {
char *line = strsep(&msg, "\n");
if (line->msg[0] == '>')
wattroff(win, COLOR_PAIR(GREEN));
else if (line->msg[0] == '<')
wattroff(win, COLOR_PAIR(RED));
if (line[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
else if (line[0] == '<')
wattron(win, COLOR_PAIR(RED));
wprintw(win, "%s%c", line, msg ? '\n' : '\0');
if (line[0] == '>')
wattroff(win, COLOR_PAIR(GREEN));
else if (line[0] == '<')
wattroff(win, COLOR_PAIR(RED));
// change the \0 set by strsep back to \n
if (msg)
msg[-1] = '\n';
}
if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
wattron(win, COLOR_PAIR(RED));
@ -350,8 +374,10 @@ void line_info_print(ToxWindow *self)
break;
case OUT_ACTION_READ:
/* fallthrough */
case OUT_ACTION:
/* fallthrough */
case IN_ACTION:
wattron(win, COLOR_PAIR(BLUE));
@ -538,20 +564,15 @@ bool line_info_onKey(ToxWindow *self, wint_t key)
if (key == user_settings->key_half_page_up) {
line_info_page_up(self, hst);
}
else if (key == user_settings->key_half_page_down) {
} else if (key == user_settings->key_half_page_down) {
line_info_page_down(self, hst);
}
else if (key == user_settings->key_scroll_line_up) {
} else if (key == user_settings->key_scroll_line_up) {
line_info_scroll_up(hst);
}
else if (key == user_settings->key_scroll_line_down) {
} else if (key == user_settings->key_scroll_line_down) {
line_info_scroll_down(hst);
}
else if (key == user_settings->key_page_bottom) {
} else if (key == user_settings->key_page_bottom) {
line_info_reset_start(self, hst);
}
else {
} else {
match = false;
}

View File

@ -35,10 +35,12 @@ 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 */
OUT_MSG_READ, /* for sent messages that have received a read reply. */
IN_ACTION,
OUT_ACTION,
OUT_ACTION_READ, /* same as OUT_MSG_READ but for actions */
IN_PRVT_MSG, /* PRVT should only be used for groups */
OUT_PRVT_MSG,
PROMPT,
CONNECTION,
DISCONNECTION,
@ -50,7 +52,7 @@ struct line_info {
char name1[TOXIC_MAX_NAME_LENGTH + 1];
char name2[TOXIC_MAX_NAME_LENGTH + 1];
char msg[MAX_LINE_INFO_MSG_SIZE];
uint64_t timestamp;
time_t timestamp;
uint8_t type;
uint8_t bold;
uint8_t colour;

View File

@ -262,6 +262,7 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
return 0;
on_error:
if (log_on)
log_enable(src, selfkey, otherkey, log, LOG_CHAT);

View File

@ -25,7 +25,7 @@
struct chatlog {
FILE *file;
uint64_t lastwrite;
time_t lastwrite;
char path[MAX_STR_SIZE];
bool log_on; /* specific to current chat window */
};

View File

@ -29,7 +29,7 @@ struct cqueue_msg {
int line_id;
uint8_t type;
uint32_t receipt;
uint64_t last_send_try;
time_t last_send_try;
struct cqueue_msg *next;
struct cqueue_msg *prev;
};

View File

@ -26,6 +26,12 @@
#include <time.h>
#include <limits.h>
#include <dirent.h>
#if defined(__FreeBSD__)
#include <netinet/in.h>
#include <sys/socket.h>
#else
#include <arpa/inet.h>
#endif
#include <sys/stat.h>
#include "toxic.h"
@ -37,8 +43,6 @@
extern ToxWindow *prompt;
extern struct user_settings *user_settings;
static uint64_t current_unix_time;
void hst_to_net(uint8_t *num, uint16_t numbytes)
{
#ifndef WORDS_BIGENDIAN
@ -54,19 +58,13 @@ void hst_to_net(uint8_t *num, uint16_t numbytes)
return;
}
/* Note: The time functions are not thread safe */
void update_unix_time(void)
time_t get_unix_time(void)
{
current_unix_time = (uint64_t) time(NULL);
}
uint64_t get_unix_time(void)
{
return current_unix_time;
return time(NULL);
}
/* Returns 1 if connection has timed out, 0 otherwise */
int timed_out(uint64_t timestamp, uint64_t timeout)
int timed_out(time_t timestamp, time_t timeout)
{
return timestamp + timeout <= get_unix_time();
}
@ -75,12 +73,12 @@ int timed_out(uint64_t timestamp, uint64_t timeout)
struct tm *get_time(void)
{
struct tm *timeinfo;
uint64_t t = get_unix_time();
timeinfo = localtime((const time_t*) &t);
time_t t = get_unix_time();
timeinfo = localtime((const time_t *) &t);
return timeinfo;
}
/*Puts the current time in buf in the format of [HH:mm:ss] */
/* Puts the current time in buf in the format of [HH:mm:ss] */
void get_time_str(char *buf, int bufsize)
{
if (user_settings->timestamps == TIMESTAMPS_OFF) {
@ -93,7 +91,7 @@ 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)
void get_elapsed_time_str(char *buf, int bufsize, time_t secs)
{
if (!secs)
return;
@ -110,6 +108,24 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
}
/* Converts seconds to string in format H hours, m minutes, s seconds */
void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs)
{
if (!secs)
return;
long int seconds = secs % 60;
long int minutes = (secs % 3600) / 60;
long int hours = secs / 3600;
if (!minutes && !hours)
snprintf(buf, bufsize, "%ld seconds", seconds);
else if (!hours)
snprintf(buf, bufsize, "%ld minutes, %ld seconds", minutes, seconds);
else
snprintf(buf, bufsize, "%ld hours, %ld minutes, %ld seconds", hours, minutes, seconds);
}
/*
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
* output_size must be exactly half of hex_len.
@ -162,7 +178,7 @@ int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_
size_t i;
for (i = 0; i < TOX_ADDRESS_SIZE; ++i)
snprintf(&output[i*2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
return 0;
}
@ -176,6 +192,15 @@ int string_is_empty(const char *string)
return string[0] == '\0';
}
/* Returns 1 if the string is empty, 0 otherwise */
int wstring_is_empty(const wchar_t *string)
{
if (!string)
return true;
return string[0] == L'\0';
}
/* convert a multibyte string to a wide character string and puts in buf. */
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
{
@ -184,7 +209,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
if (n < len)
return -1;
if ((len = mbstowcs(buf, string, n)) == (size_t) -1)
if ((len = mbstowcs(buf, string, n)) == (size_t) - 1)
return -1;
return len;
@ -198,7 +223,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
if (n < len)
return -1;
if ((len = wcstombs(buf, string, n)) == (size_t) -1)
if ((len = wcstombs(buf, string, n)) == (size_t) - 1)
return -1;
return len;
@ -225,11 +250,11 @@ int valid_nick(const char *nick)
for (i = 0; nick[i]; ++i) {
if ((nick[i] == ' ' && nick[i + 1] == ' ')
|| nick[i] == '/'
|| nick[i] == '\n'
|| nick[i] == '\t'
|| nick[i] == '\v'
|| nick[i] == '\r')
|| nick[i] == '/'
|| nick[i] == '\n'
|| nick[i] == '\t'
|| nick[i] == '\v'
|| nick[i] == '\r')
return 0;
}
@ -332,13 +357,21 @@ 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)
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum)
{
int len = tox_group_peername(m, groupnum, peernum, (uint8_t *) buf);
TOX_ERR_GROUP_PEER_QUERY err;
size_t len = tox_group_peer_get_name_size(m, groupnum, peer_id, &err);
if (len == -1) {
if (err != TOX_ERR_GROUP_PEER_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
} else {
tox_group_peer_get_name(m, groupnum, peer_id, (uint8_t *) buf, &err);
if (err != TOX_ERR_GROUP_PEER_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
}
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
@ -348,19 +381,28 @@ 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 */
returns length of msg.
returns 0 and nulls msg if length is too big for buffer size */
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;
if (length > size - 1) {
msg[0] = '\0';
return 0;
}
memcpy(msg, data, length);
msg[length] = '\0';
return length;
}
/* returns index of the first instance of ch in s starting at idx.
returns length of s if char not found */
returns length of s if char not found or 0 if s is NULL. */
int char_find(int idx, const char *s, char ch)
{
if (!s) {
return 0;
}
int i = idx;
for (i = idx; s[i]; ++i) {
@ -372,9 +414,13 @@ int char_find(int idx, const char *s, char ch)
}
/* returns index of the last instance of ch in s starting at len.
returns 0 if char not found (skips 0th index). */
returns 0 if char not found or s is NULL (skips 0th index). */
int char_rfind(const char *s, char ch, int len)
{
if (!s) {
return 0;
}
int i = 0;
for (i = len; i > 0; --i) {
@ -461,3 +507,36 @@ void set_window_title(ToxWindow *self, const char *title, int len)
snprintf(self->name, sizeof(self->name), "%s", cpy);
}
/* Return true if address appears to be a valid ipv4 address. */
bool is_ip4_address(const char *address)
{
struct sockaddr_in s_addr;
return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0;
}
/* Return true if address roughly appears to be a valid ipv6 address.
*
* TODO: Improve this function (inet_pton behaves strangely with ipv6).
* for now the only guarantee is that it won't return true if the
* address is a domain or ipv4 address, and should only be used if you're
* reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
*/
bool is_ip6_address(const char *address)
{
size_t i;
size_t num_colons = 0;
char ch = 0;
for (i = 0; (ch = address[i]); ++i) {
if (ch == '.') {
return false;
}
if (ch == ':') {
++num_colons;
}
}
return num_colons > 1 && num_colons < 8;
}

View File

@ -61,23 +61,26 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr);
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);
time_t get_unix_time(void);
/* 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);
void get_elapsed_time_str(char *buf, int bufsize, time_t secs);
/* Converts seconds to string in format H hours, m minutes, s seconds */
void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs);
/* 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) */
void update_unix_time(void);
/* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(const char *string);
/* Same as above but for wide character strings */
int wstring_is_empty(const wchar_t *string);
/* convert a multibyte string to a wide character string (must provide buffer) */
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
@ -88,7 +91,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);
int timed_out(time_t timestamp, time_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);
@ -126,18 +129,19 @@ void str_to_lower(char *str);
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);
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum);
/* copies data to msg buffer.
returns length of msg, which will be no larger than size-1 */
returns length of msg.
returns 0 and nulls msg if length is too big for buffer size */
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 */
returns length of s if char not found or 0 if s is NULL. */
int char_find(int idx, const char *s, char ch);
/* returns index of the last instance of ch in s
returns 0 if char not found */
/* returns index of the last instance of ch in s starting at len.
returns 0 if char not found or s is NULL (skips 0th index). */
int char_rfind(const char *s, char ch, int len);
/* Converts bytes to appropriate unit and puts in buf as a string */
@ -158,4 +162,16 @@ 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);
/* Return true if address appears to be a valid ipv4 address. */
bool is_ip4_address(const char *address);
/* Return true if address roughly appears to be a valid ipv6 address.
*
* TODO: Improve this function (inet_pton behaves strangely with ipv6).
* for now the only guarantee is that it won't return true if the
* address is a domain or ipv4 address, and should only be used if you're
* reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
*/
bool is_ip6_address(const char *address);
#endif /* #define MISC_TOOLS_H */

View File

@ -22,7 +22,6 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* for u_char */
#include <curl/curl.h>
#include "toxic.h"
@ -31,9 +30,10 @@
#include "global_commands.h"
#include "misc_tools.h"
#include "configdir.h"
#include "curl_util.h"
extern struct arg_opts arg_opts;
extern struct Winthread Winthread;;
extern struct Winthread Winthread;
#define NAMESERVER_API_PATH "api"
#define SERVER_KEY_SIZE 32
@ -41,9 +41,6 @@ extern struct Winthread Winthread;;
#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];
@ -56,8 +53,8 @@ static struct thread_data {
char id_bin[TOX_ADDRESS_SIZE];
char addr[MAX_STR_SIZE];
char msg[MAX_STR_SIZE];
bool busy;
bool disabled;
volatile bool busy;
} t_data;
static struct lookup_thread {
@ -189,40 +186,17 @@ static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_s
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)
static int process_response(struct Recv_Curl_Data *recv_data)
{
size_t prefix_size = strlen(ID_PREFIX);
if (recv_data->size < TOX_ADDRESS_SIZE * 2 + prefix_size)
if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size)
return -1;
const char *IDstart = strstr(recv_data->data, ID_PREFIX);
@ -243,47 +217,6 @@ static int process_response(struct Recv_Data *recv_data)
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;
@ -301,7 +234,7 @@ void *lookup_thread_func(void *data)
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");
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.");
@ -315,26 +248,38 @@ void *lookup_thread_func(void *data)
kill_lookup_thread();
}
struct Recv_Data recv_data;
memset(&recv_data, 0, sizeof(struct Recv_Data));
struct Recv_Curl_Data recv_data;
memset(&recv_data, 0, sizeof(struct Recv_Curl_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_WRITEFUNCTION, curl_cb_write_data);
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, &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)
int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);
if (proxy_ret != 0) {
lookup_error(self, "Failed to set proxy (error %d)\n");
goto on_exit;
}
int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL);
@ -437,9 +382,9 @@ void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr,
* 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)
int name_lookup_init(int curl_init_status)
{
if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
if (curl_init_status != 0) {
t_data.disabled = true;
return -1;
}
@ -454,8 +399,3 @@ int name_lookup_init(void)
return 0;
}
void name_lookup_cleanup(void)
{
curl_global_cleanup();
}

View File

@ -29,8 +29,7 @@
* Returns 0 on success.
* Returns -1 on failure.
*/
int name_lookup_init(void);
void name_lookup_cleanup(void);
int name_lookup_init(int curl_init_status);
int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message);

View File

@ -38,24 +38,24 @@
#include "xtra.h"
#if defined(AUDIO) || defined(SOUND_NOTIFY)
#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
#endif
#ifdef SOUND_NOTIFY
#include <AL/alut.h> /* freealut packet */
#endif
#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
#endif
#ifdef SOUND_NOTIFY
#include <AL/alut.h> /* freealut packet */
#endif
#endif /* AUDIO */
#ifdef BOX_NOTIFY
#include <libnotify/notify.h>
#include <libnotify/notify.h>
#endif
#define MAX_BOX_MSG_LEN 127
@ -75,7 +75,7 @@ struct Control {
#ifdef SOUND_NOTIFY
uint32_t device_idx; /* index of output device */
char* sounds[SOUNDS_SIZE];
char *sounds[SOUNDS_SIZE];
#endif /* SOUND_NOTIFY */
} Control = {0};
@ -88,7 +88,7 @@ struct _ActiveNotifications {
bool active;
int *id_indicator;
#ifdef BOX_NOTIFY
NotifyNotification* box;
NotifyNotification *box;
char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
char title[64];
size_t size;
@ -188,15 +188,17 @@ void graceful_clear()
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].active) {
#ifdef BOX_NOTIFY
#ifdef BOX_NOTIFY
if (actives[i].box) {
GError* ignore;
GError *ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
}
#endif
if(actives[i].id_indicator)
#endif
if (actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
if ( actives[i].looping ) {
@ -221,11 +223,11 @@ void graceful_clear()
control_unlock();
}
void* do_playing(void* _p)
void *do_playing(void *_p)
{
(void)_p;
while(true) {
while (true) {
control_lock();
if (!Control.poll_active) {
@ -234,65 +236,73 @@ void* do_playing(void* _p)
}
bool has_looping = false;
bool test_active_notify = false;
int i;
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].looping) has_looping = true;
if (actives[i].active && !actives[i].looping
#ifdef BOX_NOTIFY
&& !actives[i].box
#endif
) {
if(actives[i].id_indicator)
test_active_notify = actives[i].active && !actives[i].looping;
#ifdef BOX_NOTIFY
test_active_notify = test_active_notify && !actives[i].box;
#endif
if (test_active_notify) {
if (actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
if (!is_playing(actives[i].source)) {
/* Close */
/* Close */
alSourceStop(actives[i].source);
alDeleteSources(1, &actives[i].source);
alDeleteBuffers(1, &actives[i].buffer);
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
#ifdef BOX_NOTIFY
else if (actives[i].box && time(NULL) >= actives[i].n_timeout)
{
GError* ignore;
#ifdef BOX_NOTIFY
else if (actives[i].box && time(NULL) >= actives[i].n_timeout) {
GError *ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
if(actives[i].id_indicator)
if (actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
if (!actives[i].looping && !is_playing(actives[i].source)) {
/* stop source if not looping or playing, just terminate box */
/* stop source if not looping or playing, just terminate box */
alSourceStop(actives[i].source);
alDeleteSources(1, &actives[i].source);
alDeleteBuffers(1, &actives[i].buffer);
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
#endif
#endif
}
/* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
if (device_opened && !has_looping &&
(time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
(time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
m_close_device();
}
has_looping = false;
control_unlock();
usleep(10000);
}
pthread_exit(NULL);
}
int play_source(uint32_t source, uint32_t buffer, bool looping)
{
int i = 0;
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
if ( i == ACTIVE_NOTIFS_MAX ) {
return -1; /* Full */
}
@ -308,11 +318,11 @@ int play_source(uint32_t source, uint32_t buffer, bool looping)
}
#elif BOX_NOTIFY
void* do_playing(void* _p)
void *do_playing(void *_p)
{
(void)_p;
while(true) {
while (true) {
control_lock();
if (!Control.poll_active) {
@ -323,20 +333,22 @@ void* do_playing(void* _p)
int i;
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].box && time(NULL) >= actives[i].n_timeout)
{
GError* ignore;
if (actives[i].box && time(NULL) >= actives[i].n_timeout) {
GError *ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
if(actives[i].id_indicator)
if (actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
control_unlock();
usleep(10000);
}
pthread_exit(NULL);
}
@ -347,7 +359,7 @@ void graceful_clear()
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].box) {
GError* ignore;
GError *ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
}
@ -378,6 +390,7 @@ int init_notify(int login_cooldown, int notification_timeout)
#endif /* SOUND_NOTIFY */
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0)
return -1;
@ -419,7 +432,9 @@ void terminate_notify()
#ifdef SOUND_NOTIFY
int i = 0;
for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]);
alutExit();
#endif /* SOUND_NOTIFY */
@ -429,7 +444,7 @@ void terminate_notify()
}
#ifdef SOUND_NOTIFY
int set_sound(Notification sound, const char* value)
int set_sound(Notification sound, const char *value)
{
if (sound == silent) return 0;
@ -457,10 +472,11 @@ int play_sound_internal(Notification what, bool loop)
alSourcei(source, AL_LOOPING, loop);
int rc = play_source(source, buffer, loop);
if (rc < 0) {
alSourceStop(source);
alDeleteSources(1, &source);
alDeleteBuffers(1,&buffer);
alDeleteBuffers(1, &buffer);
return -1;
}
@ -472,7 +488,8 @@ int play_notify_sound(Notification notif, uint64_t flags)
int rc = -1;
if (flags & NT_BEEP) beep();
else if (notif != silent) {
if (notif != silent) {
if ( !Control.poll_active || !Control.sounds[notif] )
return -1;
@ -487,17 +504,21 @@ void stop_sound(int id)
{
if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) {
#ifdef BOX_NOTIFY
if (actives[id].box) {
GError* ignore;
GError *ignore;
notify_notification_close(actives[id].box, &ignore);
}
#endif
if (actives[id].id_indicator)
*actives[id].id_indicator = -1;
// alSourcei(actives[id].source, AL_LOOPING, false);
alSourceStop(actives[id].source);
alDeleteSources(1, &actives[id].source);
alDeleteBuffers(1,&actives[id].buffer);
alDeleteBuffers(1, &actives[id].buffer);
memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
}
}
@ -508,6 +529,7 @@ static int m_play_sound(Notification notif, uint64_t flags)
#ifdef SOUND_NOTIFY
return play_notify_sound(notif, flags);
#else
if (notif != silent)
beep();
@ -516,12 +538,12 @@ static int m_play_sound(Notification notif, uint64_t flags)
}
#ifdef BOX_NOTIFY
void m_notify_action(NotifyNotification *box, char *action, void* data)
void m_notify_action(NotifyNotification *box, char *action, void *data)
{
}
#endif
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator)
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator)
{
tab_notify(self, flags);
@ -540,6 +562,7 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in
if (id == -1) {
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
@ -558,7 +581,7 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in
return id;
}
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id)
{
tab_notify(self, flags);
@ -566,6 +589,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
return -1;
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
#ifdef SOUND_NOTIFY
control_lock();
@ -578,8 +602,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
alSourceStop(actives[id].source);
alDeleteSources(1, &actives[id].source);
alDeleteBuffers(1,&actives[id].buffer);
alDeleteBuffers(1, &actives[id].buffer);
alGenSources(1, &actives[id].source);
alGenBuffers(1, &actives[id].buffer);
@ -593,6 +616,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
return id;
#else
if (notif != silent)
beep();
@ -600,7 +624,8 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
#endif /* SOUND_NOTIFY */
}
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator, const char *title,
const char *format, ...)
{
if (notifications_are_disabled(flags)) {
tab_notify(self, flags);
@ -614,9 +639,11 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
control_lock();
#ifdef SOUND_NOTIFY
if (id == -1) { /* Could not play */
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
@ -624,17 +651,23 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
actives[id].active = 1;
actives[id].id_indicator = id_indicator;
if (id_indicator) *id_indicator = id;
}
#else
if (id == -1)
return -1;
#endif /* SOUND_NOTIFY */
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
@ -657,7 +690,7 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
#endif /* BOX_NOTIFY */
}
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...)
int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...)
{
if (notifications_are_disabled(flags)) {
tab_notify(self, flags);
@ -676,7 +709,8 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
@ -689,7 +723,8 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
for (; i < actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}
@ -707,7 +742,7 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
#endif
}
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...)
{
tab_notify(self, flags);
@ -719,7 +754,9 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
control_lock();
int id;
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
@ -731,9 +768,11 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
}
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
@ -757,7 +796,7 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
#endif
}
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...)
int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...)
{
tab_notify(self, flags);
@ -773,7 +812,8 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form
}
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
@ -786,7 +826,8 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
for (; i < actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}

View File

@ -26,8 +26,7 @@
#include <inttypes.h>
#include "windows.h"
typedef enum _Notification
{
typedef enum _Notification {
silent = -1,
notif_error,
self_log_in,
@ -63,18 +62,19 @@ typedef enum _Flags {
int init_notify(int login_cooldown, int notification_timeout);
void terminate_notify();
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator);
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id);
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator);
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, 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, ...);
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
int set_sound(Notification sound, const char* value);
int set_sound(Notification sound, const char *value);
#endif /* SOUND_NOTIFY */
#endif /* NOTIFY_H */

View File

@ -31,13 +31,16 @@
#endif /* __OBJC__ */
#define RELEASE_CHK(func, obj) if ((obj))\
func((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;
@interface OSXVideo :
NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
- (instancetype)initWithDeviceNames:
(char **)device_names AmtDevices:
(int *)size;
@end
#endif /* __OBJC__ */

View File

@ -44,19 +44,19 @@
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;
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;
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;
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)
@ -65,9 +65,10 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
uint8_t *p;
uint8_t r, g, b;
for(y = 0; y != height; y += 2) {
for (y = 0; y != height; y += 2) {
p = rgb;
for(x = 0; x != width; x++) {
for (x = 0; x != width; x++) {
b = *rgb++;
g = *rgb++;
r = *rgb++;
@ -76,7 +77,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
*plane_y++ = rgb_to_y(r, g, b);
}
for(x = 0; x != width / 2; x++) {
for (x = 0; x != width / 2; x++) {
b = *rgb++;
g = *rgb++;
r = *rgb++;
@ -91,9 +92,12 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
*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++;
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);
@ -122,28 +126,34 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
BOOL _shouldMangleDimensions;
}
- (instancetype)initWithDeviceNames: (char **)device_names AmtDevices: (int *)size {
- (instancetype)initWithDeviceNames:
(char **)device_names AmtDevices:
(int *)size
{
_session = [[AVCaptureSession alloc] init];
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
int i;
for (i = 0; i < [devices count]; ++i) {
AVCaptureDevice *device = [devices objectAtIndex: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]);
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 {
- (void)dealloc
{
pthread_mutex_destroy(&_frameLock);
[_session release];
[_linkerVideo release];
@ -151,14 +161,18 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
[super dealloc];
}
- (int)openVideoDeviceIndex: (uint32_t)device_idx Width: (uint16_t *)width Height: (uint16_t *)height {
- (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];
NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
AVCaptureDevice *device = [devices objectAtIndex: device_idx];
NSError *error = NULL;
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
if ( error != NULL ) {
[input release];
@ -166,7 +180,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
}
[_session beginConfiguration];
[_session addInput:input];
[_session addInput: input];
//_session.sessionPreset = AVCaptureSessionPreset640x480;
//*width = 640;
//*height = 480;
@ -176,8 +190,9 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
[device release];
/* Obtain device resolution */
AVCaptureInputPort *port = [input.ports objectAtIndex:0];
AVCaptureInputPort *port = [input.ports objectAtIndex: 0];
CMFormatDescriptionRef format_description = port.formatDescription;
if ( format_description ) {
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description);
*width = dimensions.width;
@ -188,36 +203,47 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
}
_linkerVideo = [[AVCaptureVideoDataOutput alloc] init];
[_linkerVideo setSampleBufferDelegate:self queue:_processingQueue];
[_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}];
[_linkerVideo setVideoSettings: @ {
(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferWidthKey: @640,
(id)kCVPixelBufferHeightKey: @480
}];
} else {
[_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
[_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
}
[_session addOutput:_linkerVideo];
[_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];
- (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];
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
[_session stopRunning];
[_session removeOutput:_linkerVideo];
[_session removeInput:input];
[_session removeOutput: _linkerVideo];
[_session removeInput: input];
[_linkerVideo release];
}
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
- (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 {
@ -228,10 +254,17 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
// 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 {
- (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;
}
@ -240,6 +273,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
CFRetain(_currentFrame);
CFTypeID imageType = CFGetTypeID(_currentFrame);
if (imageType == CVPixelBufferGetTypeID()) {
// TODO maybe handle other formats
bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height);
@ -263,7 +297,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
/*
* C-interface for OSXVideo
*/
static OSXVideo* _OSXVideo = nil;
static OSXVideo *_OSXVideo = nil;
int osx_video_init(char **device_names, int *size)
{

View File

@ -50,11 +50,11 @@ extern struct Winthread Winthread;
extern FriendsList Friends;
FriendRequests FrndRequests;
#ifdef VIDEO
#define AC_NUM_GLOB_COMMANDS 22
#define AC_NUM_GLOB_COMMANDS 23
#elif AUDIO
#define AC_NUM_GLOB_COMMANDS 20
#define AC_NUM_GLOB_COMMANDS 21
#else
#define AC_NUM_GLOB_COMMANDS 18
#define AC_NUM_GLOB_COMMANDS 19
#endif
/* Array of global command names used for tab completion. */
@ -68,6 +68,7 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/exit" },
{ "/group" },
{ "/help" },
{ "/join" },
{ "/log" },
{ "/myid" },
{ "/myqr" },
@ -189,13 +190,16 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (x2 <= 0 || y2 <= 0)
return;
if (ctx->pastemode && key == '\r')
key = '\n';
/* ignore non-menu related input if active */
if (self->help->active) {
help_onKey(self, key);
return;
}
if (ltr) { /* char is printable */
if (ltr || key == '\n') { /* char is printable */
input_new_char(self, key, x, y, x2, y2);
return;
}
@ -211,11 +215,11 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
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){
else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
const char status_cmd_list[3][8] = {
{"online"},
{"away"},
{"busy"},
{"online"},
{"away"},
{"busy"},
};
diff = complete_line(self, status_cmd_list, 3, 8);
} else
@ -232,19 +236,21 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else {
sound_notify(self, notif_error, 0, NULL);
}
} else if (key == '\n') {
} else if (key == '\r') {
rm_trailing_spaces_buf(ctx);
char line[MAX_STR_SIZE] = {0};
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
if (!string_is_empty(line))
if (!wstring_is_empty(ctx->line)) {
add_line_to_hist(ctx);
wstrsubst(ctx->line, L'', L'\n');
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
char line[MAX_STR_SIZE] = {0};
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
}
wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
@ -295,10 +301,12 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
status_text = "Online";
colour = GREEN;
break;
case TOX_USER_STATUS_AWAY:
status_text = "Away";
colour = YELLOW;
break;
case TOX_USER_STATUS_BUSY:
status_text = "Busy";
colour = RED;
@ -329,7 +337,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
pthread_mutex_lock(&Winthread.lock);
size_t slen = tox_self_get_status_message_size(m);
tox_self_get_status_message (m, (uint8_t*) statusmsg);
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);
@ -363,7 +371,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
wmove(self->window, y + 1, new_x);
wrefresh(self->window);
wnoutrefresh(self->window);
if (self->help->active)
help_onDraw(self);
@ -383,6 +391,10 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
get_time_str(timefrmt, sizeof(timefrmt));
const char *msg;
if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) {
return;
}
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);
@ -394,8 +406,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
else
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has come online", nick );
}
else if (connection_status == TOX_CONNECTION_NONE) {
} else if (connection_status == TOX_CONNECTION_NONE) {
msg = "has gone offline";
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
write_to_log(msg, nick, ctx->log, true);

View File

@ -28,25 +28,36 @@
#include "windows.h"
#include "qr_code.h"
#ifdef QRPNG
#include <png.h>
#define INCHES_PER_METER (100.0/2.54)
#define DPI 72
#define SQUARE_SIZE 5
#endif /* QRPNG */
#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.
/* Converts a tox ID string into a QRcode and prints it into the given filename.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int ID_to_QRcode(const char *tox_id, FILE *fp)
int ID_to_QRcode_txt(const char *tox_id, const char *outfile)
{
FILE *fp = fopen(outfile, "wb");
if (fp == NULL)
return -1;
QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
if (qr_obj == NULL)
if (qr_obj == NULL) {
fclose(fp);
return -1;
}
size_t width = qr_obj->width;
size_t i, j;
@ -83,7 +94,116 @@ int ID_to_QRcode(const char *tox_id, FILE *fp)
fprintf(fp, "\n");
}
fclose(fp);
QRcode_free(qr_obj);
return 0;
}
#ifdef QRPNG
/* Converts a tox ID string into a QRcode and prints it into the given filename as png.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int ID_to_QRcode_png(const char *tox_id, const char *outfile)
{
static FILE *fp;
unsigned char *p;
unsigned char black[4] = {0, 0, 0, 255};
size_t x, y, xx, yy, real_width;
png_structp png_ptr;
png_infop info_ptr;
fp = fopen(outfile, "wb");
if (fp == NULL) {
return -1;
}
QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
if (qr_obj == NULL) {
fclose(fp);
return -1;
}
real_width = (qr_obj->width + BORDER_LEN * 2) * SQUARE_SIZE;
size_t row_size = real_width * 4;
unsigned char row[row_size];
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(fp);
QRcode_free(qr_obj);
return -1;
}
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fclose(fp);
QRcode_free(qr_obj);
return -1;
}
if (setjmp(png_jmpbuf(png_ptr))) {
fclose(fp);
QRcode_free(qr_obj);
png_destroy_write_struct(&png_ptr, &info_ptr);
return -1;
}
png_init_io(png_ptr, fp);
png_set_IHDR(png_ptr, info_ptr, real_width, real_width, 8,
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_pHYs(png_ptr, info_ptr, DPI * INCHES_PER_METER,
DPI * INCHES_PER_METER, PNG_RESOLUTION_METER);
png_write_info(png_ptr, info_ptr);
/* top margin */
memset(row, 0xff, row_size);
for (y = 0; y < BORDER_LEN * SQUARE_SIZE; y++) {
png_write_row(png_ptr, row);
}
/* data */
p = qr_obj->data;
for (y = 0; y < qr_obj->width; y++) {
memset(row, 0xff, row_size);
for (x = 0; x < qr_obj->width; x++) {
for (xx = 0; xx < SQUARE_SIZE; xx++) {
if (*p & 1) {
memcpy(&row[((BORDER_LEN + x) * SQUARE_SIZE + xx) * 4], black, 4);
}
}
p++;
}
for (yy = 0; yy < SQUARE_SIZE; yy++) {
png_write_row(png_ptr, row);
}
}
/* bottom margin */
memset(row, 0xff, row_size);
for (y = 0; y < BORDER_LEN * SQUARE_SIZE; y++) {
png_write_row(png_ptr, row);
}
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
QRcode_free(qr_obj);
return 0;
}
#endif /* QRPNG */

View File

@ -25,11 +25,21 @@
#define QRCODE_FILENAME_EXT ".QRcode"
/* Converts a tox ID string into a QRcode and prints it to the given file stream.
/* Converts a tox ID string into a QRcode and prints it into the given filename.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int ID_to_QRcode(const char *tox_id, FILE *fp);
int ID_to_QRcode_txt(const char *tox_id, const char *outfile);
#ifdef QRPNG
#define QRCODE_FILENAME_EXT_PNG ".QRcode.png"
/* Converts a tox ID string into a QRcode and prints it into the given filename as png.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int ID_to_QRcode_png(const char *tox_id, const char *outfile);
#endif /* QRPNG */
#endif /* QR_CODE */

View File

@ -32,39 +32,46 @@
#include "misc_tools.h"
#ifdef AUDIO
#include "audio_device.h"
#include "audio_device.h"
#endif /* AUDIO */
#include "settings.h"
#include "line_info.h"
#ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "."
#define PACKAGE_DATADIR "."
#endif
#define NO_SOUND "silent"
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* history_size;
const char* show_typing_self;
const char* show_typing_other;
const char* show_welcome_msg;
const char *self;
const char *timestamps;
const char *time_format;
const char *timestamp_format;
const char *log_timestamp_format;
const char *alerts;
const char *bell_on_message;
const char *bell_on_filetrans;
const char *bell_on_filetrans_accept;
const char *bell_on_invite;
const char *native_colors;
const char *autolog;
const char *history_size;
const char *show_typing_self;
const char *show_typing_other;
const char *show_welcome_msg;
const char *show_connection_msg;
const char *nodeslist_update_freq;
const char* line_join;
const char* line_quit;
const char* line_alert;
const char* line_normal;
const char *line_join;
const char *line_quit;
const char *line_alert;
const char *line_normal;
const char *line_special;
const char* mplex_away;
const char* mplex_away_note;
const char *mplex_away;
const char *mplex_away_note;
} ui_strings = {
"ui",
"timestamps",
@ -72,21 +79,28 @@ static struct ui_strings {
"timestamp_format",
"log_timestamp_format",
"alerts",
"bell_on_message",
"bell_on_filetrans",
"bell_on_filetrans_accept",
"bell_on_invite",
"native_colors",
"autolog",
"history_size",
"show_typing_self",
"show_typing_other",
"show_welcome_msg",
"show_connection_msg",
"nodeslist_update_freq",
"line_join",
"line_quit",
"line_alert",
"line_normal",
"line_special",
"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;
snprintf(settings->timestamp_format, sizeof(settings->timestamp_format), "%s", TIMESTAMP_DEFAULT);
@ -94,16 +108,23 @@ static void ui_defaults(struct user_settings* settings)
settings->autolog = AUTOLOG_OFF;
settings->alerts = ALERTS_ENABLED;
settings->bell_on_message = 0;
settings->bell_on_filetrans = 0;
settings->bell_on_filetrans_accept = 0;
settings->bell_on_invite = 0;
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;
settings->show_connection_msg = SHOW_CONNECTION_MSG_ON;
settings->nodeslist_update_freq = 7;
snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN);
snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT);
snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT);
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
snprintf(settings->line_special, LINE_HINT_MAX + 1, "%s", LINE_SPECIAL);
settings->mplex_away = MPLEX_ON;
snprintf (settings->mplex_away_note,
@ -113,17 +134,18 @@ static void ui_defaults(struct user_settings* settings)
}
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;
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;
const char *toggle_pastemode;
} key_strings = {
"keys",
"next_tab",
@ -136,10 +158,11 @@ static const struct keys_strings {
"peer_list_up",
"peer_list_down",
"toggle_peerlist",
"toggle_paste_mode",
};
/* defines from toxic.h */
static void key_defaults(struct user_settings* settings)
static void key_defaults(struct user_settings *settings)
{
settings->key_next_tab = T_KEY_NEXT;
settings->key_prev_tab = T_KEY_PREV;
@ -151,33 +174,37 @@ static void key_defaults(struct user_settings* settings)
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;
settings->key_toggle_pastemode = T_KEY_C_T;
}
static const struct tox_strings {
const char* self;
const char* download_path;
const char* chatlogs_path;
const char* avatar_path;
const char *self;
const char *download_path;
const char *chatlogs_path;
const char *avatar_path;
const char *password_eval;
} tox_strings = {
"tox",
"download_path",
"chatlogs_path",
"avatar_path",
"password_eval",
};
static void tox_defaults(struct user_settings* settings)
static void tox_defaults(struct user_settings *settings)
{
strcpy(settings->download_path, "");
strcpy(settings->chatlogs_path, "");
strcpy(settings->avatar_path, "");
strcpy(settings->password_eval, "");
}
#ifdef AUDIO
static const struct audio_strings {
const char* self;
const char* input_device;
const char* output_device;
const char* VAD_treshold;
const char *self;
const char *input_device;
const char *output_device;
const char *VAD_treshold;
} audio_strings = {
"audio",
"input_device",
@ -185,7 +212,7 @@ static const struct audio_strings {
"VAD_treshold",
};
static void audio_defaults(struct user_settings* settings)
static void audio_defaults(struct user_settings *settings)
{
settings->audio_in_dev = 0;
settings->audio_out_dev = 0;
@ -195,17 +222,17 @@ static void audio_defaults(struct user_settings* settings)
#ifdef SOUND_NOTIFY
static const struct sound_strings {
const char* self;
const char* notif_error;
const char* self_log_in;
const char* self_log_out;
const char* user_log_in;
const char* user_log_out;
const char* call_incoming;
const char* call_outgoing;
const char* generic_message;
const char* transfer_pending;
const char* transfer_completed;
const char *self;
const char *notif_error;
const char *self_log_in;
const char *self_log_out;
const char *user_log_in;
const char *user_log_out;
const char *call_incoming;
const char *call_outgoing;
const char *generic_message;
const char *transfer_pending;
const char *transfer_completed;
} sound_strings = {
"sounds",
"notif_error",
@ -221,11 +248,12 @@ static const struct sound_strings {
};
#endif
static int key_parse(const char** bind){
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 && toupper(bind[0][5]) != 'M') /* ctrl+m cannot be used */
return toupper(bind[0][5]) - 'A' + 1;
}
@ -238,6 +266,15 @@ static int key_parse(const char** bind){
return -1;
}
static void set_key_binding(int *key, const char **bind)
{
int code = key_parse(bind);
if (code != -1) {
*key = code;
}
}
int settings_load(struct user_settings *s, const char *patharg)
{
config_t cfg[1];
@ -286,6 +323,7 @@ int settings_load(struct user_settings *s, const char *patharg)
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");
@ -302,26 +340,53 @@ int settings_load(struct user_settings *s, const char *patharg)
}
config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts);
if (config_setting_lookup_bool(setting, ui_strings.bell_on_message, &s->bell_on_message)) {
s->bell_on_message = s->bell_on_message ? NT_BEEP : 0;
}
if (config_setting_lookup_bool(setting, ui_strings.bell_on_filetrans, &s->bell_on_filetrans)) {
s->bell_on_filetrans = s->bell_on_filetrans ? NT_BEEP : 0;
}
if (config_setting_lookup_bool(setting, ui_strings.bell_on_filetrans_accept, &s->bell_on_filetrans_accept)) {
s->bell_on_filetrans_accept = s->bell_on_filetrans_accept ? NT_BEEP : 0;
}
if (config_setting_lookup_bool(setting, ui_strings.bell_on_invite, &s->bell_on_invite)) {
s->bell_on_invite = s->bell_on_invite ? NT_BEEP : 0;
}
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_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg);
config_setting_lookup_bool(setting, ui_strings.show_connection_msg, &s->show_connection_msg);
config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size);
config_setting_lookup_int(setting, ui_strings.nodeslist_update_freq, &s->nodeslist_update_freq);
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);
}
if ( config_setting_lookup_string(setting, ui_strings.line_special, &str) ) {
snprintf(s->line_special, sizeof(s->line_special), "%s", str);
}
config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away);
if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) {
@ -359,34 +424,56 @@ int settings_load(struct user_settings *s, const char *patharg)
if (len >= sizeof(s->avatar_path))
s->avatar_path[0] = '\0';
}
if ( config_setting_lookup_string(setting, tox_strings.password_eval, &str) ) {
snprintf(s->password_eval, sizeof(s->password_eval), "%s", str);
int len = strlen(str);
if (len >= sizeof(s->password_eval))
s->password_eval[0] = '\0';
}
}
/* keys */
if ((setting = config_lookup(cfg, key_strings.self)) != NULL) {
const char* tmp = NULL;
const char *tmp = NULL;
if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp))
s->key_next_tab = key_parse(&tmp);
set_key_binding(&s->key_next_tab, &tmp);
if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp))
s->key_prev_tab = key_parse(&tmp);
set_key_binding(&s->key_prev_tab, &tmp);
if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp))
s->key_scroll_line_up = key_parse(&tmp);
set_key_binding(&s->key_scroll_line_up, &tmp);
if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp))
s->key_scroll_line_down= key_parse(&tmp);
set_key_binding(&s->key_scroll_line_down, &tmp);
if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp))
s->key_half_page_up = key_parse(&tmp);
set_key_binding(&s->key_half_page_up, &tmp);
if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp))
s->key_half_page_down = key_parse(&tmp);
set_key_binding(&s->key_half_page_down, &tmp);
if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp))
s->key_page_bottom = key_parse(&tmp);
set_key_binding(&s->key_page_bottom, &tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp))
s->key_peer_list_up = key_parse(&tmp);
set_key_binding(&s->key_peer_list_up, &tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp))
s->key_peer_list_down = key_parse(&tmp);
set_key_binding(&s->key_peer_list_down, &tmp);
if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp))
s->key_toggle_peerlist = key_parse(&tmp);
set_key_binding(&s->key_toggle_peerlist, &tmp);
if (config_setting_lookup_string(setting, key_strings.toggle_pastemode, &tmp))
set_key_binding(&s->key_toggle_pastemode, &tmp);
}
#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;
@ -396,9 +483,11 @@ int settings_load(struct user_settings *s, const char *patharg)
config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold);
}
#endif
#ifdef SOUND_NOTIFY
if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) {
if ( (config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) ||
!set_sound(notif_error, str) ) {
@ -447,8 +536,7 @@ int settings_load(struct user_settings *s, const char *patharg)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
}
}
else {
} else {
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");
@ -458,6 +546,7 @@ int settings_load(struct user_settings *s, const char *patharg)
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
}
#endif
config_destroy(cfg);

View File

@ -30,11 +30,19 @@
/* Represents line_* hints max strlen */
#define LINE_HINT_MAX 3
#define PASSWORD_EVAL_MAX 512
/* holds user setting values */
struct user_settings {
int autolog; /* boolean */
int alerts; /* boolean */
/* boolean (is set to NT_BEEP or 0 after loading) */
int bell_on_message;
int bell_on_filetrans;
int bell_on_filetrans_accept;
int bell_on_invite;
int timestamps; /* boolean */
char timestamp_format[TIME_STR_SIZE];
char log_timestamp_format[TIME_STR_SIZE];
@ -44,15 +52,19 @@ struct user_settings {
int show_typing_self; /* boolean */
int show_typing_other; /* boolean */
int show_welcome_msg; /* boolean */
int show_connection_msg; /* boolean */
int nodeslist_update_freq; /* int (<= 0 to disable updates) */
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 line_special[LINE_HINT_MAX + 1];
char download_path[PATH_MAX];
char chatlogs_path[PATH_MAX];
char avatar_path[PATH_MAX];
char password_eval[PASSWORD_EVAL_MAX];
int key_next_tab;
int key_prev_tab;
@ -64,6 +76,7 @@ struct user_settings {
int key_peer_list_up;
int key_peer_list_down;
int key_toggle_peerlist;
int key_toggle_pastemode;
int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */
char mplex_away_note [TOX_MAX_STATUS_MESSAGE_LENGTH];
@ -94,6 +107,9 @@ enum {
SHOW_WELCOME_MSG_OFF = 0,
SHOW_WELCOME_MSG_ON = 1,
SHOW_CONNECTION_MSG_OFF = 0,
SHOW_CONNECTION_MSG_ON = 1,
DFLT_HST_SIZE = 700,
MPLEX_OFF = 0,
@ -104,6 +120,7 @@ enum {
#define LINE_QUIT "<--"
#define LINE_ALERT "-!-"
#define LINE_NORMAL "---"
#define LINE_SPECIAL ">>>"
#define TIMESTAMP_DEFAULT "%H:%M:%S"
#define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]"
#define MPLEX_AWAY_NOTE "Detached from screen"

View File

@ -52,8 +52,7 @@ extern struct Winthread Winthread;
#define PATH_SEP_S "/"
#define PATH_SEP_C '/'
typedef enum
{
typedef enum {
MPLEX_NONE,
MPLEX_SCREEN,
MPLEX_TMUX,
@ -97,13 +96,14 @@ static char *read_into_dyn_buffer (FILE *stream)
char *dyn_buffer = NULL;
int dyn_buffer_size = 1; /* account for the \0 */
while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL)
{
while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL) {
int length = dyn_buffer_size + strlen (input_ptr);
if (dyn_buffer)
dyn_buffer = (char*) realloc (dyn_buffer, length);
dyn_buffer = (char *) realloc (dyn_buffer, length);
else
dyn_buffer = (char*) malloc (length);
dyn_buffer = (char *) malloc (length);
strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr);
dyn_buffer_size = length;
}
@ -116,26 +116,29 @@ static char *extract_socket_path (const char *info)
const char *search_str = " Socket";
const char *pos = strstr (info, search_str);
char *end = NULL;
char* path = NULL;
char *path = NULL;
if (!pos)
return NULL;
pos += strlen (search_str);
pos = strchr (pos, PATH_SEP_C);
if (!pos)
return NULL;
end = strchr (pos, '\n');
if (!end)
return NULL;
*end = '\0';
end = strrchr (pos, '.');
if (!end)
return NULL;
path = (char*) malloc (end - pos + 1);
path = (char *) malloc (end - pos + 1);
*end = '\0';
return strcpy (path, pos);
}
@ -147,14 +150,17 @@ static int detect_gnu_screen ()
char *dyn_buffer = NULL;
socket_name = getenv ("STY");
if (!socket_name)
goto nomplex;
session_info_stream = popen ("env LC_ALL=C screen -ls", "r");
if (!session_info_stream)
goto nomplex;
dyn_buffer = read_into_dyn_buffer (session_info_stream);
if (!dyn_buffer)
goto nomplex;
@ -162,6 +168,7 @@ static int detect_gnu_screen ()
session_info_stream = NULL;
socket_path = extract_socket_path (dyn_buffer);
if (!socket_path)
goto nomplex;
@ -181,23 +188,29 @@ static int detect_gnu_screen ()
return 1;
nomplex:
if (session_info_stream)
pclose (session_info_stream);
if (dyn_buffer)
free (dyn_buffer);
if (socket_path)
free(socket_path);
return 0;
}
static int detect_tmux ()
{
char *tmux_env = getenv ("TMUX"), *pos;
if (!tmux_env)
return 0;
/* find second separator */
pos = strrchr (tmux_env, ',');
if (!pos)
return 0;
@ -230,6 +243,7 @@ static int gnu_screen_is_detached ()
return 0;
struct stat sb;
if (stat (mplex_data, &sb) != 0)
return 0;
@ -257,10 +271,12 @@ static int tmux_is_detached ()
const int numstr_len = strlen (mplex_data);
session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r");
if (!session_info_stream)
goto fail;
dyn_buffer = read_into_dyn_buffer (session_info_stream);
if (!dyn_buffer)
goto fail;
@ -268,7 +284,7 @@ static int tmux_is_detached ()
session_info_stream = NULL;
/* prepare search string, for finding the current session's entry */
search_str = (char*) malloc (numstr_len + 4);
search_str = (char *) malloc (numstr_len + 4);
search_str[0] = '\n';
strcpy (search_str + 1, mplex_data);
strcat (search_str, ": ");
@ -295,12 +311,16 @@ static int tmux_is_detached ()
return attached_pos == NULL || attached_pos > nl_pos;
fail:
if (session_info_stream)
pclose (session_info_stream);
if (dyn_buffer)
free (dyn_buffer);
if (search_str)
free (search_str);
return 0;
}
@ -332,26 +352,21 @@ static void mplex_timer_handler (Tox *m)
current_status = tox_self_get_status (m);
pthread_mutex_unlock (&Winthread.lock);
if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached)
{
if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached) {
auto_away_active = false;
new_status = prev_status;
new_note = prev_note;
}
else
if (current_status == TOX_USER_STATUS_NONE && detached)
{
} else if (current_status == TOX_USER_STATUS_NONE && detached) {
auto_away_active = true;
prev_status = current_status;
new_status = TOX_USER_STATUS_AWAY;
pthread_mutex_lock (&Winthread.lock);
size_t slen = tox_self_get_status_message_size(m);
tox_self_get_status_message (m, (uint8_t*) prev_note);
tox_self_get_status_message (m, (uint8_t *) prev_note);
prev_note[slen] = '\0';
pthread_mutex_unlock (&Winthread.lock);
new_note = user_settings->mplex_away_note;
}
else
} else
return;
char argv[3][MAX_STR_SIZE];

View File

@ -39,7 +39,9 @@
#include <unistd.h>
#include <limits.h>
#include <termios.h>
#include <ctype.h>
#include <curl/curl.h>
#include <tox/tox.h>
#include <tox/toxencryptsave.h>
@ -47,6 +49,7 @@
#include "toxic.h"
#include "windows.h"
#include "friendlist.h"
#include "groupchat.h"
#include "prompt.h"
#include "misc_tools.h"
#include "file_transfers.h"
@ -59,9 +62,10 @@
#include "execute.h"
#include "term_mplex.h"
#include "name_lookup.h"
#include "bootstrap.h"
#ifdef X11
#include "xtra.h"
#include "xtra.h"
#endif
#ifdef AUDIO
@ -73,7 +77,7 @@ ToxAV *av;
#endif /* AUDIO */
#ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "."
#define PACKAGE_DATADIR "."
#endif
/* Export for use in Callbacks */
@ -162,7 +166,7 @@ void exit_toxic_success(Tox *m)
free_global_data();
tox_kill(m);
endwin();
name_lookup_cleanup();
curl_global_cleanup();
#ifdef X11
/* We have to terminate xtra last coz reasons
@ -199,6 +203,7 @@ static void init_term(void)
cbreak();
keypad(stdscr, 1);
noecho();
nonl();
timeout(100);
if (has_colors()) {
@ -278,151 +283,15 @@ static void print_init_messages(ToxWindow *toxwin)
line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]);
}
#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */
#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */
#define MAXNODES 50
#define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7)
static struct toxNodes {
int lines;
char nodes[MAXNODES][NODELEN];
uint16_t ports[MAXNODES];
char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE];
} toxNodes;
static int load_nodelist(const char *filename)
static void load_groups(Tox *m)
{
if (!filename)
return 1;
size_t i;
size_t numgroups = tox_group_get_number_groups(m);
FILE *fp = fopen(filename, "r");
if (fp == NULL)
return 1;
char line[MAX_NODE_LINE];
while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) {
size_t line_len = strlen(line);
if (line_len >= MIN_NODE_LINE && line_len <= MAX_NODE_LINE) {
const char *name = strtok(line, " ");
const char *port_str = strtok(NULL, " ");
const char *key_ascii = strtok(NULL, " ");
if (name == NULL || port_str == NULL || key_ascii == NULL)
continue;
long int port = strtol(port_str, NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE)
continue;
size_t key_len = strlen(key_ascii);
size_t name_len = strlen(name);
if (key_len < TOX_PUBLIC_KEY_SIZE * 2 || name_len >= NODELEN)
continue;
snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name);
toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0;
toxNodes.ports[toxNodes.lines] = port;
/* remove possible trailing newline from key string */
char real_ascii_key[TOX_PUBLIC_KEY_SIZE * 2 + 1];
memcpy(real_ascii_key, key_ascii, TOX_PUBLIC_KEY_SIZE * 2);
key_len = TOX_PUBLIC_KEY_SIZE * 2;
real_ascii_key[key_len] = '\0';
if (hex_string_to_bin(real_ascii_key, key_len, toxNodes.keys[toxNodes.lines], TOX_PUBLIC_KEY_SIZE) == -1)
continue;
toxNodes.lines++;
}
for (i = 0; i < numgroups; ++i) {
if (init_groupchat_win(m, i, NULL, 0) == -1)
tox_group_leave(m, i, NULL, 0, NULL);
}
fclose(fp);
if (toxNodes.lines < 1)
return 1;
return 0;
}
/* Bootstraps and adds as TCP relay.
* Returns 0 if both actions are successful.
* Returns -1 otherwise.
*/
int init_connection_helper(Tox *m, int line)
{
TOX_ERR_BOOTSTRAP err;
tox_bootstrap(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], &err);
if (err != TOX_ERR_BOOTSTRAP_OK) {
fprintf(stderr, "Failed to bootstrap %s:%d\n", toxNodes.nodes[line], toxNodes.ports[line]);
return -1;
}
tox_add_tcp_relay(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], &err);
if (err != TOX_ERR_BOOTSTRAP_OK) {
fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[line], toxNodes.ports[line]);
return -1;
}
return 0;
}
/* Connects to a random DHT node listed in the DHTnodes file
*
* return codes:
* 0: success
* 1: failed to open node file
* 2: no line of sufficient length in node file
* 3: failed to resolve name to IP
* 4: nodelist file contains no acceptable line
*/
static bool srvlist_loaded = false;
#define NUM_INIT_NODES 5
int init_connection(Tox *m)
{
if (toxNodes.lines > 0) { /* already loaded nodelist */
init_connection_helper(m, rand() % toxNodes.lines);
return 0;
}
/* only once:
* - load the nodelist
* - connect to "everyone" inside
*/
if (!srvlist_loaded) {
srvlist_loaded = true;
int res;
if (!arg_opts.nodes_path[0])
res = load_nodelist(PACKAGE_DATADIR "/DHTnodes");
else
res = load_nodelist(arg_opts.nodes_path);
if (res != 0)
return res;
res = 3;
int i;
int n = MIN(NUM_INIT_NODES, toxNodes.lines);
for (i = 0; i < n; ++i) {
if (init_connection_helper(m, rand() % toxNodes.lines) == 0)
res = 0;
}
return res;
}
/* empty nodelist file */
return 4;
}
static void load_friendlist(Tox *m)
@ -463,8 +332,10 @@ static int password_prompt(char *buf, int size)
/* eat overflowed stdin and return error */
if (buf[--len] != '\n') {
int ch;
while ((ch = getchar()) != '\n' && ch > 0)
;
return 0;
}
@ -472,6 +343,49 @@ static int password_prompt(char *buf, int size)
return len;
}
/* Get the password from the eval command.
* return length of password on success, 0 on failure
*/
static int password_eval(char *buf, int size)
{
buf[0] = '\0';
/* Run password_eval command */
FILE *f = popen(user_settings->password_eval, "r");
if (f == NULL) {
fprintf(stderr, "Executing password_eval failed\n");
return 0;
}
/* Get output from command */
char *ret = fgets(buf, size, f);
if (ret == NULL) {
fprintf(stderr, "Reading password from password_eval command failed\n");
pclose(f);
return 0;
}
/* Get exit status */
int status = pclose(f);
if (status != 0) {
fprintf(stderr, "password_eval command returned error %d\n", status);
return 0;
}
/* Removez whitespace or \n at end */
int i, len = strlen(buf);
for (i = len - 1; i > 0 && isspace(buf[i]); i--) {
buf[i] = 0;
len--;
}
return len;
}
/* Ask user if they would like to encrypt the data file and set password */
static void first_time_encrypt(const char *msg)
{
@ -482,7 +396,7 @@ static void first_time_encrypt(const char *msg)
printf("%s ", msg);
if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n")
|| !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n"))
|| !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n"))
break;
} while (fgets(ch, sizeof(ch), stdin));
@ -543,25 +457,38 @@ static void first_time_encrypt(const char *msg)
#define TEMP_PROFILE_EXT ".tmp"
int store_data(Tox *m, const char *path)
{
if (path == NULL)
if (path == NULL) {
return -1;
}
char temp_path[strlen(path) + strlen(TEMP_PROFILE_EXT) + 1];
snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_PROFILE_EXT);
FILE *fp = fopen(temp_path, "wb");
if (fp == NULL)
if (fp == NULL) {
return -1;
}
size_t data_len = tox_get_savedata_size(m);
char data[data_len];
char *data = malloc(data_len * sizeof(char));
if (data == NULL) {
fclose(fp);
return -1;
}
tox_get_savedata(m, (uint8_t *) data);
if (user_password.data_is_encrypted && !arg_opts.unencrypt_data) {
size_t enc_len = data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
char enc_data[enc_len];
char *enc_data = malloc(enc_len * sizeof(char));
if (enc_data == NULL) {
fclose(fp);
free(data);
return -1;
}
TOX_ERR_ENCRYPTION err;
tox_pass_encrypt((uint8_t *) data, data_len, (uint8_t *) user_password.pass, user_password.len,
@ -570,26 +497,35 @@ int store_data(Tox *m, const char *path)
if (err != TOX_ERR_ENCRYPTION_OK) {
fprintf(stderr, "tox_pass_encrypt() failed with error %d\n", err);
fclose(fp);
free(data);
free(enc_data);
return -1;
}
if (fwrite(enc_data, enc_len, 1, fp) != 1) {
fprintf(stderr, "Failed to write profile data.\n");
fclose(fp);
free(data);
free(enc_data);
return -1;
}
free(enc_data);
} else { /* data will not be encrypted */
if (fwrite(data, data_len, 1, fp) != 1) {
fprintf(stderr, "Failed to write profile data.\n");
fclose(fp);
free(data);
return -1;
}
}
fclose(fp);
free(data);
if (rename(temp_path, path) != 0)
if (rename(temp_path, path) != 0) {
return -1;
}
return 0;
}
@ -605,15 +541,24 @@ static void init_tox_callbacks(Tox *m)
tox_callback_friend_status(m, on_statuschange, NULL);
tox_callback_friend_status_message(m, on_statusmessagechange, NULL);
tox_callback_friend_read_receipt(m, on_read_receipt, NULL);
tox_callback_group_invite(m, on_groupinvite, NULL);
tox_callback_group_message(m, on_groupmessage, NULL);
tox_callback_group_action(m, on_groupaction, NULL);
tox_callback_group_namelist_change(m, on_group_namelistchange, NULL);
tox_callback_group_title(m, on_group_titlechange, NULL);
tox_callback_file_recv(m, on_file_recv, NULL);
tox_callback_file_chunk_request(m, on_file_chunk_request, NULL);
tox_callback_file_recv_control(m, on_file_control, NULL);
tox_callback_file_recv_chunk(m, on_file_recv_chunk, NULL);
tox_callback_group_invite(m, on_group_invite, NULL);
tox_callback_group_message(m, on_group_message, NULL);
tox_callback_group_private_message(m, on_group_private_message, NULL);
tox_callback_group_peer_join(m, on_group_peer_join, NULL);
tox_callback_group_peer_exit(m, on_group_peer_exit, NULL);
tox_callback_group_peer_name(m, on_group_nick_change, NULL);
tox_callback_group_peer_status(m, on_group_status_change, NULL);
tox_callback_group_topic(m, on_group_topic_change, NULL);
tox_callback_group_peer_limit(m, on_group_peer_limit, NULL);
tox_callback_group_privacy_state(m, on_group_privacy_state, NULL);
tox_callback_group_password(m, on_group_password, NULL);
tox_callback_group_self_join(m, on_group_self_join, NULL);
tox_callback_group_join_fail(m, on_group_rejected, NULL);
tox_callback_group_moderation(m, on_group_moderation, NULL);
}
static void init_tox_options(struct Tox_Options *tox_opts)
@ -665,14 +610,14 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
if (len == 0) {
fclose(fp);
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
}
char data[len];
if (fread(data, sizeof(data), 1, fp) != 1) {
fclose(fp);
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
}
bool is_encrypted = tox_is_data_encrypted((uint8_t *) data);
@ -680,7 +625,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
/* attempt to encrypt an already encrypted data file */
if (arg_opts.encrypt_data && is_encrypted) {
fclose(fp);
exit_toxic_err("failed in load_toxic", FATALERR_ENCRYPT);
exit_toxic_err("failed in load_tox", FATALERR_ENCRYPT);
}
if (arg_opts.unencrypt_data && is_encrypted)
@ -693,14 +638,23 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
user_password.data_is_encrypted = true;
size_t pwlen = 0;
system("clear"); // TODO: is this portable?
printf("Enter password (q to quit) ");
int pweval = user_settings->password_eval[0];
if (!pweval) {
system("clear"); // TODO: is this portable?
printf("Enter password (q to quit) ");
}
size_t plain_len = len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
char plain[plain_len];
while (true) {
pwlen = password_prompt(user_password.pass, sizeof(user_password.pass));
if (pweval) {
pwlen = password_eval(user_password.pass, sizeof(user_password.pass));
} else {
pwlen = password_prompt(user_password.pass, sizeof(user_password.pass));
}
user_password.len = pwlen;
if (strcasecmp(user_password.pass, "q") == 0) {
@ -712,6 +666,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
system("clear");
sleep(1);
printf("Invalid password. Try again. ");
pweval = 0;
continue;
}
@ -736,6 +691,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
system("clear");
sleep(1);
printf("Invalid password. Try again. ");
pweval = 0;
} else {
fclose(fp);
exit_toxic_err("tox_pass_decrypt() failed", pwerr);
@ -757,7 +713,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
fclose(fp);
} else { /* Data file does not/should not exist */
if (file_exists(data_path))
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_NONE;
@ -767,7 +723,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
return NULL;
if (store_data(m, data_path) == -1)
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
}
return m;
@ -803,33 +759,9 @@ static Tox *load_toxic(char *data_path)
return m;
}
#define TRY_BOOTSTRAP_INTERVAL 5
static uint64_t last_bootstrap_time = 0;
static void do_bootstrap(Tox *m)
{
static int conn_err = 0;
if (!timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL))
return;
if (tox_self_get_connection_status(m) != TOX_CONNECTION_NONE)
return;
if (conn_err != 0)
return;
last_bootstrap_time = get_unix_time();
conn_err = init_connection(m);
if (conn_err != 0)
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Auto-connect failed with error code %d", conn_err);
}
static void do_toxic(Tox *m, ToxWindow *prompt)
static void do_toxic(Tox *m)
{
pthread_mutex_lock(&Winthread.lock);
update_unix_time();
if (arg_opts.no_connect) {
pthread_mutex_unlock(&Winthread.lock);
@ -837,8 +769,7 @@ static void do_toxic(Tox *m, ToxWindow *prompt)
}
tox_iterate(m);
do_bootstrap(m);
check_file_transfer_timeouts(m);
do_tox_connection(m);
pthread_mutex_unlock(&Winthread.lock);
}
@ -883,7 +814,7 @@ void *thread_cqueue(void *data)
ToxWindow *toxwin = get_window_ptr(i);
if (toxwin != NULL && toxwin->is_chat
&& tox_friend_get_connection_status(m, toxwin->num, NULL) != TOX_CONNECTION_NONE)
&& tox_friend_get_connection_status(m, toxwin->num, NULL) != TOX_CONNECTION_NONE)
cqueue_try_send(toxwin, m);
}
@ -1020,10 +951,6 @@ static void parse_args(int argc, char *argv[])
case 'n':
snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg);
if (!file_exists(arg_opts.nodes_path))
queue_init_message("DHTnodes file not found");
break;
case 'o':
@ -1035,10 +962,10 @@ static void parse_args(int argc, char *argv[])
arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5;
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
if (++optind > argc || argv[optind-1][0] == '-')
if (++optind > argc || argv[optind - 1][0] == '-')
exit_toxic_err("Proxy error", FATALERR_PROXY);
port = strtol(argv[optind-1], NULL, 10);
port = strtol(argv[optind - 1], NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE)
exit_toxic_err("Proxy error", FATALERR_PROXY);
@ -1050,10 +977,10 @@ static void parse_args(int argc, char *argv[])
arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP;
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
if (++optind > argc || argv[optind-1][0] == '-')
if (++optind > argc || argv[optind - 1][0] == '-')
exit_toxic_err("Proxy error", FATALERR_PROXY);
port = strtol(argv[optind-1], NULL, 10);
port = strtol(argv[optind - 1], NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE)
exit_toxic_err("Proxy error", FATALERR_PROXY);
@ -1177,28 +1104,9 @@ static void init_default_data_files(void)
free(user_config_dir);
}
#define REC_TOX_DO_LOOPS_PER_SEC 25
/* Adjusts usleep value so that tox_do runs close to the recommended number of times per second */
static useconds_t optimal_msleepval(uint64_t *looptimer, uint64_t *loopcount, uint64_t cur_time, useconds_t msleepval)
{
useconds_t new_sleep = MAX(msleepval, 3);
++(*loopcount);
if (*looptimer == cur_time)
return new_sleep;
if (*loopcount != REC_TOX_DO_LOOPS_PER_SEC)
new_sleep *= (double) *loopcount / REC_TOX_DO_LOOPS_PER_SEC;
*looptimer = cur_time;
*loopcount = 0;
return new_sleep;
}
// this doesn't do anything (yet)
#ifdef X11
void DnD_callback(const char* asdv, DropType dt)
void DnD_callback(const char *asdv, DropType dt)
{
// if (dt != DT_plain)
// return;
@ -1230,9 +1138,6 @@ int main(int argc, char **argv)
bool datafile_exists = file_exists(DATA_FILE);
if (datafile_exists)
last_bootstrap_time = get_unix_time();
if (!datafile_exists && !arg_opts.unencrypt_data)
first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)");
else if (arg_opts.encrypt_data)
@ -1247,21 +1152,26 @@ int main(int argc, char **argv)
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
if (settings_load(user_settings, p) == -1)
queue_init_message("Failed to load user settings");
if (settings_load(user_settings, p) == -1) {
queue_init_message("Failed to load user settings");
}
int nameserver_ret = name_lookup_init();
int curl_init = curl_global_init(CURL_GLOBAL_ALL);
int nameserver_ret = name_lookup_init(curl_init);
if (nameserver_ret == -1)
if (nameserver_ret == -1) {
queue_init_message("curl failed to initialize; name lookup service is disabled.");
else if (nameserver_ret == -2)
} else if (nameserver_ret == -2) {
queue_init_message("Name lookup server list could not be found.");
else if (nameserver_ret == -3)
} else if (nameserver_ret == -3) {
queue_init_message("Name lookup server list does not contain any valid entries.");
}
#ifdef X11
if (init_xtra(DnD_callback) == -1)
queue_init_message("X failed to initialize");
#endif
Tox *m = load_toxic(DATA_FILE);
@ -1286,7 +1196,6 @@ int main(int argc, char **argv)
if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0)
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
#ifdef AUDIO
av = init_audio(prompt, m);
@ -1304,6 +1213,7 @@ int main(int argc, char **argv)
set_primary_device(output, user_settings->audio_out_dev);
#elif SOUND_NOTIFY
if ( init_devices() == de_InternalError )
queue_init_message("Failed to init audio devices");
@ -1315,7 +1225,14 @@ int main(int argc, char **argv)
if (init_mplex_away_timer(m) == -1)
queue_init_message("Failed to init mplex auto-away.");
int nodeslist_ret = load_DHT_nodeslist();
if (nodeslist_ret != 0) {
queue_init_message("DHT nodeslist failed to load (error %d)", nodeslist_ret);
}
pthread_mutex_lock(&Winthread.lock);
load_groups(m);
print_init_messages(prompt);
pthread_mutex_unlock(&Winthread.lock);
@ -1326,27 +1243,25 @@ int main(int argc, char **argv)
snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path);
execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE);
uint64_t last_save = (uint64_t) time(NULL);
uint64_t looptimer = last_save;
useconds_t msleepval = 40000;
uint64_t loopcount = 0;
time_t last_save = get_unix_time();
while (true) {
do_toxic(m, prompt);
do_toxic(m);
uint64_t cur_time = get_unix_time();
time_t cur_time = get_unix_time();
if (timed_out(last_save, AUTOSAVE_FREQ)) {
pthread_mutex_lock(&Winthread.lock);
if (store_data(m, DATA_FILE) != 0)
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file");
pthread_mutex_unlock(&Winthread.lock);
last_save = cur_time;
}
msleepval = optimal_msleepval(&looptimer, &loopcount, cur_time, msleepval);
usleep(msleepval);
usleep(tox_iteration_interval(m) * 1000);
}
return 0;

View File

@ -70,6 +70,7 @@
#define T_KEY_C_L 0x0C /* ctrl-l */
#define T_KEY_C_W 0x17 /* ctrl-w */
#define T_KEY_C_B 0x02 /* ctrl-b */
#define T_KEY_C_T 0x14 /* ctrl-t */
#define T_KEY_TAB 0x09 /* TAB key */
#define ONLINE_CHAR "*"
@ -106,18 +107,21 @@ int store_data(Tox *m, const char *path);
/* callbacks */
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata);
void on_connectionchange(Tox *m, uint32_t friendnumber, TOX_CONNECTION status, void *userdata);
void on_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length, void *userdata);
void on_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length,
void *userdata);
void on_action(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata);
void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_friendadded(Tox *m, uint32_t friendnumber, bool sort);
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length, void *userdata);
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, void *userdata);
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length, void *userdata);
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata);
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length, void *userdata);
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void *userdata);
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length,
void *userdata);
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length,
void *userdata);
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length,
void *userdata);
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length,
void *userdata);
void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
size_t length, void *userdata);
void on_file_control (Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control, void *userdata);
@ -125,5 +129,25 @@ void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t k
const uint8_t *filename, size_t filename_length, void *userdata);
void on_typing_change(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata);
void on_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata);
void on_group_invite(Tox *m, uint32_t friendnumber, const uint8_t *invite_data, size_t length, void *userdata);
void on_group_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, void *userdata);
void on_group_private_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *message, size_t length,
void *userdata);
void on_group_peer_join(Tox *m, uint32_t groupnumber, uint32_t peernumber, void *userdata);
void on_group_peer_exit(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *partmsg, size_t length,
void *userdata);
void on_group_topic_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *topic, size_t length,
void *userdata);
void on_group_peer_limit(Tox *m, uint32_t groupnumber, uint32_t peer_limit, void *userdata);
void on_group_privacy_state(Tox *m, uint32_t groupnumber, TOX_GROUP_PRIVACY_STATE privacy_state, void *userdata);
void on_group_password(Tox *m, uint32_t groupnumber, const uint8_t *password, size_t length, void *userdata);
void on_group_nick_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *newname, size_t length,
void *userdata);
void on_group_status_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_USER_STATUS status, void *userdata);
void on_group_self_join(Tox *m, uint32_t groupnumber, void *userdata);
void on_group_rejected(Tox *m, uint32_t groupnumber, TOX_GROUP_JOIN_FAIL type, void *userdata);
void on_group_moderation(Tox *m, uint32_t groupnumber, uint32_t source_peernum, uint32_t target_peernum,
TOX_GROUP_MOD_EVENT type, void *userdata);
#endif /* #define TOXIC_H */

View File

@ -136,13 +136,13 @@ int del_word_buf(ChatContext *ctx)
int i = ctx->pos, count = 0;
/* traverse past empty space */
while (i > 0 && ctx->line[i-1] == L' ') {
while (i > 0 && ctx->line[i - 1] == L' ') {
++count;
--i;
}
/* traverse past last entered word */
while (i > 0 && ctx->line[i-1] != L' ') {
while (i > 0 && ctx->line[i - 1] != L' ') {
++count;
--i;
}
@ -166,19 +166,19 @@ void reset_buf(ChatContext *ctx)
ctx->start = 0;
}
/* Removes trailing spaces from line. */
/* Removes trailing spaces and newlines from line. */
void rm_trailing_spaces_buf(ChatContext *ctx)
{
if (ctx->len <= 0)
return;
if (ctx->line[ctx->len - 1] != ' ')
if (ctx->line[ctx->len - 1] != ' ' && ctx->line[ctx->len - 1] != L'')
return;
int i;
for (i = ctx->len - 1; i >= 0; --i) {
if (ctx->line[i] != ' ')
if (ctx->line[i] != ' ' && ctx->line[i] != L'')
break;
}
@ -242,3 +242,21 @@ void fetch_hist_item(ChatContext *ctx, int key_dir)
ctx->pos = h_len;
ctx->len = h_len;
}
void strsubst(char *str, char old, char new)
{
int i;
for (i = 0; str[i] != '\0'; ++i)
if (str[i] == old)
str[i] = new;
}
void wstrsubst(wchar_t *str, wchar_t old, wchar_t new)
{
int i;
for (i = 0; str[i] != L'\0'; ++i)
if (str[i] == old)
str[i] = new;
}

View File

@ -66,4 +66,8 @@ void add_line_to_hist(ChatContext *ctx);
resets line if at end of history */
void fetch_hist_item(ChatContext *ctx, int key_dir);
/* Substitutes all occurrences of old with new. */
void strsubst(char *str, char old, char new);
void wstrsubst(wchar_t *str, wchar_t old, wchar_t new);
#endif /* #define TOXIC_STRINGS_H */

View File

@ -40,13 +40,13 @@
#define default_video_bit_rate 5000
void receive_video_frame_cb( ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data );
uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data );
void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
uint32_t video_bit_rate, void *user_data);
uint32_t video_bit_rate, void *user_data);
static void print_err (ToxWindow *self, const char *error_str)
{
@ -82,22 +82,24 @@ ToxAV *init_video(ToxWindow *self, Tox *tox)
void terminate_video()
{
int i;
for (i = 0; i < MAX_CALLS; ++i) {
Call* this_call = &CallControl.calls[i];
Call *this_call = &CallControl.calls[i];
stop_video_transmission(this_call, i);
if( this_call->vout_idx != -1 )
if ( this_call->vout_idx != -1 )
close_video_device(vdt_output, this_call->vout_idx);
}
terminate_video_devices();
}
void read_video_device_callback(int16_t width, int16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, void* data)
void read_video_device_callback(int16_t width, int16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
void *data)
{
uint32_t friend_number = *((uint32_t*)data); /* TODO: Or pass an array of call_idx's */
Call* this_call = &CallControl.calls[friend_number];
uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */
Call *this_call = &CallControl.calls[friend_number];
TOXAV_ERR_SEND_FRAME error;
/* Drop frame if video sending is disabled */
@ -117,9 +119,9 @@ void read_video_device_callback(int16_t width, int16_t height, const uint8_t* y,
}
void write_video_device_callback(uint32_t friend_number, uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
{
write_video_out(width, height, y, u, v, ystride, ustride, vstride, user_data);
}
@ -175,16 +177,16 @@ int stop_video_transmission(Call *call, int friend_number)
* Callbacks
*/
void receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
{
write_video_device_callback(friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data);
}
void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
uint32_t video_bit_rate, void *user_data)
uint32_t video_bit_rate, void *user_data)
{
CallControl.video_bit_rate = video_bit_rate;
toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL);
@ -192,7 +194,7 @@ void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_
void callback_recv_video_starting(uint32_t friend_number)
{
Call* this_call = &CallControl.calls[friend_number];
Call *this_call = &CallControl.calls[friend_number];
if ( this_call->vout_idx != -1 )
return;
@ -201,21 +203,22 @@ void callback_recv_video_starting(uint32_t friend_number)
}
void callback_recv_video_end(uint32_t friend_number)
{
Call* this_call = &CallControl.calls[friend_number];
Call *this_call = &CallControl.calls[friend_number];
close_video_device(vdt_output, this_call->vout_idx);
this_call->vout_idx = -1;
}
void callback_video_starting(uint32_t friend_number)
{
ToxWindow* windows = CallControl.prompt;
Call* this_call = &CallControl.calls[friend_number];
ToxWindow *windows = CallControl.prompt;
Call *this_call = &CallControl.calls[friend_number];
TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK;
toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_SHOW_VIDEO, &error);
if (error == TOXAV_ERR_CALL_CONTROL_OK) {
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if ( windows[i].is_call && windows[i].num == friend_number ) {
if ( 0 != start_video_transmission(&windows[i], CallControl.av, this_call) ) {
@ -244,7 +247,7 @@ void callback_video_end(uint32_t friend_number)
void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *error_str;
Call* this_call = &CallControl.calls[self->num];
Call *this_call = &CallControl.calls[self->num];
if ( argc != 0 ) {
error_str = "Unknown arguments.";
@ -387,18 +390,18 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
}
if ( video_selection_valid(type, selection) == vde_InvalidSelection ) {
error_str="Invalid selection!";
error_str = "Invalid selection!";
goto on_error;
}
/* If call is active, change device */
if ( self->is_call ) {
Call* this_call = &CallControl.calls[self->num];
Call *this_call = &CallControl.calls[self->num];
if ( this_call->ttas ) {
if ( type == vdt_output ) {
}
else {
} else {
/* TODO: check for failure */
close_video_device(vdt_input, this_call->vin_idx);
open_video_device(vdt_input, selection, &this_call->vin_idx);
@ -410,7 +413,7 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
self->video_device_selection[type] = selection;
return;
on_error:
on_error:
print_err (self, error_str);
}

View File

@ -62,7 +62,7 @@ struct VideoBuffer {
typedef struct VideoDevice {
VideoDataHandleCallback cb; /* Use this to handle data from input device usually */
void* cb_data; /* Data to be passed to callback */
void *cb_data; /* Data to be passed to callback */
int32_t friend_number; /* ToxAV friend number */
#if defined(__linux__) || defined(__FreeBSD__)
@ -93,7 +93,7 @@ VideoDevice *video_devices_running[2][MAX_DEVICES] = {{NULL}}; /* Running de
uint32_t primary_video_device[2]; /* Primary device */
#ifdef VIDEO
static ToxAV* av = NULL;
static ToxAV *av = NULL;
#endif /* VIDEO */
/* q_mutex */
@ -102,15 +102,16 @@ static ToxAV* av = NULL;
pthread_mutex_t video_mutex;
bool video_thread_running = true,
video_thread_paused = true; /* Thread control */
video_thread_paused = true; /* Thread control */
void* video_thread_poll(void*);
void *video_thread_poll(void *);
static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
const uint8_t *u, const uint8_t *v, unsigned int ystride,
unsigned int ustride, unsigned int vstride, uint8_t *out)
const uint8_t *u, const uint8_t *v, unsigned int ystride,
unsigned int ustride, unsigned int vstride, uint8_t *out)
{
unsigned long int i, j;
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
uint8_t *point = out + 4 * ((i * width) + j);
@ -123,9 +124,9 @@ static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
int g = (298 * (t_y - 16) - 100 * (t_u - 128) - 208 * (t_v - 128) + 128) >> 8;
int b = (298 * (t_y - 16) + 516 * (t_u - 128) + 128) >> 8;
point[2] = r>255? 255 : r<0 ? 0 : r;
point[1] = g>255? 255 : g<0 ? 0 : g;
point[0] = b>255? 255 : b<0 ? 0 : b;
point[2] = r > 255 ? 255 : r < 0 ? 0 : r;
point[1] = g > 255 ? 255 : g < 0 ? 0 : g;
point[0] = b > 255 ? 255 : b < 0 ? 0 : b;
point[3] = ~0;
}
}
@ -133,11 +134,13 @@ static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
#if defined(__linux__) || defined(__FreeBSD__)
static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
uint8_t *input, uint16_t width, uint16_t height)
uint8_t *input, uint16_t width, uint16_t height)
{
uint8_t *end = input + width * height * 2;
while (input != end) {
uint8_t *line_end = input + width * 2;
while (input != line_end) {
*plane_y++ = *input++;
*plane_u++ = *input++;
@ -146,6 +149,7 @@ static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
}
line_end = input + width * 2;
while (input != line_end) {
*plane_y++ = *input++;
input++;//u
@ -170,7 +174,7 @@ static int xioctl(int fh, unsigned long request, void *arg)
/* Meet devices */
#ifdef VIDEO
VideoDeviceError init_video_devices(ToxAV* av_)
VideoDeviceError init_video_devices(ToxAV *av_)
#else
VideoDeviceError init_video_devices()
#endif /* VIDEO */
@ -178,31 +182,34 @@ VideoDeviceError init_video_devices()
size[vdt_input] = 0;
#if defined(__linux__) || defined(__FreeBSD__)
for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) {
int fd;
char device_address[] = "/dev/videoXX";
snprintf(device_address + 10, sizeof(char) * strlen(device_address) - 10, "%i", size[vdt_input]);
fd = open(device_address, O_RDWR | O_NONBLOCK, 0);
if ( fd == -1 ) {
break;
} else {
struct v4l2_capability cap;
char* video_input_name;
char *video_input_name;
/* Query V4L for capture capabilities */
if ( -1 != ioctl(fd, VIDIOC_QUERYCAP, &cap) ) {
video_input_name = (char*)malloc(strlen((const char*)cap.card) + strlen(device_address) + 4);
strcpy(video_input_name, (char*)cap.card);
video_input_name = (char *)malloc(strlen((const char *)cap.card) + strlen(device_address) + 4);
strcpy(video_input_name, (char *)cap.card);
strcat(video_input_name, " (");
strcat(video_input_name, (char*)device_address);
strcat(video_input_name, (char *)device_address);
strcat(video_input_name, ")");
} else {
video_input_name = (char*)malloc(strlen(device_address) + 3);
video_input_name = (char *)malloc(strlen(device_address) + 3);
strcpy(video_input_name, "(");
strcat(video_input_name, device_address);
strcat(video_input_name, ")");
}
video_devices_names[vdt_input][size[vdt_input]] = video_input_name;
close(fd);
@ -210,12 +217,14 @@ VideoDeviceError init_video_devices()
}
#else /* __OSX__ */
if( osx_video_init((char**)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
if ( osx_video_init((char **)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
return vde_InternalError;
#endif
size[vdt_output] = 1;
char* video_output_name = "Toxic Video Receiver";
char *video_output_name = "Toxic Video Receiver";
video_devices_names[vdt_output][0] = video_output_name;
// Start poll thread
@ -223,6 +232,7 @@ VideoDeviceError init_video_devices()
return vde_InternalError;
pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, video_thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0 )
return vde_InternalError;
@ -236,12 +246,16 @@ VideoDeviceError init_video_devices()
VideoDeviceError terminate_video_devices()
{
/* Cleanup if needed */
lock;
video_thread_running = false;
unlock;
usleep(20000);
int i;
for (i = 0; i < size[vdt_input]; ++i) {
free((void*)video_devices_names[vdt_input][i]);
free((void *)video_devices_names[vdt_input][i]);
}
if ( pthread_mutex_destroy(&video_mutex) != 0 )
@ -255,14 +269,19 @@ VideoDeviceError terminate_video_devices()
}
VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx,
VideoDataHandleCallback callback, void* data)
VideoDataHandleCallback callback, void *data)
{
#if defined(__linux__) || defined(__FreeBSD__)
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] || !video_devices_running[vdt_input][device_idx]->fd )
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx]
|| !video_devices_running[vdt_input][device_idx]->fd )
return vde_InvalidSelection;
#else /* __OSX__ */
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] )
return vde_InvalidSelection;
#endif
lock;
@ -283,7 +302,7 @@ VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selectio
return vde_None;
}
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t* device_idx)
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx)
{
return open_video_device(type, primary_video_device[type], device_idx);
}
@ -293,7 +312,7 @@ void get_primary_video_device_name(VideoDeviceType type, char *buf, int size)
memcpy(buf, dvideo_device_names[type], size);
}
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t* device_idx)
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx)
{
if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection;
@ -324,7 +343,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
}
}
VideoDevice* device = video_devices_running[type][temp_idx] = calloc(1, sizeof(VideoDevice));
VideoDevice *device = video_devices_running[type][temp_idx] = calloc(1, sizeof(VideoDevice));
device->selection = selection;
if ( pthread_mutex_init(device->mutex, NULL) != 0 ) {
@ -342,6 +361,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
snprintf(device_address + 10 , sizeof(device_address) - 10, "%i", selection);
device->fd = open(device_address, O_RDWR);
if ( device->fd == -1 ) {
unlock;
return vde_FailedStart;
@ -349,6 +369,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
/* Obtain video device capabilities */
struct v4l2_capability cap;
if ( -1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap) ) {
close(device->fd);
free(device);
@ -362,7 +383,8 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if( -1 == xioctl(device->fd, VIDIOC_G_FMT, &fmt) ) {
if ( -1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt) ) {
close(device->fd);
free(device);
unlock;
@ -378,6 +400,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if ( -1 == xioctl(device->fd, VIDIOC_REQBUFS, &req) ) {
close(device->fd);
free(device);
@ -411,20 +434,22 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
device->buffers[i].length = buf.length;
device->buffers[i].start = mmap(NULL /* start anywhere */,
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
device->fd, buf.m.offset);
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
device->fd, buf.m.offset);
if ( MAP_FAILED == device->buffers[i].start ) {
for (i = 0; i < buf.index; ++i)
munmap(device->buffers[i].start, device->buffers[i].length);
close(device->fd);
free(device);
unlock;
return vde_FailedStart;
}
}
device->n_buffers = i;
enum v4l2_buf_type type;
@ -440,6 +465,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
for (i = 0; i < device->n_buffers; ++i)
munmap(device->buffers[i].start, device->buffers[i].length);
close(device->fd);
free(device);
unlock;
@ -457,11 +483,13 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
}
#else /* __OSX__ */
if ( osx_video_open_device(selection, &device->video_width, &device->video_height) != 0 ) {
free(device);
unlock;
return vde_FailedStart;
}
#endif
/* Create X11 window associated to device */
@ -474,15 +502,15 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
int screen = DefaultScreen(device->x_display);
if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
device->video_width, device->video_height, 0, BlackPixel(device->x_display, screen),
BlackPixel(device->x_display, screen))) ) {
device->video_width, device->video_height, 0, BlackPixel(device->x_display, screen),
BlackPixel(device->x_display, screen))) ) {
close_video_device(vdt_input, temp_idx);
unlock;
return vde_FailedStart;
}
XStoreName(device->x_display, device->x_window, "Video Preview");
XSelectInput(device->x_display, device->x_window, ExposureMask|ButtonPressMask|KeyPressMask);
XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask);
if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) {
close_video_device(vdt_input, temp_idx);
@ -514,14 +542,14 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
int screen = DefaultScreen(device->x_display);
if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen))) ) {
100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen))) ) {
close_video_device(vdt_output, temp_idx);
unlock;
return vde_FailedStart;
}
XStoreName(device->x_display, device->x_window, "Video Receive");
XSelectInput(device->x_display, device->x_window, ExposureMask|ButtonPressMask|KeyPressMask);
XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask);
if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) {
close_video_device(vdt_output, temp_idx);
@ -548,15 +576,15 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
}
__inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
{
VideoDevice* device = video_devices_running[vdt_output][0];
VideoDevice *device = video_devices_running[vdt_output][0];
if ( !device ) return vde_DeviceNotActive;
if( !device->x_window ) return vde_DeviceNotActive;
if ( !device->x_window ) return vde_DeviceNotActive;
pthread_mutex_lock(device->mutex);
@ -591,7 +619,7 @@ __inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
.red_mask = 0xFF0000,
.green_mask = 0xFF00,
.blue_mask = 0xFF,
.data = (char*)img_data
.data = (char *)img_data
};
/* Render image data */
@ -606,7 +634,7 @@ __inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
return vde_None;
}
void* video_thread_poll (void* arg) // TODO: maybe use thread for every input source
void *video_thread_poll (void *arg) // TODO: maybe use thread for every input source
{
/*
* NOTE: We only need to poll input devices for data.
@ -614,18 +642,24 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
(void)arg;
uint32_t i;
while (video_thread_running)
{
while (1) {
lock;
if (!video_thread_running) {
unlock;
break;
}
unlock;
if ( video_thread_paused ) usleep(10000); /* Wait for unpause. */
else
{
for (i = 0; i < size[vdt_input]; ++i)
{
else {
for (i = 0; i < size[vdt_input]; ++i) {
lock;
if ( video_devices_running[vdt_input][i] != NULL )
{
if ( video_devices_running[vdt_input][i] != NULL ) {
/* Obtain frame image data from device buffers */
VideoDevice* device = video_devices_running[vdt_input][i];
VideoDevice *device = video_devices_running[vdt_input][i];
uint16_t video_width = device->video_width;
uint16_t video_height = device->video_height;
uint8_t *y = device->input.planes[0];
@ -644,16 +678,18 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
continue;
}
void *data = (void*)device->buffers[buf.index].start;
void *data = (void *)device->buffers[buf.index].start;
/* Convert frame image data to YUV420 for ToxAV */
yuv422to420(y, u, v, data, video_width, video_height);
#else /* __OSX__*/
if ( osx_video_read_device(y, u, v, &video_width, &video_height) != 0 ) {
unlock;
continue;
}
#endif
/* Send frame data to friend through ToxAV */
@ -663,7 +699,7 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
/* Convert YUV420 data to BGR */
uint8_t *img_data = malloc(video_width * video_height * 4);
yuv420tobgr(video_width, video_height, y, u, v,
video_width, video_width/2, video_width/2, img_data);
video_width, video_width / 2, video_width / 2, img_data);
/* Allocate image data in X11 */
XImage image = {
@ -679,7 +715,7 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
.red_mask = 0xFF0000,
.green_mask = 0xFF00,
.blue_mask = 0xFF,
.data = (char*)img_data
.data = (char *)img_data
};
/* Render image data */
@ -691,15 +727,19 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
free(img_data);
#if defined(__linux__) || defined(__FreeBSD__)
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
unlock;
continue;
}
#endif /* __linux__ */
}
unlock;
}
usleep(1000 * 1000 / 24);
}
}
@ -727,13 +767,16 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
if ( type == vdt_input ) {
#if defined(__linux__) || defined(__FreeBSD__)
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {}
if ( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {}
int i;
for (i = 0; i < device->n_buffers; ++i) {
if ( -1 == munmap(device->buffers[i].start, device->buffers[i].length) ) {
}
}
close(device->fd);
#else /* __OSX__ */
@ -759,14 +802,13 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
free(device);
}
}
else device->ref_count--;
} else device->ref_count--;
unlock;
return rc;
}
void print_video_devices(ToxWindow* self, VideoDeviceType type)
void print_video_devices(ToxWindow *self, VideoDeviceType type)
{
int i;

View File

@ -45,10 +45,11 @@ typedef enum VideoDeviceError {
vde_CaptureError = -9,
} VideoDeviceError;
typedef void (*VideoDataHandleCallback) (int16_t width, int16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, void* data);
typedef void (*VideoDataHandleCallback) (int16_t width, int16_t height, const uint8_t *y, const uint8_t *u,
const uint8_t *v, void *data);
#ifdef VIDEO
VideoDeviceError init_video_devices(ToxAV* av);
VideoDeviceError init_video_devices(ToxAV *av);
#else
VideoDeviceError init_video_devices();
#endif /* VIDEO */
@ -56,20 +57,22 @@ VideoDeviceError init_video_devices();
VideoDeviceError terminate_video_devices();
/* Callback handles ready data from INPUT device */
VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback, void* data);
void* get_video_device_callback_data(uint32_t device_idx);
VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback,
void *data);
void *get_video_device_callback_data(uint32_t device_idx);
VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection);
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t* device_idx);
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx);
/* Start device */
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t* device_idx);
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx);
/* Stop device */
VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx);
/* Write data to device */
VideoDeviceError write_video_out(uint16_t width, uint16_t height, uint8_t const *y, uint8_t const *u, uint8_t const *v, int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
VideoDeviceError write_video_out(uint16_t width, uint16_t height, uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
void print_video_devices(ToxWindow* self, VideoDeviceType type);
void print_video_devices(ToxWindow *self, VideoDeviceType type);
void get_primary_video_device_name(VideoDeviceType type, char *buf, int size);
VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection);

View File

@ -151,8 +151,18 @@ void on_friendadded(Tox *m, uint32_t friendnumber, bool sort)
store_data(m, DATA_FILE);
}
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length,
void *userdata)
void on_group_invite(Tox *m, uint32_t friendnumber, const uint8_t *invite_data, size_t length, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupInvite != NULL)
windows[i].onGroupInvite(&windows[i], m, friendnumber, (char *) invite_data, length);
}
}
void on_group_message(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, void *userdata)
{
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
@ -161,56 +171,151 @@ void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *mes
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupMessage != NULL)
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, msg, length);
windows[i].onGroupMessage(&windows[i], m, groupnumber, peer_id, type, msg, length);
}
}
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length,
void *userdata)
void on_group_private_message(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *message,
size_t length, void *userdata)
{
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) action, length);
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupAction != NULL)
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, msg, length);
if (windows[i].onGroupPrivateMessage != NULL)
windows[i].onGroupPrivateMessage(&windows[i], m, groupnumber, peer_id, msg, length);
}
}
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length,
void *userdata)
void on_group_status_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_USER_STATUS status, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupInvite != NULL)
windows[i].onGroupInvite(&windows[i], m, friendnumber, type, (char *) group_pub_key, length);
if (windows[i].onGroupStatusChange != NULL)
windows[i].onGroupStatusChange(&windows[i], m, groupnumber, peer_id, status);
}
}
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata)
void on_group_peer_join(Tox *m, uint32_t groupnumber, uint32_t peer_id, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupNamelistChange != NULL)
windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change);
if (windows[i].onGroupPeerJoin != NULL)
windows[i].onGroupPeerJoin(&windows[i], m, groupnumber, peer_id);
}
}
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length,
void *userdata)
void on_group_peer_exit(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *partmsg, size_t length,
void *userdata)
{
char msg[MAX_STR_SIZE + 1];
if (length == 0 || !partmsg) {
strcpy(msg, "Quit");
length = strlen(msg);
} else {
length = copy_tox_str(msg, sizeof(msg), (const char *) partmsg, length);
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPeerExit != NULL)
windows[i].onGroupPeerExit(&windows[i], m, groupnumber, peer_id, msg, length);
}
}
void on_group_topic_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *topic, size_t length,
void *userdata)
{
char data[MAX_STR_SIZE + 1];
length = copy_tox_str(data, sizeof(data), (const char *) title, length);
length = copy_tox_str(data, sizeof(data), (const char *) topic, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupTitleChange != NULL)
windows[i].onGroupTitleChange(&windows[i], m, groupnumber, peernumber, data, length);
if (windows[i].onGroupTopicChange != NULL)
windows[i].onGroupTopicChange(&windows[i], m, groupnumber, peer_id, data, length);
}
}
void on_group_peer_limit(Tox *m, uint32_t groupnumber, uint32_t peer_limit, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPeerLimit != NULL)
windows[i].onGroupPeerLimit(&windows[i], m, groupnumber, peer_limit);
}
}
void on_group_privacy_state(Tox *m, uint32_t groupnumber, TOX_GROUP_PRIVACY_STATE privacy_state, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPrivacyState != NULL)
windows[i].onGroupPrivacyState(&windows[i], m, groupnumber, privacy_state);
}
}
void on_group_password(Tox *m, uint32_t groupnumber, const uint8_t *password, size_t length, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPassword != NULL)
windows[i].onGroupPassword(&windows[i], m, groupnumber, (char *) password, length);
}
}
void on_group_nick_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *newname, size_t length,
void *userdata)
{
char name[TOXIC_MAX_NAME_LENGTH + 1];
length = copy_tox_str(name, sizeof(name), (const char *) newname, length);
filter_str(name, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupNickChange != NULL)
windows[i].onGroupNickChange(&windows[i], m, groupnumber, peer_id, name, length);
}
}
void on_group_self_join(Tox *m, uint32_t groupnumber, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupSelfJoin != NULL)
windows[i].onGroupSelfJoin(&windows[i], m, groupnumber);
}
}
void on_group_rejected(Tox *m, uint32_t groupnumber, TOX_GROUP_JOIN_FAIL type, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupRejected != NULL)
windows[i].onGroupRejected(&windows[i], m, groupnumber, type);
}
}
void on_group_moderation(Tox *m, uint32_t groupnumber, uint32_t source_peer_id, uint32_t target_peer_id,
TOX_GROUP_MOD_EVENT type, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupModeration != NULL)
windows[i].onGroupModeration(&windows[i], m, groupnumber, source_peer_id, target_peer_id, type);
}
}
@ -445,10 +550,12 @@ void on_window_resize(void)
}
#ifdef AUDIO
if (w->chatwin->infobox.active) {
delwin(w->chatwin->infobox.win);
w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH);
}
#endif /* AUDIO */
scrollok(w->chatwin->history, 0);
@ -458,19 +565,28 @@ void on_window_resize(void)
static void draw_window_tab(ToxWindow *toxwin)
{
pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert));
pthread_mutex_unlock(&Winthread.lock);
clrtoeol();
printw(" [%s]", toxwin->name);
pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert));
pthread_mutex_unlock(&Winthread.lock);
}
static void draw_bar(void)
{
int y, x;
// save current cursor position
getyx(active_window->window, y, x);
attron(COLOR_PAIR(BLUE));
mvhline(LINES - 2, 0, '_', COLS);
attroff(COLOR_PAIR(BLUE));
@ -508,14 +624,16 @@ static void draw_bar(void)
attroff(A_BOLD);
}
// restore cursor position after drawing
move(y, x);
refresh();
}
void draw_active_window(Tox *m)
{
ToxWindow *a = active_window;
pthread_mutex_lock(&Winthread.lock);
ToxWindow *a = active_window;
a->alert = WINDOW_ALERT_NONE;
pthread_mutex_unlock(&Winthread.lock);
@ -525,6 +643,7 @@ void draw_active_window(Tox *m)
touchwin(a->window);
a->onDraw(a, m);
wrefresh(a->window);
/* Handle input */
bool ltr;

View File

@ -121,11 +121,6 @@ struct ToxWindow {
void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS);
void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t);
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
void(*onGroupAction)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
void(*onGroupNamelistChange)(ToxWindow *, Tox *, int, int, uint8_t);
void(*onGroupTitleChange)(ToxWindow *, Tox *, int, int, const char *, uint8_t);
void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL);
@ -133,6 +128,21 @@ struct ToxWindow {
void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool);
void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t);
void(*onGroupInvite)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t);
void(*onGroupPrivateMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupPeerJoin)(ToxWindow *, Tox *, uint32_t, uint32_t);
void(*onGroupPeerExit)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupNickChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupStatusChange)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_USER_STATUS);
void(*onGroupTopicChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupPeerLimit)(ToxWindow *, Tox *, uint32_t, uint32_t);
void(*onGroupPrivacyState)(ToxWindow *, Tox *, uint32_t, TOX_GROUP_PRIVACY_STATE);
void(*onGroupPassword)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onGroupSelfJoin)(ToxWindow *, Tox *, uint32_t);
void(*onGroupRejected)(ToxWindow *, Tox *, uint32_t, TOX_GROUP_JOIN_FAIL);
void(*onGroupModeration)(ToxWindow *, Tox *, uint32_t, uint32_t, uint32_t, TOX_GROUP_MOD_EVENT);
#ifdef AUDIO
void(*onInvite)(ToxWindow *, ToxAV *, uint32_t, int);
@ -204,8 +214,8 @@ struct infobox {
bool hide;
bool active;
uint64_t lastupdate;
uint64_t starttime;
time_t lastupdate;
time_t starttime;
char timestr[TIME_STR_SIZE];
WINDOW *win;
@ -237,6 +247,7 @@ struct ChatContext {
#endif
uint8_t self_is_typing;
uint8_t pastemode; /* whether to translate \r to \n */
WINDOW *history;
WINDOW *linewin;

View File

@ -57,8 +57,7 @@ struct _Xtra {
Atom expecting_type;
} Xtra;
typedef struct _Property
{
typedef struct _Property {
unsigned char *data;
int read_format;
unsigned long read_num;
@ -97,21 +96,23 @@ Property read_property(Window s, Atom p)
Atom get_dnd_type(long *a, int l)
{
int i = 0;
for (; i < l; i ++) {
if (a[i] != XtraNil) return a[i]; /* Get first valid */
}
return XtraNil;
}
/* TODO maybe support only certain types in the future */
static void handle_xdnd_enter(XClientMessageEvent* e)
static void handle_xdnd_enter(XClientMessageEvent *e)
{
Xtra.handling_version = (e->data.l[1] >> 24);
if ((e->data.l[1] & 1)) {
// Fetch the list of possible conversions
Property p = read_property(e->data.l[0], XdndTypeList);
Xtra.expecting_type = get_dnd_type((long*)p.data, p.read_num);
Xtra.expecting_type = get_dnd_type((long *)p.data, p.read_num);
XFree(p.data);
} else {
// Use the available list
@ -119,7 +120,7 @@ static void handle_xdnd_enter(XClientMessageEvent* e)
}
}
static void handle_xdnd_position(XClientMessageEvent* e)
static void handle_xdnd_position(XClientMessageEvent *e)
{
XEvent ev = {
.xclient = {
@ -143,7 +144,7 @@ static void handle_xdnd_position(XClientMessageEvent* e)
XFlush(Xtra.display);
}
static void handle_xdnd_drop(XClientMessageEvent* e)
static void handle_xdnd_drop(XClientMessageEvent *e)
{
/* Not expecting any type */
if (Xtra.expecting_type == XtraNil) {
@ -172,7 +173,7 @@ static void handle_xdnd_drop(XClientMessageEvent* e)
}
}
static void handle_xdnd_selection(XSelectionEvent* e)
static void handle_xdnd_selection(XSelectionEvent *e)
{
/* DnD succesfully finished, send finished and call callback */
XEvent ev = {
@ -199,12 +200,12 @@ static void handle_xdnd_selection(XSelectionEvent* e)
/* Call callback for every entry */
if (Xtra.on_drop && p.read_num)
{
if (Xtra.on_drop && p.read_num) {
char *sptr;
char *str = strtok_r((char *) p.data, "\n\r", &sptr);
if (str) Xtra.on_drop(str, dt);
while ((str = strtok_r(NULL, "\n\r", &sptr)))
Xtra.on_drop(str, dt);
}
@ -212,7 +213,7 @@ static void handle_xdnd_selection(XSelectionEvent* e)
if (p.data) XFree(p.data);
}
void *event_loop(void* p)
void *event_loop(void *p)
{
/* Handle events like a real nigga */
@ -221,30 +222,27 @@ void *event_loop(void* p)
XEvent event;
int pending;
while (Xtra.display)
{
while (Xtra.display) {
/* NEEDMOEVENTSFODEMPROGRAMS */
XLockDisplay(Xtra.display);
if((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event);
if (!pending)
{
if ((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event);
if (!pending) {
XUnlockDisplay(Xtra.display);
usleep(10000);
continue;
}
if (event.type == ClientMessage)
{
if (event.type == ClientMessage) {
Atom type = event.xclient.message_type;
if (type == XdndEnter) handle_xdnd_enter(&event.xclient);
else if (type == XdndPosition) handle_xdnd_position(&event.xclient);
else if (type == XdndDrop) handle_xdnd_drop(&event.xclient);
else if (type == XtraTerminate) break;
}
else if (event.type == SelectionNotify) handle_xdnd_selection(&event.xselection);
} else if (event.type == SelectionNotify) handle_xdnd_selection(&event.xselection);
/* AINNOBODYCANHANDLEDEMEVENTS*/
else XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event);
@ -256,6 +254,7 @@ void *event_loop(void* p)
* otherwise HEWUSAGUDBOI happens
*/
if (Xtra.display) XCloseDisplay(Xtra.display);
return (Xtra.display = NULL);
}
@ -267,10 +266,16 @@ int init_xtra(drop_callback d)
else Xtra.on_drop = d;
XInitThreads();
if ( !(Xtra.display = XOpenDisplay(NULL))) return -1;
Xtra.terminal_window = focused_window_id();
/* OSX: if focused window is 0, it means toxic is ran from
* native terminal and not X11 terminal window, silently exit */
if (!Xtra.terminal_window)
return 0;
{
/* Create an invisible window which will act as proxy for the DnD operation. */
XSetWindowAttributes attr = {0};
@ -295,15 +300,15 @@ int init_xtra(drop_callback d)
&root, &x, &y, &wht, &hht, &b, &d);
if (! (Xtra.proxy_window = XCreateWindow
(Xtra.display, Xtra.terminal_window, /* Parent */
0, 0, /* Position */
wht, hht, /* Width + height */
0, /* Border width */
CopyFromParent, /* Depth */
InputOnly, /* Class */
CopyFromParent, /* Visual */
CWEventMask | CWCursor, /* Value mask */
&attr)) ) /* Attributes for value mask */
(Xtra.display, Xtra.terminal_window, /* Parent */
0, 0, /* Position */
wht, hht, /* Width + height */
0, /* Border width */
CopyFromParent, /* Depth */
InputOnly, /* Class */
CopyFromParent, /* Visual */
CWEventMask | CWCursor, /* Value mask */
&attr)) ) /* Attributes for value mask */
return -1;
}
@ -330,9 +335,10 @@ int init_xtra(drop_callback d)
XA_ATOM,
32,
PropModeReplace,
(unsigned char*)&XdndVersion, 1);
(unsigned char *)&XdndVersion, 1);
pthread_t id;
if (pthread_create(&id, NULL, event_loop, NULL) != 0)
return -1;
@ -343,7 +349,7 @@ int init_xtra(drop_callback d)
void terminate_xtra()
{
if (!Xtra.display) return;
if (!Xtra.display || !Xtra.terminal_window) return;
XEvent terminate = {
.xclient = {

View File

@ -31,7 +31,7 @@ typedef enum {
}
DropType;
typedef void (*drop_callback) (const char*, DropType);
typedef void (*drop_callback) (const char *, DropType);
int init_xtra(drop_callback d);
void terminate_xtra();