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

Compare commits

...

349 Commits

Author SHA1 Message Date
22d60232fb Bump to version 0.8.4 2020-10-22 22:14:33 -04:00
e428879beb Fix clashing key bindings
Scrolling the groupchat peer list now uses ctrl+up and down
2020-10-13 19:52:43 -04:00
3015138a5a Manually attempt to decode input char sequences
This is currently a fallback method for when the terminal doesn't
detect ctrl arrow sequences, but it is generalized for future additions
2020-10-13 16:12:55 -04:00
9c06ad608b Add ability to skip words in input field with ctrl-left/right arrow 2020-10-10 11:21:15 -04:00
015dbd9a96 fix: Fix non-working bell notifications
The problem with the bell notifications only happens when Toxic is
compiled with X11 support, but does not run under X. This commit changes
toxic's behavior such that it behaves identically when not running under an
X session, no matter if it has been compiled with X11 support or not.
2020-08-24 16:36:52 +02:00
a7466c3142 Add option to disable LAN 2020-08-10 21:56:45 -04:00
f012007cc4 A couple small fixes 2020-05-19 21:44:35 -04:00
dcf3baf60f Make sure logger file is closed after tox_kill 2020-05-18 13:58:55 -04:00
4bda799a4b Add the ability to log to file (#84) 2020-05-16 23:12:54 -04:00
bdeae33d48 Add macOS build.
Lots of features disabled, because I can't get it to work yet. We'll
enable features one by one as we get them to work.
2020-05-05 00:47:08 +01:00
47591d5298 chore: Use global release-drafter config. 2020-05-05 00:25:10 +01:00
b5ace27a3e chore: Remove astyle from travis build.
The astyle check is now done by restyled.
2020-05-04 23:50:31 +01:00
b334622d36 Add release-drafter config. 2020-05-04 22:01:33 +01:00
4bfb344caa Add option for toxcore logging in stderr 2020-05-04 16:15:28 -04:00
16d96d6faf Add restyled config for toxic. 2020-05-04 20:54:58 +01:00
0ab2bad226 Add CODEOWNERS and settings.yml files. 2020-04-25 03:40:02 +00:00
68db926f9f Check that files are formatted correctly.
Fail the CI build if they are not.
2020-04-23 01:16:57 +00:00
b270c1e8b7 Remove extra semicolon in lock/unlock macros.
lock/unlock are always expanded in a context followed by a semicolon.
2020-04-23 01:12:34 +00:00
e7142e49fd Terminate notify before windows
This fixes a use after free bug
2020-04-22 20:54:53 -04:00
610906d07f fix segfault on starting video 2020-04-17 00:00:00 +00:00
6f72a191ba Revert "Add feature flags and missing #includes."
This reverts commit dd5fa236ae.

Also, set `-std=gnu99` in Bazel build.
2020-04-17 19:37:22 +00:00
dd5fa236ae Add feature flags and missing #includes.
These are needed to compile on some strict Linuxes.
2020-04-15 21:34:30 +00:00
51e1ab94b3 Add QR disabling options to INSTALL.md 2020-04-04 23:20:52 -03:00
ddc8c53abf Fix unused variable warnings and change all (void)s to UNUSED macro 2020-03-30 16:02:27 -04:00
46513017e3 Half-hearted attempt at making toxic work on osx.
Video has no chance without X11 for now.
2020-03-30 18:14:43 +00:00
98cb7f58c0 Fix unused parameter and unused result warnings 2020-03-15 14:57:00 -04:00
206bf407fd Use rules_cc instead of native cc_binary rules. 2020-03-12 12:23:57 +00:00
0a8ac4de3b Fix yamllint warning about missing document start marker.
YAML apparently likes to have this marker, so let's have it.
2020-03-10 14:57:12 +00:00
87d54acad0 Add sponsorship button 2020-03-07 13:19:10 -05:00
45ff6d8bac Migrate to Camel_Case enum types.
UPPER_CASE enum types are deprecated and will be removed in 0.3.0.
2020-03-07 18:11:41 +00:00
437dd8baeb Some misc fixes 2020-02-29 14:14:56 -05:00
b080236ee5 Fix typo 2020-02-10 17:16:56 -05:00
116bff8cef format fixes 2019-12-07 23:17:02 -05:00
ddeca171a0 Add input line text to history on UP and DOWN 2019-12-07 18:29:33 -03:00
127f9462e0 make friend add fail if friend is in the block list 2019-11-16 01:55:47 -05:00
4b5a9abbd4 Fix A/V bug preventing the caller from being properly informed of call termination 2019-07-12 11:51:08 -04:00
bb2257973e Load conference titles on startup for saved conferences (#43) 2019-06-30 14:51:13 -04:00
12b9cd2386 Fix header issues 2019-03-27 01:38:15 -04:00
2cbe8fa880 Use correct integer types to fix groupchat peer scrolling bug 2019-03-26 23:05:24 -04:00
2e39bee05a Check for openal lib before attempting to build with video support 2019-03-15 16:05:54 -04:00
05eda76643 Partially fix autocomplete behaviour for input with spaces 2019-02-17 17:25:34 -05:00
f7b73af9a7 Add config option for the notification timeout duration 2018-11-10 08:54:59 -05:00
73aaa44d12 Add config option to set data file auto-save frequency 2018-10-30 18:22:30 -04:00
e4abd8b36b Bump version to 0.8.3 2018-10-30 18:03:44 -04:00
9e3d4f3889 Check if pointer is null before accessing 2018-10-28 07:09:37 -04:00
b7d67c1d86 re-init AV for unblocked contacts 2018-10-20 02:56:37 -04:00
c4a11f8dc7 Refactor ToxWindows
ToxWindow constructor functions now return pointers instead of structs
ToxWindow windows array now holds pointers instead of structs
Refactor some logic in windows.c pertaining to keeping track of active windows
Use uint8_t for window array indexing
2018-10-19 17:43:06 -04:00
d18cc8cbc2 Fix two memory leaks and properly clean up friendlist window 2018-10-19 17:43:03 -04:00
ce6d4861fb Fix bug where autocomplete added a forward slash to files with no extension
Also slightly refactored the line complete functions
2018-10-17 18:00:09 -04:00
8f0e6026f0 Add enum for file_type() and a little cleanup 2018-10-17 18:00:08 -04:00
258736995d Remove ability to set note with status command 2018-10-17 18:00:08 -04:00
56e03a3f8b No longer require quotes to enclose paths 2018-10-17 18:00:08 -04:00
b6c746b5f5 No longer require quotes for commands that take strings as arguments 2018-10-17 18:00:07 -04:00
03673cbced Improve readability of INSTALL.md 2018-10-11 14:22:37 -04:00
0fea930c24 Convert deprecated uppercase enums to camelcase 2018-10-10 13:34:17 -04:00
94d22a8853 Allow empty status messages 2018-10-09 18:21:05 -04:00
63cc23401a Improve readability of defines 2018-10-08 13:39:04 -04:00
f90a774470 Make Toxic easier to port 2018-10-03 22:00:43 +00:00
e7c5fbc873 Cleanup xtra.c
Privatise some functions that don't need to be public
Fix struct names starting with _ char
Remove unnecessary memset of Xtra struct in init_xtra()
2018-09-27 03:41:58 -04:00
d62902ffb3 Use (void) in prototypes instead of ().
Also, removed `__inline` from an extern function (why was it there?).
Also, moved one extern declaration to a header file. There are lots of
these that need to be moved, but one thing at a time.
2018-09-13 23:47:47 +00:00
bebff3be0e Use (void) for empty parameter list in C. 2018-09-08 17:23:07 +00:00
2be4847b53 Ignore warning about unused result.
These should be fixed, but for now we leave it as warning, not error.
2018-09-06 13:46:24 +00:00
4557614443 Enable .travis.yml check and use non-markdown license. 2018-08-17 23:49:01 +00:00
5b30ecf2e4 Use @x11 instead of the longer @x11//:X11 to link against xlib. 2018-08-14 09:55:54 +00:00
2413ad2b59 Use bazel import libs instead of global -l flags.
Import libs currently also point to the global system installed versions,
but at least we have the freedom to import them properly later.
2018-08-05 19:17:57 +00:00
52855b805a Add astyle check to travis. 2018-07-18 17:19:43 +00:00
20b5e46850 Rename callback implementations to be of the form on_$event.
So it's easy to map from implementation to the event without looking at
the `tox_callback_*` calls.
2018-07-18 17:06:28 +00:00
f2b796940e Add missing braces to if/for/while blocks.
Astyle doesn't quite catch all of them.
2018-07-18 15:56:21 +00:00
a37bf300f9 Update astylerc to match toxcore & reformat all source files 2018-07-18 11:33:16 -04:00
cb524dcbc3 Merge branch 'iphydf-local-libconfig' 2018-07-16 10:18:44 -04:00
4144b868ce Use libconfig built in the WORKSPACE rather than system installed.
Improves hermeticity and reproducibility.
2018-07-15 15:34:19 +00:00
af11f16bef fix merge mistake take 2 2018-07-07 10:29:12 -04:00
1d27a496ef fix merge mistake 2018-07-06 15:43:32 -04:00
32bd9dc1a7 Merge branch 'TokTok-master' 2018-07-06 12:35:46 -04:00
3cd2bc7e3c Don't compile A/V code if AUDIO/VIDEO are not #defined. 2018-07-06 15:46:12 +00:00
43ca840658 Conditionally compile api.c and python_api.c contents.
Based on `-DPYTHON`, instead of based on which files are listed in the
source list. This simplifies people's lives when compiling from the
command line with `cc *.c`.
2018-07-06 14:42:33 +00:00
685837357b Add bazel build file for toxic. 2018-07-06 14:41:16 +00:00
812a13b0fb Merge branch 'fx-carton-master' 2018-06-17 20:27:57 -04:00
641fa471d2 Merge branch 'master' of https://github.com/fx-carton/toxic into fx-carton-master 2018-06-17 20:23:12 -04:00
8d5755f2d8 Merge branch 'nieldk-master' 2018-06-17 20:21:05 -04:00
af510b6666 Merge branch 'master' of https://github.com/nieldk/toxic into nieldk-master 2018-06-17 20:09:14 -04:00
46f646afcf Add an option to disable qr codes 2018-06-16 05:07:16 +02:00
68ce17a57f Temp fix for nick change spam on group join 2018-04-19 22:23:49 -04:00
a69fad15c1 Use default timestamp format if provided one is invalid 2018-04-10 17:47:21 -04:00
7621fe9a62 fix macOS build (#494) 2018-03-31 21:10:17 -04:00
f6d9bc3a74 Update check_features.mk
Changed cfg/checks/check_features.mk to enable individual Audio/Video checks by adding proper DISABLE_VI option which takes "0" or "1" for disable/enable video checks/building.
2018-03-19 22:31:31 +01:00
29aea0b42c Remove libtoxav from install instructions as it's now part of the toxcore lib 2018-03-03 23:55:54 -05:00
815c29ee31 Significantly reduce time before trying to re-send a message with no read receipt
(also some formatting cleanup)
2018-03-03 23:54:12 -05:00
3917f664bf Bump to v0.8.2 2018-03-01 19:14:14 -05:00
a223329815 Fix persistent groupchat loading 2018-02-27 18:46:56 -05:00
3fec11d5f9 Update readme 2018-02-26 03:03:12 -05:00
221edb0012 add a .travis file and some build fixes 2018-02-26 02:34:38 -05:00
2710ab6034 Re-implement group nick change notifications 2018-02-25 03:43:19 -05:00
bc3ffac0ba re-add group peer list sorting 2018-02-25 01:21:22 -05:00
29f55c5277 Update API
-Removed usage of deprecated API functions
-Integrated conference changes with a few regressions
2018-02-25 00:00:06 -05:00
a290f0f7f8 Update toxav API calls 2018-02-24 15:38:07 -05:00
5cd196a769 Fix autocomplete bug
Formatting was messed up due to not accounting for null byte in directory list
2018-02-20 20:54:26 -05:00
b14d983a8c Merge branch 'iphydf-opaque-options' 2018-01-20 15:20:25 -05:00
51f1daeec8 Treat Tox_Options as opaque struct: no deref, no alloc.
We allocate it inside toxcore and dereference it inside as well. This
allows us to change the layout of that struct and add new members.
2018-01-20 19:51:13 +00:00
b799c6a8d7 Merge branch 'iphydf-fix-typedefs' 2018-01-20 13:34:53 -05:00
b9f9546e2b Fix typedef enums and potential uninitialised value. 2018-01-20 18:30:35 +00:00
846bc4613e Fix comment 2017-11-20 07:07:24 -05:00
a5a1f6015d Merge branch 'calls_fix' 2017-11-20 07:03:22 -05:00
fe6a7074ea Dynamically allocate audio calls structure
This fixes a bug that caused a segfault when attempting to call a friend with a
friend number above the MAX_CALLS limit
2017-11-20 07:02:56 -05:00
db7c9fe426 Merge branch 'master' of https://github.com/JFreegman/toxic 2017-11-17 18:17:47 -05:00
b1d8ab102f Bump to version 0.8.1 2017-11-17 17:44:56 -05:00
0bd5b4ddee Repair operating system checks (#474) 2017-11-16 16:59:51 -05:00
c387df35f8 Merge branch 'f8l-pkgsrc' 2017-11-16 16:03:20 -05:00
351a50c214 Do not assume what is not Linux or a BSD descendant is OSX 2017-11-15 21:02:10 +01:00
93175314b5 Merge branch 'aSourceFish-master' 2017-11-15 02:25:55 -05:00
b905a1a3c5 One more bit of standard... 2017-11-15 05:56:13 +02:00
c4386b195f A bit of standard... 2017-11-15 05:54:46 +02:00
ed1e617380 Make Toxic compile at NetBSD, STAGE 2 2017-11-15 05:50:00 +02:00
1382adb1f6 Make Toxic compile at NetBSD, STAGE 1 2017-11-15 05:46:47 +02:00
ecf1c317b7 Make Toxic compile at NetBSD, STAGE 0 2017-11-15 05:24:19 +02:00
cf0b99f1e5 Merge branch 'aramchandran-master' 2017-11-13 15:37:40 -05:00
3605a296a9 Update install.mk 2017-11-13 13:30:22 -05:00
9375d220f9 Merge branch 'aSourceFish-master' 2017-11-11 16:51:34 -05:00
8f94b0a218 Shut down the warning at *BSD 2017-11-11 21:17:34 +02:00
85a0becbf9 Make Toxic compile at OpenBSD 2017-11-11 21:04:45 +02:00
fec36ad9e6 Make Toxic compile at OpenBSD 2017-11-11 21:03:28 +02:00
ecdf6f01d2 Merge branch 'EnniRosario-refresh_devices' 2017-10-31 16:24:53 -04:00
e1bfa30769 Refresh device list on /lsdev. 2017-10-31 18:08:06 +02:00
ebcbc7497b Indicate selected device when printing. 2017-10-31 17:45:39 +02:00
e844ece28b Merge branch 'zugz-reduceRedrawing' 2017-10-29 16:00:46 -04:00
8508451ba6 avoid unnecessary redrawing 2017-10-29 16:50:42 +01:00
5cc83a7cb5 Merge branch 'patheticpat-fix-tmux' 2017-09-05 17:43:13 -04:00
febc725763 Fix tmux detached detection
The old code failed if the session had a custom name instead of the
default numeric id. To be safe in both cases, look for the session_id in
the tmux list-sessions output.
2017-09-05 12:49:48 +02:00
f2c116feb3 Fix potential string truncations with snprintf 2017-09-01 17:36:17 -04:00
52dd60dc86 Fix potential int truncation and double-check lengths before copy 2017-08-28 19:37:19 -04:00
80c0500299 Fix formatting bug caused by strings containing \r 2017-08-28 18:26:52 -04:00
ab490d28b4 Merge branch 'avoidr-status_cmd_feedback' 2017-08-28 17:13:53 -04:00
a9f7f85617 /status: give feedback to user on status change 2017-08-27 12:27:04 +02:00
1bfc1ba371 Merge branch 'Dako300-master' 2017-07-07 20:03:07 -04:00
2ede39369a added /bitrate 2017-07-07 23:47:59 +00:00
922c184195 Only include python libs when necessary 2017-06-06 19:03:40 -04:00
56a9571509 Make sure message id fits inside a signed int 2017-06-02 01:15:44 -04:00
0136f22076 Fix UI bugs & format
This fixes a bug where lines would sometimes be incorrectly marked as unread, as well as
a bug where inbound messages would sometimes be coloured incorrectly
2017-06-01 16:46:12 -04:00
c4ace288af Bump to v0.8.0 2017-05-24 15:10:50 -04:00
6d3fbfee59 Merge branch 'TsarFox-master' 2017-05-24 14:57:32 -04:00
=
be5e7906da Fix fortune.py input issues 2017-05-24 10:10:18 -04:00
369f26932e Fix error messages being printed when autorun_path is unset. 2017-05-23 19:12:53 -04:00
22ea522baf Add "/run" to autocompleter and fix example Python script. 2017-05-22 20:47:56 -04:00
4f60d546e6 Disable Python support in the default build 2017-05-21 16:18:39 -04:00
76d1eafdc0 Change visibility on Python commands list 2017-05-20 08:18:28 -04:00
37912f2d88 Update INSTALL.md 2017-05-19 21:09:29 -04:00
09710327c5 Merge branch 'Dako300-master' 2017-05-19 20:05:52 -04:00
acee4615f8 double signal handler prevents accidental closes 2017-05-19 14:38:39 -05:00
5ed26eda9b Added constants to the Python interface for calls to execute. 2017-05-19 15:35:31 -04:00
6d2b90ac9f Error reporting on failure of invoke_autoruns and solved issue with call to Py_FinalizeEx 2017-05-18 21:37:28 -04:00
02ea0fac44 get_status returns a string rather than an integer. 2017-05-17 08:39:29 -04:00
7d3d129624 Updated code style 2017-05-16 23:19:39 -04:00
b3ed8bc35c Finalized and documented the Python scripting interface. 2017-05-16 20:31:23 -04:00
90210daca7 Python interface for getting the user's nickname/status and executing commands 2017-05-15 22:12:28 -04:00
0e13a1f1bc Implemented a basic Python scripting engine, optional compilation. 2017-05-15 20:29:00 -04:00
09e2690211 Fixed INSTALL formatting (#443) 2017-04-12 12:00:43 -04:00
e65e3af274 Merge branch 'peetcamron-fix/fflush-for-musl' 2017-01-28 00:45:28 -05:00
c6c60d018e Added some fflush after the printf with no \n so we don't have a blank
screen when compiled with musl.
2017-01-28 00:06:03 -05:00
451d4ced80 Add option that prints toxic and toxcore version 2017-01-25 22:13:59 -05:00
7a7402ff86 Update text files with new toktok toxcore link 2016-12-21 17:08:32 -05:00
600e013adc Bump to version 0.7.2 2016-12-18 00:11:24 -05:00
1d71e2eb18 Port to toktok-c-toxcore branch 2016-12-18 00:01:51 -05:00
f858714edd Fix small nodeslist parsing bug 2016-10-23 12:26:18 -04:00
4df44a7274 Merge branch 'tharvik-master' 2016-10-18 12:21:58 -04:00
a26ed9d28f add missing implicit include 2016-10-18 09:38:28 +02:00
2bd5083b8f Corrected brew errors (#413)
Corrected brew errors
2016-10-07 13:24:31 -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
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
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
9f74d3a3a8 Fix memory leak 2016-06-29 21:36: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
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
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
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
74c1eef1d1 Bump version to 0.7.0 2015-11-12 17:18:37 -05:00
65c07a57db Properly close audio devices on quit 2015-11-12 17:14:10 -05:00
ab99c1ac73 Fix building on FreeBSD (courtesy of thierry-FreeBSD) 2015-11-12 05:04:37 -05:00
05f5f16af3 Fix various video bugs; it should be working now! 2015-11-12 05:01:28 -05:00
d16be574f3 Add stack protection compile flag to Makefile 2015-11-10 23:34:46 -05:00
7e0b8b4870 Slight improvement to qrcode printing 2015-11-09 02:28:41 -05:00
39c4b7ecdd Add ability to print your Tox ID's QR code to a file 2015-11-08 22:51:46 -05:00
c5d9aca3e1 Add coverity badge to readme 2015-11-08 03:59:12 -05:00
fa0e645a79 Fix a bunch of misc bugs and corner cases 2015-11-08 03:57:01 -05:00
14a8bdb874 Store temp data in same directory as original file 2015-11-07 21:38:32 -05:00
93a73cbef2 Merge pull request #351 from mneumann/fix_build_dragonfly
Fix compile for DragonFlyBSD
2015-11-07 17:26:30 -05:00
6aab9a79d8 Fix help menu 2015-11-07 01:00:56 -05:00
dfff777283 Save data in a safer manner
By saving to a temp file then renaming it we avoid the possibility of data corruption
due to an interrupt (hard reboot, power outage etc.)
2015-11-05 17:46:00 -05:00
a95fc7824c Fix bug causing profile to be overwritten under a rare condition 2015-11-05 16:15:59 -05:00
f707dce2da Disable recv video callback for now 2015-11-05 15:49:35 -05:00
0d07d14b13 Remove unnecessary/misleading line print 2015-11-05 15:28:24 -05:00
6cc1525daa Fix some build issues 2015-11-03 23:04:05 -05:00
49f5efaab0 A couple audio fixes (ugly but works)
- You will now receive an incoming call even if the caller's chat window is not opened
- Callbacks should all be working now
- Disable video command as video is currently broken
2015-11-03 20:36:41 -05:00
a5e5e98afc Disabled video calls for the time being 2015-11-03 23:50:01 +01:00
4ab99c73a0 Merge with master 2015-11-02 15:22:52 -05:00
e02cf1bb7d Use proxy for DNS requests when type is SOCKS5 2015-11-02 02:01:17 -05:00
9751cfc407 Force name lookups through proxy set by client 2015-11-01 21:34:42 -05:00
36963a5b38 rm redundant error message 2015-10-30 00:10:38 -04:00
7cf9c37aef Handle case where system doesn't support any of the specified TLS cipher suites 2015-10-29 20:35:31 -04:00
2b4b8c0289 Add more descriptive curl errors 2015-10-29 20:16:35 -04:00
368a1465ec Use a specified TLS cipher list for https name lookups 2015-10-29 15:13:42 -04:00
fea317ee24 Remove travis script. Goodbye old friend 2015-10-28 21:46:21 -04:00
8584feab80 Replace DNS-based username lookups with HTTPS & drop utox.org support 2015-10-28 17:14:17 -04:00
f7e48d294e Update README.md 2015-10-23 20:35:15 -04:00
928f25bd89 Update README.md 2015-10-23 20:34:08 -04:00
941ac1d951 Update DHTnodes 2015-10-23 04:40:33 -04:00
7af9327b37 Add note about logs not being encrypted 2015-10-22 19:55:48 -04:00
6b97df2615 Add command to change the Tox ID nospam value 2015-10-22 19:44:05 -04:00
cea5f1fe04 Fix avatar size limit and fix comment 2015-10-20 22:47:11 -04:00
abfdbfe468 Fix some new AV api changes 2015-10-20 17:59:14 -04:00
462cfca175 Merge new AV branch 2015-10-20 17:57:54 -04:00
db410cb01e Use profile name that conforms with the standard 2015-10-14 23:09:11 -04:00
a920f3edfe Replace toxme.se with toxme.io in DNSservers list and remove hardcoded servers from source 2015-09-12 21:31:19 -04:00
2c3921a9fb Fix compile for DragonFlyBSD 2015-09-09 13:23:48 +02:00
f295352495 Replace instances of unsafe atoi function with safe counterpart 2015-09-02 19:41:21 -04:00
ffcc804efe Moved version up 2015-09-01 13:00:56 -04:00
4e2db756be Merge remote-tracking branch 'upstream/master' 2015-08-21 00:52:27 -05:00
19cfe3d393 Display a user warning when log fails to initialize 2015-08-21 00:44:40 -05:00
c546df3917 Fix some filetransfer issues
- File transfers now timeout properly
- Small refactor related to creating new transfers
2015-08-21 00:44:40 -05:00
ed0a4fb3b8 simplify timeout function 2015-08-21 00:44:40 -05:00
271ca08eb2 fix a broken link 2015-08-21 00:44:40 -05:00
0e79b8a076 Update DHTnodes 2015-08-21 00:44:40 -05:00
1606d01158 Implemented video calls for OSX 2015-08-21 00:44:25 -05:00
c8a9ac21f3 Implemented OSX device listing 2015-08-21 00:44:25 -05:00
e91aaf6c73 Refactoring to compliment OSX 2015-08-21 00:44:13 -05:00
619fdc1098 Refactored cmd_video function 2015-08-21 00:44:12 -05:00
b7e613de32 Small fix to video cleanup 2015-08-21 00:44:12 -05:00
929fca3de1 Prevented user from manually closing video windows 2015-08-21 00:44:12 -05:00
b67792f9f2 Clear warnings from device listing fix 2015-08-21 00:44:12 -05:00
96162bf254 Fixed incorrect video device listing 2015-08-21 00:43:47 -05:00
8a66c3fa4c Changed to toggle video command rather than two separate commands 2015-08-21 00:43:47 -05:00
2cdcbc07a7 Fixed video call receiving issues 2015-08-21 00:43:30 -05:00
6e0d19b01d Refactored video calls 2015-08-21 00:42:57 -05:00
ad04fa4dcd Major refactoring and fixes towards AV 2015-08-21 00:42:28 -05:00
c2c612b85a Fixed v4l2 capture crash, and refactored to prepare for new features 2015-08-21 00:40:09 -05:00
d359ba6a54 Fixed YUV stride issues 2015-08-21 00:38:44 -05:00
54e2fe8d6f Implemented video frames ready to send through ToxAV. NOTE: Contains YUV stride issues 2015-08-21 00:37:57 -05:00
53353825e2 Implemented /endvideo command 2015-08-21 00:37:57 -05:00
fcdc8e8b67 Fixed incorrect colors during video capture 2015-08-21 00:37:23 -05:00
9b6efb65de Implemented video preview and preparations for ToxAV 2015-08-21 00:37:23 -05:00
c8ea02376e Progress on video call windows using X11 2015-08-21 00:36:52 -05:00
2369b5e9e2 Modified header files structuring 2015-08-21 00:36:28 -05:00
8f28f1d748 Progress on implementing v4l2 in video_device.* 2015-08-21 00:36:28 -05:00
a33e5f4bec Fixes to av.mk 2015-08-21 00:36:13 -05:00
e0a35a6569 Obtain names of video devices using v4l 2015-08-21 00:36:13 -05:00
9863dfc2ae Began implementing video_device.* 2015-08-21 00:35:55 -05:00
c755247434 Added video_device.h file 2015-08-21 00:35:55 -05:00
879b2b236e <not having red text 2015-08-21 00:35:55 -05:00
c6b9a288b6 Send file control cancel on failure to resume transfer 2015-08-21 00:35:55 -05:00
e9e5b5af8d Fix file transfers breaking when friends go offline 2015-08-21 00:35:55 -05:00
d175ff2480 Remove links to libtoxcore.so from readme 2015-08-21 00:35:55 -05:00
750258adef fix rare bug preventing toxcore from sleeping 2015-08-21 00:35:55 -05:00
60b4d62657 Makefile: allow overriding pkg-config 2015-08-21 00:35:55 -05:00
ea78dca756 Update dht nodes list and fix URL's 2015-08-21 00:35:54 -05:00
3cb412632b Add option to enable acting as TCP relay server 2015-08-21 00:35:54 -05:00
8301ab1bc2 Revert commit 312d0c3 (localization/gettext)
There were serious problems with branch merging that need to be resolved first
2015-08-21 00:35:40 -05:00
b6e90d2ebb update translations 2015-08-21 00:30:55 -05:00
06c268417f Fix some possible race conditions related to line printing 2015-08-21 00:29:33 -05:00
1458a6bbc5 s/tox.im/tox.chat/g -- reflect new tox domain 2015-08-21 00:29:32 -05:00
737d29864b Add localization system (gettext) 2015-08-21 00:29:26 -05:00
0a2ad23c15 Add localization system (gettext) 2015-08-21 00:24:02 -05:00
a455c80a1f travis.yml: update dependencies 2015-08-21 00:14:03 -05:00
43bda5f7d9 Refactoring and fix related to invite callback not being fired 2015-08-21 00:14:03 -05:00
f2121fae74 Implemented audio calls using new ToxAV API 2015-08-21 00:14:03 -05:00
3241551cfb Began conversion of audio code to new ToxAV API 2015-08-21 00:14:03 -05:00
6e90072fb8 Implemented OpenCV demo to demonstrate video device listing and set up code structure for video 2015-08-21 00:14:03 -05:00
ca1fca5aa5 separate bootstrapping and adding TCP relays per toxcore API changes 2015-08-21 00:14:03 -05:00
ef1068b6aa toxcore API changes to tox_new 2015-08-21 00:14:02 -05:00
72982cee97 default tox options on initialization 2015-08-21 00:14:02 -05:00
9a4eaa8693 correctly handle tox_new errors 2015-08-21 00:14:02 -05:00
64e7553fb0 not clear which error message is given for ipv6 failure 2015-08-21 00:14:02 -05:00
dd8df1df76 try to fall back to ipv4 if tox fails to init with ipv6 2015-08-21 00:14:02 -05:00
c8d102b02d null terminate status message 2015-08-21 00:14:02 -05:00
a3fa7fd524 fix possible segfault 2015-08-21 00:14:02 -05:00
b2ed8c0ead Makefile: try to fix Tox/toxic#307
@henriqueleng please test it and report back if it works for you.
2015-08-21 00:14:02 -05:00
bbdf4c96b9 fix a few avatar bugs 2015-08-21 00:14:02 -05:00
5496890b34 Update .travis.yml 2015-08-21 00:14:02 -05:00
fd85d8f87b Makefile: add uninstall target 2015-08-21 00:14:02 -05:00
37e7b4c3d3 Update .travis.yml 2015-08-21 00:14:02 -05:00
117 changed files with 10994 additions and 3996 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
/.github/ @TokTok/admins

2
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,2 @@
---
github: [JFreegman]

17
.github/settings.yml vendored Normal file
View File

@ -0,0 +1,17 @@
---
_extends: .github
repository:
name: toxic
description: An ncurses-based Tox client
topics: tox, console, chat
branches:
- name: "master"
protection:
required_status_checks:
contexts:
- Codacy/PR Quality Review
- CodeFactor
- Travis CI - Pull Request
- code-review/reviewable

1
.gitignore vendored
View File

@ -16,3 +16,4 @@ stamp-h1
build/toxic
build/*.o
build/*.d
apidoc/python/build

4
.restyled.yaml Normal file
View File

@ -0,0 +1,4 @@
---
restylers:
- astyle:
arguments: ["--options=astylerc"]

View File

@ -1,52 +1,60 @@
language: c
compiler:
- gcc
- clang
---
language: python
python: nightly
dist: xenial
os: linux
before_script:
# Installing yasm (needed for compiling vpx) and openal
- sudo apt-get -yq install yasm libopenal-dev libconfig-dev libalut-dev libnotify-dev clang llvm-dev
# Installing libsodium, needed for toxcore
- git clone https://github.com/jedisct1/libsodium.git libsodium
- cd libsodium
- git checkout tags/1.0.3 > /dev/null
- ./autogen.sh > /dev/null
- ./configure > /dev/null
- make check -j2 || make check || exit 1 > /dev/null
- sudo make install > /dev/null
- cd ..
# Installing libopus, needed for audio encoding/decoding
- wget http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz
- tar xzf opus-1.1.tar.gz > /dev/null
- cd opus-1.1
- ./configure > /dev/null
- make -j2 || make || exit 1 > /dev/null
- sudo make install > /dev/null
- cd ..
# Installing vpx
- git clone https://chromium.googlesource.com/webm/libvpx libvpx
- cd libvpx
- ./configure --enable-shared > /dev/null
- make -j2 || make || exit 1 > /dev/null
- sudo make install > /dev/null
- cd ..
# Creating libraries links and updating cache
- sudo ldconfig > /dev/null
# Installing toxcore
- git clone https://github.com/irungentoo/toxcore.git toxcore
- cd toxcore
- autoreconf -i
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
- make -j2 || make || exit 1
- sudo make install
- cd ..
script:
- make -j2 || make || exit 1
notifications:
email: false
irc:
channels:
- "chat.freenode.net#tox-dev"
on_success: always
on_failure: always
jobs:
include:
- env: JOB=linux
addons:
apt:
packages:
- libalut-dev
- libconfig-dev
- libnotify-dev
- libopenal-dev
- libopus-dev
- libqrencode-dev
- libvpx-dev
cache:
directories:
- $HOME/cache
install:
# Where to find libraries.
- export LD_LIBRARY_PATH=$HOME/cache/usr/lib
- export PKG_CONFIG_PATH=$HOME/cache/usr/lib/pkgconfig
# c-sodium
- git clone --depth=1 --branch=stable https://github.com/jedisct1/libsodium ../libsodium
- test -f $HOME/cache/usr/lib/libsodium.so || (cd ../libsodium && ./configure --prefix=$HOME/cache/usr && make install -j$(nproc))
# c-toxcore
- git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore
- test -f $HOME/cache/usr/lib/libtoxcore.so || (cd ../c-toxcore && cmake -B_build -H. -DCMAKE_INSTALL_PREFIX:PATH=$HOME/cache/usr && make -C_build install -j$(nproc))
script:
- make ENABLE_PYTHON=1 -j$(nproc)
- env: JOB=macos
os: macos
language: c
cache:
directories:
- $HOME/cache
install:
- brew install
freealut
libconfig
libqrencode
libsodium
openal-soft
- export LDFLAGS="-L/usr/local/Cellar/openal-soft/1.20.1/lib"
- git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore
- test -f /usr/local/lib/libtoxcore.dylib || (cd ../c-toxcore && cmake -B_build -H. && make -C_build install -j$(nproc))
script:
- make ENABLE_PYTHON=1 DISABLE_DESKTOP_NOTIFY=1 DISABLE_X11=1 DISABLE_AV=1 DISABLE_SOUND_NOTIFY=1 -j$(nproc)

41
BUILD.bazel Normal file
View File

@ -0,0 +1,41 @@
load("@rules_cc//cc:defs.bzl", "cc_binary")
load("//tools/project:build_defs.bzl", "project")
project()
cc_binary(
name = "toxic",
srcs = glob(
[
"src/*.c",
"src/*.h",
],
exclude = ["src/video*"],
) + select({
"//tools/config:linux": glob(["src/video*"]),
"//tools/config:osx": [],
}),
copts = [
"-std=gnu99",
"-DAUDIO",
"-DPACKAGE_DATADIR='\"data\"'",
"-DPYTHON",
"-DQRCODE",
] + select({
"//tools/config:linux": ["-DVIDEO"],
"//tools/config:osx": [],
}),
deps = [
"//c-toxcore",
"@curl",
"@libconfig",
"@libqrencode",
"@libvpx",
"@ncurses",
"@openal",
"@python3//:python",
] + select({
"//tools/config:linux": ["@x11"],
"//tools/config:osx": [],
}),
)

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

@ -1,68 +1,68 @@
# Installation
* [Dependencies](#deps)
* [OS X Notes](#deps_osx)
* [Dependencies](#dependencies)
* [OS X Notes](#os-x-notes)
* [Compiling](#compiling)
* [Documentation](#docs)
* [Documentation](#documentation)
* [Notes](#notes)
* [Compilation variables](#comp_vars)
* [Packaging](#packaging)
* [Compilation variables](#compilation-variables)
* [Environment variables](#environment-variables)
<a name="deps" />
## Dependencies
| Name | Needed by | Debian package |
|------------------------------------------------------|----------------------------|------------------|
| [Tox Core](https://github.com/irungentoo/toxcore) | BASE | *None* |
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO | *None* |
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
<sup>1</sup>: see [Documentation](#docs)
| Name | Needed by | Debian package |
|------------------------------------------------------|----------------------------|---------------------|
| [Tox Core](https://github.com/toktok/c-toxcore) | BASE | *None* |
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
| [libqrencode](https://fukuchi.org/works/qrencode/) | QRCODE | libqrencode-dev |
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
| [Python 3](http://www.python.org/) | PYTHON | python3-dev |
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
<sup>1</sup>: see [Documentation](#documentation)
<a name="deps_osx" />
#### OS X Notes
Using [Homebrew](http://brew.sh):
```
brew install openal-soft freealut libconfig
brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb
brew install curl qrencode openal-soft freealut libconfig libpng
brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
brew install libnotify
export PKG_CONFIG_PATH=/usr/local/opt/openal-soft/lib/pkgconfig
make
```
You can omit `libnotify` if you intend to build without desktop notifications enabled.
You can omit `libnotify` if you intend to build without desktop notifications enabled.
<a name="Compiling">
## Compiling
```
make PREFIX="/where/to/install"
sudo make install PREFIX="/where/to/install"
make
sudo make install
```
<a name="docs" />
#### Documentation
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br />
**NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
**Note for developers**: asciidoc files and generated manpages will need to be committed together.<br />
**Note for everyone**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
<a name="notes" />
## Notes
<a name="comp_vars" />
#### Compilation variables
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
* `DISABLE_X11=1`build toxic without X11 support (needed for focus tracking)
* `DISABLE_AV=1` → build toxic without audio call support
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and `USER_LDFLAGS=""` passed as arguments to make, or as environment variables
* Default compile options can be overridden by using special variables:
* `DISABLE_X11=1` → Disable X11 support (needed for focus tracking)
* `DISABLE_AV=1`Disable audio call support
* `DISABLE_SOUND_NOTIFY=1` → Disable sound notifications support
* `DISABLE_QRCODE` → Disable QR exporting support
* `DISABLE_QRPNG` → Disable support for exporting QR as PNG
* `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support
* `ENABLE_PYTHON=1` → Build toxic with Python scripting support
<a name="packaging" />
#### Packaging
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
* `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg")
#### Environment variables
* You can use the `CFLAGS` and `LDFLAGS` environment variables to add specific flags to the Makefile
* The `PREFIX` environment variable specifies a base install directory for binaries and data files. This is interchangeable with the `DESTDIR` variable, and is generally used by systems that have the `PREFIX` environment variable set by default.<br />
**Note**: `sudo` does not preserve user environment variables by default on some systems. See the `sudoers` manual for more information.

View File

View File

@ -3,36 +3,37 @@ CFG_DIR = $(BASE_DIR)/cfg
-include $(CFG_DIR)/global_vars.mk
LIBS = libtoxcore ncursesw libconfig
LIBS = toxcore ncursesw libconfig libcurl
CFLAGS = -std=gnu99 -pthread -Wall -g
CFLAGS ?= -g
CFLAGS += -std=gnu99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
CFLAGS += $(USER_CFLAGS)
LDFLAGS = $(USER_LDFLAGS)
CFLAGS += ${USER_CFLAGS}
LDFLAGS ?=
LDFLAGS += ${USER_LDFLAGS}
OBJ = chat.o chat_commands.o configdir.o dns.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
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)
ifeq ($(UNAME_S), Linux)
-include $(CFG_DIR)/systems/Linux.mk
endif
ifeq ($(UNAME_S), FreeBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
LDFLAGS += -ldl -lrt
endif
ifeq ($(UNAME_S), OpenBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
LIBS := $(filter-out ncursesw, $(LIBS))
LDFLAGS += -lncursesw
endif
ifeq ($(UNAME_S), NetBSD)
LIBS := $(filter-out ncursesw, $(LIBS))
LDFLAGS += -lncursesw
endif
ifeq ($(UNAME_S), Darwin)
-include $(CFG_DIR)/systems/Darwin.mk
endif
ifeq ($(UNAME_S), Solaris)
-include $(CFG_DIR)/systems/Solaris.mk
endif
# Check on which platform we are running
UNAME_M = $(shell uname -m)
@ -59,6 +60,10 @@ $(BUILD_DIR)/toxic: $(OBJ)
@echo " LD $(@:$(BUILD_DIR)/%=%)"
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS)
$(BUILD_DIR)/osx_video.o: $(SRC_DIR)/$(OSX_VIDEO)
@echo " CC $(@:$(BUILD_DIR)/)osx_video.o"
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/osx_video.o -c $(SRC_DIR)/$(OSX_VIDEO)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
@if [ ! -e $(BUILD_DIR) ]; then \
mkdir -p $(BUILD_DIR) ;\

View File

@ -1,11 +1,14 @@
# Toxic [![Build Status](https://travis-ci.org/Tox/toxic.png?branch=master)](https://travis-ci.org/Tox/toxic)
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.
<a href="https://scan.coverity.com/projects/toxic-tox">
<img alt="Coverity Scan Build Status"
src="https://scan.coverity.com/projects/4975/badge.svg"/>
</a>
Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client.
[![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png)
## Installation
[Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br />
[Compile it yourself](/INSTALL.md)
[See the install instructions](/INSTALL.md)
## Settings
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.

20
apidoc/python/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = toxic_api
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -0,0 +1,157 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# toxic_api documentation build configuration file, created by
# sphinx-quickstart on Tue May 16 08:58:24 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'toxic_api'
copyright = '2017, Jakob Kreuze'
author = 'Jakob Kreuze'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.8.4'
# The full version, including alpha/beta/rc tags.
release = '0.8.4'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'toxic_apidoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'toxic_api.tex', 'toxic\\_api Documentation',
'Jakob Kreuze', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'toxic_api', 'toxic_api Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'toxic_api', 'toxic_api Documentation',
author, 'toxic_api', 'One line description of project.',
'Miscellaneous'),
]

View File

@ -0,0 +1,8 @@
============
API Examples
============
Fortune
=======
.. literalinclude:: fortune.py
:language: python

View File

@ -0,0 +1,37 @@
import toxic_api
import random
FORTUNES = [
"A bug in the code is worth two in the documentation.",
"A bug in the hand is better than one as yet undetected.",
"\"A debugged program is one for which you have not yet found the "
"conditions that make it fail.\" -- Jerry Ogdin"
]
def send_fortune(args):
"""Callback function that sends the contact of the current window a
given number of random fortunes.
"""
if len(args) != 1:
toxic_api.display("Only one argument allowed!")
return
try:
count = int(args[0])
except ValueError:
toxic_api.display("Argument must be a number!")
return
if count < 0 or count > 20:
toxic_api.display("Argument is too large!")
return
name = toxic_api.get_nick()
toxic_api.send("%s has decided to send you %d fortunes:" % (name, count))
for _ in range(count):
toxic_api.send(random.choice(FORTUNES))
toxic_api.register("/fortune", "Send a fortune to the contact of the current "
"window", send_fortune)

View File

@ -0,0 +1,9 @@
Toxic Scripting Interface Documentation
=======================================
.. toctree::
:maxdepth: 2
intro
reference
examples

View File

@ -0,0 +1,12 @@
=========================
Toxic Scripting Interface
=========================
A Python scripting interface to `Toxic <https://github.com/JFreegman/toxic>`_.
Getting Started
===============
Toxic is compiled with Python support by default. To access the scripting interface, simply import "toxic_api" in your script.
Scripts can be run by issuing "/run <path>" from toxic, or placing them in the "autorun_path" from your toxic configuration file.

View File

@ -0,0 +1,73 @@
=============
API Reference
=============
Messages
========
.. function:: display(msg)
Display a message to the user through the current window.
:param msg: The message to display.
:type msg: string
:rtype: none
.. function:: send(msg)
Send a message to the user specified by the currently open conversation.
:param msg: The message to display.
:type msg: string
:rtype: none
State
=====
.. function:: get_nick()
Return the user's current nickname.
:rtype: string
.. function:: get_status()
Return a string representing the user's current status. Can be either "online", "away", or "busy".
:rtype: string
.. function:: get_status_message()
Return the user's current status message.
:rtype: string
.. function:: get_all_friends()
Return a list of all the user's friends.
:rtype: list of (string, string) tuples containing the nickname followed by their public key
Commands
========
.. function:: execute(command, class)
Executes the given command. The API exports three constants for the class parameter; GLOBAL_COMMAND, CHAT_COMMAND, and GROUPCHAT_COMMAND.
:param command: The command to execute.
:type command: string
:param class: The class of the command.
:type class: int
:rtype: none
.. function:: register(command, help, callback)
Register a callback to be executed whenever command is run. The callback function will be called with one argument, a list of arguments from when the user calls the command.
:param command: The command to listen for.
:type command: string
:param help: A description of the command to be shown in the help menu.
:type help: string
:param callback: The function to be called.
:type callback: callable
:rtype: none

26
astylerc Normal file
View File

@ -0,0 +1,26 @@
# Bracket Style Options
--style=kr
# Tab Options
--indent=spaces=4
# Indentation Options
--indent-switches
# Padding Options
--pad-header
--break-blocks
--pad-oper
--unpad-paren
--align-pointer=name
--align-reference=name
# Formatting Options
--add-brackets
--convert-tabs
--max-code-length=120
# Other Options
--preserve-date
--formatted
--lineend=linux

View File

@ -1,20 +1,20 @@
# Variables for audio call support
AUDIO_LIBS = libtoxav openal
AUDIO_LIBS = openal
AUDIO_CFLAGS = -DAUDIO
ifneq (, $(findstring device.o, $(OBJ)))
ifneq (, $(findstring audio_device.o, $(OBJ)))
AUDIO_OBJ = audio_call.o
else
AUDIO_OBJ = audio_call.o device.o
AUDIO_OBJ = audio_call.o audio_device.o
endif
# Check if we can build audio support
CHECK_AUDIO_LIBS = $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
CHECK_AUDIO_LIBS := $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
ifneq ($(CHECK_AUDIO_LIBS), error)
LIBS += $(AUDIO_LIBS)
CFLAGS += $(AUDIO_CFLAGS)
OBJ += $(AUDIO_OBJ)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
MISSING_AUDIO_LIBS := $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without audio support)
$(warning WARNING -- You need these libraries for audio support)
$(warning WARNING -- $(MISSING_AUDIO_LIBS))

View File

@ -1,36 +1,64 @@
CHECKS_DIR = $(CFG_DIR)/checks
# Check if we want build X11 support
X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
X11 := $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(X11), disabled)
-include $(CHECKS_DIR)/x11.mk
endif
# Check if we want build audio support
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
AUDIO := $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(AUDIO), disabled)
-include $(CHECKS_DIR)/av.mk
-include $(CHECKS_DIR)/audio.mk
endif
# Check if we want build video support
VIDEO := $(shell if [ -z "$(DISABLE_VI)" ] || [ "$(DISABLE_VI)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(X11), disabled)
ifneq ($(AUDIO), disabled)
ifneq ($(VIDEO), disabled)
-include $(CHECKS_DIR)/video.mk
endif
endif
endif
# Check if we want build sound notifications support
SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
SND_NOTIFY := $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(SND_NOTIFY), disabled)
-include $(CHECKS_DIR)/sound_notifications.mk
endif
# Check if we want build desktop notifications support
DESK_NOTIFY = $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
DESK_NOTIFY := $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(DESK_NOTIFY), disabled)
-include $(CHECKS_DIR)/desktop_notifications.mk
endif
# Check if we want build QR export support
QR_CODE := $(shell if [ -z "$(DISABLE_QRCODE)" ] || [ "$(DISABLE_QRCODE)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(QR_CODE), disabled)
-include $(CHECKS_DIR)/qr.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 want build Python scripting support
PYTHON := $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
ifneq ($(PYTHON), disabled)
-include $(CHECKS_DIR)/python.mk
endif
# Check if we can build Toxic
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
CHECK_LIBS := $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
ifneq ($(CHECK_LIBS), error)
CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS))
LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS))
else ifneq ($(MAKECMDGOALS), clean)
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
MISSING_LIBS := $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning ERROR -- Cannot compile Toxic)
$(warning ERROR -- You need these libraries)
$(warning ERROR -- $(MISSING_LIBS))

View File

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

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

@ -0,0 +1,15 @@
# Variables for Python scripting support
PYTHON3_LIBS = python3
PYTHON_CFLAGS = -DPYTHON
PYTHON_OBJ = api.o python_api.o
# Check if we can build Python scripting support
CHECK_PYTHON3_LIBS = $(shell $(PKG_CONFIG) --exists $(PYTHON3_LIBS) || echo -n "error")
ifneq ($(CHECK_PYTHON3_LIBS), error)
LDFLAGS += $(shell python3-config --ldflags)
CFLAGS += $(PYTHON_CFLAGS) $(shell python3-config --includes)
OBJ += $(PYTHON_OBJ)
else ifneq ($(MAKECMDGOALS), clean)
$(warning WARNING -- Toxic will be compiled without Python scripting support)
$(warning WARNING -- You need python3 installed for Python scripting support)
endif

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

@ -0,0 +1,15 @@
# Variables for QR export support
QR_LIBS = libqrencode
QR_CFLAGS = -DQRCODE
# Check if we can build QR export support
CHECK_QR_LIBS = $(shell pkg-config --exists $(QR_LIBS) || echo -n "error")
ifneq ($(CHECK_QR_LIBS), error)
LIBS += $(QR_LIBS)
CFLAGS += $(QR_CFLAGS)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_QR_LIBS = $(shell for lib in $(QR_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without QR export support)
$(warning WARNING -- You need these libraries for QR export support)
$(warning WARNING -- $(MISSING_QR_LIBS))
endif

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

@ -1,10 +1,10 @@
# Variables for sound notifications support
SND_NOTIFY_LIBS = openal freealut
SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY
ifneq (, $(findstring device.o, $(OBJ)))
ifneq (, $(findstring audio_device.o, $(OBJ)))
SND_NOTIFY_OBJ =
else
SND_NOTIFY_OBJ = device.o
SND_NOTIFY_OBJ = audio_device.o
endif
# Check if we can build sound notifications support

21
cfg/checks/video.mk Normal file
View File

@ -0,0 +1,21 @@
# Variables for video call support
VIDEO_LIBS = openal vpx x11
VIDEO_CFLAGS = -DVIDEO
ifneq (, $(findstring video_device.o, $(OBJ)))
VIDEO_OBJ = video_call.o
else
VIDEO_OBJ = video_call.o video_device.o
endif
# Check if we can build video support
CHECK_VIDEO_LIBS = $(shell $(PKG_CONFIG) --exists $(VIDEO_LIBS) || echo -n "error")
ifneq ($(CHECK_VIDEO_LIBS), error)
LIBS += $(VIDEO_LIBS)
CFLAGS += $(VIDEO_CFLAGS)
OBJ += $(VIDEO_OBJ)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without video support)
$(warning WARNING -- You will need these libraries for video support)
$(warning WARNING -- $(MISSING_VIDEO_LIBS))
endif

View File

@ -1,5 +1,5 @@
# Version
TOXIC_VERSION = 0.6.0
TOXIC_VERSION = 0.8.4
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
ifneq (, $(findstring error, $(REV)))
VERSION = $(TOXIC_VERSION)
@ -16,17 +16,17 @@ MISC_DIR = $(BASE_DIR)/misc
# Project files
MANFILES = toxic.1 toxic.conf.5
DATAFILES = DHTnodes DNSservers 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
SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav
# Install directories
PREFIX = /usr/local
PREFIX ?= /usr/local
BINDIR = $(PREFIX)/bin
DATADIR = $(PREFIX)/share/toxic
MANDIR = $(PREFIX)/share/man
MANDIR ?= $(PREFIX)/share/man
APPDIR = $(PREFIX)/share/applications
# Platform tools

View File

@ -6,5 +6,13 @@ PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/o
LIBS := $(filter-out ncursesw, $(LIBS))
# OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw.
LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -ltoxdns -lresolv -lconfig -ltoxencryptsave -g
LDFLAGS += -lncurses -lalut -ltoxcore -lcurl -lconfig -lqrencode -lpng -lopenal -g
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
OSX_LIBRARIES = -lobjc -lresolv
OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \
-framework QuartzCore -framework CoreMedia
OSX_VIDEO = osx_video.m
LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS)
OBJ += osx_video.o

View File

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

View File

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

View File

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

View File

@ -14,9 +14,22 @@ 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_QRCODE: Set to \"1\" to force building without QR export support"
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
@echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting 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))\")"
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
@echo " MANDIR: Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")"
@echo
@echo "-- Environment Variables --"
@echo " CFLAGS: Add custom flags to default CFLAGS"
@echo " LDFLAGS: Add custom flags to default LDFLAGS"
@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))\")"
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
@echo " MANDIR: Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")"
.PHONY: help

View File

@ -27,7 +27,7 @@ install: $(BUILD_DIR)/toxic
if [ ! -e "$(DOC_DIR)/$$f" ]; then \
continue ;\
fi ;\
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\
file=$$section/$$f ;\
mkdir -p $$section ;\
install -m 0644 $(DOC_DIR)/$$f $$file ;\
@ -35,7 +35,7 @@ install: $(BUILD_DIR)/toxic
mv temp_file $$file ;\
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
mv temp_file $$file ;\
gzip -f -9 $$file ;\
gzip -f -n -9 $$file ;\
done
.PHONY: install

View File

@ -16,7 +16,7 @@ uninstall:
@echo "Removing man pages"
@for f in $(MANFILES) ; do \
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\
file=$$section/$$f ;\
rm -f $$file $$file.gz ;\
done

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-03-28
.\" Date: 2016-09-20
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC" "1" "2015\-03\-28" "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
@ -78,12 +78,17 @@ instead of
Show help message
.RE
.PP
\-l, \-\-logging
.RS 4
Enable toxcore logging to stderr
.RE
.PP
\-n, \-\-nodes nodes\-file
.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
@ -101,9 +106,9 @@ Use a SOCKS5 proxy: Requires [IP] [port]
Use a HTTP proxy: Requires [IP] [port]
.RE
.PP
\-r, \-\-dnslist
\-r, \-\-namelist
.RS 4
Use specified DNSservers file
Use specified nameservers list
.RE
.PP
\-t, \-\-force\-tcp
@ -122,12 +127,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 +151,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 +164,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,16 @@ 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
-l, --logging::
Enable toxcore logging to stderr
-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
@ -53,8 +55,8 @@ OPTIONS
-P, --HTTP-proxy::
Use a HTTP proxy: Requires [IP] [port]
-r, --dnslist::
Use specified DNSservers file
-r, --namelist::
Use specified nameservers list
-t, --force-tcp::
Force TCP connection (use this with proxies)
@ -68,10 +70,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 +86,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 +103,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

@ -1,13 +1,13 @@
'\" t
.\" Title: toxic.conf
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2015-03-28
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
.\" Date: 2018-10-27
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC\&.CONF" "5" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual"
.TH "TOXIC\&.CONF" "5" "2018\-10\-27" "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,11 +113,31 @@ 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\&. (integer; 0 to disable)
.RE
.PP
\fBautosave_freq\fR
.RS 4
How often in seconds to auto\-save the Tox data file\&. (integer; 0 to disable)
.RE
.PP
\fBhistory_size\fR
.RS 4
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
.RE
.PP
\fBnotification_timeout\fR
.RS 4
Time in milliseconds to display a notification\&. (for example: 3000)
.RE
.PP
\fBline_join\fR
.RS 4
Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&.
@ -146,6 +166,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
@ -185,10 +237,20 @@ Default path for downloads\&. String value\&. Absolute path for downloaded files
Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
.RE
.PP
\fBautorun_path\fR
.RS 4
Path for any scripts that should be run on startup
.RE
.PP
\fBchatlogs_path\fR
.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
@ -289,20 +351,15 @@ Key combination to scroll half page down\&.
Key combination to scroll to page bottom\&.
.RE
.PP
\fBpeer_list_up\fR
.RS 4
Key combination to scroll contacts list up\&.
.RE
.PP
\fBpeer_list_down\fR
.RS 4
Key combination to scroll contacts list down\&.
.RE
.PP
\fBtoggle_peerlist\fR
.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,9 +72,21 @@ 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. (integer; 0 to disable)
*autosave_freq*;;
How often in seconds to auto-save the Tox data file. (integer; 0 to disable)
*history_size*;;
Maximum lines for chat window history. Integer value. (for example: 700)
*notification_timeout*;;
Time in milliseconds to display a notification. Integer value. (for example: 3000)
*line_join*;;
Indicator for when someone connects or joins a group.
Three characters max for line_ settings.
@ -97,6 +109,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.
@ -120,9 +149,16 @@ OPTIONS
*avatar_path*;;
Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
*autorun_path*;;
Path for any scripts that should be run on startup
*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. +
@ -185,15 +221,12 @@ OPTIONS
*page_bottom*;;
Key combination to scroll to page bottom.
*peer_list_up*;;
Key combination to scroll contacts list up.
*peer_list_down*;;
Key combination to scroll contacts list down.
*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,25 +0,0 @@
192.254.75.102 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
178.62.125.224 33445 10B20C49ACBD968D7C80F2E8438F92EA51F189F4E70CFBBB2C2C8C799E97F03E
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
195.154.119.113 33445 E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
76.191.23.96 33445 93574A3FAB7D612FEA29FD8D67D3DD10DFD07A075A5D62E8AF3DD9F5D0932E11
46.38.239.179 33445 F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A
178.62.250.138 33445 788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B
78.225.128.39 33445 7A2306BFBA665E5480AE59B31E116BE9C04DCEFE04D9FE25082316FA34B4DA0C
130.133.110.14 33445 461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F
104.167.101.29 33445 5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57
195.154.109.148 33445 391C96CB67AE893D4782B8E4495EB9D89CF1031F48460C06075AA8CE76D50A21
192.3.173.88 33445 3E1FFDEB667BFF549F619EC6737834762124F50A89C8D0DBF1DDF64A2DD6CD1B
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
194.249.212.109 443 3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B
103.38.216.87 33445 601AEE6FC8C17E8CD8F8F1FFC4D4AD84E59A73BE451F037194E7A404E3795320
185.25.116.107 33445 DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43
192.99.168.140 33445 6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F

View File

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

1
misc/nameservers Normal file
View File

@ -0,0 +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,9 +41,21 @@ 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;
// How often in seconds to auto-save the Tox data file. (0 to disable periodic auto-saves)
autosave_freq=600;
// maximum lines for chat window history
history_size=700;
// time in milliseconds to display a notification
notification_timeout=3000;
// Indicator for display when someone connects or joins a group.
line_join="-->";
@ -57,7 +81,7 @@ audio = {
// preferred audio output device; numbers correspond to /lsdev out
output_device=0;
// default VAD treshold; float (recommended values are around 40)
VAD_treshold=40.0;
};
@ -66,9 +90,12 @@ tox = {
// Path for downloaded files
// download_path="/home/USERNAME/Downloads/";
// Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
// Path for your avatar (file must be a .png and cannot exceed 64 KiB)
// avatar_path="/home/USERNAME/Pictures/youravatar.png";
// Path for scripts that should be run on startup
// autorun_path="/home/USERNAME/toxic_scripts/";
// Path for chatlogs
// chatlogs_path="/home/USERNAME/toxic_chatlogs/";
};
@ -86,7 +113,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";
@ -95,8 +122,6 @@ keys = {
half_page_up="Ctrl+F";
half_page_down="Ctrl+V";
page_bottom="Ctrl+H";
peer_list_up="Ctrl+[";
peer_list_down="Ctrl+]";
toggle_peerlist="Ctrl+b";
toggle_paste_mode="Ctrl+T";
};

219
src/api.c Normal file
View File

@ -0,0 +1,219 @@
/* api.c
*
*
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
*
* 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 <dirent.h>
#include <stdint.h>
#include <tox/tox.h>
#include "execute.h"
#include "friendlist.h"
#include "line_info.h"
#include "message_queue.h"
#include "misc_tools.h"
#include "settings.h"
#include "toxic_strings.h"
#include "windows.h"
#ifdef PYTHON
#include "python_api.h"
Tox *user_tox;
static WINDOW *cur_window;
static ToxWindow *self_window;
extern FriendsList Friends;
extern struct user_settings *user_settings;
void api_display(const char *const msg)
{
if (msg == NULL) {
return;
}
self_window = get_active_window();
line_info_add(self_window, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
}
FriendsList api_get_friendslist(void)
{
return Friends;
}
char *api_get_nick(void)
{
size_t len = tox_self_get_name_size(user_tox);
uint8_t *name = malloc(len + 1);
if (name == NULL) {
return NULL;
}
tox_self_get_name(user_tox, name);
name[len] = '\0';
return (char *) name;
}
Tox_User_Status api_get_status(void)
{
return tox_self_get_status(user_tox);
}
char *api_get_status_message(void)
{
size_t len = tox_self_get_status_message_size(user_tox);
uint8_t *status = malloc(len + 1);
if (status == NULL) {
return NULL;
}
tox_self_get_status_message(user_tox, status);
status[len] = '\0';
return (char *) status;
}
void api_send(const char *msg)
{
if (msg == NULL || self_window->chatwin->cqueue == NULL) {
return;
}
char *name = api_get_nick();
char timefrmt[TIME_STR_SIZE];
if (name == NULL) {
return;
}
self_window = get_active_window();
get_time_str(timefrmt, sizeof(timefrmt));
strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line));
add_line_to_hist(self_window->chatwin);
int id = line_info_add(self_window, timefrmt, name, NULL, OUT_MSG, 0, 0, "%s", msg);
cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id);
free(name);
}
void api_execute(const char *input, int mode)
{
self_window = get_active_window();
execute(cur_window, self_window, user_tox, input, mode);
}
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE])
{
return do_python_command(num_args, args);
}
int num_registered_handlers(void)
{
return python_num_registered_handlers();
}
int help_max_width(void)
{
return python_help_max_width();
}
void draw_handler_help(WINDOW *win)
{
python_draw_handler_help(win);
}
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
FILE *fp;
const char *error_str;
cur_window = window;
self_window = self;
if (argc != 1) {
if (argc < 1) {
error_str = "Path must be specified.";
} else {
error_str = "Only one argument allowed.";
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
return;
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
error_str = "Path does not exist.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
return;
}
run_python(fp, argv[1]);
fclose(fp);
}
void invoke_autoruns(WINDOW *window, ToxWindow *self)
{
struct dirent *dir;
char abspath_buf[PATH_MAX + 1], err_buf[PATH_MAX + 1];
size_t path_len;
DIR *d;
FILE *fp;
if (user_settings->autorun_path[0] == '\0') {
return;
}
d = opendir(user_settings->autorun_path);
if (d == NULL) {
snprintf(err_buf, PATH_MAX + 1, "Autorun path does not exist: %s", user_settings->autorun_path);
api_display(err_buf);
return;
}
cur_window = window;
self_window = self;
while ((dir = readdir(d)) != NULL) {
path_len = strlen(dir->d_name);
if (!strcmp(dir->d_name + path_len - 3, ".py")) {
snprintf(abspath_buf, PATH_MAX + 1, "%s%s", user_settings->autorun_path, dir->d_name);
fp = fopen(abspath_buf, "r");
if (fp == NULL) {
snprintf(err_buf, PATH_MAX + 1, "Invalid path: %s", abspath_buf);
api_display(err_buf);
continue;
}
run_python(fp, abspath_buf);
fclose(fp);
}
}
closedir(d);
}
#endif /* PYTHON */

43
src/api.h Normal file
View File

@ -0,0 +1,43 @@
/* api.h
*
*
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
*
* 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 API_H
#define API_H
#include "friendlist.h"
#include "windows.h"
void api_display(const char *const msg);
FriendsList api_get_friendslist(void);
char *api_get_nick(void);
Tox_User_Status api_get_status(void);
char *api_get_status_message(void);
void api_send(const char *msg);
void api_execute(const char *input, int mode);
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE]);
int num_registered_handlers(void);
int help_max_width(void);
void draw_handler_help(WINDOW *win);
void invoke_autoruns(WINDOW *w, ToxWindow *self);
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* API_H */

File diff suppressed because it is too large Load Diff

View File

@ -20,12 +20,12 @@
*
*/
#ifndef AUDIO_H
#define AUDIO_H
#ifndef AUDIO_CALL_H
#define AUDIO_CALL_H
#include <tox/toxav.h>
#include "device.h"
#include "audio_device.h"
typedef enum _AudioError {
ae_None = 0,
@ -34,18 +34,62 @@ typedef enum _AudioError {
ae_StartingCoreAudio = 1 << 2
} AudioError;
#ifdef VIDEO
typedef enum _VideoError {
ve_None = 0,
ve_StartingCaptureDevice = 1 << 0,
ve_StartingOutputDevice = 1 << 1,
ve_StartingCoreVideo = 1 << 2
} VideoError;
#endif /* VIDEO */
typedef struct Call {
pthread_t ttid; /* Transmission thread id */
bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
uint32_t in_idx, out_idx;
uint32_t in_idx, out_idx; /* Audio Index */
#ifdef VIDEO
uint32_t vin_idx, vout_idx; /* Video Index */
#endif /* VIDEO */
pthread_mutex_t mutex;
} Call;
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
ToxAv *init_audio(ToxWindow *self, Tox *tox);
void terminate_audio();
int start_transmission(ToxWindow *self, Call *call);
int stop_transmission(Call *call, int call_index);
void stop_current_call(ToxWindow *self);
struct CallControl {
AudioError audio_errors;
#ifdef VIDEO
VideoError video_errors;
#endif /* VIDEO */
#endif /* AUDIO_H */
ToxAV *av;
ToxWindow *prompt;
Call *calls;
uint32_t max_calls;
uint32_t call_state;
bool pending_call;
bool audio_enabled;
bool video_enabled;
uint32_t audio_bit_rate;
int32_t audio_frame_duration;
uint32_t audio_sample_rate;
uint8_t audio_channels;
uint32_t video_bit_rate;
int32_t video_frame_duration;
};
extern struct CallControl CallControl;
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
ToxAV *init_audio(ToxWindow *self, Tox *tox);
void terminate_audio(void);
int start_transmission(ToxWindow *self, Call *call);
int stop_transmission(Call *call, uint32_t friend_number);
void stop_current_call(ToxWindow *self);
void init_friend_AV(uint32_t index);
void del_friend_AV(uint32_t index);
#endif /* AUDIO_CALL_H */

View File

@ -1,4 +1,4 @@
/* device.c
/* audio_device.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
@ -20,7 +20,7 @@
*
*/
#include "device.h"
#include "audio_device.h"
#ifdef AUDIO
#include "audio_call.h"
@ -28,18 +28,14 @@
#include "line_info.h"
#include "settings.h"
#include "misc_tools.h"
#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 /* ALC_ALL_DEVICES_SPECIFIER */
#include <stdbool.h>
#include <string.h>
@ -56,8 +52,8 @@ typedef struct Device {
ALCdevice *dhndl; /* Handle of device selected/opened */
ALCcontext *ctx; /* Device context */
DataHandleCallback cb; /* Use this to handle data from input device usually */
void* cb_data; /* Data to be passed to callback */
int32_t call_idx; /* ToxAv call index */
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 */
uint32_t ref_count;
@ -80,7 +76,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,45 +86,28 @@ 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()
DeviceError init_devices(void)
#endif /* AUDIO */
{
const char *stringed_device_list;
size[input] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
size[output] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) {
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
get_devices_names();
// Start poll thread
if (pthread_mutex_init(&mutex, NULL) != 0)
if (pthread_mutex_init(&mutex, NULL) != 0) {
return de_InternalError;
}
pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
if (pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) {
return de_InternalError;
}
#ifdef AUDIO
av = av_;
@ -137,7 +116,7 @@ DeviceError init_devices()
return (DeviceError) de_None;
}
DeviceError terminate_devices()
DeviceError terminate_devices(void)
{
/* Cleanup if needed */
lock;
@ -146,18 +125,56 @@ DeviceError terminate_devices()
usleep(20000);
if (pthread_mutex_destroy(&mutex) != 0)
if (pthread_mutex_destroy(&mutex) != 0) {
return (DeviceError) de_InternalError;
}
return (DeviceError) de_None;
}
void get_devices_names(void)
{
const char *stringed_device_list;
size[input] = 0;
if ((stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER))) {
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for (; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input]) {
devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen(stringed_device_list) + 1;
}
}
size[output] = 0;
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]) {
devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen(stringed_device_list) + 1;
}
}
}
DeviceError device_mute(DeviceType type, uint32_t device_idx)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
if (device_idx >= MAX_DEVICES) {
return de_InvalidSelection;
}
lock;
Device* device = running[type][device_idx];
Device *device = running[type][device_idx];
if (!device) {
unlock;
@ -173,10 +190,13 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx)
#ifdef AUDIO
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
if (device_idx >= MAX_DEVICES) {
return de_InvalidSelection;
}
lock;
Device* device = running[input][device_idx];
Device *device = running[input][device_idx];
if (!device) {
unlock;
@ -193,13 +213,17 @@ 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;
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,24 +234,34 @@ 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;
if (size[type] <= selection || selection < 0) {
return de_InvalidSelection;
}
if (channels != 1 && channels != 2) return de_UnsupportedMode;
if (channels != 1 && channels != 2) {
return de_UnsupportedMode;
}
lock;
const uint32_t frame_size = (sample_rate * frame_duration / 1000);
uint32_t i;
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i);
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 ) {
if (running[type][i] && running[type][i]->selection == selection) {
// printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl);
running[type][*device_idx] = running[type][i];
@ -238,7 +272,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,13 +288,13 @@ 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 ) {
if (!device->dhndl) {
free(device);
running[type][*device_idx] = NULL;
unlock;
@ -275,10 +309,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);
for (i = 0; i < OPENAL_BUFS; ++i) {
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate);
}
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
@ -303,10 +337,12 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
DeviceError close_device(DeviceType type, uint32_t device_idx)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
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) {
@ -316,54 +352,68 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
running[type][device_idx] = NULL;
if ( !device->ref_count ) {
// printf("Closed device ");
if (!device->ref_count) {
if (type == input) {
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
}
else {
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
if (!alcCaptureCloseDevice(device->dhndl)) {
rc = de_AlError;
}
} 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;
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 call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD)
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
void *data, bool enable_VAD)
{
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) {
return de_InvalidSelection;
}
lock;
running[input][device_idx]->cb = callback;
running[input][device_idx]->cb_data = data;
running[input][device_idx]->enable_VAD = enable_VAD;
running[input][device_idx]->call_idx = call_idx;
running[input][device_idx]->friend_number = friend_number;
unlock;
return de_None;
}
inline__ DeviceError write_out(uint32_t device_idx, 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;
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;
if (!device || device->muted) {
return de_DeviceNotActive;
}
pthread_mutex_lock(device->mutex);
@ -373,59 +423,66 @@ 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 {
} 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.
*/
(void)arg;
UNUSED_VAR(arg);
uint32_t i;
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 +491,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);
@ -444,10 +502,14 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
continue;
}
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
if (device->cb) {
device->cb(frame, f_size, device->cb_data);
}
}
unlock;
}
usleep(5000);
}
}
@ -455,12 +517,14 @@ 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;
for (i = 0; i < size[type]; ++i)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]);
for (i = 0; i < size[type]; ++i) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, i == primary_device[type] ? 1 : 0, 0, "%d: %s", i,
devices_names[type][i]);
}
return;
}
@ -470,10 +534,11 @@ 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)
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) {
return NULL;
}
return running[input][device_idx]->cb_data;
}

View File

@ -1,4 +1,4 @@
/* device.h
/* audio_device.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
@ -22,16 +22,16 @@
/*
* You can have multiple sources (Input devices) but only one output device.
* Pass buffers to output device via write();
* Pass buffers to output device via write();
* Read from running input device(s) via select()/callback combo.
*/
#ifndef DEVICE_H
#define DEVICE_H
#ifndef AUDIO_DEVICE_H
#define AUDIO_DEVICE_H
#define OPENAL_BUFS 5
#define MAX_DEVICES 32
#include <inttypes.h>
#include "windows.h"
typedef enum DeviceType {
@ -52,20 +52,22 @@ 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();
DeviceError init_devices(void);
#endif /* AUDIO */
DeviceError terminate_devices();
void get_devices_names(void);
DeviceError terminate_devices(void);
/* Callback handles ready data from INPUT device */
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
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,17 +77,20 @@ 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);
#endif /* DEVICE_H */
#endif /* AUDIO_DEVICE_H */

View File

@ -25,11 +25,11 @@
#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>
#endif /* ifdef __APPLE__ */
#include <dirent.h>
#endif /* __APPLE__ */
#include "windows.h"
#include "toxic.h"
@ -38,27 +38,33 @@
#include "execute.h"
#include "configdir.h"
static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items, int size)
static void print_matches(ToxWindow *self, Tox *m, const void *list, size_t n_items, size_t size)
{
if (m)
if (m) {
execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
}
const char *L = (char *) list;
int i;
for (i = 0; i < n_items; ++i)
for (i = 0; i < n_items; ++i) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); /* formatting */
}
/* 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)
{
UNUSED_VAR(self);
if (n == 1) {
strcpy(match, matches[0]);
return;
return snprintf(match, match_sz, "%s", matches[0]);
}
int i;
@ -71,59 +77,70 @@ 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,
then fills line with the complete word. e.g. "Hello jo" would complete the line
with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of line on success, -1 if error */
int complete_line(ToxWindow *self, const void *list, int n_items, int size)
/*
* Looks for all instances in list that begin with the last entered word in line according to pos,
* then fills line with the complete word. e.g. "Hello jo" would complete the line
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
*
* list is a pointer to the list of strings being compared, n_items is the number of items
* in the list, and size is the size of each item in the list.
*
* dir_search should be true if the line being completed is a file path.
*
* Returns the difference between the old len and new len of line on success.
* Returns -1 on error.
*
* Note: This function should not be called directly. Use complete_line() and complete_path() instead.
*/
static int complete_line_helper(ToxWindow *self, const void *list, size_t n_items, size_t size, bool dir_search)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len) {
return -1;
}
const char *L = (char *) list;
if (ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) {
return -1;
}
const char *L = (const char *) list;
const char *endchrs = " ";
char ubuf[MAX_STR_SIZE];
char ubuf[MAX_STR_SIZE] = {0};
/* work with multibyte string copy of buf for simplicity */
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) {
return -1;
/* TODO: generalize this */
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
}
/* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", ubuf);
tmp[ctx->pos] = '\0';
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
char *sub = malloc(strlen(ubuf) + 1);
const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' ');
char *sub = calloc(1, strlen(ubuf) + 1);
if (sub == NULL)
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
if (sub == NULL) {
exit_toxic_err("failed in complete_line_helper", FATALERR_MEMORY);
}
if (!s && !dir_search) {
strcpy(sub, tmp);
if (sub[0] != '/')
if (sub[0] != '/') {
endchrs = ": ";
}
} else if (s) {
strcpy(sub, &s[1]);
@ -131,70 +148,110 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
int sub_len = strlen(sub);
int si = char_rfind(sub, '/', sub_len);
if (si || *sub == '/')
if (si || *sub == '/') {
memmove(sub, &sub[si + 1], sub_len - si);
}
}
}
if (!sub[0]) {
free(sub);
return -1;
return 0;
}
int s_len = strlen(sub);
const char *str;
int n_matches = 0;
size_t n_matches = 0;
char matches[n_items][MAX_STR_SIZE];
int i = 0;
/* put all list matches in matches array */
for (i = 0; i < n_items; ++i) {
str = &L[i * size];
char str[MAX_CMDNAME_SIZE + 1];
snprintf(str, sizeof(str), "%s", &L[i * size]);
if (strncasecmp(str, sub, s_len) == 0)
if (strncasecmp(str, sub, s_len) == 0) {
strcpy(matches[n_matches++], str);
}
}
free(sub);
if (!n_matches)
if (!n_matches) {
return -1;
}
if (!dir_search && n_matches > 1)
if (!dir_search && n_matches > 1) {
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 = get_str_match(self, match, sizeof(match), matches, n_matches);
if (match_len == 0) {
return 0;
}
if (dir_search) {
if (n_matches == 1)
endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/";
else
if (n_matches == 1) {
endchrs = char_rfind(match, '.', match_len) ? "" : "/";
} else {
endchrs = "";
}
} else if (n_matches > 1) {
endchrs = "";
}
/* put match in correct spot in buf and append endchars */
int n_endchrs = strlen(endchrs);
int m_len = strlen(match);
int strt = ctx->pos - s_len;
int diff = m_len - s_len + n_endchrs;
int diff = match_len - s_len + n_endchrs;
if (ctx->len + diff >= MAX_STR_SIZE)
if (ctx->len + diff >= MAX_STR_SIZE) {
return -1;
}
char tmpend[MAX_STR_SIZE];
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) {
return -1;
}
strcpy(&ubuf[strt], match);
strcpy(&ubuf[strt + m_len], endchrs);
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
/* If path points to a file with no extension don't append a forward slash */
if (dir_search && *endchrs == '/') {
const char *path_start = strchr(ubuf + 1, '/');
if (!path_start) {
path_start = strchr(ubuf + 1, ' ');
if (!path_start) {
return -1;
}
}
if (strlen(path_start) < 2) {
return -1;
}
++path_start;
if (file_type(path_start) == FILE_TYPE_REGULAR) {
endchrs = "";
diff -= n_endchrs;
}
}
strcpy(&ubuf[strt + match_len], endchrs);
strcpy(&ubuf[strt + match_len + n_endchrs], tmpend);
/* convert to widechar and copy back to original buf */
wchar_t newbuf[MAX_STR_SIZE];
if (mbs_to_wcs_buf(newbuf, ubuf, 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);
@ -204,8 +261,18 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
return diff;
}
/* transforms a tab complete starting with the shorthand "~" into the full home directory.*/
static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size)
{
return complete_line_helper(self, list, n_items, size, false);
}
static int complete_path(ToxWindow *self, const void *list, size_t n_items, size_t size)
{
return complete_line_helper(self, list, n_items, size, true);
}
/* Transforms a tab complete starting with the shorthand "~" into the full home directory. */
static void complete_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
{
ChatContext *ctx = self->chatwin;
@ -213,45 +280,51 @@ static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const cha
get_home_dir(homedir, sizeof(homedir));
char newline[MAX_STR_SIZE];
snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1);
snprintf(path, pathsize, "%s", &newline[cmdlen]);
snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1);
snprintf(path, pathsize, "%s", &newline[cmdlen - 1]);
wchar_t wline[MAX_STR_SIZE];
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1)
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) {
return;
}
int newlen = wcslen(wline);
if (ctx->len + newlen >= MAX_STR_SIZE)
if (ctx->len + newlen >= MAX_STR_SIZE) {
return;
}
wmemcpy(ctx->line, wline, newlen + 1);
ctx->pos = newlen;
ctx->len = ctx->pos;
}
/* attempts to match /command "<incomplete-dir>" line to matching directories.
if only one match, auto-complete line.
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
* If there is only one match the line is auto-completed.
*
* Returns the diff between old len and new len of ctx->line on success.
* Returns -1 if no matches or more than one match.
*/
#define MAX_DIRS 512
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
{
char b_path[MAX_STR_SIZE];
char b_name[MAX_STR_SIZE];
char b_cmd[MAX_STR_SIZE];
const wchar_t *tmpline = &line[wcslen(cmd) + 2]; /* start after "/command \"" */
const wchar_t *tmpline = &line[wcslen(cmd) + 1]; /* start after "/command " */
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1) {
return -1;
}
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1)
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) {
return -1;
}
if (b_path[0] == '~')
complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
if (b_path[0] == '~') {
complete_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
}
int si = char_rfind(b_path, '/', strlen(b_path));
@ -269,16 +342,17 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
int b_name_len = strlen(b_name);
DIR *dp = opendir(b_path);
if (dp == NULL)
if (dp == NULL) {
return -1;
}
char dirnames[MAX_DIRS][NAME_MAX];
char dirnames[MAX_DIRS][NAME_MAX + 1];
struct dirent *entry;
int dircount = 0;
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;
}
@ -286,13 +360,14 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
closedir(dp);
if (dircount == 0)
if (dircount == 0) {
return -1;
if (dircount > 1) {
qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr);
print_matches(self, m, dirnames, dircount, NAME_MAX);
}
return complete_line(self, dirnames, dircount, NAME_MAX);
if (dircount > 1) {
qsort(dirnames, dircount, NAME_MAX + 1, qsort_strcasecmp_hlpr);
print_matches(self, m, dirnames, dircount, NAME_MAX + 1);
}
return complete_path(self, dirnames, dircount, NAME_MAX + 1);
}

View File

@ -23,20 +23,25 @@
#ifndef AUTOCOMPLETE_H
#define AUTOCOMPLETE_H
/* looks for all instances in list that begin with the last entered word in line according to pos,
then fills line with the complete word. e.g. "Hello jo" would complete the line
with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
/*
* Looks for all instances in list that begin with the last entered word in line according to pos,
* then fills line with the complete word. e.g. "Hello jo" would complete the line
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
*
* list is a pointer to the list of strings being compared, n_items is the number of items
* in the list, and size is the size of each item in the list.
*
* Returns the difference between the old len and new len of line on success.
* Returns -1 on error.
*/
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size);
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of line on success, -1 if error */
int complete_line(ToxWindow *self, const void *list, int n_items, int size);
/* attempts to match /command "<incomplete-dir>" line to matching directories.
if only one match, auto-complete line.
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
* If there is only one match the line is auto-completed.
*
* Returns the diff between old len and new len of ctx->line on success.
* Returns -1 if no matches or more than one match.
*/
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd);
#endif /* #define AUTOCOMPLETE_H */
#endif /* AUTOCOMPLETE_H */

View File

@ -52,11 +52,13 @@ static void avatar_clear(void)
*/
int avatar_send(Tox *m, uint32_t friendnum)
{
TOX_ERR_FILE_SEND err;
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)
if (Avatar.size == 0) {
return 0;
}
if (err != TOX_ERR_FILE_SEND_OK) {
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
@ -65,13 +67,15 @@ int avatar_send(Tox *m, uint32_t friendnum)
struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
if (!ft)
if (!ft) {
return -1;
}
ft->file = fopen(Avatar.path, "r");
if (ft->file == NULL)
if (ft->file == NULL) {
return -1;
}
snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
ft->file_size = Avatar.size;
@ -85,8 +89,9 @@ static void avatar_send_all(Tox *m)
size_t i;
for (i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE)
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) {
avatar_send(m, Friends.list[i].num);
}
}
}
@ -97,15 +102,17 @@ static void avatar_send_all(Tox *m)
*/
int avatar_set(Tox *m, const char *path, size_t path_len)
{
if (path_len == 0 || path_len >= sizeof(Avatar.path))
if (path_len == 0 || path_len >= sizeof(Avatar.path)) {
return -1;
}
FILE *fp = fopen(path, "rb");
if (fp == NULL)
if (fp == NULL) {
return -1;
}
char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
unsigned char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
fclose(fp);
@ -116,8 +123,9 @@ int avatar_set(Tox *m, const char *path, size_t path_len)
off_t size = file_size(path);
if (size == 0 || size > MAX_AVATAR_FILE_SIZE)
if (size == 0 || size > MAX_AVATAR_FILE_SIZE) {
return -1;
}
get_file_name(Avatar.name, sizeof(Avatar.name), path);
Avatar.name_len = strlen(Avatar.name);
@ -141,7 +149,7 @@ void avatar_unset(Tox *m)
avatar_send_all(m);
}
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control)
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control)
{
switch (control) {
case TOX_FILE_CONTROL_RESUME:
@ -150,6 +158,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:
@ -164,8 +173,9 @@ void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL co
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length)
{
if (ft->state != FILE_TRANSFER_STARTED)
if (ft->state != FILE_TRANSFER_STARTED) {
return;
}
if (length == 0) {
close_file_transfer(NULL, m, ft, -1, NULL, silent);
@ -194,11 +204,12 @@ void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position,
return;
}
TOX_ERR_FILE_SEND_CHUNK err;
Tox_Err_File_Send_Chunk err;
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
if (err != TOX_ERR_FILE_SEND_CHUNK_OK) {
fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
}
ft->position += send_length;
ft->last_keep_alive = get_unix_time();

View File

@ -47,6 +47,6 @@ int avatar_set(Tox *m, const char *path, size_t length);
void avatar_unset(Tox *m);
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control);
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control);
#endif /* AVATARS_H */

592
src/bootstrap.c Normal file
View File

@ -0,0 +1,592 @@
/* 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"
#include "prompt.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.
*/
static bool node_is_offline(unsigned long long int last_ping)
{
return 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(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)
{
UNUSED_VAR(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 (Nodes.count == 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 = prompt_selfConnectionStatus() != 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 */

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,6 @@
set msg to NULL if we don't want to display a message */
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
void kill_chat_window(ToxWindow *self, Tox *m);
ToxWindow new_chat(Tox *m, int32_t friendnum);
ToxWindow *new_chat(Tox *m, int32_t friendnum);
#endif /* end of include guard: CHAT_H */

View File

@ -38,6 +38,8 @@ extern FriendsList Friends;
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
if (argc < 2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
return;
@ -45,9 +47,9 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
char msg[MAX_STR_SIZE];
const char *inoutstr = argv[1];
int idx = atoi(argv[2]);
long int idx = strtol(argv[2], NULL, 10);
if (idx >= MAX_FILES || idx < 0) {
if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
@ -80,20 +82,24 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
return;
}
int groupnum = atoi(argv[1]);
long int groupnum = strtol(argv[1], NULL, 10);
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == LONG_MAX) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
return;
}
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_Conference_Invite err;
if (!tox_conference_invite(m, self->num, groupnum, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d)", err);
return;
}
@ -102,6 +108,10 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
UNUSED_VAR(argc);
UNUSED_VAR(argv);
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;
@ -116,24 +126,23 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
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.");
if (type != TOX_CONFERENCE_TYPE_TEXT) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
return;
}
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
Tox_Err_Conference_Join err;
uint32_t groupnum = tox_conference_join(m, self->num, (const uint8_t *) groupkey, length, &err);
if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
return;
}
if (init_groupchat_win(m, groupnum, type, NULL, 0) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
tox_conference_delete(m, groupnum, NULL);
return;
}
@ -141,14 +150,16 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
return;
}
int idx = atoi(argv[1]);
long int idx = strtol(argv[1], NULL, 10);
if ((idx == 0 && strcmp(argv[1], "0")) || idx >= MAX_FILES) {
if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
@ -166,16 +177,17 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
}
if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
const char *msg = "File transfer failed: Invalid file path.";
const char *msg = "File transfer failed: Invalid download path.";
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return;
}
TOX_ERR_FILE_CONTROL err;
Tox_Err_File_Control err;
tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
if (err != TOX_ERR_FILE_CONTROL_OK)
if (err != TOX_ERR_FILE_CONTROL_OK) {
goto on_recv_error;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
@ -190,6 +202,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.");
@ -215,6 +228,8 @@ on_recv_error:
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
const char *errmsg = NULL;
if (argc < 1) {
@ -222,16 +237,9 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
return;
}
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes.");
return;
}
/* remove opening and closing quotes */
char path[MAX_STR_SIZE];
snprintf(path, sizeof(path), "%s", &argv[1][1]);
int path_len = strlen(path) - 1;
path[path_len] = '\0';
snprintf(path, sizeof(path), "%s", argv[1]);
int path_len = strlen(path);
if (path_len >= MAX_STR_SIZE) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
@ -256,12 +264,13 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
char file_name[TOX_MAX_FILENAME_LENGTH];
size_t namelen = get_file_name(file_name, sizeof(file_name), path);
TOX_ERR_FILE_SEND err;
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)
if (err != TOX_ERR_FILE_SEND_OK) {
goto on_send_error;
}
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
@ -282,6 +291,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

@ -41,6 +41,12 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* AUDIO */
#endif /* #define CHAT_COMMANDS_H */
#ifdef VIDEO
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* VIDEO */
#endif /* CHAT_COMMANDS_H */

View File

@ -31,8 +31,9 @@
#include "toxic.h"
#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;
@ -47,8 +48,9 @@ void get_home_dir(char *home, int size)
} else {
hmstr = getenv("HOME");
if (hmstr == NULL)
if (hmstr == NULL) {
return;
}
snprintf(buf, sizeof(buf), "%s", hmstr);
hmstr = buf;
@ -69,27 +71,29 @@ char *get_user_config_dir(void)
char home[NSS_BUFLEN_PASSWD] = {0};
get_home_dir(home, sizeof(home));
char *user_config_dir;
size_t len;
char *user_config_dir = NULL;
size_t len = 0;
# if defined(__APPLE__)
len = strlen(home) + strlen("/Library/Application Support") + 1;
user_config_dir = malloc(len);
if (user_config_dir == NULL)
if (user_config_dir == NULL) {
return NULL;
}
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
# else /* __APPLE__ */
const char *tmp;
const char *tmp = getenv("XDG_CONFIG_HOME");
if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
if (tmp == NULL) {
len = strlen(home) + strlen("/.config") + 1;
user_config_dir = malloc(len);
if (user_config_dir == NULL)
if (user_config_dir == NULL) {
return NULL;
}
snprintf(user_config_dir, len, "%s/.config", home);
} else {
@ -101,22 +105,26 @@ char *get_user_config_dir(void)
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)
{
struct stat buf;
int mkdir_err = mkdir(path, 0700);
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode)))
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) {
return -1;
}
char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1);
char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1);
if (fullpath == NULL || logpath == NULL)
if (fullpath == NULL || logpath == NULL) {
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
}
strcpy(fullpath, path);
strcat(fullpath, CONFIGDIR);

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 */
#endif /* CONFIGDIR_H */

94
src/curl_util.c Normal file
View File

@ -0,0 +1,94 @@
/* 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 */

432
src/dns.c
View File

@ -1,432 +0,0 @@
/* dns.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* for u_char */
#include <netinet/in.h>
#include <resolv.h>
#ifdef __APPLE__
#include <arpa/nameser_compat.h>
#else
#include <arpa/nameser.h>
#endif /* ifdef __APPLE__ */
#include <tox/toxdns.h>
#include "toxic.h"
#include "windows.h"
#include "line_info.h"
#include "dns.h"
#include "global_commands.h"
#include "misc_tools.h"
#include "configdir.h"
#define DNS3_KEY_SIZE 32
#define MAX_DNS_REQST_SIZE 255
#define TOX_DNS3_TXT_PREFIX "v=tox3;id="
extern struct Winthread Winthread;
extern struct dns3_servers dns3_servers;
extern struct arg_opts arg_opts;
#define NUM_DNS3_BACKUP_SERVERS 2
/* Hardcoded backup in case domain list is not loaded */
static struct dns3_server_backup {
const char *name;
char key[DNS3_KEY_SIZE];
} dns3_servers_backup[] = {
{
"utox.org",
{
0xD3, 0x15, 0x4F, 0x65, 0xD2, 0x8A, 0x5B, 0x41, 0xA0, 0x5D, 0x4A, 0xC7, 0xE4, 0xB3, 0x9C, 0x6B,
0x1C, 0x23, 0x3C, 0xC8, 0x57, 0xFB, 0x36, 0x5C, 0x56, 0xE8, 0x39, 0x27, 0x37, 0x46, 0x2A, 0x12
}
},
{
"toxme.se",
{
0x5D, 0x72, 0xC5, 0x17, 0xDF, 0x6A, 0xEC, 0x54, 0xF1, 0xE9, 0x77, 0xA6, 0xB6, 0xF2, 0x59, 0x14,
0xEA, 0x4C, 0xF7, 0x27, 0x7A, 0x85, 0x02, 0x7C, 0xD9, 0xF5, 0x19, 0x6D, 0xF1, 0x7E, 0x0B, 0x13
}
},
};
static struct thread_data {
ToxWindow *self;
char id_bin[TOX_ADDRESS_SIZE];
char addr[MAX_STR_SIZE];
char msg[MAX_STR_SIZE];
uint8_t busy;
Tox *m;
} t_data;
static struct dns_thread {
pthread_t tid;
pthread_attr_t attr;
} dns_thread;
#define MAX_DNS_SERVERS 50
#define MAX_DOMAIN_SIZE 32
#define MAX_DNS_LINE MAX_DOMAIN_SIZE + (DNS3_KEY_SIZE * 2) + 3
struct dns3_servers {
bool loaded;
int lines;
char names[MAX_DNS_SERVERS][MAX_DOMAIN_SIZE];
char keys[MAX_DNS_SERVERS][DNS3_KEY_SIZE];
} dns3_servers;
static int load_dns_domainlist(const char *path)
{
FILE *fp = fopen(path, "r");
if (fp == NULL)
return -1;
char line[MAX_DNS_LINE];
while (fgets(line, sizeof(line), fp) && dns3_servers.lines < MAX_DNS_SERVERS) {
int linelen = strlen(line);
if (linelen < DNS3_KEY_SIZE * 2 + 5)
continue;
if (line[linelen - 1] == '\n')
line[--linelen] = '\0';
const char *name = strtok(line, " ");
const char *keystr = strtok(NULL, " ");
if (name == NULL || keystr == NULL)
continue;
if (strlen(keystr) != DNS3_KEY_SIZE * 2)
continue;
snprintf(dns3_servers.names[dns3_servers.lines], sizeof(dns3_servers.names[dns3_servers.lines]), "%s", name);
int res = hex_string_to_bytes(dns3_servers.keys[dns3_servers.lines], DNS3_KEY_SIZE, keystr);
if (res == -1)
continue;
++dns3_servers.lines;
}
fclose(fp);
if (dns3_servers.lines < 1)
return -2;
return 0;
}
static int dns_error(ToxWindow *self, const char *errmsg)
{
pthread_mutex_lock(&Winthread.lock);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "User lookup failed: %s", errmsg);
pthread_mutex_unlock(&Winthread.lock);
return -1;
}
static void killdns_thread(void *dns_obj)
{
if (dns_obj)
tox_dns3_kill(dns_obj);
memset(&t_data, 0, sizeof(struct thread_data));
pthread_attr_destroy(&dns_thread.attr);
pthread_exit(NULL);
}
/* puts TXT from dns response in buf. Returns length of TXT on success, -1 on fail.*/
static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char *buf)
{
uint8_t *ans_pt = answer + sizeof(HEADER);
uint8_t *ans_end = answer + ans_len;
char exp_ans[PACKETSZ];
int len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
if (len == -1)
return dns_error(self, "dn_expand failed.");
ans_pt += len;
if (ans_pt > ans_end - 4)
return dns_error(self, "DNS reply was too short.");
int type;
GETSHORT(type, ans_pt);
if (type != T_TXT)
return dns_error(self, "Broken DNS reply.");
ans_pt += INT16SZ; /* class */
uint32_t size = 0;
/* recurse through CNAME rr's */
do {
ans_pt += size;
len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
if (len == -1)
return dns_error(self, "Second dn_expand failed.");
ans_pt += len;
if (ans_pt > ans_end - 10)
return dns_error(self, "DNS reply was too short.");
GETSHORT(type, ans_pt);
ans_pt += INT16SZ;
ans_pt += 4;
GETSHORT(size, ans_pt);
if (ans_pt + size < answer || ans_pt + size > ans_end)
return dns_error(self, "RR overflow.");
} while (type == T_CNAME);
if (type != T_TXT)
return dns_error(self, "DNS response failed.");
uint32_t txt_len = *ans_pt;
if (!size || txt_len >= size || !txt_len)
return dns_error(self, "No record found.");
if (txt_len > MAX_DNS_REQST_SIZE)
return dns_error(self, "Invalid DNS response.");
ans_pt++;
ans_pt[txt_len] = '\0';
memcpy(buf, ans_pt, txt_len + 1);
return txt_len;
}
/* Takes address addr in the form "username@domain", puts the username in namebuf,
and the domain in dombuf.
return length of username on success, -1 on failure */
static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz)
{
if (strlen(addr) >= MAX_STR_SIZE)
return -1;
char tmpaddr[MAX_STR_SIZE];
char *tmpname = NULL;
char *tmpdom = NULL;
snprintf(tmpaddr, sizeof(tmpaddr), "%s", addr);
tmpname = strtok(tmpaddr, "@");
tmpdom = strtok(NULL, "");
if (tmpname == NULL || tmpdom == NULL)
return -1;
str_to_lower(tmpdom);
snprintf(namebuf, namebuf_sz, "%s", tmpname);
snprintf(dombuf, dombuf_sz, "%s", tmpdom);
return strlen(namebuf);
}
/* matches input domain name with domains in list and obtains key. Return 0 on success, -1 on failure */
static int get_domain_match(char *pubkey, char *domain, const char *inputdomain)
{
/* check server list first */
int i;
bool match = false;
for (i = 0; i < dns3_servers.lines; ++i) {
if (strcmp(dns3_servers.names[i], inputdomain) == 0) {
memcpy(pubkey, dns3_servers.keys[i], DNS3_KEY_SIZE);
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers.names[i]);
match = true;
break;
}
}
/* fall back to hard-coded domains on server list failure */
if (!match) {
for (i = 0; i < NUM_DNS3_BACKUP_SERVERS; ++i) {
if (strcmp(dns3_servers_backup[i].name, inputdomain) == 0) {
memcpy(pubkey, dns3_servers_backup[i].key, DNS3_KEY_SIZE);
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers_backup[i].name);
match = true;
break;
}
}
if (!match)
return -1;
}
return 0;
}
/* Does DNS lookup for addr and puts resulting tox id in id_bin. */
void *dns3_lookup_thread(void *data)
{
ToxWindow *self = t_data.self;
char inputdomain[MAX_STR_SIZE];
char name[MAX_STR_SIZE];
int namelen = parse_addr(t_data.addr, name, sizeof(name), inputdomain, sizeof(inputdomain));
if (namelen == -1) {
dns_error(self, "Must be a Tox ID or an address in the form username@domain");
killdns_thread(NULL);
}
char DNS_pubkey[DNS3_KEY_SIZE];
char domain[MAX_DOMAIN_SIZE];
int match = get_domain_match(DNS_pubkey, domain, inputdomain);
if (match == -1) {
dns_error(self, "Domain not found.");
killdns_thread(NULL);
}
void *dns_obj = tox_dns3_new((uint8_t *) DNS_pubkey);
if (dns_obj == NULL) {
dns_error(self, "Core failed to create DNS object.");
killdns_thread(NULL);
}
char string[MAX_DNS_REQST_SIZE + 1];
uint32_t request_id;
int str_len = tox_generate_dns3_string(dns_obj, (uint8_t *) string, sizeof(string), &request_id,
(uint8_t *) name, namelen);
if (str_len == -1) {
dns_error(self, "Core failed to generate DNS3 string.");
killdns_thread(dns_obj);
}
string[str_len] = '\0';
u_char answer[PACKETSZ];
char d_string[MAX_DOMAIN_SIZE + MAX_DNS_REQST_SIZE + 10];
/* format string and create dns query */
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domain);
int ans_len = res_query(d_string, C_IN, T_TXT, answer, sizeof(answer));
if (ans_len <= 0) {
dns_error(self, "DNS query failed.");
killdns_thread(dns_obj);
}
char ans_id[MAX_DNS_REQST_SIZE + 1];
/* extract TXT from DNS response */
if (parse_dns_response(self, answer, ans_len, ans_id) == -1)
killdns_thread(dns_obj);
char encrypted_id[MAX_DNS_REQST_SIZE + 1];
int prfx_len = strlen(TOX_DNS3_TXT_PREFIX);
/* extract the encrypted ID from TXT response */
if (strncmp(ans_id, TOX_DNS3_TXT_PREFIX, prfx_len) != 0) {
dns_error(self, "Bad DNS3 TXT response.");
killdns_thread(dns_obj);
}
memcpy(encrypted_id, ans_id + prfx_len, ans_len - prfx_len);
if (tox_decrypt_dns3_TXT(dns_obj, (uint8_t *) t_data.id_bin, (uint8_t *) encrypted_id,
strlen(encrypted_id), request_id) == -1) {
dns_error(self, "Core failed to decrypt DNS response.");
killdns_thread(dns_obj);
}
pthread_mutex_lock(&Winthread.lock);
cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
pthread_mutex_unlock(&Winthread.lock);
killdns_thread(dns_obj);
return 0;
}
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg)
{
if (arg_opts.proxy_type != TOX_PROXY_TYPE_NONE && arg_opts.force_tcp) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "DNS lookups are disabled.");
return;
}
if (t_data.busy) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous user lookup to finish.");
return;
}
if (!dns3_servers.loaded) {
const char *path = arg_opts.dns_path[0] ? arg_opts.dns_path : PACKAGE_DATADIR "/DNSservers";
dns3_servers.loaded = true;
int ret = load_dns_domainlist(path);
if (ret < 0) {
const char *errmsg = "DNS server list failed to load with error code %d. Falling back to hard-coded list.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg, ret);
}
}
snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin);
snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr);
snprintf(t_data.msg, sizeof(t_data.msg), "%s", msg);
t_data.self = self;
t_data.m = m;
t_data.busy = 1;
if (pthread_attr_init(&dns_thread.attr) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread attr failed to init");
memset(&t_data, 0, sizeof(struct thread_data));
return;
}
if (pthread_attr_setdetachstate(&dns_thread.attr, PTHREAD_CREATE_DETACHED) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread attr failed to set");
pthread_attr_destroy(&dns_thread.attr);
memset(&t_data, 0, sizeof(struct thread_data));
return;
}
if (pthread_create(&dns_thread.tid, &dns_thread.attr, dns3_lookup_thread, NULL) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread failed to init");
pthread_attr_destroy(&dns_thread.attr);
memset(&t_data, 0, sizeof(struct thread_data));
return;
}
}

View File

@ -33,6 +33,7 @@
#include "line_info.h"
#include "misc_tools.h"
#include "notify.h"
#include "api.h"
struct cmd_func {
const char *name;
@ -51,8 +52,12 @@ static struct cmd_func global_commands[] = {
{ "/help", cmd_prompt_help },
{ "/log", cmd_log },
{ "/myid", cmd_myid },
#ifdef QRCODE
{ "/myqr", cmd_myqr },
#endif /* QRCODE */
{ "/nick", cmd_nick },
{ "/note", cmd_note },
{ "/nospam", cmd_nospam },
{ "/q", cmd_quit },
{ "/quit", cmd_quit },
{ "/requests", cmd_requests },
@ -61,6 +66,13 @@ static struct cmd_func global_commands[] = {
{ "/lsdev", cmd_list_devices },
{ "/sdev", cmd_change_device },
#endif /* AUDIO */
#ifdef VIDEO
{ "/lsvdev", cmd_list_video_devices },
{ "/svdev", cmd_change_video_device },
#endif /* VIDEO */
#ifdef PYTHON
{ "/run", cmd_run },
#endif /* PYTHON */
{ NULL, NULL },
};
@ -77,7 +89,11 @@ static struct cmd_func chat_commands[] = {
{ "/hangup", cmd_hangup },
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
{ "/bitrate", cmd_bitrate },
#endif /* AUDIO */
#ifdef VIDEO
{ "/video", cmd_video },
#endif /* VIDEO */
{ NULL, NULL },
};
@ -91,52 +107,104 @@ static struct cmd_func group_commands[] = {
{ NULL, NULL },
};
/* Parses input command and puts args into arg array.
Returns number of arguments on success, -1 on failure. */
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
#ifdef PYTHON
#define SPECIAL_COMMANDS 6
#else
#define SPECIAL_COMMANDS 5
#endif /* PYTHON */
/* Special commands are commands that only take one argument even if it contains spaces */
static const char special_commands[SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = {
"/avatar",
"/nick",
"/note",
#ifdef PYTHON
"/run",
#endif /* PYTHON */
"/sendfile",
"/title",
};
/* Returns true if input command is in the special_commands array. */
static bool is_special_command(const char *input)
{
const int s = char_find(0, input, ' ');
for (int i = 0; i < SPECIAL_COMMANDS; ++i) {
if (strncmp(input, special_commands[i], s) == 0) {
return true;
}
}
return false;
}
/* Parses commands in the special_commands array. Unlike parse_command, this function
* does not split the input string at spaces.
*
* Returns the number of arguments.
*/
static int parse_special_command(const char *input, char (*args)[MAX_STR_SIZE])
{
int len = strlen(input);
int s = char_find(0, input, ' ');
memcpy(args[0], input, s);
args[0][s++] = '\0'; // increment to remove space after "/command "
if (s >= len) {
return 1; // No additional args
}
memcpy(args[1], input + s, len - s);
args[1][len - s] = '\0';
return 2;
}
/* Parses input command and puts args into arg array.
*
* Returns the number of arguments.
*/
static int parse_command(const char *input, char (*args)[MAX_STR_SIZE])
{
if (is_special_command(input)) {
return parse_special_command(input, args);
}
char *cmd = strdup(input);
if (cmd == NULL)
if (cmd == NULL) {
exit_toxic_err("failed in parse_command", FATALERR_MEMORY);
}
int num_args = 0;
int i = 0; /* index of last char in an argument */
/* characters wrapped in double quotes count as one arg */
while (num_args < MAX_NUM_ARGS) {
int qt_ofst = 0; /* set to 1 to offset index for quote char at end of arg */
int i = char_find(0, cmd, ' '); // index of last char in an argument
memcpy(args[num_args], cmd, i);
args[num_args++][i] = '\0';
if (*cmd == '\"') {
qt_ofst = 1;
i = char_find(1, cmd, '\"');
if (cmd[i] == '\0') {
const char *errmsg = "Invalid argument. Did you forget a closing \"?";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
free(cmd);
return -1;
}
} else {
i = char_find(0, cmd, ' ');
}
memcpy(args[num_args], cmd, i + qt_ofst);
args[num_args++][i + qt_ofst] = '\0';
if (cmd[i] == '\0') /* no more args */
if (cmd[i] == '\0') { // no more args
break;
}
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
strcpy(cmd, tmp); /* tmp will always fit inside cmd */
strcpy(cmd, tmp); // tmp will always fit inside cmd
}
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])
{
@ -154,34 +222,49 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct c
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
{
if (string_is_empty(input))
if (string_is_empty(input)) {
return;
}
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
int num_args = parse_command(w, self, input, args);
int num_args = parse_command(input, args);
if (num_args == -1)
if (num_args <= 0) {
return;
}
/* Try to match input command to command functions. If non-global command mode is specified,
try specified mode's commands first, then upon failure try global commands.
Note: Global commands must come last in case of duplicate command names */
* try specified mode's commands first, then upon failure try global commands.
*
* Note: Global commands must come last in case of duplicate command names
*/
switch (mode) {
case CHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, chat_commands, args) == 0)
if (do_command(w, self, m, num_args, chat_commands, args) == 0) {
return;
}
break;
case GROUPCHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, group_commands, args) == 0)
if (do_command(w, self, m, num_args, group_commands, args) == 0) {
return;
}
break;
}
if (do_command(w, self, m, num_args, global_commands, args) == 0)
if (do_command(w, self, m, num_args, global_commands, args) == 0) {
return;
}
#ifdef PYTHON
if (do_plugin_command(num_args, args) == 0) {
return;
}
#endif
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
}

View File

@ -36,4 +36,4 @@ enum {
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);
#endif /* #define EXECUTE_H */
#endif /* EXECUTE_H */

View File

@ -38,97 +38,65 @@ 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)
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)
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, "#");
for (i = 0; i < n; ++i) {
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)
static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft)
{
if (ft->state == FILE_TRANSFER_INACTIVE)
if (ft->state == FILE_TRANSFER_INACTIVE) {
return;
}
/* Timeout must be set to 1 second to show correct bytes per second */
if (!timed_out(ft->last_line_progress, 1))
if (!timed_out(ft->last_line_progress, 1)) {
return;
}
double remain = ft->file_size - ft->position;
double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
@ -139,13 +107,13 @@ static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer
}
/* refreshes active file transfer status bars. */
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnum)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]);
refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]);
refresh_progress_helper(self, &Friends.list[friendnum].file_receiver[i]);
refresh_progress_helper(self, &Friends.list[friendnum].file_sender[i]);
}
}
@ -159,13 +127,15 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum)
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum) {
return ft_send;
}
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum)
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum) {
return ft_recv;
}
}
return NULL;
@ -175,20 +145,22 @@ 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)
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) {
return NULL;
}
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
&Friends.list[friendnum].file_sender[i] :
&Friends.list[friendnum].file_receiver[i];
&Friends.list[friendnum].file_sender[i] :
&Friends.list[friendnum].file_receiver[i];
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) {
return ft;
}
}
return NULL;
@ -254,11 +226,13 @@ static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friend
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
FILE_TRANSFER_DIRECTION direction, uint8_t type)
{
if (direction == FILE_TRANSFER_RECV)
if (direction == FILE_TRANSFER_RECV) {
return new_file_receiver(window, friendnum, filenum, type);
}
if (direction == FILE_TRANSFER_SEND)
if (direction == FILE_TRANSFER_SEND) {
return new_file_sender(window, friendnum, filenum, type);
}
return NULL;
}
@ -272,23 +246,28 @@ struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, ui
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
Notification sound_type)
{
if (!ft)
if (!ft) {
return;
}
if (ft->state == FILE_TRANSFER_INACTIVE)
if (ft->state == FILE_TRANSFER_INACTIVE) {
return;
}
if (ft->file)
if (ft->file) {
fclose(ft->file);
}
if (CTRL >= 0)
tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
if (CTRL >= 0) {
tox_file_control(m, ft->friendnum, ft->filenum, (Tox_File_Control) CTRL, NULL);
}
if (message && self) {
if (self->active_box != -1 && sound_type != silent)
if (self->active_box != -1 && sound_type != silent) {
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
else
} else {
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
}
@ -311,6 +290,7 @@ void kill_all_file_transfers(Tox *m)
{
size_t i;
for (i = 0; i < Friends.max_idx; ++i)
for (i = 0; i < Friends.max_idx; ++i) {
kill_all_file_transfers_friend(m, Friends.list[i].num);
}
}

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);
@ -79,7 +75,7 @@ void init_progress_bar(char *progline);
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
/* refreshes active file transfer status bars. */
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnum);
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
* Returns NULL if filenum is invalid.
@ -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.
@ -112,4 +108,4 @@ void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
void kill_all_file_transfers(Tox *m);
#endif /* #define FILE_TRANSFERS_H */
#endif /* FILE_TRANSFERS_H */

File diff suppressed because it is too large Load Diff

View File

@ -44,17 +44,17 @@ struct GroupChatInvite {
typedef struct {
char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength;
uint16_t namelength;
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
size_t statusmsg_len;
char pub_key[TOX_PUBLIC_KEY_SIZE];
uint32_t num;
int chatwin;
bool active;
TOX_CONNECTION connection_status;
Tox_Connection connection_status;
bool is_typing;
bool logging_on; /* saves preference for friend irrespective of global settings */
uint8_t status;
Tox_User_Status status;
struct LastOnline last_online;
struct GroupChatInvite group_invite;
@ -65,7 +65,7 @@ typedef struct {
typedef struct {
char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength;
uint16_t namelength;
char pub_key[TOX_PUBLIC_KEY_SIZE];
uint32_t num;
bool active;
@ -81,14 +81,23 @@ typedef struct {
ToxicFriend *list;
} FriendsList;
ToxWindow new_friendlist(void);
ToxWindow *new_friendlist(void);
void disable_chatwin(uint32_t f_num);
int get_friendnum(uint8_t *name);
int load_blocklist(char *data);
void kill_friendlist(void);
void kill_friendlist(ToxWindow *self);
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
Tox_User_Status get_friend_status(uint32_t friendnumber);
Tox_Connection get_friend_connection_status(uint32_t friendnumber);
/* sorts friendlist_index first by connection status then alphabetically */
void sort_friendlist_index(void);
/*
* Returns true if friend associated with `public_key` is in the block list.
*
* `public_key` must be at least TOX_PUBLIC_KEY_SIZE bytes.
*/
bool friend_is_blocked(const char *public_key);
#endif /* end of include guard: FRIENDLIST_H */

View File

@ -22,7 +22,6 @@
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include "toxic.h"
#include "windows.h"
@ -30,29 +29,32 @@
#include "friendlist.h"
#include "log.h"
#include "line_info.h"
#include "dns.h"
#include "groupchat.h"
#include "prompt.h"
#include "help.h"
#include "term_mplex.h"
#include "avatars.h"
#include "name_lookup.h"
#include "qr_code.h"
#include "toxic_strings.h"
extern char *DATA_FILE;
extern ToxWindow *prompt;
extern FriendsList Friends;
extern FriendRequests FrndRequests;
/* command functions */
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
return;
}
int req = atoi(argv[1]);
long int req = strtol(argv[1], NULL, 10);
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
@ -62,7 +64,7 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
return;
}
TOX_ERR_FRIEND_ADD err;
Tox_Err_Friend_Add err;
uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err);
if (err != TOX_ERR_FRIEND_ADD_OK) {
@ -70,7 +72,7 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
return;
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
on_friendadded(m, friendnum, true);
on_friend_added(m, friendnum, true);
}
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
@ -78,8 +80,9 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
int i;
for (i = FrndRequests.max_idx; i > 0; --i) {
if (FrndRequests.request[i - 1].active)
if (FrndRequests.request[i - 1].active) {
break;
}
}
FrndRequests.max_idx = i;
@ -91,8 +94,8 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
{
const char *errmsg;
TOX_ERR_FRIEND_ADD err;
uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err);
Tox_Err_Friend_Add err;
uint32_t f_num = tox_friend_add(m, (const uint8_t *) id_bin, (const uint8_t *) msg, strlen(msg), &err);
switch (err) {
case TOX_ERR_FRIEND_ADD_TOO_LONG:
@ -125,13 +128,14 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
case TOX_ERR_FRIEND_ADD_OK:
errmsg = "Friend request sent.";
on_friendadded(m, f_num, true);
on_friend_added(m, f_num, true);
break;
case TOX_ERR_FRIEND_ADD_NULL:
/* fallthrough */
default:
errmsg = "Faile to add friend: Unknown error.";
errmsg = "Failed to add friend: Unknown error.";
break;
}
@ -140,6 +144,8 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
return;
@ -191,29 +197,30 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
id_bin[i] = x;
}
if (friend_is_blocked(id_bin)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend is in your block list.");
return;
}
cmd_add_helper(self, m, id_bin, msg);
} else { /* assume id is a username@domain address and do DNS lookup */
dns3_lookup(self, m, id_bin, id, msg);
} else { /* assume id is a username@domain address and do http name server lookup */
name_lookup(self, m, id_bin, id, msg);
}
}
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 2 || strlen(argv[1]) < 3) {
UNUSED_VAR(window);
if (argc != 1 || strlen(argv[1]) < 3) {
avatar_unset(m);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar is not set.");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar has been unset.");
return;
}
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes.");
return;
}
/* remove opening and closing quotes */
char path[MAX_STR_SIZE];
snprintf(path, sizeof(path), "%s", &argv[1][1]);
int len = strlen(path) - 1;
snprintf(path, sizeof(path), "%s", argv[1]);
int len = strlen(path);
if (len <= 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
@ -236,35 +243,44 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(m);
UNUSED_VAR(argc);
UNUSED_VAR(argv);
line_info_clear(self->chatwin->hst);
force_refresh(window);
}
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
if (argc != 3) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
return;
}
const char *ip = argv[1];
const char *port = argv[2];
const char *port_str = argv[2];
const char *ascii_key = argv[3];
if (atoi(port) == 0) {
long int port = strtol(port_str, NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
return;
}
char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
return;
}
TOX_ERR_BOOTSTRAP err;
tox_bootstrap(m, ip, atoi(port), (uint8_t *) key_binary, &err);
tox_add_tcp_relay(m, ip, atoi(port), (uint8_t *) key_binary, &err);
Tox_Err_Bootstrap err;
tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err);
tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err);
switch (err) {
case TOX_ERR_BOOTSTRAP_BAD_HOST:
@ -278,6 +294,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;
}
@ -285,14 +302,17 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
UNUSED_VAR(m);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
return;
}
int req = atoi(argv[1]);
long int req = strtol(argv[1], NULL, 10);
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
@ -307,8 +327,9 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
int i;
for (i = FrndRequests.max_idx; i > 0; --i) {
if (FrndRequests.request[i - 1].active)
if (FrndRequests.request[i - 1].active) {
break;
}
}
FrndRequests.max_idx = i;
@ -317,6 +338,8 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
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;
@ -329,32 +352,32 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
uint8_t type;
if (!strcasecmp(argv[1], "audio"))
type = TOX_GROUPCHAT_TYPE_AV;
else if (!strcasecmp(argv[1], "text"))
type = TOX_GROUPCHAT_TYPE_TEXT;
else {
if (!strcasecmp(argv[1], "audio")) {
type = TOX_CONFERENCE_TYPE_AV;
} else if (!strcasecmp(argv[1], "text")) {
type = TOX_CONFERENCE_TYPE_TEXT;
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
return;
}
int groupnum = -1;
if (type == TOX_GROUPCHAT_TYPE_TEXT)
groupnum = tox_add_groupchat(m);
#ifdef AUDIO
else
groupnum = toxav_add_av_groupchat(m, NULL, NULL);
#endif
if (groupnum == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
if (type != TOX_CONFERENCE_TYPE_TEXT) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
return;
}
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
Tox_Err_Conference_New err;
uint32_t groupnum = tox_conference_new(m, &err);
if (err != TOX_ERR_CONFERENCE_NEW_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
return;
}
if (init_groupchat_win(m, groupnum, type, NULL, 0) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
tox_conference_delete(m, groupnum, NULL);
return;
}
@ -363,14 +386,17 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
const char *msg;
struct chatlog *log = self->chatwin->log;
if (argc == 0) {
if (log->log_on)
msg = "Logging for this window is ON. Type \"/log off\" to disable.";
else
msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
if (log->log_on) {
msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)";
} else {
msg = "Logging for this window is OFF; type \"/log on\" to enable.";
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
@ -397,8 +423,9 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
if (self->is_chat)
if (self->is_chat) {
Friends.list[self->num].logging_on = false;
}
log_disable(log);
@ -413,39 +440,96 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char id[TOX_ADDRESS_SIZE * 2 + 1] = {0};
char address[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) address);
UNUSED_VAR(window);
UNUSED_VAR(argc);
UNUSED_VAR(argv);
size_t i;
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
char bin_id[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) bin_id);
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
char xx[3];
snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff);
strcat(id, xx);
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID.");
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
}
#ifdef QRCODE
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
char bin_id[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) bin_id);
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
return;
}
char nick[TOX_MAX_NAME_LENGTH];
tox_self_get_name(m, (uint8_t *) nick);
size_t nick_len = tox_self_get_name_size(m);
nick[nick_len] = '\0';
size_t data_file_len = strlen(DATA_FILE);
char dir[data_file_len + 1];
size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir);
#ifdef QRPNG
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")) {
#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;
}
#endif /* QRPNG */
}
#endif /* QRCODE */
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
return;
}
char nick[MAX_STR_SIZE];
size_t len = 0;
if (argv[1][0] == '\"') { /* remove opening and closing quotes */
snprintf(nick, sizeof(nick), "%s", &argv[1][1]);
len = strlen(nick) - 1;
nick[len] = '\0';
} else {
snprintf(nick, sizeof(nick), "%s", argv[1]);
len = strlen(nick);
}
snprintf(nick, sizeof(nick), "%s", argv[1]);
size_t len = strlen(nick);
if (!valid_nick(nick)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
@ -463,37 +547,68 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
return;
}
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
return;
prompt_update_statusmessage(prompt, m, argv[1]);
}
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
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;
}
}
/* 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';
uint32_t old_nospam = tox_self_get_nospam(m);
tox_self_set_nospam(m, (uint32_t) nospam);
prompt_update_statusmessage(prompt, m, msg);
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, "");
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])
{
UNUSED_VAR(window);
UNUSED_VAR(m);
UNUSED_VAR(argc);
UNUSED_VAR(argv);
help_init_menu(self);
}
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
UNUSED_VAR(argc);
UNUSED_VAR(argv);
UNUSED_VAR(self);
exit_toxic_success(m);
}
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
UNUSED_VAR(m);
UNUSED_VAR(argc);
UNUSED_VAR(argv);
if (FrndRequests.num_requests == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
return;
@ -503,8 +618,9 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
int count = 0;
for (i = 0; i < FrndRequests.max_idx; ++i) {
if (!FrndRequests.request[i].active)
if (!FrndRequests.request[i].active) {
continue;
}
char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0};
@ -517,36 +633,36 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg);
if (++count < FrndRequests.num_requests)
if (++count < FrndRequests.num_requests) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
}
}
}
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
bool have_note = false;
UNUSED_VAR(window);
const char *errmsg;
lock_status ();
lock_status();
if (argc >= 2) {
have_note = true;
} else if (argc < 1) {
if (argc < 1) {
errmsg = "Require a status. Statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
goto finish;
}
const char *status_str = argv[1];
TOX_USER_STATUS status;
Tox_User_Status status;
if (!strcasecmp(status_str, "online"))
if (!strcasecmp(status_str, "online")) {
status = TOX_USER_STATUS_NONE;
else if (!strcasecmp(status_str, "away"))
} else if (!strcasecmp(status_str, "away")) {
status = TOX_USER_STATUS_AWAY;
else if (!strcasecmp(status_str, "busy"))
} else if (!strcasecmp(status_str, "busy")) {
status = TOX_USER_STATUS_BUSY;
else {
} else {
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
goto finish;
@ -554,22 +670,9 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
tox_self_set_status(m, status);
prompt_update_status(prompt, status);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
if (have_note) {
if (argv[2][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
goto finish;
}
/* remove opening and closing quotes */
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "%s", &argv[2][1]);
int len = strlen(msg) - 1;
msg[len] = '\0';
prompt_update_statusmessage(prompt, m, msg);
}
finish:
unlock_status ();
unlock_status();
}

View File

@ -35,8 +35,12 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef QRCODE
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* QRCODE */
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
@ -49,4 +53,13 @@ void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_S
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* AUDIO */
#endif /* #define GLOBAL_COMMANDS_H */
#ifdef VIDEO
void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* VIDEO */
#ifdef PYTHON
void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* PYTHON */
#endif /* GLOBAL_COMMANDS_H */

View File

@ -30,33 +30,35 @@
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
Tox_Err_Conference_Title err;
char title[MAX_STR_SIZE];
if (argc < 1) {
int tlen = tox_group_get_title(m, self->num, (uint8_t *) title, TOX_MAX_NAME_LENGTH);
size_t tlen = tox_conference_get_title_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 {
if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
return;
}
if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
return;
}
title[tlen] = '\0';
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
return;
}
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes.");
return;
}
snprintf(title, sizeof(title), "%s", argv[1]);
int len = strlen(title);
/* remove opening and closing quotes */
snprintf(title, sizeof(title), "%s", &argv[1][1]);
int len = strlen(title) - 1;
title[len] = '\0';
if (tox_group_set_title(m, self->num, (uint8_t *) title, len) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title.");
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
return;
}

View File

@ -28,4 +28,4 @@
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* GROUP_COMMANDS_H */
#endif /* GROUP_COMMANDS_H */

File diff suppressed because it is too large Load Diff

View File

@ -26,61 +26,39 @@
#include "toxic.h"
#include "windows.h"
#ifdef AUDIO
#include "audio_call.h"
#endif
#ifdef AUDIO
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#endif /* AUDIO */
#define SIDEBAR_WIDTH 16
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
#define GROUP_EVENT_WAIT 3
#ifdef AUDIO
struct GAudio {
ALCdevice *dvhandle; /* Handle of device selected/opened */
ALCcontext *dvctx;
ALuint source;
ALuint buffers[OPENAL_BUFS];
};
#endif /* AUDIO */
typedef struct GroupPeer {
bool active;
char name[TOX_MAX_NAME_LENGTH];
size_t name_length;
uint32_t peernumber;
} GroupPeer;
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;
time_t start_time;
GroupPeer *peer_list;
uint32_t max_idx;
char *name_list;
uint32_t num_peers;
#ifdef AUDIO
struct GAudio audio;
#endif
} GroupChat;
void close_groupchat(ToxWindow *self, Tox *m, int groupnum);
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type);
/* Frees all Toxic associated data structures for a groupchat (does not call tox_conference_delete() ) */
void free_groupchat(ToxWindow *self, uint32_t groupnum);
int init_groupchat_win(Tox *m, uint32_t groupnum, uint8_t type, const char *title, size_t title_length);
/* destroys and re-creates groupchat window with or without the peerlist */
void redraw_groupchat_win(ToxWindow *self);
ToxWindow new_group_chat(Tox *m, int groupnum);
#endif /* #define GROUPCHAT_H */
#endif /* GROUPCHAT_H */

View File

@ -27,19 +27,29 @@
#include "help.h"
#include "misc_tools.h"
#ifdef PYTHON
#include "api.h"
#endif /* PYTHON */
#ifdef PYTHON
#define HELP_MENU_HEIGHT 10
#else
#define HELP_MENU_HEIGHT 9
#endif /* PYTHON */
#define HELP_MENU_WIDTH 26
void help_init_menu(ToxWindow *self)
{
if (self->help->win)
if (self->help->win) {
delwin(self->help->win);
}
int y2, x2;
getmaxyx(self->window, y2, x2);
if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH)
if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH) {
return;
}
self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3);
self->help->active = true;
@ -54,12 +64,17 @@ static void help_exit(ToxWindow *self)
static void help_init_window(ToxWindow *self, int height, int width)
{
if (self->help->win)
if (self->help->win) {
delwin(self->help->win);
}
int y2, x2;
getmaxyx(stdscr, y2, x2);
if (y2 <= 0 || x2 <= 0) {
return;
}
height = MIN(height, y2);
width = MIN(width, x2);
@ -92,6 +107,13 @@ static void help_draw_menu(ToxWindow *self)
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "oup commands\n");
#ifdef PYTHON
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " p");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "lugin commands\n");
#endif /* PYTHON */
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " f");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
@ -109,14 +131,15 @@ static void help_draw_menu(ToxWindow *self)
wprintw(win, "it menu\n");
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
static void help_draw_bottom_menu(WINDOW *win)
{
int y2, x2;
getmaxyx(win, y2, x2);
(void) x2;
UNUSED_VAR(x2);
wmove(win, y2 - 2, 1);
@ -151,9 +174,17 @@ static void help_draw_global(ToxWindow *self)
wprintw(win, " /status <type> <msg> : Set status with optional note\n");
wprintw(win, " /note <msg> : Set a personal note\n");
wprintw(win, " /nick <nick> : Set your nickname\n");
wprintw(win, " /nospam <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 QRCODE
#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 */
#endif /* QRCODE */
wprintw(win, " /clear : Clear window history\n");
wprintw(win, " /close : Close the current chat window\n");
wprintw(win, " /quit or /exit : Exit Toxic\n");
@ -167,10 +198,27 @@ static void help_draw_global(ToxWindow *self)
wprintw(win, " /sdev <type> <id> : Set active device\n");
#endif /* AUDIO */
#ifdef VIDEO
wattron(win, A_BOLD);
wprintw(win, "\n Video:\n");
wattroff(win, A_BOLD);
wprintw(win, " /lsvdev <type> : List video devices where type: in|out\n");
wprintw(win, " /svdev <type> <id> : Set active video device\n");
#endif /* VIDEO */
#ifdef PYTHON
wattron(win, A_BOLD);
wprintw(win, "\n Scripting:\n");
wattroff(win, A_BOLD);
wprintw(win, " /run <path> : Load and run the script at path\n");
#endif /* PYTHON */
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
static void help_draw_chat(ToxWindow *self)
@ -201,12 +249,20 @@ static void help_draw_chat(ToxWindow *self)
wprintw(win, " /sdev <type> <id> : Change active device\n");
wprintw(win, " /mute <type> : Mute active device if in call\n");
wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
wprintw(win, " /bitrate <n> : Set the audio encoding bitrate\n");
#endif /* AUDIO */
#ifdef VIDEO
wattron(win, A_BOLD);
wprintw(win, "\n Video:\n");
wattroff(win, A_BOLD);
wprintw(win, " /video : Toggle video call\n");
#endif /* VIDEO */
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
static void help_draw_keys(ToxWindow *self)
@ -223,14 +279,16 @@ static void help_draw_keys(ToxWindow *self)
wprintw(win, " Page Up and Page Down : Scroll window history one line\n");
wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\n");
wprintw(win, " Ctrl+H : Move to the bottom of window history\n");
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n");
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n\n");
wprintw(win, " Ctrl+up and Ctrl+down : Scroll peer list in groupchats\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);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
static void help_draw_group(ToxWindow *self)
@ -248,9 +306,29 @@ static void help_draw_group(ToxWindow *self)
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
#ifdef PYTHON
static void help_draw_plugin(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, "Plugin commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
draw_handler_help(win);
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wnoutrefresh(win);
}
#endif /* PYTHON */
static void help_draw_contacts(ToxWindow *self)
{
WINDOW *win = self->help->win;
@ -270,32 +348,41 @@ static void help_draw_contacts(ToxWindow *self)
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
void help_onKey(ToxWindow *self, wint_t key)
{
switch(key) {
int height;
switch (key) {
case 'x':
case T_KEY_ESC:
help_exit(self);
break;
case 'c':
#ifdef AUDIO
help_init_window(self, 19, 80);
#ifdef VIDEO
help_init_window(self, 23, 80);
#elif AUDIO
help_init_window(self, 20, 80);
#else
help_init_window(self, 9, 80);
help_init_window(self, 10, 80);
#endif
self->help->type = HELP_CHAT;
break;
case 'g':
#ifdef AUDIO
help_init_window(self, 24, 80);
#else
help_init_window(self, 20, 80);
height = 22;
#ifdef VIDEO
height += 8;
#elif AUDIO
height += 4;
#endif
#ifdef PYTHON
height += 2;
#endif
help_init_window(self, height, 80);
self->help->type = HELP_GLOBAL;
break;
@ -304,13 +391,21 @@ void help_onKey(ToxWindow *self, wint_t key)
self->help->type = HELP_GROUP;
break;
#ifdef PYTHON
case 'p':
help_init_window(self, 4 + num_registered_handlers(), help_max_width());
self->help->type = HELP_PLUGIN;
break;
#endif /* PYTHON */
case 'f':
help_init_window(self, 10, 80);
self->help->type = HELP_CONTACTS;
break;
case 'k':
help_init_window(self, 13, 80);
help_init_window(self, 15, 80);
self->help->type = HELP_KEYS;
break;
@ -323,9 +418,7 @@ void help_onKey(ToxWindow *self, wint_t key)
void help_onDraw(ToxWindow *self)
{
curs_set(0);
switch(self->help->type) {
switch (self->help->type) {
case HELP_MENU:
help_draw_menu(self);
return;
@ -349,5 +442,12 @@ void help_onDraw(ToxWindow *self)
case HELP_GROUP:
help_draw_group(self);
break;
#ifdef PYTHON
case HELP_PLUGIN:
help_draw_plugin(self);
break;
#endif /* PYTHON */
}
}

View File

@ -33,10 +33,13 @@ typedef enum {
HELP_GROUP,
HELP_KEYS,
HELP_CONTACTS,
#ifdef PYTHON
HELP_PLUGIN,
#endif
} HELP_TYPES;
void help_onDraw(ToxWindow *self);
void help_init_menu(ToxWindow *self);
void help_onKey(ToxWindow *self, wint_t key);
#endif /* #define HELP_H */
#endif /* HELP_H */

View File

@ -38,13 +38,17 @@
extern struct user_settings *user_settings;
/* add a char to input field and buffer */
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
void input_new_char(ToxWindow *self, wint_t key, int x, int mx_x)
{
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;
@ -74,42 +78,45 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
if (ctx->start && (x >= mx_x - cur_len))
if (ctx->start && (x >= mx_x - cur_len)) {
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
else if (ctx->start)
} else if (ctx->start) {
ctx->start = MAX(0, ctx->start - cur_len);
}
}
/* delete a char via delete key from input field and buffer */
static void input_delete(ToxWindow *self)
{
if (del_char_buf_frnt(self->chatwin) == -1)
if (del_char_buf_frnt(self->chatwin) == -1) {
sound_notify(self, notif_error, 0, NULL);
}
}
/* delete last typed word */
static void input_del_word(ToxWindow *self, int x, int mx_x)
static void input_del_word(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
if (del_word_buf(ctx) == -1) {
sound_notify(self, notif_error, 0, NULL);
return;
}
}
/* deletes entire line before cursor from input field and buffer */
static void input_discard(ToxWindow *self)
{
if (discard_buf(self->chatwin) == -1)
if (discard_buf(self->chatwin) == -1) {
sound_notify(self, notif_error, 0, NULL);
}
}
/* deletes entire line after cursor from input field and buffer */
static void input_kill(ChatContext *ctx)
{
if (kill_buf(ctx) == -1)
if (kill_buf(ctx) == -1) {
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
}
static void input_yank(ToxWindow *self, int x, int mx_x)
@ -131,7 +138,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
}
/* moves cursor/line position to end of line in input field and buffer */
static void input_mv_end(ToxWindow *self, int y, int mx_x)
static void input_mv_end(ToxWindow *self, int mx_x)
{
ChatContext *ctx = self->chatwin;
@ -146,8 +153,9 @@ static void input_mv_home(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0)
if (ctx->pos <= 0) {
return;
}
ctx->pos = 0;
ctx->start = 0;
@ -158,18 +166,44 @@ static void input_mv_left(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0)
if (ctx->pos <= 0) {
return;
}
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
--ctx->pos;
if (ctx->start && (x >= mx_x - cur_len))
if (ctx->start > 0 && (x >= mx_x - cur_len)) {
int s_len = wcwidth(ctx->line[ctx->start - 1]);
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
else if (ctx->start)
} else if (ctx->start > 0) {
ctx->start = MAX(0, ctx->start - cur_len);
}
}
/* moves the cursor to the beginning of the previous word in input field and buffer */
static void input_skip_left(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0) {
return;
}
int count = 0;
do {
--ctx->pos;
count += wcwidth(ctx->line[ctx->pos]);
} while (ctx->pos > 0 && (ctx->line[ctx->pos - 1] != L' ' || ctx->line[ctx->pos] == L' '));
if (ctx->start > 0 && (x >= mx_x - count)) {
int s_len = wcwidth(ctx->line[ctx->start - 1]);
ctx->start = MAX(0, ctx->start - 1 + (s_len - count));
} else if (ctx->start > 0) {
ctx->start = MAX(0, ctx->start - count);
}
}
/* moves cursor/line position right in input field and buffer */
@ -177,8 +211,9 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos >= ctx->len)
if (ctx->pos >= ctx->len) {
return;
}
++ctx->pos;
@ -190,6 +225,29 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
}
}
/* moves the cursor to the end of the next word in input field and buffer */
static void input_skip_right(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos >= ctx->len) {
return;
}
int count = 0;
do {
count += wcwidth(ctx->line[ctx->pos]);
++ctx->pos;
} while (ctx->pos < ctx->len && !(ctx->line[ctx->pos] == L' ' && ctx->line[ctx->pos - 1] != L' '));
int newpos = x + count;
if (newpos >= mx_x) {
ctx->start += (1 + (newpos - mx_x));
}
}
/* puts a line history item in input field and buffer */
static void input_history(ToxWindow *self, wint_t key, int mx_x)
{
@ -202,7 +260,7 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x)
/* Handles non-printable input keys that behave the same for all types of chat windows.
return true if key matches a function, false otherwise */
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x)
{
bool match = true;
@ -229,7 +287,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
break;
case T_KEY_C_W:
input_del_word(self, x, mx_x);
input_del_word(self);
break;
case KEY_HOME:
@ -239,7 +297,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
case KEY_END:
case T_KEY_C_E:
input_mv_end(self, y, mx_x);
input_mv_end(self, mx_x);
break;
case KEY_LEFT:
@ -259,6 +317,14 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
force_refresh(self->chatwin->history);
break;
case T_KEY_C_LEFT:
input_skip_left(self, x, mx_x);
break;
case T_KEY_C_RIGHT:
input_skip_right(self, x, mx_x);
break;
default:
match = false;
break;
@ -266,15 +332,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

@ -24,10 +24,10 @@
#define INPUT_H
/* add a char to input field and buffer for given chatcontext */
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
void input_new_char(ToxWindow *self, wint_t key, int x, int mx_x);
/* Handles non-printable input keys that behave the same for all types of chat windows.
return true if key matches a function, false otherwise */
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x);
#endif /* #define INPUT_H */
#endif /* INPUT_H */

View File

@ -40,8 +40,9 @@ void line_info_init(struct history *hst)
{
hst->line_root = calloc(1, sizeof(struct line_info));
if (hst->line_root == NULL)
if (hst->line_root == NULL) {
exit_toxic_err("failed in line_info_init", FATALERR_MEMORY);
}
hst->line_start = hst->line_root;
hst->line_end = hst->line_start;
@ -53,8 +54,9 @@ void line_info_reset_start(ToxWindow *self, struct history *hst)
{
struct line_info *line = hst->line_end;
if (line->prev == NULL)
if (line->prev == NULL) {
return;
}
int y2, x2;
getmaxyx(self->window, y2, x2);
@ -88,8 +90,9 @@ void line_info_cleanup(struct history *hst)
int i;
for (i = 0; i < hst->queue_sz; ++i) {
if (hst->queue[i])
if (hst->queue[i]) {
free(hst->queue[i]);
}
}
free(hst);
@ -114,37 +117,46 @@ static void line_info_root_fwd(struct history *hst)
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
static struct line_info *line_info_ret_queue(struct history *hst)
{
if (hst->queue_sz <= 0)
if (hst->queue_sz <= 0) {
return NULL;
}
struct line_info *line = hst->queue[0];
int i;
for (i = 0; i < hst->queue_sz; ++i)
for (i = 0; i < hst->queue_sz; ++i) {
hst->queue[i] = hst->queue[i + 1];
}
--hst->queue_sz;
return line;
}
/* creates new line_info line and puts it in the queue. */
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...)
/* creates new line_info line and puts it in the queue.
*
* Returns the id of the new line.
* Returns -1 on failure.
*/
int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...)
{
if (!self)
return;
if (!self) {
return -1;
}
struct history *hst = self->chatwin->hst;
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
return;
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) {
return -1;
}
struct line_info *new_line = calloc(1, sizeof(struct line_info));
if (new_line == NULL)
if (new_line == NULL) {
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
}
char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0};
@ -158,12 +170,14 @@ 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;
@ -200,8 +214,9 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
int i;
for (i = 0; frmt_msg[i]; ++i) {
if (frmt_msg[i] == '\n')
if (frmt_msg[i] == '\n') {
++new_line->newlines;
}
}
}
@ -220,6 +235,7 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
len += strlen(new_line->name2);
}
new_line->id = (hst->line_end->id + 1 + hst->queue_sz) % INT_MAX;
new_line->len = len;
new_line->type = type;
new_line->bold = bold;
@ -228,6 +244,8 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
new_line->timestamp = get_unix_time();
hst->queue[hst->queue_sz++] = new_line;
return new_line->id;
}
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
@ -236,24 +254,28 @@ static void line_info_check_queue(ToxWindow *self)
struct history *hst = self->chatwin->hst;
struct line_info *line = line_info_ret_queue(hst);
if (line == NULL)
if (line == NULL) {
return;
}
if (hst->start_id > user_settings->history_size)
if (hst->start_id > user_settings->history_size) {
line_info_root_fwd(hst);
}
line->id = hst->line_end->id + 1;
line->prev = hst->line_end;
hst->line_end->next = line;
hst->line_end = line;
hst->line_end->id = line->id;
int y, y2, x, x2;
getmaxyx(self->window, y2, x2);
getyx(self->chatwin->history, y, x);
(void) x;
if (x2 <= SIDEBAR_WIDTH)
UNUSED_VAR(x);
if (x2 <= SIDEBAR_WIDTH) {
return;
}
int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
int lines = 1 + line->newlines + (line->len / (x2 - offst));
@ -275,8 +297,9 @@ void line_info_print(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
if (ctx == NULL)
if (ctx == NULL) {
return;
}
struct history *hst = ctx->hst;
@ -284,19 +307,25 @@ void line_info_print(ToxWindow *self)
line_info_check_queue(self);
WINDOW *win = ctx->history;
wclear(win);
int y2, x2;
getmaxyx(self->window, y2, x2);
if (x2 <= SIDEBAR_WIDTH)
if (x2 <= SIDEBAR_WIDTH) {
return;
}
if (self->is_groupchat)
if (self->is_groupchat) {
wmove(win, 0, 0);
else
} else {
wmove(win, 2, 0);
}
struct line_info *line = hst->line_start->next;
int numlines = 0;
while (line && numlines++ <= y2) {
@ -304,8 +333,10 @@ void line_info_print(ToxWindow *self)
switch (type) {
case OUT_MSG:
/* fallthrough */
case OUT_MSG_READ:
/* fallthrough */
case IN_MSG:
wattron(win, COLOR_PAIR(BLUE));
@ -314,30 +345,44 @@ void line_info_print(ToxWindow *self)
int nameclr = GREEN;
if (line->colour)
if (line->colour) {
nameclr = line->colour;
else if (type == IN_MSG)
} else if (type == IN_MSG) {
nameclr = CYAN;
}
wattron(win, COLOR_PAIR(nameclr));
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
wattroff(win, COLOR_PAIR(nameclr));
if (line->msg[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
else if (line->msg[0] == '<')
wattron(win, COLOR_PAIR(RED));
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));
wprintw(win, " x", line->msg);
wprintw(win, " x");
wattroff(win, COLOR_PAIR(RED));
if (line->noread_flag == false) {
@ -350,8 +395,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));
@ -383,19 +430,23 @@ void line_info_print(ToxWindow *self)
wattroff(win, COLOR_PAIR(BLUE));
}
if (line->bold)
if (line->bold) {
wattron(win, A_BOLD);
}
if (line->colour)
if (line->colour) {
wattron(win, COLOR_PAIR(line->colour));
}
wprintw(win, "%s\n", line->msg);
if (line->bold)
if (line->bold) {
wattroff(win, A_BOLD);
}
if (line->colour)
if (line->colour) {
wattroff(win, COLOR_PAIR(line->colour));
}
break;
@ -404,8 +455,9 @@ void line_info_print(ToxWindow *self)
wprintw(win, "$ ");
wattroff(win, COLOR_PAIR(GREEN));
if (line->msg[0])
if (line->msg[0]) {
wprintw(win, "%s", line->msg);
}
wprintw(win, "\n");
break;
@ -469,8 +521,9 @@ void line_info_print(ToxWindow *self)
}
/* keep calling until queue is empty */
if (hst->queue_sz > 0)
if (hst->queue_sz > 0) {
line_info_print(self);
}
}
/* puts msg in specified line_info msg buffer */
@ -495,40 +548,50 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg)
static void line_info_scroll_up(struct history *hst)
{
if (hst->line_start->prev)
if (hst->line_start->prev) {
hst->line_start = hst->line_start->prev;
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
} else {
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
}
static void line_info_scroll_down(struct history *hst)
{
if (hst->line_start->next)
if (hst->line_start->next) {
hst->line_start = hst->line_start->next;
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
} else {
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
}
static void line_info_page_up(ToxWindow *self, struct history *hst)
{
int x2, y2;
getmaxyx(self->window, y2, x2);
(void) x2;
UNUSED_VAR(x2);
int jump_dist = y2 / 2;
int i;
for (i = 0; i < jump_dist && hst->line_start->prev; ++i)
for (i = 0; i < jump_dist && hst->line_start->prev; ++i) {
hst->line_start = hst->line_start->prev;
}
}
static void line_info_page_down(ToxWindow *self, struct history *hst)
{
int x2, y2;
getmaxyx(self->window, y2, x2);
(void) x2;
UNUSED_VAR(x2);
int jump_dist = y2 / 2;
int i;
for (i = 0; i < jump_dist && hst->line_start->next; ++i)
for (i = 0; i < jump_dist && hst->line_start->next; ++i) {
hst->line_start = hst->line_start->next;
}
}
bool line_info_onKey(ToxWindow *self, wint_t key)
@ -538,20 +601,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

@ -31,7 +31,7 @@
#define MAX_LINE_INFO_QUEUE 1024
#define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* needs extra room for log loading */
enum {
typedef enum {
SYS_MSG,
IN_MSG,
OUT_MSG,
@ -50,7 +50,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;
@ -74,9 +74,13 @@ struct history {
int queue_sz;
};
/* creates new line_info line and puts it in the queue. */
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...);
/* creates new line_info line and puts it in the queue.
*
* Returns the id of the new line.
* Returns -1 on failure.
*/
int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...);
/* Prints a section of history starting at line_start */
void line_info_print(ToxWindow *self);
@ -96,4 +100,4 @@ void line_info_reset_start(ToxWindow *self, struct history *hst);
void line_info_init(struct history *hst);
bool line_info_onKey(ToxWindow *self, wint_t key); /* returns true if key is a match */
#endif /* #define LINE_INFO_H */
#endif /* LINE_INFO_H */

View File

@ -45,8 +45,9 @@ extern struct user_settings *user_settings;
Returns 0 on success, -1 if the path is too long */
static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype)
{
if (!valid_nick(name))
if (!valid_nick(name)) {
name = UNKNOWN_NAME;
}
const char *namedash = logtype == LOG_PROMPT ? "" : "-";
const char *set_path = user_settings->chatlogs_path;
@ -85,10 +86,11 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke
return -1;
}
if (!string_is_empty(set_path))
if (!string_is_empty(set_path)) {
snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id);
else
} else {
snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id);
}
free(user_config_dir);
@ -98,19 +100,22 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke
/* Opens log file or creates a new one */
static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
{
if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL))
if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL)) {
return -1;
}
char log_path[MAX_STR_SIZE];
if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1)
if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1) {
return -1;
}
log->file = fopen(log_path, "a+");
snprintf(log->path, sizeof(log->path), "%s", log_path);
if (log->file == NULL)
if (log->file == NULL) {
return -1;
}
return 0;
}
@ -119,8 +124,9 @@ static int init_logging_session(char *name, const char *selfkey, const char *oth
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
{
if (!log->log_on)
if (!log->log_on) {
return;
}
if (log->file == NULL) {
log->log_on = false;
@ -129,10 +135,11 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
char name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
if (event)
if (event) {
snprintf(name_frmt, sizeof(name_frmt), "* %s", name);
else
} else {
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
}
const char *t = user_settings->log_timestamp_format;
char s[MAX_STR_SIZE];
@ -147,8 +154,9 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
void log_disable(struct chatlog *log)
{
if (log->file != NULL)
if (log->file != NULL) {
fclose(log->file);
}
memset(log, 0, sizeof(struct chatlog));
}
@ -157,8 +165,9 @@ int log_enable(char *name, const char *selfkey, const char *otherkey, struct cha
{
log->log_on = true;
if (log->file != NULL)
if (log->file != NULL) {
return 0;
}
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
log_disable(log);
@ -171,18 +180,21 @@ int log_enable(char *name, const char *selfkey, const char *otherkey, struct cha
/* Loads previous history from chat log */
void load_chat_history(ToxWindow *self, struct chatlog *log)
{
if (log->file == NULL)
if (log->file == NULL) {
return;
}
off_t sz = file_size(log->path);
if (sz <= 0)
if (sz <= 0) {
return;
}
char *hstbuf = malloc(sz + 1);
if (hstbuf == NULL)
if (hstbuf == NULL) {
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
}
if (fseek(log->file, 0L, SEEK_SET) == -1) {
free(hstbuf);
@ -204,8 +216,9 @@ void load_chat_history(ToxWindow *self, struct chatlog *log)
/* start at end and backtrace L lines or to the beginning of buffer */
for (start = sz - 1; start >= 0 && count < L; --start) {
if (hstbuf[start] == '\n')
if (hstbuf[start] == '\n') {
++count;
}
}
const char *line = strtok(&hstbuf[start + 1], "\n");
@ -238,32 +251,40 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
log_on = log->log_on;
}
if (log_on)
if (log_on) {
log_disable(log);
}
char newpath[MAX_STR_SIZE];
char oldpath[MAX_STR_SIZE];
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1)
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1) {
goto on_error;
}
if (!file_exists(oldpath))
if (!file_exists(oldpath)) {
return 0;
}
if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1)
if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1) {
goto on_error;
}
if (rename(oldpath, newpath) != 0)
if (rename(oldpath, newpath) != 0) {
goto on_error;
}
if (log_on)
if (log_on) {
log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
}
return 0;
on_error:
if (log_on)
if (log_on) {
log_enable(src, selfkey, otherkey, log, LOG_CHAT);
}
return -1;
}

View File

@ -25,12 +25,12 @@
struct chatlog {
FILE *file;
uint64_t lastwrite;
time_t lastwrite;
char path[MAX_STR_SIZE];
bool log_on; /* specific to current chat window */
};
enum {
typedef enum {
LOG_GROUP,
LOG_PROMPT,
LOG_CHAT,
@ -56,4 +56,4 @@ void load_chat_history(ToxWindow *self, struct chatlog *log);
Returns 0 on success or if no log exists, -1 on failure. */
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
#endif /* #define LOG_H */
#endif /* LOG_H */

View File

@ -42,12 +42,17 @@ void cqueue_cleanup(struct chat_queue *q)
free(q);
}
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id)
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id)
{
if (line_id < 0) {
return;
}
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
if (new_m == NULL)
if (new_m == NULL) {
exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY);
}
snprintf(new_m->message, sizeof(new_m->message), "%s", msg);
new_m->len = len;
@ -114,8 +119,9 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
struct cqueue_msg *next = msg->next;
if (msg->prev == NULL) { /* root */
if (next)
if (next) {
next->prev = NULL;
}
free(msg);
q->root = next;
@ -129,7 +135,7 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
}
}
#define CQUEUE_TRY_SEND_INTERVAL 60
#define CQUEUE_TRY_SEND_INTERVAL 10
/* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m)
@ -137,18 +143,15 @@ void cqueue_try_send(ToxWindow *self, Tox *m)
struct chat_queue *q = self->chatwin->cqueue;
struct cqueue_msg *msg = q->root;
if (!msg)
if (!msg) {
return;
}
if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL))
if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL)) {
return;
}
uint32_t receipt = 0;
TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
Tox_Message_Type type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
msg->receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
msg->last_send_try = get_unix_time();
msg->receipt = receipt;
return;
}

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;
};
@ -40,7 +40,7 @@ struct chat_queue {
};
void cqueue_cleanup(struct chat_queue *q);
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id);
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id);
/* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m);
@ -48,4 +48,4 @@ void cqueue_try_send(ToxWindow *self, Tox *m);
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt);
#endif /* #define MESSAGE_QUEUE_H */
#endif /* MESSAGE_QUEUE_H */

View File

@ -26,6 +26,9 @@
#include <time.h>
#include <limits.h>
#include <dirent.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include "toxic.h"
@ -37,7 +40,12 @@
extern ToxWindow *prompt;
extern struct user_settings *user_settings;
static uint64_t current_unix_time;
void clear_screen(void)
{
if (system("clear") != 0) {
fprintf(stderr, "Warning: system() failed to clear screen\n");
}
}
void hst_to_net(uint8_t *num, uint16_t numbytes)
{
@ -54,19 +62,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,8 +77,8 @@ 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;
}
@ -89,25 +91,30 @@ void get_time_str(char *buf, int bufsize)
}
const char *t = user_settings->timestamp_format;
strftime(buf, bufsize, t, get_time());
if (strftime(buf, bufsize, t, get_time()) == 0) {
strftime(buf, bufsize, TIMESTAMP_DEFAULT, get_time());
}
}
/* 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)
if (!secs) {
return;
}
long int seconds = secs % 60;
long int minutes = (secs % 3600) / 60;
long int hours = secs / 3600;
if (!minutes && !hours)
if (!minutes && !hours) {
snprintf(buf, bufsize, "%.2ld", seconds);
else if (!hours)
} else if (!hours) {
snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds);
else
} else {
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
}
}
/*
@ -119,11 +126,12 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
*/
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size)
{
if (output_size == 0 || hex_len != output_size * 2)
if (output_size == 0 || hex_len != output_size * 2) {
return -1;
}
for (size_t i = 0; i < output_size; ++i) {
sscanf(hex_string, "%2hhx", &output[i]);
sscanf(hex_string, "%2hhx", (unsigned char *)&output[i]);
hex_string += 2;
}
@ -132,18 +140,40 @@ int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size
int hex_string_to_bytes(char *buf, int size, const char *keystr)
{
if (size % 2 != 0)
if (size % 2 != 0) {
return -1;
}
int i, res;
const char *pos = keystr;
for (i = 0; i < size; ++i) {
res = sscanf(pos, "%2hhx", &buf[i]);
res = sscanf(pos, "%2hhx", (unsigned char *)&buf[i]);
pos += 2;
if (res == EOF || res < 1)
if (res == EOF || res < 1) {
return -1;
}
}
return 0;
}
/* Converts a binary representation of a Tox ID into a string.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size)
{
if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1)) {
return -1;
}
size_t i;
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
}
return 0;
@ -152,22 +182,35 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr)
/* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(const char *string)
{
if (!string)
if (!string) {
return true;
}
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)
{
size_t len = mbstowcs(NULL, string, 0) + 1;
if (n < len)
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;
}
@ -177,11 +220,13 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
{
size_t len = wcstombs(NULL, string, 0) + 1;
if (n < len)
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;
}
@ -200,20 +245,23 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2)
- must not contain a newline or tab seqeunce */
int valid_nick(const char *nick)
{
if (!nick[0] || nick[0] == ' ')
if (!nick[0] || nick[0] == ' ') {
return 0;
}
int i;
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;
}
}
return 1;
@ -225,8 +273,9 @@ void filter_str(char *str, size_t len)
size_t i;
for (i = 0; i < len; ++i) {
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0')
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0') {
str[i] = ' ';
}
}
}
@ -238,22 +287,26 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
int len = strlen(pathname) - 1;
char *path = strdup(pathname);
if (path == NULL)
if (path == NULL) {
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
}
while (len >= 0 && pathname[len] == '/')
while (len >= 0 && pathname[len] == '/') {
path[len--] = '\0';
}
char *finalname = strdup(path);
if (finalname == NULL)
if (finalname == NULL) {
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
}
const char *basenm = strrchr(path, '/');
if (basenm != NULL) {
if (basenm[1])
if (basenm[1]) {
strcpy(finalname, &basenm[1]);
}
}
snprintf(namebuf, bufsize, "%s", finalname);
@ -263,13 +316,37 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
return strlen(namebuf);
}
/* Gets the base directory of path and puts it in dir.
* dir must have at least as much space as path_len + 1.
*
* Returns the length of the base directory.
*/
size_t get_base_dir(const char *path, size_t path_len, char *dir)
{
if (path_len == 0 || path == NULL) {
return 0;
}
size_t dir_len = char_rfind(path, '/', path_len);
if (dir_len != 0 && dir_len < path_len) {
++dir_len; /* Leave trailing slash */
}
memcpy(dir, path, dir_len);
dir[dir_len] = '\0';
return dir_len;
}
/* converts str to all lowercase */
void str_to_lower(char *str)
{
int i;
for (i = 0; str[i]; ++i)
for (i = 0; str[i]; ++i) {
str[i] = tolower(str[i]);
}
}
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
@ -277,70 +354,106 @@ void str_to_lower(char *str)
Returns nick len */
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
{
size_t len = tox_friend_get_name_size(m, friendnum, NULL);
Tox_Err_Friend_Query err;
size_t len = tox_friend_get_name_size(m, friendnum, &err);
if (len == 0) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
if (err != TOX_ERR_FRIEND_QUERY_OK) {
goto on_error;
} else {
tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL);
if (!tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL)) {
goto on_error;
}
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
filter_str(buf, len);
return len;
on_error:
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
buf[len] = '\0';
return len;
}
/* 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 peernum, uint32_t groupnum)
{
int len = tox_group_peername(m, groupnum, peernum, (uint8_t *) buf);
Tox_Err_Conference_Peer_Query err;
size_t len = tox_conference_peer_get_name_size(m, groupnum, peernum, &err);
if (len == -1) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
goto on_error;
} else {
if (!tox_conference_peer_get_name(m, groupnum, peernum, (uint8_t *) buf, NULL)) {
goto on_error;
}
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
filter_str(buf, len);
return len;
}
/* copies data to msg buffer.
returns length of msg, which will be no larger than size-1 */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length)
{
size_t len = MIN(length, size - 1);
memcpy(msg, data, len);
msg[len] = '\0';
on_error:
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
buf[len] = '\0';
return len;
}
/* copies data to msg buffer, removing return characters.
returns length of msg, which will be no larger than size-1 */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length)
{
size_t i;
size_t j = 0;
for (i = 0; (i < length) && (j < size - 1); ++i) {
if (data[i] != '\r') {
msg[j++] = data[i];
}
}
msg[j] = '\0';
return j;
}
/* 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) {
if (s[i] == ch)
if (s[i] == ch) {
break;
}
}
return i;
}
/* returns index of the last instance of ch in s starting at len
returns 0 if char not found (skips 0th index) */
/* returns index of the last instance of ch in s starting at len.
returns 0 if char not found 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) {
if (s[i] == ch)
if (s[i] == ch) {
break;
}
}
return i;
@ -375,13 +488,41 @@ bool file_exists(const char *path)
return stat(path, &s) == 0;
}
/*
* Checks the file type path points to and returns a File_Type enum value.
*
* Returns FILE_TYPE_DIRECTORY if path points to a directory.
* Returns FILE_TYPE_REGULAR if path points to a regular file.
* Returns FILE_TYPE_OTHER on any other result, including an invalid path.
*/
File_Type file_type(const char *path)
{
struct stat s;
if (stat(path, &s) == -1) {
return FILE_TYPE_OTHER;
}
switch (s.st_mode & S_IFMT) {
case S_IFDIR:
return FILE_TYPE_DIRECTORY;
case S_IFREG:
return FILE_TYPE_REGULAR;
default:
return FILE_TYPE_OTHER;
}
}
/* returns file size. If file doesn't exist returns 0. */
off_t file_size(const char *path)
{
struct stat st;
if (stat(path, &st) == -1)
if (stat(path, &st) == -1) {
return 0;
}
return st.st_size;
}
@ -390,17 +531,19 @@ off_t file_size(const char *path)
Returns 0 if they are the same, 1 if they differ, and -1 on error.
On success this function will seek back to the beginning of fp */
int check_file_signature(const char *signature, size_t size, FILE *fp)
int check_file_signature(const unsigned char *signature, size_t size, FILE *fp)
{
char buf[size];
if (fread(buf, size, 1, fp) != 1)
if (fread(buf, size, 1, fp) != 1) {
return -1;
}
int ret = memcmp(signature, buf, size);
if (fseek(fp, 0L, SEEK_SET) == -1)
if (fseek(fp, 0L, SEEK_SET) == -1) {
return -1;
}
return ret == 0 ? 0 : 1;
}
@ -408,12 +551,17 @@ 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)
{
if (len <= 0 || !title) {
return;
}
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
if (self->is_groupchat) /* keep groupnumber in title */
if (self->is_groupchat) { /* keep groupnumber in title */
snprintf(cpy, sizeof(cpy), "%d %s", self->num, title);
else
} else {
snprintf(cpy, sizeof(cpy), "%s", title);
}
if (len > MAX_WINDOW_NAME_LENGTH) {
strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "...");
@ -422,3 +570,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

@ -39,6 +39,17 @@
#define net_to_host(x, y) hst_to_net(x, y)
#endif
#define UNUSED_VAR(x) ((void) x)
typedef enum File_Type {
FILE_TYPE_REGULAR,
FILE_TYPE_DIRECTORY,
FILE_TYPE_OTHER,
} File_Type;
void clear_screen(void);
void hst_to_net(uint8_t *num, uint16_t numbytes);
/*
@ -53,35 +64,42 @@ int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size
/* convert a hex string to bytes. returns 0 on success, -1 on failure */
int hex_string_to_bytes(char *buf, int size, const char *keystr);
/* Converts a binary representation of a Tox ID into a string.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size);
/* get the current unix time (not thread safe) */
uint64_t get_unix_time(void);
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);
/* 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);
/* convert a multibyte string to a wide character string (must provide buffer) */
/* Same as above but for wide character strings */
int wstring_is_empty(const wchar_t *string);
/* converts 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);
/* converts wide character string into a multibyte string and puts in buf. */
int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
/* convert a multibyte string to a wide character string and puts in buf) */
/* converts 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);
/* 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);
@ -103,6 +121,14 @@ void filter_str(char *str, size_t len);
/* gets base file name from path or original file name if no path is supplied */
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname);
/* Gets the base directory of path and puts it in dir.
* dir must have at least as much space as path_len.
*
* Returns the length of the base directory on success.
* Returns -1 on failure.
*/
size_t get_base_dir(const char *path, size_t path_len, char *dir);
/* converts str to all lowercase */
void str_to_lower(char *str);
@ -111,18 +137,18 @@ 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 peernum, uint32_t groupnum);
/* copies data to msg buffer.
returns length of msg, which will be no larger than size-1 */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length);
/* 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 */
@ -131,6 +157,15 @@ void bytes_convert_str(char *buf, int size, uint64_t bytes);
/* checks if a file exists. Returns true or false */
bool file_exists(const char *path);
/*
* Checks the file type path points to and returns a File_Type enum value.
*
* Returns FILE_TYPE_DIRECTORY if path points to a directory.
* Returns FILE_TYPE_REGULAR if path points to a regular file.
* Returns FILE_TYPE_OTHER on any other result, including an invalid path.
*/
File_Type file_type(const char *path);
/* returns file size. If file doesn't exist returns 0. */
off_t file_size(const char *path);
@ -138,9 +173,21 @@ off_t file_size(const char *path);
Returns 0 if they are the same, 1 if they differ, and -1 on error.
On success this function will seek back to the beginning of fp */
int check_file_signature(const char *signature, size_t size, FILE *fp);
int check_file_signature(const unsigned char *signature, size_t size, FILE *fp);
/* sets window title in tab bar. */
void set_window_title(ToxWindow *self, const char *title, int len);
#endif /* #define MISC_TOOLS_H */
/* 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 /* MISC_TOOLS_H */

418
src/name_lookup.c Normal file
View File

@ -0,0 +1,418 @@
/* name_lookup.c
*
*
* Copyright (C) 2015 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <curl/curl.h>
#include "toxic.h"
#include "windows.h"
#include "line_info.h"
#include "global_commands.h"
#include "misc_tools.h"
#include "configdir.h"
#include "curl_util.h"
extern struct arg_opts arg_opts;
extern struct Winthread Winthread;
#define NAMESERVER_API_PATH "api"
#define SERVER_KEY_SIZE 32
#define MAX_SERVERS 50
#define MAX_DOMAIN_SIZE 32
#define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3
static struct Nameservers {
int lines;
char names[MAX_SERVERS][MAX_DOMAIN_SIZE];
char keys[MAX_SERVERS][SERVER_KEY_SIZE];
} Nameservers;
static struct thread_data {
Tox *m;
ToxWindow *self;
char id_bin[TOX_ADDRESS_SIZE];
char addr[MAX_STR_SIZE];
char msg[MAX_STR_SIZE];
bool disabled;
volatile bool busy;
} t_data;
static struct lookup_thread {
pthread_t tid;
pthread_attr_t attr;
} lookup_thread;
static int lookup_error(ToxWindow *self, const char *errmsg, ...)
{
char frmt_msg[MAX_STR_SIZE];
va_list args;
va_start(args, errmsg);
vsnprintf(frmt_msg, sizeof(frmt_msg), errmsg, args);
va_end(args);
pthread_mutex_lock(&Winthread.lock);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg);
pthread_mutex_unlock(&Winthread.lock);
return -1;
}
static void kill_lookup_thread(void)
{
memset(&t_data, 0, sizeof(struct thread_data));
pthread_attr_destroy(&lookup_thread.attr);
pthread_exit(NULL);
}
/* Attempts to load the nameserver list pointed at by path into the Nameservers structure.
*
* Returns 0 on success.
* -1 is reserved.
* Returns -2 if the supplied path does not exist.
* Returns -3 if the list does not contain any valid entries.
*/
static int load_nameserver_list(const char *path)
{
FILE *fp = fopen(path, "r");
if (fp == NULL) {
return -2;
}
char line[MAX_SERVER_LINE];
while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) {
int linelen = strlen(line);
if (linelen < SERVER_KEY_SIZE * 2 + 5) {
continue;
}
if (line[linelen - 1] == '\n') {
line[--linelen] = '\0';
}
const char *name = strtok(line, " ");
const char *keystr = strtok(NULL, " ");
if (name == NULL || keystr == NULL) {
continue;
}
if (strlen(keystr) != SERVER_KEY_SIZE * 2) {
continue;
}
snprintf(Nameservers.names[Nameservers.lines], sizeof(Nameservers.names[Nameservers.lines]), "%s", name);
int res = hex_string_to_bytes(Nameservers.keys[Nameservers.lines], SERVER_KEY_SIZE, keystr);
if (res == -1) {
continue;
}
++Nameservers.lines;
}
fclose(fp);
if (Nameservers.lines < 1) {
return -3;
}
return 0;
}
/* Takes address addr in the form "username@domain", puts the username in namebuf,
* and the domain in dombuf.
*
* Returns 0 on success.
* Returns -1 on failure
*/
static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz)
{
if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH))) {
return -1;
}
char tmpaddr[MAX_STR_SIZE];
char *tmpname = NULL;
char *tmpdom = NULL;
snprintf(tmpaddr, sizeof(tmpaddr), "%s", addr);
tmpname = strtok(tmpaddr, "@");
tmpdom = strtok(NULL, "");
if (tmpname == NULL || tmpdom == NULL) {
return -1;
}
str_to_lower(tmpdom);
snprintf(namebuf, namebuf_sz, "%s", tmpname);
snprintf(dombuf, dombuf_sz, "%s", tmpdom);
return 0;
}
/* matches input domain name with domains in list and obtains key.
* Turns out_domain into the full domain we need to make a POST request.
*
* Return true on match.
* Returns false on no match.
*/
static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_size, const char *inputdomain)
{
int i;
for (i = 0; i < Nameservers.lines; ++i) {
if (strcmp(Nameservers.names[i], inputdomain) == 0) {
memcpy(pubkey, Nameservers.keys[i], SERVER_KEY_SIZE);
snprintf(out_domain, out_domain_size, "https://%s/%s", Nameservers.names[i], NAMESERVER_API_PATH);
return true;
}
}
return false;
}
/* 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_Curl_Data *recv_data)
{
size_t prefix_size = strlen(ID_PREFIX);
if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size) {
return -1;
}
const char *IDstart = strstr(recv_data->data, ID_PREFIX);
if (IDstart == NULL) {
return -1;
}
if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size) {
return -1;
}
char ID_string[TOX_ADDRESS_SIZE * 2 + 1];
memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2);
ID_string[TOX_ADDRESS_SIZE * 2] = 0;
if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1) {
return -1;
}
return 0;
}
void *lookup_thread_func(void *data)
{
UNUSED_VAR(data);
ToxWindow *self = t_data.self;
char input_domain[MAX_STR_SIZE];
char name[MAX_STR_SIZE];
if (parse_addr(t_data.addr, name, sizeof(name), input_domain, sizeof(input_domain)) == -1) {
lookup_error(self, "Input must be a 76 character Tox ID or an address in the form: username@domain");
kill_lookup_thread();
}
char nameserver_key[SERVER_KEY_SIZE];
char real_domain[MAX_DOMAIN_SIZE];
if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) {
if (!strcasecmp(input_domain, "utox.org")) {
lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic.");
} else {
lookup_error(self, "Name server domain not found.");
}
kill_lookup_thread();
}
CURL *c_handle = curl_easy_init();
if (!c_handle) {
lookup_error(self, "curl handler error");
kill_lookup_thread();
}
struct Recv_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, 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);
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", proxy_ret);
goto on_exit;
}
int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL);
if (ret != CURLE_OK) {
lookup_error(self, "TLS could not be enabled (libcurl error %d)", ret);
goto on_exit;
}
ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
if (ret != CURLE_OK) {
lookup_error(self, "TLSv1.2 could not be set (libcurl error %d)", ret);
goto on_exit;
}
ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST);
if (ret != CURLE_OK) {
lookup_error(self, "Failed to set TLS cipher list (libcurl error %d)", ret);
goto on_exit;
}
ret = curl_easy_perform(c_handle);
if (ret != CURLE_OK) {
/* If system doesn't support any of the specified ciphers suites, fall back to default */
if (ret == CURLE_SSL_CIPHER) {
curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL);
ret = curl_easy_perform(c_handle);
}
if (ret != CURLE_OK) {
lookup_error(self, "HTTPS lookup error (libcurl error %d)", ret);
goto on_exit;
}
}
if (process_response(&recv_data) == -1) {
lookup_error(self, "Bad response.");
goto on_exit;
}
pthread_mutex_lock(&Winthread.lock);
cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
pthread_mutex_unlock(&Winthread.lock);
on_exit:
curl_slist_free_all(headers);
curl_easy_cleanup(c_handle);
kill_lookup_thread();
return 0;
}
void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message)
{
if (t_data.disabled) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled.");
return;
}
if (t_data.busy) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish.");
return;
}
snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin);
snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr);
snprintf(t_data.msg, sizeof(t_data.msg), "%s", message);
t_data.self = self;
t_data.m = m;
t_data.busy = true;
if (pthread_attr_init(&lookup_thread.attr) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init");
memset(&t_data, 0, sizeof(struct thread_data));
return;
}
if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set");
pthread_attr_destroy(&lookup_thread.attr);
memset(&t_data, 0, sizeof(struct thread_data));
return;
}
if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init");
pthread_attr_destroy(&lookup_thread.attr);
memset(&t_data, 0, sizeof(struct thread_data));
return;
}
}
/* Initializes http based name lookups. Note: This function must be called only once before additional
* threads are spawned.
*
* Returns 0 on success.
* Returns -1 if curl failed to init.
* Returns -2 if the nameserver list cannot be found.
* Returns -3 if the nameserver list does not contain any valid entries.
*/
int name_lookup_init(int curl_init_status)
{
if (curl_init_status != 0) {
t_data.disabled = true;
return -1;
}
const char *path = arg_opts.nameserver_path[0] ? arg_opts.nameserver_path : PACKAGE_DATADIR "/nameservers";
int ret = load_nameserver_list(path);
if (ret != 0) {
t_data.disabled = true;
return ret;
}
return 0;
}

View File

@ -1,7 +1,7 @@
/* dns.c
/* name_lookup.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
* Copyright (C) 2015 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
@ -20,13 +20,17 @@
*
*/
/* Does DNS lookup for addr and puts resulting tox id in id_bin.
Return 0 on success, -1 on failure. */
#ifndef NAME_LOOKUP
#define NAME_LOOKUP
#ifndef DNS_H
#define DNS_H
/* Initializes http based name lookups. Note: This function must be called only once before additional
* threads are spawned.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int name_lookup_init(int curl_init_status);
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg);
int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message);
#endif /* #define DNS_H */
#endif /* NAME_LOOKUP */

View File

@ -31,31 +31,31 @@
#include <sys/stat.h>
#include "notify.h"
#include "device.h"
#include "audio_device.h"
#include "settings.h"
#include "line_info.h"
#include "misc_tools.h"
#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
#endif /* 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__ */
#ifdef SOUND_NOTIFY
#include <AL/alut.h> /* freealut packet */
#endif /* SOUND_NOTIFY */
#endif /* defined(AUDIO) || defined(SOUND_NOTIFY) */
#ifdef BOX_NOTIFY
#include <libnotify/notify.h>
#include <libnotify/notify.h>
#endif
#define MAX_BOX_MSG_LEN 127
@ -64,7 +64,7 @@
extern struct user_settings *user_settings;
struct Control {
static struct Control {
time_t cooldown;
time_t notif_timeout;
@ -75,25 +75,25 @@ 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};
struct _ActiveNotifications {
static struct _ActiveNotifications {
#ifdef SOUND_NOTIFY
uint32_t source;
uint32_t buffer;
bool looping;
#endif
#endif /* SOUND_NOTIFY */
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;
time_t n_timeout;
#endif
#endif /* BOX_NOTIFY */
} actives[ACTIVE_NOTIFS_MAX];
/**********************************************************************************/
/**********************************************************************************/
@ -104,21 +104,24 @@ struct _ActiveNotifications {
/* coloured tab notifications: primary notification type */
static void tab_notify(ToxWindow *self, uint64_t flags)
{
if (self == NULL)
if (self == NULL) {
return;
}
if (flags & NT_WNDALERT_0)
if (flags & NT_WNDALERT_0) {
self->alert = WINDOW_ALERT_0;
else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) )
} else if ((flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0)) {
self->alert = WINDOW_ALERT_1;
else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) )
} else if ((flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1)) {
self->alert = WINDOW_ALERT_2;
}
}
static bool notifications_are_disabled(uint64_t flags)
{
if (user_settings->alerts != ALERTS_ENABLED)
if (user_settings->alerts != ALERTS_ENABLED) {
return true;
}
bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time());
#ifdef X11
@ -128,14 +131,14 @@ static bool notifications_are_disabled(uint64_t flags)
#endif
}
static void control_lock()
static void control_lock(void)
{
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_lock(Control.poll_mutex);
#endif
}
static void control_unlock()
static void control_unlock(void)
{
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_unlock(Control.poll_mutex);
@ -157,11 +160,13 @@ static bool device_opened = false;
time_t last_opened_update = 0;
/* Opens primary device. Returns true on succe*/
void m_open_device()
void m_open_device(void)
{
last_opened_update = get_unix_time();
if (device_opened) return;
if (device_opened) {
return;
}
/* Blah error check */
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
@ -169,9 +174,11 @@ void m_open_device()
device_opened = true;
}
void m_close_device()
void m_close_device(void)
{
if (!device_opened) return;
if (!device_opened) {
return;
}
close_device(output, Control.device_idx);
@ -179,7 +186,7 @@ void m_close_device()
}
/* Terminate all sounds but wait for them to finish first */
void graceful_clear()
void graceful_clear(void)
{
control_lock();
@ -188,23 +195,28 @@ 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 /* BOX_NOTIFY */
if (actives[i].id_indicator) {
*actives[i].id_indicator = -1; /* reset indicator value */
}
if ( actives[i].looping ) {
if (actives[i].looping) {
stop_sound(i);
} else {
if (!is_playing(actives[i].source))
if (!is_playing(actives[i].source)) {
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
else break;
} else {
break;
}
}
}
}
@ -221,11 +233,11 @@ void graceful_clear()
control_unlock();
}
void* do_playing(void* _p)
void *do_playing(void *_p)
{
(void)_p;
UNUSED_VAR(_p);
while(true) {
while (true) {
control_lock();
if (!Control.poll_active) {
@ -234,66 +246,78 @@ 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].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 /* BOX_NOTIFY */
}
/* 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 ) {
if (i == ACTIVE_NOTIFS_MAX) {
return -1; /* Full */
}
@ -308,11 +332,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;
UNUSED_VAR(_p);
while(true) {
while (true) {
control_lock();
if (!Control.poll_active) {
@ -323,44 +347,48 @@ 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);
}
void graceful_clear()
void graceful_clear(void)
{
int i;
control_lock();
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;
}
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();
}
#endif
#endif /* SOUND_NOTIFY */
/**********************************************************************************/
/**********************************************************************************/
@ -378,19 +406,21 @@ 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)
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) {
return -1;
}
Control.poll_active = 1;
pthread_t thread;
if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) {
if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0) {
pthread_mutex_destroy(Control.poll_mutex);
Control.poll_active = 0;
return -1;
}
#endif
#endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */
Control.cooldown = time(NULL) + login_cooldown;
@ -401,12 +431,12 @@ int init_notify(int login_cooldown, int notification_timeout)
return 1;
}
void terminate_notify()
void terminate_notify(void)
{
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
control_lock();
if ( !Control.poll_active ) {
if (!Control.poll_active) {
control_unlock();
return;
}
@ -415,11 +445,15 @@ void terminate_notify()
control_unlock();
graceful_clear();
#endif
#endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */
#ifdef SOUND_NOTIFY
int i = 0;
for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]);
for (; i < SOUNDS_SIZE; i ++) {
free(Control.sounds[i]);
}
alutExit();
#endif /* SOUND_NOTIFY */
@ -429,9 +463,11 @@ 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;
if (sound == silent) {
return 0;
}
free(Control.sounds[sound]);
@ -457,10 +493,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;
}
@ -471,10 +508,14 @@ int play_notify_sound(Notification notif, uint64_t flags)
{
int rc = -1;
if (flags & NT_BEEP) beep();
else if (notif != silent) {
if ( !Control.poll_active || !Control.sounds[notif] )
if (flags & NT_BEEP) {
beep();
}
if (notif != silent) {
if (!Control.poll_active || !Control.sounds[notif]) {
return -1;
}
rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0);
}
@ -485,70 +526,74 @@ int play_notify_sound(Notification notif, uint64_t flags)
void stop_sound(int id)
{
if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) {
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)
#endif /* BOX_NOTIFY */
if (actives[id].id_indicator) {
*actives[id].id_indicator = -1;
// alSourcei(actives[id].source, AL_LOOPING, false);
}
// 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));
}
}
#endif
#endif /* SOUND_NOTIFY */
static int m_play_sound(Notification notif, uint64_t flags)
{
#ifdef SOUND_NOTIFY
return play_notify_sound(notif, flags);
#else
if (notif != silent)
if (notif != silent) {
beep();
}
return -1;
#endif /* SOUND_NOTIFY */
}
#ifdef BOX_NOTIFY
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);
if (notifications_are_disabled(flags))
if (notifications_are_disabled(flags)) {
return -1;
}
int id = -1;
control_lock();
if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY))
if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY)) {
id = m_play_sound(notif, flags);
else if (flags & NT_ALWAYS)
} else if (flags & NT_ALWAYS) {
id = m_play_sound(notif, flags);
}
#if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY)
if (id == -1) {
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++);
if ( id == ACTIVE_NOTIFS_MAX ) {
if (id == ACTIVE_NOTIFS_MAX) {
control_unlock();
return -1; /* Full */
}
}
#endif
#endif /* defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY) */
if ( id_indicator && id != -1 ) {
if (id_indicator && id != -1) {
actives[id].id_indicator = id_indicator;
*id_indicator = id;
}
@ -558,14 +603,18 @@ 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);
if (notifications_are_disabled(flags))
if (notifications_are_disabled(flags)) {
return -1;
}
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) {
return -1;
}
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
#ifdef SOUND_NOTIFY
control_lock();
@ -578,8 +627,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,14 +641,17 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
return id;
#else
if (notif != silent)
if (notif != silent) {
beep();
}
return 0;
#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,32 +665,46 @@ 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 ) {
if (id == ACTIVE_NOTIFS_MAX) {
control_unlock();
return -1; /* Full */
}
actives[id].active = 1;
actives[id].id_indicator = id_indicator;
if (id_indicator) *id_indicator = id;
if (id_indicator) {
*id_indicator = id;
}
}
#else
if (id == -1)
if (id == -1) {
return -1;
#endif /* SOUND_NOTIFY */
}
#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);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(title) > 23) {
strcpy(actives[id].title + 20, "...");
}
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
va_list __ARGS__;
va_start(__ARGS__, format);
vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end(__ARGS__);
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) {
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
}
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size++;
@ -654,10 +719,10 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
return id;
#else
return sound_notify(self, notif, flags, id_indicator);
#endif /* BOX_NOTIFY */
#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);
@ -666,8 +731,9 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
#ifdef BOX_NOTIFY
if (sound_notify2(self, notif, flags, id) == -1)
if (sound_notify2(self, notif, flags, id) == -1) {
return -1;
}
control_lock();
@ -676,12 +742,14 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
va_list __ARGS__;
va_start(__ARGS__, format);
vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end(__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) {
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
}
actives[id].size++;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
@ -689,7 +757,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");
}
@ -704,23 +773,26 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
return id;
#else
return sound_notify2(self, notif, flags, id);
#endif
#endif /* BOX_NOTIFY */
}
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);
if (notifications_are_disabled(flags))
if (notifications_are_disabled(flags)) {
return -1;
}
#ifdef BOX_NOTIFY
control_lock();
int id;
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
if (id == ACTIVE_NOTIFS_MAX) {
control_unlock();
return -1; /* Full */
}
@ -731,14 +803,19 @@ 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);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(title) > 23) {
strcpy(actives[id].title + 20, "...");
}
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
va_list __ARGS__;
va_start(__ARGS__, format);
vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end(__ARGS__);
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) {
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
}
actives[id].active = 1;
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
@ -754,31 +831,34 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
return id;
#else
return -1;
#endif
#endif /* BOX_NOTIFY */
}
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);
if (notifications_are_disabled(flags))
if (notifications_are_disabled(flags)) {
return -1;
}
#ifdef BOX_NOTIFY
control_lock();
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1 ) {
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1) {
control_unlock();
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
va_list __ARGS__;
va_start(__ARGS__, format);
vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end(__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) {
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
}
actives[id].size ++;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
@ -786,7 +866,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");
}
@ -801,5 +882,5 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form
return id;
#else
return -1;
#endif
#endif /* BOX_NOTIFY */
}

View File

@ -23,11 +23,10 @@
#ifndef NOTIFY_H
#define NOTIFY_H
#include <inttypes.h>
#include <stdint.h>
#include "windows.h"
typedef enum _Notification
{
typedef enum _Notification {
silent = -1,
notif_error,
self_log_in,
@ -61,20 +60,21 @@ typedef enum _Flags {
} Flags;
int init_notify(int login_cooldown, int notification_timeout);
void terminate_notify();
void terminate_notify(void);
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 */

57
src/osx_video.h Normal file
View File

@ -0,0 +1,57 @@
/* osx_video.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef OSX_VIDEO_H
#define OSX_VIDEO_H
#include <netinet/in.h>
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#endif /* __OBJC__ */
#define RELEASE_CHK(func, obj) if ((obj))\
func((obj));
void bgrtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height);
#ifdef __OBJC__
@interface OSXVideo :
NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
- (instancetype)initWithDeviceNames:
(char **)device_names AmtDevices:
(int *)size;
@end
#endif /* __OBJC__ */
int osx_video_init(char **device_names, int *size);
void osx_video_release(void);
/* Start device */
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height);
/* Stop device */
void osx_video_close_device(uint32_t device_idx);
/* Read data from device */
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height);
#endif /* OSX_VIDEO_H */

346
src/osx_video.m Normal file
View File

@ -0,0 +1,346 @@
/* osx_video.m
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef __OBJC__
#include "osx_video.h"
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#include "line_info.h"
#include "settings.h"
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
/*
* Helper video format functions
*/
static uint8_t rgb_to_y(int r, int g, int b)
{
int y = ((9798 * r + 19235 * g + 3736 * b) >> 15);
return y > 255 ? 255 : y < 0 ? 0 : y;
}
static uint8_t rgb_to_u(int r, int g, int b)
{
int u = ((-5538 * r + -10846 * g + 16351 * b) >> 15) + 128;
return u > 255 ? 255 : u < 0 ? 0 : u;
}
static uint8_t rgb_to_v(int r, int g, int b)
{
int v = ((16351 * r + -13697 * g + -2664 * b) >> 15) + 128;
return v > 255 ? 255 : v < 0 ? 0 : v;
}
void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height)
{
uint16_t x, y;
uint8_t *p;
uint8_t r, g, b;
for (y = 0; y != height; y += 2) {
p = rgb;
for (x = 0; x != width; x++) {
b = *rgb++;
g = *rgb++;
r = *rgb++;
rgb++;
*plane_y++ = rgb_to_y(r, g, b);
}
for (x = 0; x != width / 2; x++) {
b = *rgb++;
g = *rgb++;
r = *rgb++;
rgb++;
*plane_y++ = rgb_to_y(r, g, b);
b = *rgb++;
g = *rgb++;
r = *rgb++;
rgb++;
*plane_y++ = rgb_to_y(r, g, b);
b = ((int)b + (int) * (rgb - 8) + (int) * p + (int) * (p + 4) + 2) / 4;
p++;
g = ((int)g + (int) * (rgb - 7) + (int) * p + (int) * (p + 4) + 2) / 4;
p++;
r = ((int)r + (int) * (rgb - 6) + (int) * p + (int) * (p + 4) + 2) / 4;
p++;
p++;
*plane_u++ = rgb_to_u(r, g, b);
*plane_v++ = rgb_to_v(r, g, b);
p += 4;
}
}
}
/*
* End of helper video format functions
*/
/*
* Implementation for OSXVideo
*/
@implementation OSXVideo {
dispatch_queue_t _processingQueue;
AVCaptureSession *_session;
AVCaptureVideoDataOutput *_linkerVideo;
CVImageBufferRef _currentFrame;
pthread_mutex_t _frameLock;
BOOL _shouldMangleDimensions;
}
- (instancetype)initWithDeviceNames:
(char **)device_names AmtDevices:
(int *)size
{
_session = [[AVCaptureSession alloc] init];
NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
int i;
for (i = 0; i < [devices count]; ++i) {
AVCaptureDevice *device = [devices objectAtIndex: i];
char *video_input_name;
NSString *localizedName = [device localizedName];
video_input_name = (char *)malloc(strlen([localizedName cStringUsingEncoding: NSUTF8StringEncoding]) + 1);
strcpy(video_input_name, (char *)[localizedName cStringUsingEncoding: NSUTF8StringEncoding]);
device_names[i] = video_input_name;
}
if (i <= 0) {
return nil;
}
*size = i;
return self;
}
- (void)dealloc
{
pthread_mutex_destroy(&_frameLock);
[_session release];
[_linkerVideo release];
dispatch_release(_processingQueue);
[super dealloc];
}
- (int)openVideoDeviceIndex:
(uint32_t)device_idx Width:
(uint16_t *)width Height:
(uint16_t *)height
{
pthread_mutex_init(&_frameLock, NULL);
pthread_mutex_lock(&_frameLock);
_processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL);
NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
AVCaptureDevice *device = [devices objectAtIndex: device_idx];
NSError *error = NULL;
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
if (error != NULL) {
[input release];
return -1;
}
[_session beginConfiguration];
[_session addInput: input];
//_session.sessionPreset = AVCaptureSessionPreset640x480;
//*width = 640;
//*height = 480;
_shouldMangleDimensions = YES;
[_session commitConfiguration];
[input release];
[device release];
/* Obtain device resolution */
AVCaptureInputPort *port = [input.ports objectAtIndex: 0];
CMFormatDescriptionRef format_description = port.formatDescription;
if (format_description) {
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description);
*width = dimensions.width;
*height = dimensions.height;
} else {
*width = 0;
*height = 0;
}
_linkerVideo = [[AVCaptureVideoDataOutput alloc] init];
[_linkerVideo setSampleBufferDelegate: self queue: _processingQueue];
// TODO possibly get a better pixel format
if (_shouldMangleDimensions) {
[_linkerVideo setVideoSettings: @ {
(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferWidthKey: @640,
(id)kCVPixelBufferHeightKey: @480
}];
} else {
[_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
}
[_session addOutput: _linkerVideo];
[_session startRunning];
pthread_mutex_unlock(&_frameLock);
return 0;
}
- (void)closeVideoDeviceIndex:
(uint32_t)device_idx
{
NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
AVCaptureDevice *device = [devices objectAtIndex: device_idx];
NSError *error = NULL;
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
[_session stopRunning];
[_session removeOutput: _linkerVideo];
[_session removeInput: input];
[_linkerVideo release];
}
- (void)captureOutput:
(AVCaptureOutput *)captureOutput didOutputSampleBuffer:
(CMSampleBufferRef)sampleBuffer fromConnection:
(AVCaptureConnection *)connection
{
pthread_mutex_lock(&_frameLock);
CVImageBufferRef img = CMSampleBufferGetImageBuffer(sampleBuffer);
if (!img) {
NSLog(@"Toxic WARNING: Bad sampleBuffer from AVfoundation!");
} else {
CVPixelBufferUnlockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
RELEASE_CHK(CFRelease, _currentFrame);
_currentFrame = (CVImageBufferRef)CFRetain(img);
// we're not going to do anything to it, so it's safe to lock it always
CVPixelBufferLockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
}
pthread_mutex_unlock(&_frameLock);
}
- (int)getVideoFrameY:
(uint8_t *)y U:
(uint8_t *)u V:
(uint8_t *)v Width:
(uint16_t *)width Height:
(uint16_t *)height
{
if (!_currentFrame) {
return -1;
}
pthread_mutex_lock(&_frameLock);
CFRetain(_currentFrame);
CFTypeID imageType = CFGetTypeID(_currentFrame);
if (imageType == CVPixelBufferGetTypeID()) {
// TODO maybe handle other formats
bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height);
} else if (imageType == CVOpenGLBufferGetTypeID()) {
// OpenGL pbuffer
} else if (imageType == CVOpenGLTextureGetTypeID()) {
// OpenGL Texture (Do we need to handle these?)
}
CVPixelBufferRelease(_currentFrame);
pthread_mutex_unlock(&_frameLock);
return 0;
}
@end
/*
* End of implementation for OSXVideo
*/
/*
* C-interface for OSXVideo
*/
static OSXVideo *_OSXVideo = nil;
int osx_video_init(char **device_names, int *size)
{
_OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size];
if (_OSXVideo == nil) {
return -1;
}
return 0;
}
void osx_video_release()
{
[_OSXVideo release];
_OSXVideo = nil;
}
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height)
{
if (_OSXVideo == nil) {
return -1;
}
return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height];
}
void osx_video_close_device(uint32_t device_idx)
{
[_OSXVideo closeVideoDeviceIndex: device_idx];
}
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height)
{
if (_OSXVideo == nil) {
return -1;
}
return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height];
}
/*
* End of C-interface for OSXVideo
*/
#endif /* __OBJC__ */

View File

@ -49,12 +49,27 @@ extern struct Winthread Winthread;
extern FriendsList Friends;
FriendRequests FrndRequests;
#ifdef AUDIO
#define AC_NUM_GLOB_COMMANDS 18
#define AC_NUM_GLOB_COMMANDS_AUDIO 2
#else
#define AC_NUM_GLOB_COMMANDS 16
#define AC_NUM_GLOB_COMMANDS_AUDIO 0
#endif /* AUDIO */
#ifdef VIDEO
#define AC_NUM_GLOB_COMMANDS_VIDEO 2
#else
#define AC_NUM_GLOB_COMMANDS_VIDEO 0
#endif /* VIDEO */
#ifdef PYTHON
#define AC_NUM_GLOB_COMMANDS_PYTHON 1
#else
#define AC_NUM_GLOB_COMMANDS_PYTHON 0
#endif /* PYTHON */
#ifdef QRCODE
#define AC_NUM_GLOB_COMMANDS_QRCODE 1
#else
#define AC_NUM_GLOB_COMMANDS_QRCODE 0
#endif /* QRCODE */
#define AC_NUM_GLOB_COMMANDS (17 + AC_NUM_GLOB_COMMANDS_AUDIO + AC_NUM_GLOB_COMMANDS_VIDEO + AC_NUM_GLOB_COMMANDS_PYTHON + AC_NUM_GLOB_COMMANDS_QRCODE)
/* Array of global command names used for tab completion. */
static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
@ -69,8 +84,12 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/help" },
{ "/log" },
{ "/myid" },
#ifdef QRCODE
{ "/myqr" },
#endif /* QRCODE */
{ "/nick" },
{ "/note" },
{ "/nospam" },
{ "/quit" },
{ "/requests" },
{ "/status" },
@ -81,6 +100,20 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/sdev" },
#endif /* AUDIO */
#ifdef VIDEO
{ "/lsvdev" },
{ "/svdev" },
#endif /* VIDEO */
#ifdef PYTHON
{ "/run" },
#endif /* PYTHON */
};
void kill_prompt_window(ToxWindow *self)
@ -104,8 +137,10 @@ void kill_prompt_window(ToxWindow *self)
}
/* callback: Updates own connection status in prompt statusbar */
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata)
void on_self_connection_status(Tox *m, Tox_Connection connection_status, void *userdata)
{
UNUSED_VAR(m);
UNUSED_VAR(userdata);
StatusBar *statusbar = prompt->stb;
statusbar->connection = connection_status;
}
@ -126,26 +161,35 @@ void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusms
size_t len = strlen(statusbar->statusmsg);
statusbar->statusmsg_len = len;
TOX_ERR_SET_INFO err;
tox_self_set_status_message(m, (uint8_t *) statusmsg, len, &err);
Tox_Err_Set_Info err;
tox_self_set_status_message(m, (const uint8_t *) statusmsg, len, &err);
if (err != TOX_ERR_SET_INFO_OK)
if (err != TOX_ERR_SET_INFO_OK) {
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err);
}
}
/* Updates own status in prompt statusbar */
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status)
void prompt_update_status(ToxWindow *prompt, Tox_User_Status status)
{
StatusBar *statusbar = prompt->stb;
statusbar->status = status;
}
/* Returns our own connection status */
Tox_Connection prompt_selfConnectionStatus(void)
{
StatusBar *statusbar = prompt->stb;
return statusbar->connection;
}
/* Adds friend request to pending friend requests.
Returns request number on success, -1 if queue is full. */
static int add_friend_request(const char *public_key, const char *data)
{
if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS)
if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS) {
return -1;
}
int i;
@ -155,8 +199,9 @@ static int add_friend_request(const char *public_key, const char *data)
memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE);
snprintf(FrndRequests.request[i].msg, sizeof(FrndRequests.request[i].msg), "%s", data);
if (i == FrndRequests.max_idx)
if (i == FrndRequests.max_idx) {
++FrndRequests.max_idx;
}
++FrndRequests.num_requests;
@ -167,7 +212,10 @@ static int add_friend_request(const char *public_key, const char *data)
return -1;
}
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
/*
* Return true if input is recognized by handler
*/
static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{
ChatContext *ctx = self->chatwin;
@ -175,40 +223,64 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
getyx(self->window, y, x);
getmaxyx(self->window, y2, x2);
if (x2 <= 0)
return;
UNUSED_VAR(y);
if (x2 <= 0 || y2 <= 0) {
return false;
}
if (ctx->pastemode && key == '\r') {
key = '\n';
}
/* ignore non-menu related input if active */
if (self->help->active) {
help_onKey(self, key);
return;
return true;
}
if (ltr) { /* char is printable */
input_new_char(self, key, x, y, x2, y2);
return;
if (ltr || key == '\n') { /* char is printable */
input_new_char(self, key, x, x2);
return true;
}
if (line_info_onKey(self, key))
return;
if (line_info_onKey(self, key)) {
return true;
}
input_handle(self, key, x, y, x2, y2);
if (input_handle(self, key, x, x2)) {
return true;
}
int input_ret = false;
if (key == '\t') { /* TAB key: auto-completes command */
input_ret = true;
if (ctx->len > 1 && ctx->line[0] == '/') {
int diff = -1;
if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0)
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){
}
#ifdef PYTHON
else if (wcsncmp(ctx->line, L"/run ", wcslen(L"/run ")) == 0) {
diff = dir_match(self, m, ctx->line, L"/run");
}
#endif
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
} else {
diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
}
if (diff != -1) {
if (x + diff > x2 - 1) {
@ -221,24 +293,31 @@ 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') {
input_ret = true;
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);
reset_buf(ctx);
}
return input_ret;
}
static void prompt_onDraw(ToxWindow *self, Tox *m)
@ -246,6 +325,10 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
int x2, y2;
getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0) {
return;
}
ChatContext *ctx = self->chatwin;
pthread_mutex_lock(&Winthread.lock);
@ -256,8 +339,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
curs_set(1);
if (ctx->len > 0)
if (ctx->len > 0) {
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
}
StatusBar *statusbar = self->stb;
@ -265,7 +349,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
wmove(statusbar->topline, 0, 0);
pthread_mutex_lock(&Winthread.lock);
TOX_CONNECTION connection = statusbar->connection;
Tox_Connection connection = statusbar->connection;
pthread_mutex_unlock(&Winthread.lock);
if (connection != TOX_CONNECTION_NONE) {
@ -273,7 +357,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
const char *status_text = "ERROR";
pthread_mutex_lock(&Winthread.lock);
TOX_USER_STATUS status = statusbar->status;
Tox_User_Status status = statusbar->status;
pthread_mutex_unlock(&Winthread.lock);
switch (status) {
@ -281,10 +365,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;
@ -315,7 +401,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);
@ -335,8 +421,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
statusbar->statusmsg_len = maxlen;
}
if (statusbar->statusmsg[0])
if (statusbar->statusmsg[0]) {
wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
}
pthread_mutex_unlock(&Winthread.lock);
@ -344,59 +431,70 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
int y, x;
getyx(self->window, y, x);
(void) x;
UNUSED_VAR(x);
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)
if (self->help->active) {
help_onDraw(self);
}
}
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status)
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum, Tox_Connection connection_status)
{
ChatContext *ctx = self->chatwin;
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
get_nick_truncate(m, nick, friendnum);
if (!nick[0])
if (!nick[0]) {
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
}
char timefrmt[TIME_STR_SIZE];
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);
write_to_log(msg, nick, ctx->log, true);
if (self->active_box != -1)
if (self->active_box != -1) {
box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
"%s has come online", nick );
else
"%s has come online", nick);
} else {
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has come online", nick );
}
else if (connection_status == TOX_CONNECTION_NONE) {
"Toxic", "%s has come online", nick);
}
} 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);
if (self->active_box != -1)
if (self->active_box != -1) {
box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
"%s has gone offline", nick );
else
"%s has gone offline", nick);
} else {
box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has gone offline", nick );
"Toxic", "%s has gone offline", nick);
}
}
}
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, size_t length)
{
UNUSED_VAR(m);
UNUSED_VAR(length);
ChatContext *ctx = self->chatwin;
char timefrmt[TIME_STR_SIZE];
@ -417,11 +515,16 @@ static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, con
sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL);
}
void prompt_init_statusbar(ToxWindow *self, Tox *m)
void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run)
{
int x2, y2;
getmaxyx(self->window, y2, x2);
(void) y2;
if (y2 <= 0 || x2 <= 0) {
exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES);
}
UNUSED_VAR(y2);
/* Init statusbar info */
StatusBar *statusbar = self->stb;
@ -437,12 +540,12 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
size_t s_len = tox_self_get_status_message_size(m);
tox_self_get_status_message(m, (uint8_t *) statusmsg);
TOX_USER_STATUS status = tox_self_get_status(m);
Tox_User_Status status = tox_self_get_status(m);
nick[n_len] = '\0';
statusmsg[s_len] = '\0';
if (s_len == 0 || !strncmp(statusmsg, "Toxing on Toxic", strlen("Toxing on Toxic"))) {
if (first_time_run) {
snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic");
s_len = strlen(statusmsg);
statusmsg[s_len] = '\0';
@ -465,7 +568,7 @@ static void print_welcome_msg(ToxWindow *self)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_| \\___/_/\\_\\___\\____| v." TOXICVER);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging client.";
const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messaging client.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
msg = "Type \"/help\" for assistance. Further help may be found via the man page.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
@ -478,6 +581,10 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
int y2, x2;
getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0) {
exit_toxic_err("failed in prompt_onInit", FATALERR_CURSES);
}
ChatContext *ctx = self->chatwin;
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
@ -485,8 +592,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
ctx->log = calloc(1, sizeof(struct chatlog));
ctx->hst = calloc(1, sizeof(struct history));
if (ctx->log == NULL || ctx->hst == NULL)
if (ctx->log == NULL || ctx->hst == NULL) {
exit_toxic_err("failed in prompt_onInit", FATALERR_MEMORY);
}
line_info_init(ctx->hst);
@ -494,45 +602,51 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
char myid[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) myid);
if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1)
if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
}
}
scrollok(ctx->history, 0);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
if (user_settings->show_welcome_msg == SHOW_WELCOME_MSG_ON)
if (user_settings->show_welcome_msg == SHOW_WELCOME_MSG_ON) {
print_welcome_msg(self);
}
}
ToxWindow new_prompt(void)
ToxWindow *new_prompt(void)
{
ToxWindow ret;
memset(&ret, 0, sizeof(ret));
ToxWindow *ret = calloc(1, sizeof(ToxWindow));
ret.active = true;
ret.is_prompt = true;
if (ret == NULL) {
exit_toxic_err("failed in new_prompt", FATALERR_MEMORY);
}
ret.onKey = &prompt_onKey;
ret.onDraw = &prompt_onDraw;
ret.onInit = &prompt_onInit;
ret.onConnectionChange = &prompt_onConnectionChange;
ret.onFriendRequest = &prompt_onFriendRequest;
ret->num = -1;
ret->is_prompt = true;
strcpy(ret.name, "home");
ret->onKey = &prompt_onKey;
ret->onDraw = &prompt_onDraw;
ret->onInit = &prompt_onInit;
ret->onConnectionChange = &prompt_onConnectionChange;
ret->onFriendRequest = &prompt_onFriendRequest;
strcpy(ret->name, "home");
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
StatusBar *stb = calloc(1, sizeof(StatusBar));
Help *help = calloc(1, sizeof(Help));
if (stb == NULL || chatwin == NULL || help == NULL)
if (stb == NULL || chatwin == NULL || help == NULL) {
exit_toxic_err("failed in new_prompt", FATALERR_MEMORY);
}
ret.chatwin = chatwin;
ret.stb = stb;
ret.help = help;
ret->chatwin = chatwin;
ret->stb = stb;
ret->help = help;
ret.active_box = -1;
ret->active_box = -1;
return ret;
}

View File

@ -40,17 +40,22 @@ typedef struct {
struct friend_request request[MAX_FRIEND_REQUESTS];
} FriendRequests;
ToxWindow new_prompt(void);
extern FriendRequests FrndRequests;
ToxWindow *new_prompt(void);
void prep_prompt_win(void);
void prompt_init_statusbar(ToxWindow *self, Tox *m);
void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run);
void prompt_update_nick(ToxWindow *prompt, const char *nick);
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg);
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status);
void prompt_update_status(ToxWindow *prompt, Tox_User_Status status);
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
void kill_prompt_window(ToxWindow *self);
/* callback: Updates own connection status in prompt statusbar */
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata);
void on_self_connection_status(Tox *m, Tox_Connection connection_status, void *userdata);
/* Returns our own connection status */
Tox_Connection prompt_selfConnectionStatus(void);
#endif /* end of include guard: PROMPT_H */

367
src/python_api.c Normal file
View File

@ -0,0 +1,367 @@
/* python_api.c
*
*
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef PYTHON
#include <Python.h>
#include "api.h"
#include "execute.h"
extern Tox *user_tox;
static struct python_registered_func {
char *name;
char *help;
PyObject *callback;
struct python_registered_func *next;
} python_commands = {0};
static PyObject *python_api_display(PyObject *self, PyObject *args)
{
const char *msg;
if (!PyArg_ParseTuple(args, "s", &msg)) {
return NULL;
}
api_display(msg);
return Py_None;
}
static PyObject *python_api_get_nick(PyObject *self, PyObject *args)
{
char *name;
PyObject *ret;
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
name = api_get_nick();
if (name == NULL) {
return NULL;
}
ret = Py_BuildValue("s", name);
free(name);
return ret;
}
static PyObject *python_api_get_status(PyObject *self, PyObject *args)
{
PyObject *ret = NULL;
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
switch (api_get_status()) {
case TOX_USER_STATUS_NONE:
ret = Py_BuildValue("s", "online");
break;
case TOX_USER_STATUS_AWAY:
ret = Py_BuildValue("s", "away");
break;
case TOX_USER_STATUS_BUSY:
ret = Py_BuildValue("s", "busy");
break;
}
return ret;
}
static PyObject *python_api_get_status_message(PyObject *self, PyObject *args)
{
char *status;
PyObject *ret;
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
status = api_get_status_message();
if (status == NULL) {
return NULL;
}
ret = Py_BuildValue("s", status);
free(status);
return ret;
}
static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args)
{
size_t i, ii;
FriendsList friends;
PyObject *cur, *ret;
char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1];
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
friends = api_get_friendslist();
ret = PyList_New(0);
for (i = 0; i < friends.num_friends; i++) {
for (ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) {
snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff);
}
pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0';
cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf);
PyList_Append(ret, cur);
}
return ret;
}
static PyObject *python_api_send(PyObject *self, PyObject *args)
{
const char *msg;
if (!PyArg_ParseTuple(args, "s", &msg)) {
return NULL;
}
api_send(msg);
return Py_None;
}
static PyObject *python_api_execute(PyObject *self, PyObject *args)
{
int mode;
const char *command;
if (!PyArg_ParseTuple(args, "si", &command, &mode)) {
return NULL;
}
api_execute(command, mode);
return Py_None;
}
static PyObject *python_api_register(PyObject *self, PyObject *args)
{
struct python_registered_func *cur;
size_t command_len, help_len;
const char *command, *help;
PyObject *callback;
if (!PyArg_ParseTuple(args, "ssO:register_command", &command, &help, &callback)) {
return NULL;
}
if (!PyCallable_Check(callback)) {
PyErr_SetString(PyExc_TypeError, "Calback parameter must be callable");
return NULL;
}
if (command[0] != '/') {
PyErr_SetString(PyExc_TypeError, "Command must be prefixed with a '/'");
return NULL;
}
for (cur = &python_commands; ; cur = cur->next) {
if (cur->name != NULL && !strcmp(command, cur->name)) {
Py_XDECREF(cur->callback);
Py_XINCREF(callback);
cur->callback = callback;
break;
}
if (cur->next == NULL) {
Py_XINCREF(callback);
cur->next = malloc(sizeof(struct python_registered_func));
if (cur->next == NULL) {
return PyErr_NoMemory();
}
command_len = strlen(command);
cur->next->name = malloc(command_len + 1);
if (cur->next->name == NULL) {
return PyErr_NoMemory();
}
strncpy(cur->next->name, command, command_len + 1);
help_len = strlen(help);
cur->next->help = malloc(help_len + 1);
if (cur->next->help == NULL) {
return PyErr_NoMemory();
}
strncpy(cur->next->help, help, help_len + 1);
cur->next->callback = callback;
cur->next->next = NULL;
break;
}
}
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef ToxicApiMethods[] = {
{"display", python_api_display, METH_VARARGS, "Display a message to the current prompt"},
{"get_nick", python_api_get_nick, METH_VARARGS, "Return the user's current nickname"},
{"get_status", python_api_get_status, METH_VARARGS, "Returns the user's current status"},
{"get_status_message", python_api_get_status_message, METH_VARARGS, "Return the user's current status message"},
{"get_all_friends", python_api_get_all_friends, METH_VARARGS, "Return all of the user's friends"},
{"send", python_api_send, METH_VARARGS, "Send the message to the current user"},
{"execute", python_api_execute, METH_VARARGS, "Execute a command like `/nick`"},
{"register", python_api_register, METH_VARARGS, "Register a command like `/nick` to a Python function"},
{NULL, NULL, 0, NULL},
};
static struct PyModuleDef toxic_api_module = {
PyModuleDef_HEAD_INIT,
"toxic_api",
NULL,
-1,
ToxicApiMethods
};
PyMODINIT_FUNC PyInit_toxic_api(void)
{
PyObject *m = PyModule_Create(&toxic_api_module);
PyObject *global_command_const = Py_BuildValue("i", GLOBAL_COMMAND_MODE);
PyObject *chat_command_const = Py_BuildValue("i", CHAT_COMMAND_MODE);
PyObject *groupchat_command_const = Py_BuildValue("i", GROUPCHAT_COMMAND_MODE);
PyObject_SetAttrString(m, "GLOBAL_COMMAND", global_command_const);
PyObject_SetAttrString(m, "CHAT_COMMAND", chat_command_const);
PyObject_SetAttrString(m, "GROUPCHAT_COMMAND", groupchat_command_const);
Py_DECREF(global_command_const);
Py_DECREF(chat_command_const);
Py_DECREF(groupchat_command_const);
return m;
}
void terminate_python(void)
{
struct python_registered_func *cur, *old;
if (python_commands.name != NULL) {
free(python_commands.name);
}
for (cur = python_commands.next; cur != NULL;) {
old = cur;
cur = cur->next;
free(old->name);
free(old);
}
Py_Finalize();
}
void init_python(Tox *m)
{
user_tox = m;
PyImport_AppendInittab("toxic_api", PyInit_toxic_api);
Py_Initialize();
}
void run_python(FILE *fp, char *path)
{
PyRun_SimpleFile(fp, path);
}
int do_python_command(int num_args, char (*args)[MAX_STR_SIZE])
{
int i;
PyObject *callback_args, *args_strings;
struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name == NULL) {
continue;
}
if (!strcmp(args[0], cur->name)) {
args_strings = PyList_New(0);
for (i = 1; i < num_args; i++) {
PyList_Append(args_strings, Py_BuildValue("s", args[i]));
}
callback_args = PyTuple_Pack(1, args_strings);
if (PyObject_CallObject(cur->callback, callback_args) == NULL) {
api_display("Exception raised in callback function");
}
return 0;
}
}
return 1;
}
int python_num_registered_handlers(void)
{
int n = 0;
struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name != NULL) {
n++;
}
}
return n;
}
int python_help_max_width(void)
{
size_t tmp;
int max = 0;
struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name != NULL) {
tmp = strlen(cur->help);
max = tmp > max ? tmp : max;
}
}
max = max > 50 ? 50 : max;
return 37 + max;
}
void python_draw_handler_help(WINDOW *win)
{
struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name != NULL) {
wprintw(win, " %-29s: %.50s\n", cur->name, cur->help);
}
}
}
#endif /* PYTHON */

39
src/python_api.h Normal file
View File

@ -0,0 +1,39 @@
/* python_api.h
*
*
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
*
* 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 PYTHON_API_H
#define PYTHON_API_H
#ifdef PYTHON
#include <Python.h>
#endif /* PYTHON */
PyMODINIT_FUNC PyInit_toxic_api(void);
void terminate_python(void);
void init_python(Tox *m);
void run_python(FILE *fp, char *path);
int do_python_command(int num_args, char (*args)[MAX_STR_SIZE]);
int python_num_registered_handlers(void);
int python_help_max_width(void);
void python_draw_handler_help(WINDOW *win);
#endif /* PYTHON_API_H */

219
src/qr_code.c Normal file
View File

@ -0,0 +1,219 @@
/* qr_obj_code.c
*
*
* Copyright (C) 2015 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef QRCODE
#include <stdlib.h>
#include <qrencode.h>
#include <stdbool.h>
#include <string.h>
#include "toxic.h"
#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 into the given filename.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
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) {
fclose(fp);
return -1;
}
size_t width = qr_obj->width;
size_t i, j;
for (i = 0; i < width + BORDER_LEN * 2; ++i) {
fprintf(fp, "%s", CHAR_1);
}
fprintf(fp, "\n");
for (i = 0; i < width; i += 2) {
for (j = 0; j < BORDER_LEN; ++j) {
fprintf(fp, "%s", CHAR_1);
}
const unsigned char *row_1 = qr_obj->data + width * i;
const unsigned char *row_2 = row_1 + width;
for (j = 0; j < width; ++j) {
bool x = row_1[j] & 1;
bool y = (i + 1) < width ? (row_2[j] & 1) : false;
if (x && y) {
fprintf(fp, " ");
} else if (x) {
fprintf(fp, "%s", CHAR_2);
} else if (y) {
fprintf(fp, "%s", CHAR_3);
} else {
fprintf(fp, "%s", CHAR_1);
}
}
for (j = 0; j < BORDER_LEN; ++j) {
fprintf(fp, "%s", CHAR_1);
}
fprintf(fp, "\n");
}
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 */
#endif /* QRCODE */

Some files were not shown because too many files have changed in this diff Show More