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

Compare commits

...

534 Commits

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

Fix build on FreeBSD with clang.

Without this patch, compilation fails with these messages:

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

* Fix build on FreeBSD with clang

Without this patch, compilation fails with these messages:

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

Also fixed a couple file descriptor leaks and cleaned some things up.
2016-09-20 00:00:09 -04:00
1f8c11a33a These strlen() calls are unncessary 2016-09-18 11:34:13 -04:00
5e20e6b279 Switch to using json DHT nodes file
Parsing json manually like this is ugly, but this allows us to use the json formatted nodeslist file
at nodes.tox.chat instead of having to update the list by hand. We could also potentially
use curl to fetch the list and update it automatically.
2016-09-17 21:38:32 -04:00
1f02bb2be5 Refactor DHT bootstrap code
- Separate node list loading from connecting
- Put code in its own source file
- Rename a few functions
2016-09-17 14:26:23 -04:00
98154b3cba Merge branch 'kdhp-master' 2016-09-15 11:27:04 -04:00
379ad9e116 Use ALC_ENUMERATE_ALL_EXT when available 2016-09-14 05:43:10 -05:00
cb21672600 Merge pull request #403 from metala/fix-set-video-fmt
Changed video get format to set format
2016-07-28 12:30:28 -04:00
4019395f44 Changed video get format to set format
* Fixes issue #394
* This was obviously meant to set the video format.
2016-07-28 17:58:24 +02:00
ee084c572c Merge pull request #402 from metala/fix-chat-infobox-flicker
Remove chat infobox flicker
2016-07-27 22:38:23 -04:00
41a8401ac5 Merge branch 'master' into fix-chat-infobox-flicker
Fixed cursor position issue
2016-07-27 22:04:41 +02:00
d8a3f7de4c Merge pull request #401 from metala/fix-remote-help-flicker
Remove help window flicker
2016-07-27 12:16:12 -04:00
c425aa2f27 Fixed input lag 2016-07-27 18:06:33 +02:00
94e026d114 Remove chat infobox flicker 2016-07-27 02:39:31 +02:00
f89638635a Remove help window flicker 2016-07-27 02:07:31 +02:00
402b86687f Merge branch 'mphe-beep' 2016-07-21 11:35:22 -04:00
5b1b420ac0 Add further explanation to the beep_on* options
This also renames beep_on* to bell_on*.
2016-07-21 11:35:09 +02:00
62ec514f17 Merge pull request #400 from Encrypt/master
Added a changelog to the project
2016-07-19 14:04:05 -04:00
f893dd755f Added a changelog to the project 2016-07-19 00:06:52 +01:00
9aedcf7753 Update docs 2016-07-17 19:22:40 +02:00
d3effa26b5 Add options to enable terminal bell on certain events
Some terminals can mark the terminal window as urgent on bell.
This is useful for window managers that provide a shortcut to jump to an
urgent client.
2016-07-17 19:22:40 +02:00
2ec180789b Fix crash on AV error call state during active call 2016-07-11 17:42:57 -04:00
76bfa34c45 Merge branch 'master' into new_groupchats 2016-06-29 21:54:30 -04:00
9f74d3a3a8 Fix memory leak 2016-06-29 21:36:54 -04:00
43d9623877 Fix merge conflicts 2016-06-12 18:27:54 -04:00
9fcbc3bde0 Fix tab-complete bug causing buffer to sometimes clear 2016-06-11 23:49:56 -04:00
cf16849b37 Dynamically allocate memory for save file instead of using stack 2016-05-03 14:13:16 -04:00
32442b6286 Disallow ctrl+m keybinding and revert to default settings on invalid bindings
Note: Ctrl+m uses the same control sequence as the enter key on many systems
2016-05-01 14:57:46 -04:00
50f227418b Update man pages 2016-04-25 22:04:43 -04:00
fc06a625a6 Put toxme.io back as it has a new owner 2016-04-25 14:49:23 -04:00
70bd39eb74 Fix small bug 2016-04-11 18:50:28 -04:00
4e0e322e32 Fix cross-client audio issue 2016-04-05 00:49:46 -04:00
e73ac9b6a4 Remove defunct nameserver 2016-03-30 00:30:47 -04:00
bcda6e476e Fix bug for real this time 2016-03-12 02:29:59 -05:00
5b29ce7132 Fix bug preventing friend connection status from changing 2016-03-03 20:49:48 -05:00
f43f644451 Merge branch 'mall0c-multiline' 2016-02-29 19:22:41 -05:00
d6fdac9739 Fix markup in multiline messages
A '<' or '>' at the beginning of a line will now highlight only this
particular line instead of the whole message.
2016-02-29 01:46:48 +01:00
c6a2bb8a90 Add settings option to set paste-mode key and update docs 2016-02-28 21:53:33 +01:00
04576fea7e Add paste-mode to allow multiline text pasting
Ctrl-T toggles paste mode.
Useful when pasting multiline text because it avoids triggering an
enter keypress after linebreaks.
Instead, every \r is translated to \n which is again translated to a
pilcrow (see previous commit).
The pasted text can then be sent as a whole instead of splitting up in
multiple messages.
2016-02-28 21:40:41 +01:00
e6f839f9ac Enable multiline input
Adds a nonl() call to avoid translation from \r to \n when pressing
enter.
C-J -> \n
Enter -> \r
To allow multiline input, \n chars (e.g. when pressing C-J) are replaced
with a pilcrow. After hitting enter, every pilcrow is substituted with
\n again.
2016-02-28 02:35:36 +01:00
eb02424f8a Update DHT nodes list 2016-02-25 19:04:37 -05:00
2e609c46f6 Remove file transfer timeoutes, and remove a couple unused functions 2016-02-25 17:58:02 -05:00
a474e3bf39 Add settings option to disable friend connection change notifications 2016-02-25 17:06:49 -05:00
93835f0455 Merge pull request #379 from FreakyPenguin/password_eval
Add password_eval option to skip password prompt
2016-01-19 18:31:04 -05:00
ac6d8ff89c Add password_eval option to skip password prompt
Runs a command and uses its output as the password. This can be used for
getting the password from a password manager such as pass.
2016-01-19 13:45:52 -08:00
88e74224ed Merge pull request #374 from quininer/master
sleep use tox_iteration_interval
2016-01-11 04:09:56 -05:00
deccaec40e fix milliseconds 2015-12-22 10:43:08 +08:00
4419be36e8 remove useless var 2015-12-21 20:00:48 +08:00
b34b51e8c1 use tox_iteration_interval 2015-12-21 18:13:03 +08:00
74416b4b58 Merge pull request #373 from wedge-jarrad/master
Fix #372 - can't start with missing ~/.config
2015-12-18 01:11:16 -05:00
675712cea0 Fix #372 - can't start with missing ~/.config
This block was added in fa0e645. I'm unsure what bug or corner case it
was intended to address but it causes Toxic to exit with an error if
the ~/.config directory doesn't exist. Without this block the ~/.config
directory and tox-specific subdirectories will be created when Toxic
starts.
2015-12-17 18:17:34 -08:00
36feebfe8d Merge branch 'RomeroMalaquias-master' 2015-12-09 01:32:07 -05:00
3fe9abd84d Avoiding conditional directives that split up parts os statements 2015-12-08 23:13:39 -03:00
fd6432c727 Avoiding conditional directives that split up parts os statements 2015-12-08 23:10:16 -03:00
60b431459c Add another bootstrap node 2015-12-07 23:56:29 -05:00
1feffcc2f0 Merge pull request #369 from nil0x42/master
update doc: DATA_FILE is now `toxic_profile.tox`
2015-12-07 16:11:07 -05:00
6bba3cb9e2 update doc: DATA_FILE is now toxic_profile.tox 2015-12-07 13:38:07 +01:00
6e7b0a5430 Fix groupchat creation bug
Users can no longer create new groupchat windows if they have been kicked from a group and haven't closed the window
2015-12-07 02:33:54 -05:00
86b36d7a13 Add working bootstrap server to DHTnodes 2015-12-06 23:09:15 -05:00
7e122ceec6 Merge branch 'master' into new_groupchats 2015-12-06 22:12:02 -05:00
3cb6db3d60 Remove pointless null check 2015-12-05 00:19:11 -05:00
77238eeadf Improve file transfer progress bar 2015-12-04 19:41:50 -05:00
fe0eba9107 Fix merge conflict 2015-12-01 18:01:02 -05:00
1d1c051a44 Merge branch 'master' into new_groupchats 2015-12-01 17:57:45 -05:00
88270827a9 Merge pull request #367 from landswellsong/osx-terminal-xtra-fix
Correctly operational from OSX terminals
2015-11-25 03:42:55 -05:00
aade65bfe1 Correctly operational from OSX terminals 2015-11-25 10:26:12 +02:00
b24c5d8cf8 Fix AV bug
Pending call now properly hangs up when you use the /hangup command or close the chat window
2015-11-19 23:49:58 -05:00
9f0feb7223 Allow custom nospam values 2015-11-12 18:03:45 -05:00
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
fbd22003a8 Merge with upstream master 2015-11-10 23:52:54 -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
3cfda32b5e Fix help menu 2015-11-07 01:17:06 -05:00
6aab9a79d8 Fix help menu 2015-11-07 01:00:56 -05:00
9b1592b335 Merge new_av from upstream 2015-11-07 00:21:42 -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
fbf50ecc8c Merge branch 'master' of https://github.com/JFreegman/toxic into new_groupchats 2015-11-02 14:49:48 -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
7025a33097 Merge with upstream 2015-11-01 21:07:22 -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
171024428b Merge with master branch 2015-10-16 21:01:49 -04:00
db410cb01e Use profile name that conforms with the standard 2015-10-14 23:09:11 -04:00
2e4c86be4b Add last seen and public key fields for group peers and add whois command 2015-09-13 23:15:58 -04:00
97d5fb84fc Add command to print your own group public key 2015-09-13 19:38:49 -04:00
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
b2c512687a Sort group peerlist by role/name 2015-09-01 17:27:17 -04:00
ffcc804efe Moved version up 2015-09-01 13:00:56 -04:00
8526d4d77e Refactor group peerlist to match API refactor
Peerlists are now similar to the friendlist, which makes managing them much easier, and
improves efficiency
2015-09-01 02:29:25 -04:00
69be1bc398 Fix nodes parsing bug 2015-08-28 02:44:38 -04:00
ed1429afa1 Merge with upstream 2015-08-27 21:36:20 -04:00
b4464eda4d Fix more threading issues 2015-08-27 21:29:34 -04:00
28dd43608d Fix a few notify/threading bugs and fix DHTnodes loading bug 2015-08-27 15:13:13 -04:00
11701d22a1 Lots of bug fixes and general code cleanup 2015-08-27 03:38:08 -04:00
1270f34596 group API type change 2015-08-23 19:15:44 -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
92d76c7f99 Display a user warning when log fails to initialize 2015-08-19 00:42:28 -04:00
2a787c1097 Fix some filetransfer issues
- File transfers now timeout properly
- Small refactor related to creating new transfers
2015-08-18 15:12:48 -04:00
327259c4c8 simplify timeout function 2015-08-18 01:46:22 -04:00
f173f4275e Merge pull request #350 from vinegret/patch-1
fix a broken link
2015-08-15 12:10:56 -04:00
48eaf8a14f fix a broken link 2015-08-15 15:42:32 +00:00
083611f18e Update DHTnodes 2015-08-13 15:03:02 -04:00
48ffae68a9 <not having red text 2015-08-11 19:22:57 -04:00
c39f8909cd Send file control cancel on failure to resume transfer 2015-08-10 20:41:11 -04:00
32e541bd1c Fix file transfers breaking when friends go offline 2015-08-10 14:22:13 -04:00
f559bdabfe Remove links to libtoxcore.so from readme 2015-08-08 16:51:04 -04:00
6eef188d3c fix travis 2015-08-08 16:45:48 -04:00
0047ba0e9f fix rare bug preventing toxcore from sleeping 2015-08-08 12:55:46 -04:00
8e23ce1b83 Group API changes 2015-07-22 14:55:52 -04:00
15ef50e46c Refactor groupchats 2015-07-16 17:10:04 -04:00
0597152352 merge with master 2015-07-14 02:19:10 -04:00
ecefc19b23 Merge branch 'ony-master' 2015-07-14 02:15:38 -04:00
23bb980173 Add group callbacks for password, peerlimit and privacy state changes 2015-07-14 02:07:52 -04:00
e83b397494 Makefile: allow overriding pkg-config 2015-07-14 08:27:07 +03:00
a846c38695 Port to new group API 2015-07-13 22:12:13 -04:00
688ea927f8 Update dht nodes list and fix URL's 2015-07-08 22:40:17 -04:00
1f7f4490b2 merge with master 2015-07-08 21:09:58 -04:00
40b220c821 Revert "Merge branch 'Ansa89-add_translation'"
This reverts commit 312d0c3f42, reversing
changes made to 409e4ddd96.
2015-07-08 21:08:55 -04:00
904f58c0e8 Add option to enable acting as TCP relay server 2015-07-08 21:01:32 -04:00
035420e5c7 Revert commit 312d0c3 (localization/gettext)
There were serious problems with branch merging that need to be resolved first
2015-07-04 01:19:16 -04:00
444d8e7a74 update translations 2015-06-30 23:51:27 -04:00
84a0276668 Fix some possible race conditions related to line printing 2015-06-30 22:40:45 -04:00
312d0c3f42 Merge branch 'Ansa89-add_translation' 2015-06-29 03:17:52 -04:00
d8eca8393c s/tox.im/tox.chat/g -- reflect new tox domain 2015-06-29 03:16:09 -04:00
374b78c763 manually fix merge conflicts 2015-06-29 03:11:21 -04:00
cb4a631df0 strcasecmp should be strcmp 2015-06-12 23:16:00 -04:00
409e4ddd96 Merge pull request #340 from Ansa89/update_travis_deps
travis.yml: update dependencies
2015-06-12 18:23:22 +02:00
1beb35025b travis.yml: update dependencies 2015-06-12 18:22:08 +02:00
35718db46f fix ban command bug 2015-06-10 19:30:44 -04:00
1ba0891f71 fix ban API calls 2015-06-08 21:56:32 -04:00
b36ada0f5b remove obsolete prune command 2015-06-04 16:46:30 -04:00
9cd2158c72 add group silencing and improve group help menu 2015-06-04 04:08:11 -04:00
928134da7a implement banning/unbanning 2015-06-03 04:32:59 -04:00
52a3367b5e small API fix 2015-06-01 03:12:12 -04:00
7e7087e94e add mod-list prune command 2015-05-30 23:57:30 -04:00
51a1c660b4 Add localization system (gettext) 2015-05-30 11:55:59 +02:00
9cd8afe90a add mod and unmod commands 2015-05-29 02:00:18 -04:00
85d3c18ba6 Add localization system (gettext) 2015-05-28 15:50:55 +02:00
526bd02189 Add group peer kicking && update related callback 2015-05-26 01:23:09 -04:00
3826fd793d Merge branch 'master' into new_groupchats 2015-05-24 18:13:15 -04:00
d0a7ca17d2 separate bootstrapping and adding TCP relays per toxcore API changes 2015-05-24 17:56:30 -04:00
36640224af toxcore API changes to tox_new 2015-05-24 17:36:13 -04:00
a6d7e9b839 add ability for founder to set group peer limit 2015-05-24 03:27:50 -04:00
29d0384a90 add ability for founder to set group privacy state 2015-05-22 21:41:32 -04:00
0481f43746 Update .travis.yml 2015-05-20 21:00:28 -04:00
f021908f8b implement group passwords 2015-05-20 19:07:25 -04:00
f82d58bbfc Merge branch 'master' of https://github.com/Tox/toxic into new_groupchats 2015-05-19 04:26:38 -04:00
231078b6b9 default tox options on initialization 2015-05-18 19:52:22 -04:00
9385f1145f update group api call 2015-05-05 22:32:42 -04:00
414f58d896 correctly handle tox_new errors 2015-04-14 17:18:09 -04:00
2acd99b04c show self in group when it's first created 2015-04-14 01:59:39 -04:00
4d73f8b241 not clear which error message is given for ipv6 failure 2015-04-13 00:58:33 -04:00
6cd7fb9e5b Merge branch 'master' into new_groupchats 2015-04-13 00:55:05 -04:00
540bf4a5c4 not clear which error message is given for ipv6 failure 2015-04-13 00:45:15 -04:00
82e76a3b5b try to fall back to ipv4 if tox fails to init with ipv6 2015-04-13 00:32:35 -04:00
a360afe08a remove group self-timeout callback 2015-04-12 21:56:42 -04:00
b66a1f6ba1 merge branch 'master' into new_groupchats 2015-04-10 00:22:07 -04:00
0bc610e18d null terminate status message 2015-04-10 00:16:01 -04:00
b66932fcec Merge branch 'master' into new_groupchats 2015-04-09 02:14:50 -04:00
02e6d2db3c fix possible segfault 2015-04-09 02:14:23 -04:00
de38df32c2 merge with upstream 2015-04-07 17:28:12 -04:00
5a2c341259 Makefile: try to fix Tox/toxic#307
@henriqueleng please test it and report back if it works for you.
2015-04-06 11:44:02 +02:00
1a7eaeddba fix a few avatar bugs 2015-04-04 21:15:34 -04:00
f656d0a722 Update .travis.yml 2015-04-04 03:42:43 -04:00
bdfbda7cda merge with upstream 2015-04-04 03:36:09 -04:00
09c1ad4566 Merge branch 'master' of https://github.com/Tox/toxic 2015-04-04 03:27:27 -04:00
8b9e34db75 Merge branch 'master' of https://github.com/JFreegman/toxic 2015-04-04 03:27:01 -04:00
dd9186e834 re-implement avatar setting 2015-04-04 03:26:38 -04:00
5ff1517b28 Merge pull request #322 from Ansa89/trivial-fix
Makefile: add uninstall target
2015-04-03 15:43:18 +02:00
bbb639c5aa Makefile: add uninstall target 2015-04-03 13:04:28 +02:00
0c49ab392d Merge branch 'master' into new_groupchats 2015-04-02 22:45:06 -04:00
860db2f612 Update .travis.yml 2015-04-02 22:44:19 -04:00
b2c0753fdf Update .travis.yml 2015-04-02 22:31:20 -04:00
523f205646 re-implement friendlist last seen 2015-04-02 22:19:09 -04:00
c97095be68 Merge branch 'master' into new_groupchats 2015-03-31 21:59:26 -04:00
e998c8a866 fix encryption API breakage 2015-03-31 21:56:11 -04:00
c2b9967691 merge with master 2015-03-31 19:34:33 -04:00
eaea68c33e fix bug where contacts would appear online twice in a row 2015-03-31 18:39:42 -04:00
4780cfeafc remove non-existant group commands 2015-03-31 17:11:43 -04:00
ee074f334e merge with master (new api) 2015-03-30 21:46:25 -04:00
bdb0951c84 a few small filetransfer fixes 2015-03-29 19:29:38 -04:00
e3130c92c0 small fix 2015-03-29 19:26:16 -04:00
12c880ab51 fix bootstrapping 2015-03-29 19:05:24 -04:00
522aabd4e4 Refactor and clean up file transfers
(No longer rely on undefined core filenumber property for indexing)
2015-03-29 18:33:51 -04:00
bf09b3b6c4 Merge branch 'master' of https://github.com/Tox/toxic 2015-03-28 02:58:24 -04:00
2d3c5c9450 Mostly finished with new API port
- File transfers currently don't support pausing/resuming
- Avatars are not yet done
2015-03-28 02:56:54 -04:00
23429d8da4 Merge pull request #5 from stqism/patch-2
I'm not allowed to do this correctly on the first try.
2015-03-26 03:16:29 -04:00
e34ac70a72 I'm not allowed to do this correctly on the first try. 2015-03-26 00:12:30 -07:00
77d45334ac Merge pull request #4 from stqism/patch-1
Build on commit
2015-03-26 03:00:08 -04:00
88719c4ccc Update .travis.yml 2015-03-25 23:56:40 -07:00
ae87b2eb2d begin port to new API 2015-03-25 22:56:45 -04:00
c34ecc8dfd Merge pull request #318 from Ansa89/trivial-fix
README.md: fix typo
2015-03-23 15:44:19 +01:00
2c2938c647 README.md: fix typo 2015-03-23 15:36:58 +01:00
386c5a8fa5 sound/desktop alert on private group message 2015-03-22 17:07:33 -04:00
5caa9bed51 fix nick parsing bug for private messages 2015-03-22 17:01:46 -04:00
59b90b1328 better format for private messages 2015-03-22 01:37:07 -04:00
05c05868c6 implement group private messaging 2015-03-22 01:07:56 -04:00
88d6d907d8 Merge branch 'master' into new_groupchats 2015-03-21 15:15:48 -04:00
22dd883f28 Merge pull request #316 from Ansa89/trivial-fix
Makefile: be less aggressive when cleaning
2015-03-21 14:32:23 -04:00
dfbb1338a5 Makefile: be less aggressive when cleaning 2015-03-21 19:31:00 +01:00
f8dc82516e Merge pull request #315 from Ansa89/trivial-fix
Move makefile into root directory
2015-03-21 19:25:05 +01:00
59b16f7760 Fix travis build 2015-03-21 18:38:28 +01:00
a11289de79 Move makefile into root directory
Requested by @mannol.
2015-03-21 18:35:40 +01:00
6cc8fdf215 don't delete groups on client exit 2015-03-19 17:39:11 -04:00
a64b8cae89 Merge branch 'hardlyeven-master' 2015-03-18 15:51:18 -04:00
470eaeb97b Merge branch 'master' of https://github.com/hardlyeven/toxic into hardlyeven-master 2015-03-18 15:45:37 -04:00
6503349ed3 Merge pull request #314 from al42and/fd_leak
Fixing couple leaking file descriptors
2015-03-18 15:38:40 -04:00
ec23cedc3a Fixing couple leaking file descriptors 2015-03-18 19:28:04 +03:00
jlo
ce1ae1d7dc added tab autocomplete for "/status o" => "/status online" 2015-03-17 14:25:15 -05:00
6669d5632f Merge pull request #310 from Ansa89/trivial-fix
Some cosmetics changes
2015-03-16 13:26:17 +01:00
59e1114997 Some cosmetics changes 2015-03-16 13:16:03 +01:00
c20510e5aa Merge pull request #308 from henriqueleng/openbsd
Openbsd
2015-03-15 15:52:37 -04:00
2b2e746549 added gnumake as dep., because you can't compile with *BSDmake 2015-03-15 16:34:56 -03:00
dc5a8d44e5 Add OpenBSD option to Makefile 2015-03-15 16:27:00 -03:00
02a0cac85a . 2015-03-13 20:38:44 -04:00
0976804fdf Merge branch 'master' into new_groupchats 2015-03-13 20:34:49 -04:00
090d3339a0 fix auto-away bugs
Manually set aways no longer unaway when they shouldn't, and busy status is no longer overridden
2015-03-13 20:33:23 -04:00
f8ff5df2f3 fix race conditions 2015-03-13 16:32:35 -04:00
22acba5aa8 merge wit upstream 2015-03-11 18:52:28 -04:00
442d9e22b4 use pthread for mplex detach polling for better portability 2015-03-11 18:38:07 -04:00
0c39e7b158 Add support for auto-away based on screen attach/detach 2015-03-09 01:38:30 +02:00
2bcce234a8 add group rejoin command 2015-03-07 00:05:54 -05:00
5859763f04 load saved group names 2015-03-04 00:08:30 -05:00
4cc0805036 fix small bug 2015-02-28 14:37:28 -05:00
830ddb21b5 add support for group persistence 2015-02-28 02:44:41 -05:00
b31bd93e7d api function name change 2015-02-26 19:59:55 -05:00
3e3f2614b5 Merge pull request #303 from louipc/interface
Add support for custom timestamps in chat and logs.
2015-02-23 22:38:50 -05:00
53e4b2c971 Add support for custom timestamps in chat and logs.
Internal representations of the time_format setting are replaced by timestamp format strings.
time_format is only used in the setup.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2015-02-23 00:46:33 -05:00
a89e6b2cdc merge mistake 2015-02-19 21:36:21 -05:00
dd3ea3dab5 merge with upstream 2015-02-19 21:33:46 -05:00
c3179b3b22 fix memory leak 2015-02-19 21:24:52 -05:00
d6c57c7aa3 Merge pull request #302 from Ansa89/trivial-fix
README.md: update download section
2015-02-19 12:49:50 +01:00
3efc984ae9 README.md: update download section 2015-02-18 15:56:19 +01:00
7618a0871a Merge pull request #301 from Ansa89/trivial-fix
Add INSTALL.md
2015-02-17 14:37:17 -05:00
b87f73cc47 Add INSTALL.md 2015-02-17 11:12:58 +01:00
785d39d78f Merge pull request #296 from louipc/interface
Interface
2015-02-13 14:23:44 -05:00
38a0f6fae4 Make little action indicators on each output line configurable.
Also added a new DISCONNECTION line info type.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2015-02-13 02:09:40 -05:00
710be51cad Merge pull request #298 from Ansa89/trivial-fix
travis.yml: use latest libsodium stable
2015-02-11 19:19:47 -05:00
50438425e5 Update README.md 2015-02-11 19:06:40 -05:00
4b4bfe8876 travis.yml: use latest libsodium stable 2015-02-11 10:12:05 +01:00
67da4bdd5b Merge pull request #297 from urras/patch-1
Travis should build with Libsodium stable, fix clang
2015-02-10 14:27:13 -05:00
e230badb47 Fix Clang
I didn't expect this to work, but it is and I'm not complaining.
2015-02-10 10:01:44 -05:00
d48d9ed4dc Travis should build with Libsodium stable 2015-02-10 09:47:06 -05:00
b9fe6bfa10 Merge pull request #280 from DomT4/darwin
Darwin Build
2015-02-09 21:05:10 -08:00
5cbbb62ce2 Fix whitespace
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2015-02-09 23:33:49 -05:00
3369c9d808 Merge pull request #293 from Spagy/patch-1
Correct filename comment from main.c to toxic.c
2015-02-04 15:47:58 -05:00
dfc594f949 Correct filename comment from main.c to toxic.c 2015-02-04 13:56:31 +00:00
d714cda145 Vital commit fixing serious Toxic bug 2015-02-01 17:10:44 -08:00
428e1e4100 Merge pull request #292 from Ansa89/trivial-fix
Update for toxcore API break
2015-02-01 15:16:29 -05:00
2b19f56e63 Update for toxcore API break 2015-02-01 21:09:48 +01:00
5c98c1c51e group invite API change 2015-01-27 18:00:25 -05:00
4badc983ea Merge pull request #291 from dantok/master
Fix some edge cases when obtaining paths
2015-01-25 15:54:27 -05:00
c8a4f88ff3 Update configdir.c 2015-01-25 21:53:13 +01:00
eeeaf8d707 Update autocomplete.c 2015-01-25 21:52:07 +01:00
37b3b5a5ed Fix an edge case when obtaining home directory.
Make the path to home directory empty before calling get_home_dir(), otherwise if the call fails, the contents are undefined.
2015-01-25 20:49:52 +01:00
a561ef8c49 Fixed emptying avatar_path if the path is too long. 2015-01-25 20:17:32 +01:00
ff69cdd253 add friend group invites 2015-01-23 00:52:09 -05:00
38e55d55b3 fix formatting bug 2015-01-20 18:06:26 -05:00
6f45085d86 update DHTnodes for new test network 2015-01-20 00:52:00 -05:00
0348f81ba8 remove old group audio code 2015-01-18 22:32:59 -05:00
7ee858110c improve command parsing so you don't need quotes & add group ignoring 2015-01-12 20:56:32 -05:00
89637e7d2f implement group join rejection callback 2015-01-12 01:09:56 -05:00
ff3da5f657 implement group status/nick changes, various fixes 2015-01-11 16:48:40 -05:00
89f200e870 Merge pull request #290 from urras/master
Update DHT nodes again
2015-01-11 15:11:04 -05:00
2823115a6c Update DHT nodes again 2015-01-11 15:06:15 -05:00
5599d73760 remove invalid node from nodes list 2015-01-11 14:28:52 -05:00
bac66cb5ad Merge pull request #289 from urras/master
Update DHT node list
2015-01-11 14:16:50 -05:00
61c50972e3 Update DHT node list 2015-01-11 14:06:33 -05:00
b3ab0bde05 a couple fixes 2015-01-04 17:22:24 -05:00
26dda8dbf9 implement new groupchats 2015-01-04 01:56:37 -05:00
ff0fbbc379 small fix 2015-01-01 12:50:51 -05:00
4a124dea43 Merge branch 'flussence-master' 2015-01-01 01:56:31 -05:00
e722b665d1 Make "Last seen" handle year rollover correctly 2015-01-01 05:44:18 +00:00
4365b8d5ad update man page 2014-12-27 13:17:05 -05:00
e55e5f3f7e add HTTP proxy support 2014-12-27 13:12:16 -05:00
d1036c8538 Darwin Build
Explains/Better Enables building on the OS X Platform. I’ve written a
Darwin.mk file for the purpose, given one is referenced in the Makefile
but doesn’t actually exist yet. I presume this is due to Toxic’s recent
move away from configure scripts?

Anyhow. It’s not perfect, and it could be prettier, but it should fix
2014-12-18 03:49:52 +00:00
9240f62829 Merge branch 'jdp5087-tour' 2014-12-17 17:35:01 -05:00
1b89bc9051 Made the keys section of settings_load more readable in settings.c 2014-12-17 12:15:18 -05:00
7700ef83e0 disable broken callback 2014-12-09 20:30:13 -05:00
9922b3a4f3 forgot IFDEFs 2014-12-09 18:55:14 -05:00
d561d2e1ac fix 2014-12-09 18:35:14 -05:00
7865cea284 group audio stuff 2014-12-09 18:29:07 -05:00
424189cad9 small fix 2014-12-07 21:48:29 -05:00
82aa64e8f7 properly close all groupchats on exit 2014-12-07 13:51:12 -05:00
a0418520a2 Merge branch 'master' of https://github.com/Tox/toxic 2014-12-07 12:19:14 -05:00
f451d961bc close output device on group exit 2014-12-07 12:16:49 -05:00
a4da9fd49e "Refactor" this super important file 2014-12-07 01:50:39 -08:00
0a0891fa98 fix building without audio 2014-12-06 13:40:45 -05:00
9464b369a4 fix possible crash 2014-12-06 13:26:27 -05:00
47692edee4 update DHTnodes for real 2014-12-05 20:33:13 -05:00
73c94b25ba update DHTnodes, fix comment 2014-12-05 20:26:10 -05:00
ab2ea5936d correct value 2014-12-05 13:47:47 -05:00
90985af007 properly handle file pausing 2014-12-05 13:23:45 -05:00
5aad8764b1 ensure messages in queue are always delivered in order 2014-12-03 22:10:21 -05:00
5a175f374a fix file statusbar draw bug 2014-12-03 21:38:37 -05:00
4acfe84171 remove spammy connection messages 2014-11-30 12:20:01 -05:00
e995a1cb69 AV api changes 2014-11-30 09:27:45 -05:00
867f041c23 more group stuff 2014-11-29 09:39:55 -05:00
7e49ba92b2 Merge pull request #283 from stal888/master
Destroy AL context before closing dhndl
2014-11-29 08:18:56 -05:00
2fa53c0531 Destroy AL context before closing dhndl
fixes seggy on os x, i think
2014-11-28 22:08:31 -08:00
673800c947 forgot something 2014-11-28 23:51:32 -05:00
835b821b75 put toxav loop in separate thread 2014-11-28 23:47:22 -05:00
78587ad20b disable group audio callback until it works properly 2014-11-26 17:39:02 -05:00
09bbec79cf group audio almost works 2014-11-26 15:22:34 -05:00
751b5f9943 only include audio_call.h if audio is defined 2014-11-26 12:24:52 -05:00
4a337ae3cb update for core audio refactor 2014-11-26 00:57:02 -05:00
1baeb15073 update DHTnodes 2014-11-20 02:57:42 -05:00
141b36af04 handle errors for name functions 2014-11-15 22:39:24 -05:00
4238c20e72 api update 2014-11-15 15:59:19 -05:00
4e177d60b0 group title fixes and api update 2014-11-15 15:55:45 -05:00
1bd880708b a couple formatting fixes 2014-11-14 22:35:23 -05:00
76c21c8b34 implement group titles 2014-11-14 22:15:59 -05:00
dd8c2caac7 more prep for audio groups 2014-11-14 20:13:08 -05:00
7a7e8a7f8d prep for audio groups 2014-11-11 20:49:05 -05:00
43f45d67a4 core API update 2014-11-11 18:30:23 -05:00
adb36a39bb minor visual adjustment 2014-11-05 18:46:18 -05:00
5a0d9f5e9f Merge pull request #278 from Ansa89/trivial-fix
Fix Tox/toxic#276
2014-10-30 15:41:17 +01:00
a4e8cb3971 Fix Tox/toxic#276 2014-10-30 15:23:47 +01:00
e22b7a336c Merge pull request #274 from Ansa89/trivial-fix
Makefile: revert back to mkdir
2014-10-21 06:20:51 -07:00
317fa42b20 Makefile: revert back to mkdir 2014-10-21 11:51:02 +02:00
884f8dda37 Merge pull request #273 from Ansa89/trivial-fix
Makefile: add toxic.desktop to install target
2014-10-21 01:38:29 -07:00
f5090532fb Makefile: add toxic.desktop to install target 2014-10-21 10:31:30 +02:00
ae04dd8fa5 Create toxic.desktop 2014-10-20 13:00:14 -07:00
00e4075937 Merge pull request #271 from Ansa89/trivial-fix
Toxic.conf.exmaple: fix sound namefile
2014-10-20 15:35:52 -04:00
d65f3ea58d Fix sound namefile 2014-10-20 10:55:06 +02:00
008163bf6c fix 2014-10-18 17:29:23 -04:00
f09002a49a Merge branch 'Ansa89-trivial-fix' 2014-10-16 14:27:58 -04:00
431290d47a Version: fix revision calculation 2014-10-16 20:26:00 +02:00
b66874b7b3 Merge pull request #262 from Ansa89/trivial-fix
Version: add revision only if git is available
2014-10-15 14:26:54 +02:00
1b9fd7f936 Install: check if manpages exist 2014-10-15 14:25:14 +02:00
0a1457acde Version: add revision only if git is available 2014-10-15 10:39:49 +02:00
d2008aa92f Merge branch 'louipc-new' 2014-10-13 00:16:05 -04:00
e83356faef Merge branch 'new' of https://github.com/louipc/toxic into louipc-new 2014-10-13 00:14:29 -04:00
fd4161832b travis.yml: Remove asciidoc
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-10-12 23:35:48 -04:00
0a132b0eb5 doc: Unlink docs from the rest of the build process.
Since generated man pages are also included, we can't
guarantee proper timestamps. This should fix issues that trigger doc
building when it isn't needed.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-10-12 23:21:48 -04:00
9c7ac9043a prompt: Add version line to startup message.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-10-12 23:12:56 -04:00
b86c0d357f Update toxencryptsave api change.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-10-12 23:04:30 -04:00
75dd26518f Finished drag and drop support 2014-10-12 23:26:28 +02:00
2cc261c619 Some random fixups 2014-10-12 22:54:18 +02:00
f7d46f1100 possibly fix sound notification bug 2014-10-12 01:49:56 -04:00
be264528d2 possibly fix sound notification bug 2014-10-12 00:28:28 -04:00
c825adc1e3 Dnd works 2014-10-11 23:53:02 +02:00
4e9f125e95 core API function name change 2014-10-10 22:44:05 -04:00
d26c8fe447 Merge pull request #264 from zetok/indicator
Change action messages indicator
2014-10-09 16:46:51 -04:00
e42d635195 non-critical thread failures shouldn't be fatal 2014-10-09 16:39:01 -04:00
a32cd1c19b Change action messages indicator 2014-10-09 21:36:42 +01:00
14dc02ac83 fix segfault 2014-10-09 15:47:48 -04:00
e4a28d1839 make groupchat announcements behave properly 2014-10-09 01:39:22 -04:00
0908920b51 fix segfault 2014-10-08 18:06:15 -04:00
8a882916fb fix setting custom keybinding 2014-10-08 15:22:49 -04:00
55ada8ad2f fix groupchat resizing when peerlist is off 2014-10-08 14:53:09 -04:00
2094186c0f update DHTnodes 2014-10-08 14:44:15 -04:00
d3a489b756 update settings and docs with peerlist-toggle keybinding 2014-10-08 03:08:08 -04:00
e6b18231c0 add ability to toggle groupchat peerlist with ctrl-b 2014-10-08 02:45:08 -04:00
cc0145d561 reduce system calls, increase time between message queue re-send attempts 2014-10-08 01:50:34 -04:00
9f0daca5eb filter escape sequences from all inbound nicks and status messages 2014-10-07 16:18:06 -04:00
1f2bd44dce don't allow newlines and tabs in nicks 2014-10-07 00:43:03 -04:00
6492bd12f9 escape newlines in contact names/status messages 2014-10-06 16:23:43 -04:00
43552161f9 implement word deletion via ^W 2014-10-05 23:12:58 -04:00
e5d45fdf1d correct return code check 2014-10-05 02:29:40 -04:00
bd69c8da18 small fix 2014-10-04 17:01:02 -04:00
8c3e3ebe24 fix blocklist saving bug 2014-10-03 23:52:54 -04:00
7eebbd982d fix 2014-10-03 21:12:59 -04:00
5c66f5c161 fixes 2014-10-03 19:29:12 -04:00
40f70fc1e3 groupchat fixes 2014-10-03 18:57:32 -04:00
9cf4427375 core groupchat names should be working 2014-10-03 17:55:36 -04:00
23cf9686cb safer string handling 2014-10-03 17:53:50 -04:00
70add920fe Making progress with dnd 2014-10-03 16:59:32 +02:00
c56c6cc219 some temporary fixes for new wip groupchats 2014-10-02 23:22:37 -04:00
e9a0296851 ignore tox_load errors until toxcore is fixed 2014-10-02 22:25:21 -04:00
de30433e26 better data operation error handling 2014-10-02 02:07:54 -04:00
fa64fa8b93 replace call sounds
Signed-off-by: Jfreegman <jfreegman@gmail.com>
2014-10-01 19:38:09 -04:00
50c7942cb5 Started working on dnd for toxic 2014-10-01 23:28:34 +02:00
e7697b8fba make ctrl-L force refresh the window 2014-10-01 16:24:36 -04:00
b46a77f977 forced end of page is not working out 2014-10-01 15:01:23 -04:00
38909afa89 refine a couple sound effects 2014-10-01 13:46:19 -04:00
5af561c811 new default sounds; rm unused sounds 2014-10-01 01:16:17 -04:00
ae984d4f04 don't show version in default status message 2014-09-30 17:02:03 -04:00
97a8ecd115 small fix and bump version to 0.5.2 2014-09-29 13:56:17 -04:00
64d782569a add asciidoc and libnotify-dev to install script 2014-09-29 01:54:53 -04:00
6248baf98b fix segfault 2014-09-29 01:30:22 -04:00
33cfca7ecc bug fixes and code cleanup 2014-09-28 17:49:48 -04:00
a01cc35368 Merge branch 'master' of github.com:Tox/toxic 2014-09-28 16:48:09 +02:00
b10eebd77e This should fix audio problem 2014-09-28 16:47:31 +02:00
05661ca9b6 core API update and refactor group invites a bit 2014-09-27 22:50:20 -04:00
784883f773 What if, like, photosynthesis is a lie?! 2014-09-28 00:13:45 +02:00
a9e5723ca4 a couple fixes 2014-09-27 03:08:19 -04:00
95dfea8d29 keep chatlog names consistent with friend name changes 2014-09-27 02:28:11 -04:00
e574af7d68 fix CPU usage issue regarding audio drivers, courtesy of mannol 2014-09-26 20:32:48 -04:00
2918ca45a2 Merge pull request #255 from Ansa89/trivial-fix
Fix "error: unknown type name 'off_t'"
2014-09-26 13:01:44 -04:00
6ad744f770 Add stdlib.h header to allow off_t type 2014-09-26 10:50:42 +02:00
5b394e6f35 notify.c: better headers order 2014-09-26 10:46:14 +02:00
940af2c711 a few fixes, update example config file 2014-09-26 03:39:36 -04:00
544c402f78 implement avatars (setting only) and generalize path tab-complete 2014-09-26 03:10:44 -04:00
cee9e624b8 more UI changes 2014-09-25 15:23:50 -04:00
2e65ee3609 a few UI changes 2014-09-25 04:31:45 -04:00
3a176e1cab move to page end after pressing return 2014-09-25 00:42:08 -04:00
00cccad22c a few general fixes 2014-09-24 15:20:15 -04:00
a432d733d7 use 64-bit off_t for file sizes 2014-09-24 14:23:08 -04:00
893e88294b more succinct way to get file sizes 2014-09-24 00:06:02 -04:00
b071a9e992 more thorough error checking 2014-09-23 22:51:56 -04:00
48cf4ebf02 fix for issue #254 2014-09-23 21:32:05 -04:00
773a75b948 update makefile checks 2014-09-22 21:51:55 -04:00
133c0e8d63 rename reserved identifiers per C standard 2014-09-22 21:24:45 -04:00
bf54cb36ef set signal handlers at more appropriate places 2014-09-22 20:00:36 -04:00
032853b5c9 Merge branch 'Pyrohh-master' 2014-09-22 19:41:23 -04:00
650c13ca7a There's no need for rm -rf, just rm -f 2014-09-22 18:45:45 -04:00
9d5fe03285 small fix 2014-09-22 18:38:46 -04:00
c3f68b60d3 bigger log identifiers to reduce chance of collision 2014-09-22 17:34:30 -04:00
1c16467eb9 don't share chatlogs across data files (changes log naming convention) 2014-09-22 17:09:39 -04:00
3c74385f5c use proper variable type for sig handler flags 2014-09-22 14:27:33 -04:00
c07c0028bb increase line_info msg buffer size so won't cut off long log lines 2014-09-22 13:49:09 -04:00
dc3b2e04ab load chatlog history when opening a chatwindow 2014-09-22 04:29:28 -04:00
7fd3aa9164 Merge pull request #246 from urras/patch-2
Update screenshot
2014-09-21 13:17:13 -04:00
4c60312e2d Update screenshot 2014-09-21 12:42:04 -04:00
16e29aa4e0 Merge pull request #243 from Ansa89/trivial-fix
Makefile: use single quotes also for PACKAGE_DATADIR
2014-09-19 12:28:32 -04:00
bd7b073155 Makefile: fix typo 2014-09-19 10:47:25 +02:00
f25cf870e6 Makefile: use single quotes also for PACKAGE_DATADIR 2014-09-19 10:45:27 +02:00
13291d0365 bump version to 0.5.1 2014-09-19 01:24:39 -04:00
121 changed files with 13362 additions and 4495 deletions

View File

@ -1,53 +0,0 @@
language: c
compiler:
- gcc
# - clang # Fix me
before_script:
# Installing yasm (needed for compiling vpx) and openal
- sudo apt-get -yq install yasm libopenal-dev libconfig-dev libalut-dev
# Installing libsodium, needed for toxcore
- git clone https://github.com/jedisct1/libsodium.git libsodium
- cd libsodium
- git checkout tags/0.7.0 > /dev/null
- ./autogen.sh > /dev/null
- ./configure > /dev/null
- make check -j2 || make check || exit 1 > /dev/null
- sudo make install > /dev/null
- cd ..
# Installing libopus, needed for audio encoding/decoding
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz
- tar xzf opus-1.0.3.tar.gz > /dev/null
- cd opus-1.0.3
- ./configure > /dev/null
- make -j2 || make || exit 1 > /dev/null
- sudo make install > /dev/null
- cd ..
# Installing vpx
- git clone http://git.chromium.org/webm/libvpx.git libvpx
- cd libvpx
- ./configure --enable-shared > /dev/null
- make -j2 || make || exit 1 > /dev/null
- sudo make install > /dev/null
- cd ..
# Creating libraries links and updating cache
- sudo ldconfig > /dev/null
# Installing toxcore
- git clone https://github.com/irungentoo/toxcore.git toxcore
- cd toxcore
- autoreconf -i
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
- make -j2 || make || exit 1
- sudo make install
- cd ..
script:
- cd build
- make -j2 || make || exit 1
notifications:
email: false
irc:
channels:
- "chat.freenode.net#tox-dev"
on_success: always
on_failure: always

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)*

70
INSTALL.md Normal file
View File

@ -0,0 +1,70 @@
# Installation
* [Dependencies](#deps)
* [OS X Notes](#deps_osx)
* [Compiling](#compiling)
* [Documentation](#docs)
* [Notes](#notes)
* [Compilation variables](#comp_vars)
* [Packaging](#packaging)
<a name="deps" />
## Dependencies
| Name | Needed by | Debian package |
|------------------------------------------------------|----------------------------|---------------------|
| [Tox Core](https://github.com/irungentoo/toxcore) | BASE | *None* |
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
| [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev |
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO | *None* |
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
<sup>1</sup>: see [Documentation](#docs)
<a name="deps_osx" />
#### OS X Notes
Using [Homebrew](http://brew.sh):
```
brew install openal-soft freealut libconfig
brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb
```
You can omit `libnotify` if you intend to build without desktop notifications enabled.
<a name="Compiling">
## Compiling
```
make PREFIX="/where/to/install"
sudo make install PREFIX="/where/to/install"
```
<a name="docs" />
#### Documentation
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br />
**NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
<a name="notes" />
## Notes
<a name="comp_vars" />
#### Compilation variables
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
* `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking)
* `DISABLE_AV=1` → build toxic without audio call support
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support
<a name="packaging" />
#### Packaging
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")

84
Makefile Normal file
View File

@ -0,0 +1,84 @@
BASE_DIR = $(shell pwd -P)
CFG_DIR = $(BASE_DIR)/cfg
-include $(CFG_DIR)/global_vars.mk
LIBS = libtoxcore ncursesw libconfig libqrencode
CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
CFLAGS += $(USER_CFLAGS)
LDFLAGS = $(USER_LDFLAGS)
OBJ = 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
endif
ifeq ($(UNAME_S), DragonFly)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), OpenBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), Darwin)
-include $(CFG_DIR)/systems/Darwin.mk
endif
ifeq ($(UNAME_S), Solaris)
-include $(CFG_DIR)/systems/Solaris.mk
endif
# Check on which platform we are running
UNAME_M = $(shell uname -m)
ifeq ($(UNAME_M), x86_64)
-include $(CFG_DIR)/platforms/x86_64.mk
endif
ifneq ($(filter %86, $(UNAME_M)),)
-include $(CFG_DIR)/platforms/x86.mk
endif
ifneq ($(filter arm%, $(UNAME_M)),)
-include $(CFG_DIR)/platforms/arm.mk
endif
# Include all needed checks
-include $(CFG_DIR)/checks/check_features.mk
# Fix path for object files
OBJ := $(addprefix $(BUILD_DIR)/, $(OBJ))
# Targets
all: $(BUILD_DIR)/toxic
$(BUILD_DIR)/toxic: $(OBJ)
@echo " LD $(@:$(BUILD_DIR)/%=%)"
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS)
$(BUILD_DIR)/osx_video.o: $(SRC_DIR)/$(OSX_VIDEO)
@echo " CC $(@:$(BUILD_DIR)/)osx_video.o"
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/osx_video.o -c $(SRC_DIR)/$(OSX_VIDEO)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
@if [ ! -e $(BUILD_DIR) ]; then \
mkdir -p $(BUILD_DIR) ;\
fi
@echo " CC $(@:$(BUILD_DIR)/%=%)"
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d
clean:
rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic
-include $(BUILD_DIR)/$(OBJ:.o=.d)
-include $(CFG_DIR)/targets/*.mk
.PHONY: clean all

View File

@ -1,53 +1,21 @@
# Toxic [![Build Status](https://travis-ci.org/Tox/toxic.png?branch=master)](https://travis-ci.org/Tox/toxic)
<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.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
![Toxic Screenshot](https://i.imgur.com/ryaEmQZ.png "Home Screen")
[![Toxic Screenshot](https://i.imgur.com/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)
### Dependencies
##### Base
* [libtoxcore](https://github.com/irungentoo/toxcore)
* [ncurses](https://www.gnu.org/software/ncurses) (for Debian based systems, 'libncursesw5-dev')
* [libconfig](http://www.hyperrealm.com/libconfig) (for Debian based systems, 'libconfig-dev')
## Settings
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.
You can view our example config file [here](misc/toxic.conf.example).
##### Audio
* libtoxav ([libtoxcore](https://github.com/irungentoo/toxcore) compiled with audio support)
* [openal](http://openal.org) (for Debian based systems, 'libopenal-dev')
##### Sound notifications
* [openal](http://openal.org) (for Debian based systems, 'libopenal-dev')
* [openalut](http://openal.org) (for Debian based systems, 'libalut-dev')
##### Desktop notifications
* [libnotify](https://developer.gnome.org/libnotify) (for Debian based systems, 'libnotify-dev')
##### Documentation
* [Asciidoc](http://asciidoc.org/index.html) (only required for regenerating manpages)
* Run `make doc` after editing the asciidoc files to regenerate the manpages.
* **NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.
### Compiling
1. `cd build/`
2. `make PREFIX="/where/to/install"`
3. `sudo make install PREFIX="/where/to/install"`
### Compilation Notes
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
* `DISABLE_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
### Packaging
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
### Troubleshooting
## Troubleshooting
If your default prefix is "/usr/local" and you receive the following:
```
error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory
@ -58,12 +26,3 @@ echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
sudo ldconfig
```
## Precompiled binaries
You can download precompiled binaries from [jenkins](https://jenkins.libtoxcore.so):
* [Linux 32 bit](https://jenkins.libtoxcore.so/job/toxic_linux_i386/lastSuccessfulBuild/artifact/toxic_linux_i386.tar.xz)
* [Linux 64 bit](https://jenkins.libtoxcore.so/job/toxic_linux_amd64/lastSuccessfulBuild/artifact/toxic_linux_amd64.tar.xz)
## Settings
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.
You can view our example config file [here](misc/toxic.conf.example).

11
astylerc Normal file
View File

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

View File

@ -1,67 +0,0 @@
BASE_DIR = $(shell cd .. && pwd -P)
CFG_DIR = $(BASE_DIR)/cfg
-include $(CFG_DIR)/global_vars.mk
LIBS = libtoxcore ncursesw libconfig
CFLAGS = -std=gnu99 -pthread -Wall -g
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED
CFLAGS += -DPACKAGE_DATADIR="\"$(abspath $(DATADIR))\""
CFLAGS += $(USER_CFLAGS)
LDFLAGS = $(USER_LDFLAGS)
OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_senders.o notify.o
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o
# Check on wich system we are running
UNAME_S = $(shell uname -s)
ifeq ($(UNAME_S), Linux)
-include $(CFG_DIR)/systems/Linux.mk
endif
ifeq ($(UNAME_S), FreeBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), Darwin)
-include $(CFG_DIR)/systems/Darwin.mk
endif
ifeq ($(UNAME_S), Solaris)
-include $(CFG_DIR)/systems/Solaris.mk
endif
# Check on which platform we are running
UNAME_M = $(shell uname -m)
ifeq ($(UNAME_M), x86_64)
-include $(CFG_DIR)/platforms/x86_64.mk
endif
ifneq ($(filter %86, $(UNAME_M)),)
-include $(CFG_DIR)/platforms/x86.mk
endif
ifneq ($(filter arm%, $(UNAME_M)),)
-include $(CFG_DIR)/platforms/arm.mk
endif
# Include all needed checks
-include $(CFG_DIR)/checks/check_features.mk
# Targets
all: toxic doc
toxic: $(OBJ)
@echo " LD $@"
@$(CC) $(CFLAGS) -o toxic $(OBJ) $(LDFLAGS)
%.o: $(SRC_DIR)/%.c
@echo " CC $@"
@$(CC) $(CFLAGS) -o $*.o -c $(SRC_DIR)/$*.c
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $*.d
clean:
rm -rf *.d *.o toxic
-include $(OBJ:.o=.d)
-include $(CFG_DIR)/targets/*.mk
.PHONY: clean all

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

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

View File

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

View File

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

View File

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

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,23 +1,21 @@
# Variables for sound notifications support
SND_NOTIFY_LIBS = openal freealut
SND_NOTIFY_CFLAGS = -D_SOUND_NOTIFY
ifneq (, $(findstring device.o, $(OBJ)))
SND_NOTIFY_OBJ =
SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY
ifneq (, $(findstring audio_device.o, $(OBJ)))
SND_NOTIFY_OBJ =
else
SND_NOTIFY_OBJ = device.o
SND_NOTIFY_OBJ = audio_device.o
endif
# Check if we can build sound notifications support
CHECK_SND_NOTIFY_LIBS = $(shell pkg-config --exists $(SND_NOTIFY_LIBS) || echo -n "error")
CHECK_SND_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(SND_NOTIFY_LIBS) || echo -n "error")
ifneq ($(CHECK_SND_NOTIFY_LIBS), error)
LIBS += $(SND_NOTIFY_LIBS)
CFLAGS += $(SND_NOTIFY_CFLAGS)
OBJ += $(SND_NOTIFY_OBJ)
else
ifneq ($(MAKECMDGOALS), clean)
MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without sound notifications support)
$(warning WARNING -- You need these libraries for sound notifications support)
$(warning WARNING -- $(MISSING_SND_NOTIFY_LIBS))
endif
LIBS += $(SND_NOTIFY_LIBS)
CFLAGS += $(SND_NOTIFY_CFLAGS)
OBJ += $(SND_NOTIFY_OBJ)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without sound notifications support)
$(warning WARNING -- You need these libraries for sound notifications support)
$(warning WARNING -- $(MISSING_SND_NOTIFY_LIBS))
endif

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

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

View File

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

View File

@ -1,9 +1,14 @@
# Version
TOXIC_VERSION = 0.5.0
REV = $(shell git rev-list HEAD --count)
VERSION = $(TOXIC_VERSION)_r$(REV)
TOXIC_VERSION = 0.7.1
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
ifneq (, $(findstring error, $(REV)))
VERSION = $(TOXIC_VERSION)
else
VERSION = $(TOXIC_VERSION)_r$(REV)
endif
# Project directories
BUILD_DIR = $(BASE_DIR)/build
DOC_DIR = $(BASE_DIR)/doc
SRC_DIR = $(BASE_DIR)/src
SND_DIR = $(BASE_DIR)/sounds
@ -11,13 +16,17 @@ MISC_DIR = $(BASE_DIR)/misc
# Project files
MANFILES = toxic.1 toxic.conf.5
DATAFILES = DHTnodes DNSservers toxic.conf.example
SNDFILES = ContactLogsIn.wav ContactLogsOut.wav Error.wav IncomingCall.wav
SNDFILES += LogIn.wav LogOut.wav NewMessage.wav OutgoingCall.wav
SNDFILES += TransferComplete.wav TransferPending.wav
DATAFILES = nameservers toxic.conf.example
DESKFILE = toxic.desktop
SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav
SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav
# Install directories
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
DATADIR = $(PREFIX)/share/toxic
MANDIR = $(PREFIX)/share/man
APPDIR = $(PREFIX)/share/applications
# Platform tools
PKG_CONFIG = pkg-config

18
cfg/systems/Darwin.mk Normal file
View File

@ -0,0 +1,18 @@
# Special options for OS X
# This assumes the use of Homebrew. Change the paths if using MacPorts or Fink.
PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/opt/libconfig/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig)
LIBS := $(filter-out ncursesw, $(LIBS))
# OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw.
LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -lcurl -lconfig -ltoxencryptsave -g
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
OSX_LIBRARIES = -lobjc -lresolv
OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \
-framework QuartzCore -framework CoreMedia
OSX_VIDEO = osx_video.m
LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS)
OBJ += osx_video.o

View File

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

View File

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

View File

@ -1,18 +1,20 @@
# Help target
help:
@echo "-- Targets --"
@echo " all: Build toxic and documentation [DEFAULT]"
@echo " toxic: Build toxic"
@echo " doc: Build documentation"
@echo " install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
@echo " clean: Remove built files"
@echo " help: This help"
@echo " all: Build toxic and documentation [DEFAULT]"
@echo " toxic: Build toxic"
@echo " doc: Build documentation"
@echo " install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
@echo " uninstall: Remove toxic from PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
@echo " clean: Remove built files"
@echo " help: This help"
@echo
@echo "-- Variables --"
@echo " DISABLE_X11: Set to \"1\" to force building without X11 support"
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"

View File

@ -1,31 +1,40 @@
# Install target
install: toxic doc
mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
install: $(BUILD_DIR)/toxic
@echo "Installing toxic executable"
@install -m 0755 toxic $(abspath $(DESTDIR)/$(BINDIR))
@mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
@install -m 0755 $(BUILD_DIR)/toxic $(abspath $(DESTDIR)/$(BINDIR)/toxic)
@echo "Installing desktop file"
@mkdir -p $(abspath $(DESTDIR)/$(APPDIR))
@install -m 0644 $(MISC_DIR)/$(DESKFILE) $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
@echo "Installing data files"
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
@for f in $(DATAFILES) ; do \
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)) ;\
file=$(abspath $(DESTDIR)/$(DATADIR))/$$f ;\
sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
file=$(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
mv temp_file $$file ;\
done
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
@for f in $(SNDFILES) ; do \
install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR))/sounds ;\
install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
done
@echo "Installing man pages"
@mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
@for f in $(MANFILES) ; do \
if [ ! -e "$(DOC_DIR)/$$f" ]; then \
continue ;\
fi ;\
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
file=$$section/$$f ;\
mkdir -p $$section ;\
install -m 0644 $(DOC_DIR)/$$f $$file ;\
sed -i'' -e 's:__VERSION__:'$(VERSION)':g' $$file ;\
sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\
sed -e 's:__VERSION__:'$(VERSION)':g' $$file > temp_file && \
mv temp_file $$file ;\
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
mv temp_file $$file ;\
gzip -f -9 $$file ;\
done

24
cfg/targets/uninstall.mk Normal file
View File

@ -0,0 +1,24 @@
# Uninstall target
uninstall:
@echo "Removing toxic executable"
@rm -f $(abspath $(DESTDIR)/$(BINDIR)/toxic)
@echo "Removing desktop file"
@rm -f $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
@echo "Removing data files"
@for f in $(DATAFILES) ; do \
rm -f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
done
@for f in $(SNDFILES) ; do \
rm -f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
done
@echo "Removing man pages"
@for f in $(MANFILES) ; do \
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
file=$$section/$$f ;\
rm -f $$file $$file.gz ;\
done
.PHONY: uninstall

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: 2014-09-19
.\" Date: 2016-09-20
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC" "1" "2014\-09\-19" "toxic __VERSION__" "Toxic Manual"
.TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@ -70,7 +70,7 @@ Encrypt an unencrypted data file\&. An error will occur if this option is used w
Use specified
\fIdata\-file\fR
instead of
\fI~/\&.config/tox/data\fR
\fI~/\&.config/tox/toxic_profile\&.tox\fR
.RE
.PP
\-h, \-\-help
@ -82,8 +82,8 @@ Show help message
.RS 4
Use specified
\fInodes\-file\fR
for DHT bootstrap nodes, instead of
\fI__DATADIR__/DHTnodes\fR
for DHT bootstrap nodes instead of
\fI~/\&.config/tox/DHTnodes\&.json\fR
.RE
.PP
\-o, \-\-noconnect
@ -91,14 +91,19 @@ for DHT bootstrap nodes, instead of
Do not connect to the DHT network
.RE
.PP
\-p, \-\-proxy
\-p, \-\-SOCKS5\-proxy
.RS 4
Use proxy: Requires [IP] [port]
Use a SOCKS5 proxy: Requires [IP] [port]
.RE
.PP
\-r, \-\-dnslist
\-P, \-\-HTTP\-proxy
.RS 4
Use specified DNSservers file
Use a HTTP proxy: Requires [IP] [port]
.RE
.PP
\-r, \-\-namelist
.RS 4
Use specified nameservers list
.RE
.PP
\-t, \-\-force\-tcp
@ -106,23 +111,25 @@ Use specified DNSservers file
Force TCP connection (use this with proxies)
.RE
.PP
\-T, \-\-tcp\-relay
.RS 4
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
.RE
.PP
\-u, \-\-unencrypt\-data
.RS 4
Unencrypt a data file\&. A warning will appear if this option is used with a data file that is already unencrypted\&.
.RE
.PP
\-x, \-\-nodata
.RS 4
Ignore data file
.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
@ -139,7 +146,11 @@ Configuration example\&.
.RE
.SH "BUGS"
.sp
Unicode characters with a width larger than 1 column may cause strange behaviour\&. Expect more bugs and bad behaviour: this software is in a pre\-alpha stage\&.
\-Unicode characters with a width larger than 1 column may cause strange behaviour\&.
.sp
\-Text occasionally fails to auto\-scroll to the bottom\&.
.sp
\-Screen flickering sometimes occurs on certain terminals\&.
.SH "AUTHORS"
.sp
JFreegman <JFreegman@gmail\&.com>
@ -148,6 +159,6 @@ JFreegman <JFreegman@gmail\&.com>
\fBtoxic\&.conf\fR(5)
.SH "LINKS"
.sp
Project page: https://github\&.com/Tox/toxic
Project page: https://github\&.com/JFreegman/toxic
.sp
IRC channel: chat\&.freenode\&.net#tox

View File

@ -31,44 +31,47 @@ OPTIONS
Use default locale
-e, --encrypt-data::
Encrypt an unencrypted data file. An error will occur if this option
Encrypt an unencrypted data file. An error will occur if this option
is used with an encrypted data file.
-f, --file data-file::
Use specified 'data-file' instead of '~/.config/tox/data'
Use specified 'data-file' instead of '~/.config/tox/toxic_profile.tox'
-h, --help::
Show help message
-n, --nodes nodes-file::
Use specified 'nodes-file' for DHT bootstrap nodes, instead of
'{datadir}/DHTnodes'
Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json'
-o, --noconnect::
Do not connect to the DHT network
-p, --proxy::
Use proxy: Requires [IP] [port]
-p, --SOCKS5-proxy::
Use a SOCKS5 proxy: Requires [IP] [port]
-r, --dnslist::
Use specified DNSservers file
-P, --HTTP-proxy::
Use a HTTP proxy: Requires [IP] [port]
-r, --namelist::
Use specified nameservers list
-t, --force-tcp::
Force TCP connection (use this with proxies)
-u, --unencrypt-data::
Unencrypt a data file. A warning will appear if this option is used
with a data file that is already unencrypted.
-T, --tcp-relay::
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
-x, --nodata::
Ignore data file
-u, --unencrypt-data::
Unencrypt a data file. A warning will appear if this option is used
with a data file that is already unencrypted.
FILES
-----
{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)
@ -80,9 +83,12 @@ FILES
BUGS
----
Unicode characters with a width larger than 1 column may cause strange
behaviour. Expect more bugs and bad behaviour: this software is in a
pre-alpha stage.
-Unicode characters with a width larger than 1 column may cause strange
behaviour.
-Text occasionally fails to auto-scroll to the bottom.
-Screen flickering sometimes occurs on certain terminals.
AUTHORS
-------
@ -94,6 +100,6 @@ SEE ALSO
LINKS
-----
Project page: <https://github.com/Tox/toxic>
Project page: <https://github.com/JFreegman/toxic>
IRC channel: chat.freenode.net#tox

View File

@ -2,12 +2,12 @@
.\" Title: toxic.conf
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2014-08-26
.\" Date: 2016-07-21
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC\&.CONF" "5" "2014\-08\-26" "toxic __VERSION__" "Toxic Manual"
.TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@ -66,9 +66,26 @@ Configuration related to interface elements\&.
Enable or disable timestamps\&. true or false
.RE
.PP
\fBtime_format\fR
.RS 4
Select between 24 and 12 hour time\&. Specify 24 or 12\&. Setting timestamp_format and log_timestamp_format will override this setting\&.
.RE
.PP
\fBtimestamp_format\fR
.RS 4
Time format string for the interface enclosed by double quotes\&. See
\fBdate\fR(1)
.RE
.PP
\fBlog_timestamp_format\fR
.RS 4
Time format string for logging enclosed by double quotes\&. See
\fBdate\fR(1)
.RE
.PP
\fBalerts\fR
.RS 4
Enable or disable terminal alerts on events\&. true or false
Enable or disable acoustic alerts on events\&. true or false
.RE
.PP
\fBnative_colors\fR
@ -81,11 +98,6 @@ Select between native terminal colors and toxic color theme\&. true or false
Enable or disable autologging\&. true or false
.RE
.PP
\fBtime_format\fR
.RS 4
Select between 24 and 12 hour time\&. Specify 24 or 12
.RE
.PP
\fBshow_typing_other\fR
.RS 4
Show when others are typing in a 1\-on\-1 chat\&. true or false
@ -101,10 +113,87 @@ Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false
Show welcome message on startup\&. true or false
.RE
.PP
\fBshow_connection_msg\fR
.RS 4
Enable friend connection change notifications\&. true or false
.RE
.PP
\fBnodelist_update_freq\fR
.RS 4
How often in days to update the DHT nodes list\&. (0 to disable updates)
.RE
.PP
\fBhistory_size\fR
.RS 4
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
.RE
.PP
\fBline_join\fR
.RS 4
Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&.
.RE
.PP
\fBline_quit\fR
.RS 4
Indicator for when someone disconnects or leaves a group\&.
.RE
.PP
\fBline_alert\fR
.RS 4
Indicator for alert messages\&.
.RE
.PP
\fBline_normal\fR
.RS 4
Indicator for normal messages\&.
.RE
.PP
\fBline_special\fR
.RS 4
Indicator for special messages\&.
.RE
.PP
\fBmplex_away\fR
.RS 4
Set user status when attaching and detaching from GNU screen or tmux\&. true or false
.RE
.PP
\fBmplex_away_note\fR
.RS 4
Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&.
.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
\fBaudio\fR
@ -138,10 +227,20 @@ Configuration related to paths\&.
Default path for downloads\&. String value\&. Absolute path for downloaded files\&.
.RE
.PP
\fBavatar_path\fR
.RS 4
Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
.RE
.PP
\fBchatlogs_path\fR
.RS 4
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
.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
@ -150,7 +249,7 @@ Configuration related to notification sounds\&. Special value "silent" can be us
Each value is a string which corresponds to the absolute path of a wav sound file\&.
.PP
\fBerror\fR
\fBnotif_error\fR
.RS 4
Sound to play when an error occurs\&.
.RE
@ -251,6 +350,16 @@ Key combination to scroll contacts list up\&.
.RS 4
Key combination to scroll contacts list down\&.
.RE
.PP
\fBtoggle_peerlist\fR
.RS 4
Toggle the peer list on and off\&.
.RE
.PP
\fBtoggle_paste_mode\fR
.RS 4
Toggle treating linebreaks as enter key press\&.
.RE
.RE
.SH "FILES"
.PP
@ -268,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

@ -42,8 +42,20 @@ OPTIONS
*timestamps*;;
Enable or disable timestamps. true or false
*time_format*;;
Select between 24 and 12 hour time. Specify 24 or 12. Setting
timestamp_format and log_timestamp_format will override this setting.
*timestamp_format*;;
Time format string for the interface enclosed by double quotes.
See *date*(1)
*log_timestamp_format*;;
Time format string for logging enclosed by double quotes.
See *date*(1)
*alerts*;;
Enable or disable terminal alerts on events. true or false
Enable or disable acoustic alerts on events. true or false
*native_colors*;;
Select between native terminal colors and toxic color theme. true or false
@ -51,9 +63,6 @@ OPTIONS
*autolog*;;
Enable or disable autologging. true or false
*time_format*;;
Select between 24 and 12 hour time. Specify 24 or 12
*show_typing_other*;;
Show when others are typing in a 1-on-1 chat. true or false
@ -63,9 +72,56 @@ OPTIONS
*show_welcome_msg*;;
Show welcome message on startup. true or false
*show_connection_msg*;;
Enable friend connection change notifications. true or false
*nodelist_update_freq*;;
How often in days to update the DHT nodes list. (0 to disable updates)
*history_size*;;
Maximum lines for chat window history. Integer value. (for example: 700)
*line_join*;;
Indicator for when someone connects or joins a group.
Three characters max for line_ settings.
*line_quit*;;
Indicator for when someone disconnects or leaves a group.
*line_alert*;;
Indicator for alert messages.
*line_normal*;;
Indicator for normal messages.
*line_special*;;
Indicator for special messages.
*mplex_away*;;
Set user status when attaching and detaching from GNU screen or tmux.
true or false
*mplex_away_note*;;
Status message to set when status is set to away due to screen/tmux
detach. When attaching, the status message is set back to the original
value.
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.
@ -87,16 +143,23 @@ OPTIONS
Default path for downloads. String value. Absolute path for downloaded
files.
*avatar_path*;;
Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
*chatlogs_path*;;
Default path for chatlogs. String value. Absolute path for chatlog files.
*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. +
Each value is a string which corresponds to the absolute path of a wav
sound file.
*error*;;
*notif_error*;;
Sound to play when an error occurs.
*self_log_in*;;
@ -158,6 +221,12 @@ OPTIONS
*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
-----
@ -175,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,11 +0,0 @@
192.254.75.98 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
37.187.46.132 33445 A9D98212B3F972BD11DA52BEB0658C326FCCC1BFD49F347F9C2D3D8B61E1B927
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
37.59.102.176 33445 B98A2CEAA6C6A2FADC2C3632D284318B60FE5375CCB41EFA081AB67F500C1B0B
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
107.161.17.51 33445 7BE3951B97CA4B9ECDDA768E8C52BA19E9E2690AB584787BF4C90E04DBB75111
31.7.57.236 443 2A4B50D1D525DA2E669592A20C327B5FAD6C7E5962DC69296F9FEC77C4436E4E
63.165.243.15 443 8CD087E31C67568103E8C2A28653337E90E6B8EDA0D765D57C6B5172B4F1F04C

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,6 +29,9 @@ ui = {
// 24 or 12 hour time
time_format=24;
// Timestamp format string according to date/strftime format. Overrides time_format setting
timestamp_format="%H:%M:%S";
// true to show you when others are typing a message in 1-on-1 chats
show_typing_other=true;
@ -26,8 +41,35 @@ ui = {
// true to show the welcome message on startup
show_welcome_msg=true;
// true to show friend connection change messages on the home screen
show_connection_msg=true;
// How often in days to update the DHT nodes list. (0 to disable updates)
nodeslist_update_freq=7;
// maximum lines for chat window history
history_size=700;
// Indicator for display when someone connects or joins a group.
line_join="-->";
// Indicator for display when someone disconnects or leaves a group.
line_quit="<--";
// Indicator for alert messages.
line_alert="-!-";
// Indicator for normal messages.
line_normal="---";
// Indicator for special messages (currently only used for private group messages)
line_special=">>>";
// true to change status based on screen/tmux attach/detach, false to disable
mplex_away=true;
// Status message to use when status set to away due to screen/tmux detach
mplex_away_note="Away from keyboard, be back soon!"
};
audio = {
@ -36,7 +78,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;
};
@ -45,26 +87,27 @@ tox = {
// Path for downloaded files
// download_path="/home/USERNAME/Downloads/";
// Path for your avatar (file must be a .png and cannot exceed 64 KiB)
// avatar_path="/home/USERNAME/Pictures/youravatar.png";
// Path for chatlogs
// chatlogs_path="/home/USERNAME/toxic_chatlogs/";
};
// To disable a sound set the path to "silent"
sounds = {
error="__DATADIR__/sounds/Error.wav";
self_log_in="__DATADIR__/sounds/LogIn.wav";
self_log_out="__DATADIR__/sounds/LogOut.wav";
user_log_in="__DATADIR__/sounds/ContactLogsIn.wav";
user_log_out="__DATADIR__/sounds/ContactLogsOut.wav";
call_incoming="__DATADIR__/sounds/IncomingCall.wav";
call_outgoing="__DATADIR__/sounds/OutgoingCall.wav";
generic_message="__DATADIR__/sounds/NewMessage.wav";
transfer_pending="__DATADIR__/sounds/TransferPending.wav";
transfer_completed="__DATADIR__/sounds/TransferComplete.wav";
error="__DATADIR__/sounds/ToxicError.wav";
user_log_in="__DATADIR__/sounds/ToxicContactOnline.wav";
user_log_out="__DATADIR__/sounds/ToxicContactOffline.wav";
call_incoming="__DATADIR__/sounds/ToxicIncomingCall.wav";
call_outgoing="__DATADIR__/sounds/ToxicOutgoingCall.wav";
generic_message="__DATADIR__/sounds/ToxicRecvMessage.wav";
transfer_pending="__DATADIR__/sounds/ToxicTransferStart.wav";
transfer_completed="__DATADIR__/sounds/ToxicTransferComplete.wav";
};
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
// Note: All printable keys register as input
// Note: Ctrl+M does not work
keys = {
next_tab="Ctrl+P";
prev_tab="Ctrl+O";
@ -75,5 +118,7 @@ keys = {
page_bottom="Ctrl+H";
peer_list_up="Ctrl+[";
peer_list_down="Ctrl+]";
toggle_peerlist="Ctrl+b";
toggle_paste_mode="Ctrl+T";
};

11
misc/toxic.desktop Normal file
View File

@ -0,0 +1,11 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=Toxic
Comment=A CLI based Tox client
TryExec=toxic
Exec=toxic
Icon=utilities-terminal
Categories=InstantMessaging;AudioVideo;Network;
Terminal=true
MimeType=x-scheme-handler/tox;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
sounds/ToxicError.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
sounds/ToxicRecvMessage.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +1,2 @@
Tox's sounds are licensed under the "Creative Commons Attribution 3.0 Unported", all credit attributed to Adam Reid.
ToxicError.wav, ToxicRecvMessage.wav, ToxicContactOffline.wav, ToxicIncomingCall.wav, ToxicTransferComplete.wav, ToxicContactOnline.wav, ToxicOutgoingCall.wav and ToxicTransferStart.wav
are licensed under the "Creative Commons Attribution 3.0 Unported". All credit attributed to Jfreegman.

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,4 +1,4 @@
/* device.c
/* audio_device.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
@ -20,9 +20,9 @@
*
*/
#include "device.h"
#include "audio_device.h"
#ifdef _AUDIO
#ifdef AUDIO
#include "audio_call.h"
#endif
@ -38,37 +38,37 @@
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif
#endif
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#define OPENAL_BUFS 5
#define inline__ inline __attribute__((always_inline))
extern struct user_settings *user_settings_;
extern struct user_settings *user_settings;
typedef struct _Device {
typedef struct Device {
ALCdevice *dhndl; /* Handle of device selected/opened */
ALCcontext *ctx; /* Device context */
DataHandleCallback cb; /* Use this to handle data from input device usually */
void* cb_data; /* Data to be passed to callback */
int32_t call_idx; /* ToxAv call index */
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 */
size_t ref_count;
uint32_t ref_count;
int32_t selection;
_Bool enable_VAD;
_Bool muted;
bool enable_VAD;
bool muted;
pthread_mutex_t mutex[1];
uint32_t sample_rate;
uint32_t sample_rate;
uint32_t frame_duration;
int32_t sound_mode;
#ifdef _AUDIO
#ifdef AUDIO
float VAD_treshold; /* 40 is usually recommended value */
#endif
} Device;
@ -76,12 +76,12 @@ typedef struct _Device {
const char *ddevice_names[2]; /* Default device */
const char *devices_names[2][MAX_DEVICES]; /* Container of available devices */
static int size[2]; /* Size of above containers */
Device *running[2][MAX_DEVICES]; /* Running devices */
Device *running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
uint32_t primary_device[2]; /* Primary device */
#ifdef _AUDIO
static ToxAv* av = NULL;
#endif /* _AUDIO */
#ifdef AUDIO
static ToxAV *av = NULL;
#endif /* AUDIO */
/* q_mutex */
#define lock pthread_mutex_lock(&mutex)
@ -89,102 +89,112 @@ static ToxAv* av = NULL;
pthread_mutex_t mutex;
_Bool thread_running = _True,
thread_paused = _True; /* Thread control */
bool thread_running = true,
thread_paused = true; /* Thread control */
void* thread_poll(void*);
void *thread_poll(void *);
/* Meet devices */
#ifdef _AUDIO
DeviceError init_devices(ToxAv* av_)
#ifdef AUDIO
DeviceError init_devices(ToxAV *av_)
#else
DeviceError init_devices()
#endif /* _AUDIO */
#endif /* AUDIO */
{
const char *stringed_device_list;
size[input] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
devices_names[input][size[input]] = stringed_device_list;
devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
size[output] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) {
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;
devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
// Start poll thread
pthread_mutex_init(&mutex, NULL);
if (pthread_mutex_init(&mutex, NULL) != 0)
return de_InternalError;
pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
return de_InternalError;
#ifdef _AUDIO
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
return de_InternalError;
#ifdef AUDIO
av = av_;
#endif /* _AUDIO */
#endif /* AUDIO */
return (DeviceError) de_None;
}
DeviceError terminate_devices()
{
/* Cleanup if needed */
lock;
thread_running = false;
unlock;
usleep(20000);
pthread_mutex_destroy(&mutex);
if (pthread_mutex_destroy(&mutex) != 0)
return (DeviceError) de_InternalError;
return (DeviceError) de_None;
}
DeviceError device_mute(DeviceType type, uint32_t device_idx)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock;
Device* device = running[type][device_idx];
if (!device) {
Device *device = running[type][device_idx];
if (!device) {
unlock;
return de_DeviceNotActive;
}
device->muted = !device->muted;
unlock;
return de_None;
}
#ifdef _AUDIO
#ifdef AUDIO
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock;
Device* device = running[input][device_idx];
if (!device) {
Device *device = running[input][device_idx];
if (!device) {
unlock;
return de_DeviceNotActive;
}
device->VAD_treshold = value;
unlock;
return de_None;
}
@ -194,103 +204,115 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
DeviceError set_primary_device(DeviceType type, int32_t selection)
{
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
primary_device[type] = selection;
return de_None;
}
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
uint8_t channels)
{
return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels);
}
void get_primary_device_name(DeviceType type, char *buf, int size)
{
memcpy(buf, ddevice_names[type], size);
}
// TODO: generate buffers separately
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
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 (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;
Device* device = running[type][*device_idx] = calloc(1, sizeof(Device));
device->selection = selection;
device->sample_rate = sample_rate;
device->frame_duration = frame_duration;
device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
for (i = 0; i < *device_idx; i ++) { /* Check if any previous has the same selection */
if ( running[type][i]->selection == selection ) {
device->dhndl = running[type][i]->dhndl;
if (type == output) {
device->ctx = running[type][i]->ctx;
memcpy(device->buffers, running[type][i]->buffers, sizeof(running[type][i]->buffers));
device->source = running[type][i]->source;
}
device->ref_count++;
pthread_mutex_init(device->mutex, NULL);
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++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 ) {
// printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl);
running[type][*device_idx] = running[type][i];
running[type][i]->ref_count ++;
unlock;
return de_None;
}
}
if (type == input) {
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
sample_rate, device->sound_mode, frame_size * 2);
#ifdef _AUDIO
device->VAD_treshold = user_settings_->VAD_treshold;
#endif
Device *device = running[type][*device_idx] = calloc(1, sizeof(Device));
device->selection = selection;
device->sample_rate = sample_rate;
device->frame_duration = frame_duration;
device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
if (pthread_mutex_init(device->mutex, NULL) != 0) {
free(device);
unlock;
return de_InternalError;
}
else {
if (type == input) {
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
sample_rate, device->sound_mode, frame_size * 2);
#ifdef AUDIO
device->VAD_treshold = user_settings->VAD_treshold;
#endif
} else {
device->dhndl = alcOpenDevice(devices_names[type][selection]);
if ( !device->dhndl ) {
if ( !device->dhndl ) {
free(device);
running[type][*device_idx] = NULL;
unlock;
return de_FailedStart;
}
device->ctx = alcCreateContext(device->dhndl, NULL);
alcMakeContextCurrent(device->ctx);
alGenBuffers(OPENAL_BUFS, device->buffers);
alGenSources((uint32_t)1, &device->source);
alSourcei(device->source, AL_LOOPING, AL_FALSE);
uint16_t zeros[frame_size];
memset(zeros, 0, frame_size*2);
for ( i =0; i < OPENAL_BUFS; ++i) {
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate);
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);
}
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
alSourcePlay(device->source);
}
if (alcGetError(device->dhndl) != AL_NO_ERROR) {
free(device);
running[type][*device_idx] = NULL;
unlock;
return de_FailedStart;
}
if (type == input) {
alcCaptureStart(device->dhndl);
thread_paused = _False;
thread_paused = false;
}
pthread_mutex_init(device->mutex, NULL);
unlock;
return de_None;
}
@ -298,102 +320,101 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
DeviceError close_device(DeviceType type, uint32_t device_idx)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock;
Device* device = running[type][device_idx];
if (!device) {
Device *device = running[type][device_idx];
DeviceError rc = de_None;
if (!device) {
unlock;
return de_DeviceNotActive;
}
if ( !(device->ref_count--) ) {
running[type][device_idx] = NULL;
unlock;
DeviceError rc = de_None;
running[type][device_idx] = NULL;
if ( !device->ref_count ) {
if (type == input) {
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
}
else {
} else {
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
alDeleteSources(1, &device->source);
alDeleteBuffers(OPENAL_BUFS, device->buffers);
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
alcMakeContextCurrent(NULL);
if ( device->ctx ) alcDestroyContext(device->ctx);
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
}
free(device);
return rc;
}
} else device->ref_count--;
unlock;
return de_None;
return rc;
}
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD)
DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
void *data, bool enable_VAD)
{
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
return de_InvalidSelection;
lock;
running[input][device_idx]->cb = callback;
running[input][device_idx]->cb_data = data;
running[input][device_idx]->enable_VAD = enable_VAD;
running[input][device_idx]->call_idx = call_idx;
running[input][device_idx]->friend_number = friend_number;
unlock;
return de_None;
}
inline__ DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels)
inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
uint32_t sample_rate)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
Device* device = running[output][device_idx];
Device *device = running[output][device_idx];
if (!device || device->muted) return de_DeviceNotActive;
pthread_mutex_lock(device->mutex);
ALuint bufid;
ALint processed, queued;
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
if(processed) {
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, lenght * 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.
@ -401,53 +422,66 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
(void)arg;
uint32_t i;
int32_t sample = 0;
while (thread_running)
{
if (thread_paused) usleep(10000); /* Wait for unpause. */
else
{
for (i = 0; i < size[input]; i ++)
{
while (1) {
lock;
if (!thread_running) {
unlock;
break;
}
bool paused = thread_paused;
unlock;
/* 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);
if (sample < f_size) {
if (sample < f_size) {
unlock;
continue;
}
Device* device = running[input][i];
Device *device = running[input][i];
int16_t frame[16000];
alcCaptureSamples(device->dhndl, frame, f_size);
if ( device->muted
#ifdef _AUDIO
|| (device->enable_VAD && !toxav_has_activity(av, device->call_idx, frame, f_size, device->VAD_treshold))
#endif /* _AUDIO */
)
{ unlock; continue; } /* Skip if no voice activity */
if (device->muted) {
unlock;
continue;
}
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
}
}
unlock;
}
usleep(5000);
}
}
pthread_exit(NULL);
}
void print_devices(ToxWindow* self, DeviceType type)
void print_devices(ToxWindow *self, DeviceType type)
{
int i = 0;
for ( ; i < size[type]; i ++)
int i;
for (i = 0; i < size[type]; ++i)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]);
return;
@ -458,10 +492,10 @@ 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,20 +22,18 @@
/*
* 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"
#define _True 1
#define _False 0
typedef enum DeviceType {
input,
output,
@ -54,39 +52,44 @@ 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);
#ifdef AUDIO
DeviceError init_devices(ToxAV *av);
#else
DeviceError init_devices();
#endif /* _AUDIO */
#endif /* AUDIO */
DeviceError terminate_devices();
/* Callback handles ready data from INPUT device */
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD);
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);
#ifdef _AUDIO
#ifdef AUDIO
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, int16_t* data, uint32_t lenght, uint8_t channels);
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels,
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,10 +25,10 @@
#include <limits.h>
#ifdef __APPLE__
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/types.h>
#include <sys/dir.h>
#else
#include <dirent.h>
#include <dirent.h>
#endif /* ifdef __APPLE__ */
#include "windows.h"
@ -53,12 +53,14 @@ static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items
}
/* puts match in match buffer. if more than one match, add first n chars that are identical.
e.g. if matches contains: [foo, foobar, foe] we put fo in matches. */
static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_SIZE], int n)
* e.g. if matches contains: [foo, foobar, foe] we put fo in match.
*
* Returns the length of the match.
*/
static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n)
{
if (n == 1) {
strcpy(match, matches[0]);
return;
return snprintf(match, match_sz, "%s", matches[0]);
}
int i;
@ -71,14 +73,14 @@ static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_
char ch2 = matches[j][i];
if (ch1 != ch2 || !ch1) {
strcpy(match, matches[0]);
snprintf(match, match_sz, "%s", matches[0]);
match[i] = '\0';
return;
return i;
}
}
}
strcpy(match, matches[0]);
return snprintf(match, match_sz, "%s", matches[0]);
}
/* looks for all instances in list that begin with the last entered word in line according to pos,
@ -93,7 +95,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
return -1;
const char *L = (char *) list;
@ -104,7 +106,9 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
return -1;
bool dir_search = strncmp(ubuf, "/sendfile", strlen("/sendfile")) == 0;
/* TODO: generalize this */
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
/* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE];
@ -112,7 +116,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
tmp[ctx->pos] = '\0';
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
char *sub = malloc(strlen(ubuf) + 1);
char *sub = calloc(1, strlen(ubuf) + 1);
if (sub == NULL)
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
@ -134,20 +138,20 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
}
}
if (string_is_empty(sub)) {
if (!sub[0]) {
free(sub);
return -1;
}
int s_len = strlen(sub);
const char *str;
int n_matches = 0;
char matches[n_items][MAX_STR_SIZE];
int i = 0;
/* put all list matches in matches array */
for (i = 0; i < n_items; ++i) {
str = &L[i * size];
char str[MAX_CMDNAME_SIZE + 1];
snprintf(str, sizeof(str), "%s", &L[i * size]);
if (strncasecmp(str, sub, s_len) == 0)
strcpy(matches[n_matches++], str);
@ -162,11 +166,15 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
char match[MAX_STR_SIZE];
get_str_match(self, match, matches, n_matches);
size_t match_len = 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)) ? "\"" : "/";
endchrs = char_rfind(match, '.', match_len) ? "\"" : "/";
else
endchrs = "";
} else if (n_matches > 1) {
@ -175,24 +183,29 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
/* put match in correct spot in buf and append endchars */
int n_endchrs = strlen(endchrs);
int m_len = strlen(match);
int strt = ctx->pos - s_len;
int diff = m_len - s_len + n_endchrs;
int diff = match_len - s_len + n_endchrs;
if (ctx->len + diff >= MAX_STR_SIZE)
return -1;
char tmpend[MAX_STR_SIZE];
strcpy(tmpend, &ubuf[ctx->pos]);
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) {
return -1;
}
strcpy(&ubuf[strt], match);
strcpy(&ubuf[strt + m_len], endchrs);
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
strcpy(&ubuf[strt + match_len], endchrs);
strcpy(&ubuf[strt + match_len + n_endchrs], tmpend);
/* convert to widechar and copy back to original buf */
wchar_t newbuf[MAX_STR_SIZE];
if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1)
if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) {
return -1;
}
wcscpy(ctx->line, newbuf);
@ -202,21 +215,21 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
return diff;
}
/* transforms a sendfile tab complete contaning the shorthand "~/" into the full home directory.*/
static void complt_home_dir(ToxWindow *self, char *path, int pathsize)
/* transforms a tab complete starting with the shorthand "~" into the full home directory.*/
static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
{
ChatContext *ctx = self->chatwin;
char homedir[MAX_STR_SIZE];
char homedir[MAX_STR_SIZE] = {0};
get_home_dir(homedir, sizeof(homedir));
char newline[MAX_STR_SIZE];
snprintf(newline, sizeof(newline), "/sendfile \"%s%s", homedir, path + 1);
snprintf(path, pathsize, "%s", &newline[11]);
snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1);
snprintf(path, pathsize, "%s", &newline[cmdlen]);
wchar_t wline[MAX_STR_SIZE];
if (mbs_to_wcs_buf(wline, newline, sizeof(wline)) == -1)
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1)
return;
int newlen = wcslen(wline);
@ -229,39 +242,42 @@ static void complt_home_dir(ToxWindow *self, char *path, int pathsize)
ctx->len = ctx->pos;
}
/* attempts to match /sendfile "<incomplete-dir>" line to matching directories.
/* attempts to match /command "<incomplete-dir>" line to matching directories.
if only one match, auto-complete line.
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
#define MAX_DIRS 512
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line)
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
{
char b_path[MAX_STR_SIZE];
char b_name[MAX_STR_SIZE];
const wchar_t *tmpline = &line[11]; /* start after "/sendfile \"" */
char b_cmd[MAX_STR_SIZE];
const wchar_t *tmpline = &line[wcslen(cmd) + 2]; /* start after "/command \"" */
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
return -1;
return -1;
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1)
return -1;
if (b_path[0] == '~')
complt_home_dir(self, b_path, sizeof(b_path));
complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
int si = char_rfind(b_path, '/', strlen(b_path));
if (!b_path[0]) { /* list everything in pwd */
b_path[0] = '.';
b_path[1] = '\0';
b_path[1] = '\0';
} else if (!si && b_path[0] != '/') { /* look for matches in pwd */
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), ".%s", b_path);
strcpy(b_path, tmp);
snprintf(b_path, sizeof(b_path), "%s", tmp);
}
strcpy(b_name, &b_path[si + 1]);
snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]);
b_path[si + 1] = '\0';
int b_name_len = strlen(b_name);
DIR *dp = opendir(b_path);
if (dp == NULL)
@ -272,13 +288,15 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line)
int dircount = 0;
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
if (strncmp(entry->d_name, b_name, b_name_len) == 0
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
if (strncmp(entry->d_name, b_name, b_name_len) == 0
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
++dircount;
}
}
closedir(dp);
if (dircount == 0)
return -1;

View File

@ -20,8 +20,8 @@
*
*/
#ifndef _autocomplete_h
#define _autocomplete_h
#ifndef AUTOCOMPLETE_H
#define AUTOCOMPLETE_H
/* looks for all instances in list that begin with the last entered word in line according to pos,
then fills line with the complete word. e.g. "Hello jo" would complete the line
@ -33,10 +33,10 @@
Returns the difference between the old len and new len of line on success, -1 if error */
int complete_line(ToxWindow *self, const void *list, int n_items, int size);
/* attempts to match /sendfile "<incomplete-dir>" line to matching directories.
/* attempts to match /command "<incomplete-dir>" line to matching directories.
if only one match, auto-complete line.
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line);
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd);
#endif /* #define _autocomplete_h */
#endif /* #define AUTOCOMPLETE_H */

207
src/avatars.c Normal file
View File

@ -0,0 +1,207 @@
/* avatars.c
*
*
* Copyright (C) 2015 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "misc_tools.h"
#include "file_transfers.h"
#include "friendlist.h"
#include "avatars.h"
extern FriendsList Friends;
static struct Avatar {
char name[TOX_MAX_FILENAME_LENGTH + 1];
size_t name_len;
char path[PATH_MAX + 1];
size_t path_len;
off_t size;
} Avatar;
static void avatar_clear(void)
{
memset(&Avatar, 0, sizeof(struct Avatar));
}
/* Sends avatar to friendnum.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int avatar_send(Tox *m, uint32_t friendnum)
{
TOX_ERR_FILE_SEND err;
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
if (Avatar.size == 0)
return 0;
if (err != TOX_ERR_FILE_SEND_OK) {
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
return -1;
}
struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
if (!ft)
return -1;
ft->file = fopen(Avatar.path, "r");
if (ft->file == NULL)
return -1;
snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
ft->file_size = Avatar.size;
return 0;
}
/* Sends avatar to all friends */
static void avatar_send_all(Tox *m)
{
size_t i;
for (i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE)
avatar_send(m, Friends.list[i].num);
}
}
/* Sets avatar to path and sends it to all online contacts.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int avatar_set(Tox *m, const char *path, size_t path_len)
{
if (path_len == 0 || path_len >= sizeof(Avatar.path))
return -1;
FILE *fp = fopen(path, "rb");
if (fp == NULL)
return -1;
char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
fclose(fp);
return -1;
}
fclose(fp);
off_t size = file_size(path);
if (size == 0 || size > MAX_AVATAR_FILE_SIZE)
return -1;
get_file_name(Avatar.name, sizeof(Avatar.name), path);
Avatar.name_len = strlen(Avatar.name);
snprintf(Avatar.path, sizeof(Avatar.path), "%s", path);
Avatar.path_len = path_len;
Avatar.size = size;
avatar_send_all(m);
return 0;
}
/* Unsets avatar and sends to all online contacts.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
void avatar_unset(Tox *m)
{
avatar_clear();
avatar_send_all(m);
}
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control)
{
switch (control) {
case TOX_FILE_CONTROL_RESUME:
if (ft->state == FILE_TRANSFER_PENDING) {
ft->state = FILE_TRANSFER_STARTED;
} else if (ft->state == FILE_TRANSFER_PAUSED) {
ft->state = FILE_TRANSFER_STARTED;
}
break;
case TOX_FILE_CONTROL_PAUSE:
ft->state = FILE_TRANSFER_PAUSED;
break;
case TOX_FILE_CONTROL_CANCEL:
close_file_transfer(NULL, m, ft, -1, NULL, silent);
break;
}
}
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length)
{
if (ft->state != FILE_TRANSFER_STARTED)
return;
if (length == 0) {
close_file_transfer(NULL, m, ft, -1, NULL, silent);
return;
}
if (ft->file == NULL) {
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
return;
}
if (ft->position != position) {
if (fseek(ft->file, position, SEEK_SET) == -1) {
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
return;
}
ft->position = position;
}
uint8_t send_data[length];
size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
if (send_length != length) {
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
return;
}
TOX_ERR_FILE_SEND_CHUNK err;
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
ft->position += send_length;
ft->last_keep_alive = get_unix_time();
}

52
src/avatars.h Normal file
View File

@ -0,0 +1,52 @@
/* avatars.h
*
*
* Copyright (C) 2015 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AVATARS_H
#define AVATARS_H
#define MAX_AVATAR_FILE_SIZE 65536
/* Sends avatar to friendnum.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int avatar_send(Tox *m, uint32_t friendnum);
/* Sets avatar to path and sends it to all online contacts.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int avatar_set(Tox *m, const char *path, size_t length);
/* Unsets avatar and sends to all online contacts.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
void avatar_unset(Tox *m);
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control);
#endif /* AVATARS_H */

586
src/bootstrap.c Normal file
View File

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

42
src/bootstrap.h Normal file
View File

@ -0,0 +1,42 @@
/* bootstrap.h
*
*
* Copyright (C) 2016 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BOOTSTRAP_H
#define BOOTSTRAP_H
/* Manages connection to the Tox DHT network. */
void do_tox_connection(Tox *m);
/* Creates a new thread that will load the DHT nodeslist to memory
* from json encoded nodes file obtained at NODES_LIST_URL. Only one
* thread may run at a time.
*
* Return 0 on success.
* Return -1 if a thread is already active.
* Return -2 if mutex fails to init.
* Return -3 if pthread attribute fails to init.
* Return -4 if pthread fails to set detached state.
* Return -5 if thread creation fails.
*/
int load_DHT_nodeslist(void);
#endif /* BOOTSTRAP_H */

1096
src/chat.c

File diff suppressed because it is too large Load Diff

View File

@ -20,8 +20,8 @@
*
*/
#ifndef CHAT_H_6489PZ13
#define CHAT_H_6489PZ13
#ifndef CHAT_H
#define CHAT_H
#include "windows.h"
#include "toxic.h"
@ -32,4 +32,4 @@ void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
void kill_chat_window(ToxWindow *self, Tox *m);
ToxWindow new_chat(Tox *m, int32_t friendnum);
#endif /* end of include guard: CHAT_H_6489PZ13 */
#endif /* end of include guard: CHAT_H */

View File

@ -31,14 +31,10 @@
#include "line_info.h"
#include "groupchat.h"
#include "chat.h"
#include "file_senders.h"
#include "file_transfers.h"
extern ToxWindow *prompt;
extern _Friends Friends;
extern FileSender file_senders[MAX_FILES];
extern uint8_t max_file_senders_index;
extern uint8_t num_active_file_senders;
extern FriendsList Friends;
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
@ -47,51 +43,80 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
return;
}
char msg[MAX_STR_SIZE];
const char *inoutstr = argv[1];
int filenum = atoi(argv[2]);
long int idx = strtol(argv[2], NULL, 10);
if (filenum >= MAX_FILES || filenum < 0) {
if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
if (strcasecmp(inoutstr, "in") == 0) { /* cancel an incoming file transfer */
if (!Friends.list[self->num].file_receiver[filenum].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
struct FileTransfer *ft = NULL;
const char *filepath = Friends.list[self->num].file_receiver[filenum].filename;
char name[MAX_STR_SIZE];
get_file_name(name, sizeof(name), filepath);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer for '%s' canceled.", name);
chat_close_file_receiver(m, filenum, self->num, TOX_FILECONTROL_KILL);
return;
} else if (strcasecmp(inoutstr, "out") == 0) { /* cancel an outgoing file transfer */
int i;
bool match = false;
for (i = 0; i < MAX_FILES; ++i) {
if (file_senders[i].active && file_senders[i].filenum == filenum) {
match = true;
break;
}
}
if (!match) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
const char *filename = file_senders[i].filename;
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "File transfer for '%s' canceled.", filename);
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, self->num);
return;
/* cancel an incoming file transfer */
if (strcasecmp(inoutstr, "in") == 0) {
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
} else if (strcasecmp(inoutstr, "out") == 0) {
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
return;
}
if (!ft) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
if (ft->state == FILE_TRANSFER_INACTIVE) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
snprintf(msg, sizeof(msg), "File transfer for '%s' aborted.", ft->file_name);
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
}
void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
if (Friends.list[self->num].group_invite.length == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group invite");
return;
}
const char *passwd = NULL;
uint16_t passwd_len = 0;
if (argc > 0) {
passwd = argv[1];
passwd_len = strlen(passwd);
}
TOX_ERR_GROUP_INVITE_ACCEPT err;
uint32_t groupnumber = tox_group_invite_accept(m, Friends.list[self->num].group_invite.data,
Friends.list[self->num].group_invite.length,
(uint8_t *) passwd, passwd_len, &err);
if (err != TOX_ERR_GROUP_INVITE_ACCEPT_OK) {
if (err == TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to joing group: Password too long.");
else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to joing group (error %d).", err);
return;
}
if (init_groupchat_win(m, groupnumber, NULL, 0) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_group_leave(m, groupnumber, NULL, 0, NULL);
return;
}
}
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -103,47 +128,22 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
int groupnum = atoi(argv[1]);
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
return;
}
if (tox_invite_friend(m, self->num, groupnum) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group.");
TOX_ERR_GROUP_INVITE_FRIEND err;
if (!tox_group_invite_friend(m, groupnum, self->num, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d).", err);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum);
}
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
const char *groupkey = Friends.list[self->num].groupchat_key;
if (!Friends.list[self->num].groupchat_pending) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
return;
}
int groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey);
if (groupnum == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
return;
}
if (init_groupchat_win(prompt, m, groupnum) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
return;
}
}
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
@ -151,50 +151,77 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
return;
}
uint8_t filenum = atoi(argv[1]);
long int idx = strtol(argv[1], NULL, 10);
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
if (!Friends.list[self->num].file_receiver[filenum].pending) {
struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
if (!ft) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
const char *filename = Friends.list[self->num].file_receiver[filenum].filename;
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", filenum, filename);
/* prep progress bar line */
char progline[MAX_STR_SIZE];
prep_prog_line(progline);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
Friends.list[self->num].file_receiver[filenum].line_id = self->chatwin->hst->line_end->id + 2;
if ((Friends.list[self->num].file_receiver[filenum].file = fopen(filename, "a")) == NULL) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "* Error writing to file.");
tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
} else {
Friends.list[self->num].file_receiver[filenum].active = true;
++Friends.list[self->num].active_file_receivers;
}
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed.");
if (ft->state != FILE_TRANSFER_PENDING) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
Friends.list[self->num].file_receiver[filenum].pending = false;
if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
const char *msg = "File transfer failed: Invalid file path.";
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return;
}
TOX_ERR_FILE_CONTROL err;
tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
if (err != TOX_ERR_FILE_CONTROL_OK)
goto on_recv_error;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
/* prep progress bar line */
char progline[MAX_STR_SIZE];
init_progress_bar(progline);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
ft->line_id = self->chatwin->hst->line_end->id + 2;
ft->state = FILE_TRANSFER_STARTED;
return;
on_recv_error:
switch (err) {
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
return;
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
return;
case TOX_ERR_FILE_CONTROL_NOT_FOUND:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
return;
case TOX_ERR_FILE_CONTROL_SENDQ:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
return;
default:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
return;
}
}
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (max_file_senders_index >= (MAX_FILES - 1)) {
const char *errmsg = "Please wait for some of your outgoing file transfers to complete.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
return;
}
const char *errmsg = NULL;
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
@ -224,47 +251,67 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
return;
}
fseek(file_to_send, 0, SEEK_END);
uint64_t filesize = ftell(file_to_send);
fseek(file_to_send, 0, SEEK_SET);
off_t filesize = file_size(path);
char filename[MAX_STR_SIZE] = {0};
get_file_name(filename, sizeof(filename), path);
int namelen = strlen(filename);
int filenum = tox_new_file_sender(m, self->num, filesize, (const uint8_t *) filename, namelen);
if (filenum == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error sending file.");
if (filesize == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
fclose(file_to_send);
return;
}
int i;
char file_name[TOX_MAX_FILENAME_LENGTH];
size_t namelen = get_file_name(file_name, sizeof(file_name), path);
for (i = 0; i < MAX_FILES; ++i) {
if (!file_senders[i].active) {
memcpy(file_senders[i].filename, filename, namelen + 1);
file_senders[i].active = true;
file_senders[i].toxwin = self;
file_senders[i].file = file_to_send;
file_senders[i].filenum = filenum;
file_senders[i].friendnum = self->num;
file_senders[i].timestamp = get_unix_time();
file_senders[i].size = filesize;
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
tox_file_data_size(m, self->num), file_to_send);
TOX_ERR_FILE_SEND err;
uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL,
(uint8_t *) file_name, namelen, &err);
char sizestr[32];
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"Sending file [%d]: '%s' (%s)", filenum, filename, sizestr);
if (err != TOX_ERR_FILE_SEND_OK)
goto on_send_error;
++num_active_file_senders;
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
if (i == max_file_senders_index)
++max_file_senders_index;
reset_file_sender_queue();
return;
}
if (!ft) {
err = TOX_ERR_FILE_SEND_TOO_MANY;
goto on_send_error;
}
memcpy(ft->file_name, file_name, namelen + 1);
ft->file = file_to_send;
ft->file_size = filesize;
tox_file_get_file_id(m, self->num, filenum, ft->file_id, NULL);
char sizestr[32];
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
return;
on_send_error:
switch (err) {
case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND:
errmsg = "File transfer failed: Invalid friend.";
break;
case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED:
errmsg = "File transfer failed: Friend is offline.";
break;
case TOX_ERR_FILE_SEND_NAME_TOO_LONG:
errmsg = "File transfer failed: Filename is too long.";
break;
case TOX_ERR_FILE_SEND_TOO_MANY:
errmsg = "File transfer failed: Too many concurrent file transfers.";
break;
default:
errmsg = "File transfer failed.";
break;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
fclose(file_to_send);
}

View File

@ -20,19 +20,19 @@
*
*/
#ifndef _chat_commands_h
#define _chat_commands_h
#ifndef CHAT_COMMANDS_H
#define CHAT_COMMANDS_H
#include "windows.h"
#include "toxic.h"
void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef _AUDIO
#ifdef AUDIO
void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_reject(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
@ -41,6 +41,11 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* _AUDIO */
#endif /* AUDIO */
#endif /* #define _chat_commands_h */
#ifdef VIDEO
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* VIDEO */
#endif /* #define CHAT_COMMANDS_H */

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;
@ -66,11 +67,11 @@ void get_home_dir(char *home, int size)
*/
char *get_user_config_dir(void)
{
char home[NSS_BUFLEN_PASSWD];
char home[NSS_BUFLEN_PASSWD] = {0};
get_home_dir(home, sizeof(home));
char *user_config_dir;
size_t len;
char *user_config_dir = NULL;
size_t len = 0;
# if defined(__APPLE__)
len = strlen(home) + strlen("/Library/Application Support") + 1;
@ -82,9 +83,9 @@ char *get_user_config_dir(void)
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
# else /* __APPLE__ */
const char *tmp;
const char *tmp = getenv("XDG_CONFIG_HOME");
if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
if (tmp == NULL) {
len = strlen(home) + strlen("/.config") + 1;
user_config_dir = malloc(len);
@ -101,8 +102,10 @@ 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)
{

View File

@ -20,8 +20,8 @@
*
*/
#ifndef _configdir_h
#define _configdir_h
#ifndef CONFIGDIR_H
#define CONFIGDIR_H
#ifndef NSS_BUFLEN_PASSWD
#define NSS_BUFLEN_PASSWD 4096
@ -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 /* #define CONFIGDIR_H */

93
src/curl_util.c Normal file
View File

@ -0,0 +1,93 @@
/* curl_util.c
*
*
* Copyright (C) 2016 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <string.h>
#include <curl/curl.h>
#include <tox/tox.h>
#include "curl_util.h"
/* Sets proxy info for given CURL handler.
*
* Returns 0 on success or if no proxy is set by the client.
* Returns -1 if proxy info is invalid.
* Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
*/
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
{
if (proxy_type == TOX_PROXY_TYPE_NONE)
return 0;
if (proxy_address == NULL || port == 0) {
return -1;
}
int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
if (ret != CURLE_OK) {
return ret;
}
long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP;
ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type);
if (ret != CURLE_OK) {
return ret;
}
ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
if (ret != CURLE_OK) {
return ret;
}
return 0;
}
/* Callback function for CURL to write received data.
*
* This function will append data from an http request to the data buffer
* until the request is complete or the buffer is full. Buffer will be null terminated.
*
* Returns number of bytes received from http request on success (don't change this).
* Returns 0 if data exceeds buffer size.
*/
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer)
{
struct Recv_Curl_Data *recv_data = (struct Recv_Curl_Data *) user_pointer;
size_t length = size * nmemb;
size_t total_size = length + recv_data->length;
if (total_size > MAX_RECV_CURL_DATA_SIZE) {
return 0;
}
memcpy(recv_data->data + recv_data->length, data, length);
recv_data->data[total_size] = '\0';
recv_data->length += length;
return length;
}

55
src/curl_util.h Normal file
View File

@ -0,0 +1,55 @@
/* curl_util.h
*
*
* Copyright (C) 2016 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CURL_UTIL_H
#define CURL_UTIL_H
/* List based on Mozilla's recommended configurations for modern browsers */
#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"
/* Max size of an http response that we can store in Recv_Data */
#define MAX_RECV_CURL_DATA_SIZE 32767
/* Holds data received from curl lookup */
struct Recv_Curl_Data {
char data[MAX_RECV_CURL_DATA_SIZE + 1]; /* Data received from curl write data callback */
size_t length; /* Total number of bytes written to data buffer (doesn't include null) */
};
/* Sets proxy info for given CURL handler.
*
* Returns 0 on success or if no proxy is set by the client.
* Returns -1 if proxy info is invalid.
* Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
*/
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type);
/* Callback function for CURL to write received data.
*
* This function will append data from an http request to the data buffer
* until the request is complete or the buffer is full. Buffer will be null terminated.
*
* Returns size of bytes written to the data buffer.
*/
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer);
#endif /* CURL_UTIL_H */

418
src/dns.c
View File

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

View File

@ -29,10 +29,13 @@
#include "execute.h"
#include "chat_commands.h"
#include "global_commands.h"
#include "group_commands.h"
#include "line_info.h"
#include "misc_tools.h"
#include "notify.h"
#define MAX_NUM_ARGS 10 /* Includes command */
struct cmd_func {
const char *name;
void (*func)(WINDOW *w, ToxWindow *, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
@ -41,49 +44,149 @@ struct cmd_func {
static struct cmd_func global_commands[] = {
{ "/accept", cmd_accept },
{ "/add", cmd_add },
{ "/avatar", cmd_avatar },
{ "/clear", cmd_clear },
{ "/connect", cmd_connect },
{ "/decline", cmd_decline },
{ "/exit", cmd_quit },
{ "/groupchat", cmd_groupchat },
{ "/group", cmd_groupchat },
{ "/help", cmd_prompt_help },
{ "/join", cmd_join },
{ "/log", cmd_log },
{ "/myid", cmd_myid },
{ "/myqr", cmd_myqr },
{ "/nick", cmd_nick },
{ "/note", cmd_note },
{ "/nospam", cmd_nospam },
{ "/q", cmd_quit },
{ "/quit", cmd_quit },
{ "/requests", cmd_requests },
{ "/status", cmd_status },
#ifdef _AUDIO
#ifdef AUDIO
{ "/lsdev", cmd_list_devices },
{ "/sdev", cmd_change_device },
#endif /* _AUDIO */
#endif /* AUDIO */
#ifdef VIDEO
{ "/lsvdev", cmd_list_video_devices },
{ "/svdev" , cmd_change_video_device },
#endif /* VIDEO */
{ NULL, NULL },
};
static struct cmd_func chat_commands[] = {
{ "/cancel", cmd_cancelfile },
{ "/gaccept", cmd_groupaccept },
{ "/invite", cmd_groupinvite },
{ "/join", cmd_join_group },
{ "/savefile", cmd_savefile },
{ "/sendfile", cmd_sendfile },
#ifdef _AUDIO
#ifdef AUDIO
{ "/call", cmd_call },
{ "/answer", cmd_answer },
{ "/reject", cmd_reject },
{ "/hangup", cmd_hangup },
{ "/sdev", cmd_ccur_device },
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
#endif /* _AUDIO */
#endif /* AUDIO */
#ifdef VIDEO
{ "/video", cmd_video },
#endif /* VIDEO */
{ NULL, NULL },
};
/* Parses input command and puts args into arg array.
Returns number of arguments on success, -1 on failure. */
static struct cmd_func group_commands[] = {
{ "/ban", cmd_ban },
{ "/chatid", cmd_chatid },
{ "/ignore", cmd_ignore },
{ "/kick", cmd_kick },
{ "/mod", cmd_mod },
{ "/mykey", cmd_mykey },
{ "/passwd", cmd_set_passwd },
{ "/peerlimit", cmd_set_peerlimit },
{ "/privacy", cmd_set_privacy },
{ "/rejoin", cmd_rejoin },
{ "/silence", cmd_silence },
{ "/topic", cmd_set_topic },
{ "/unban", cmd_unban },
{ "/unignore", cmd_unignore },
{ "/unmod", cmd_unmod },
{ "/unsilence", cmd_unsilence },
{ "/whois", cmd_whois },
#ifdef AUDIO
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
#endif /* AUDIO */
{ NULL, NULL },
};
#define NUM_SPECIAL_COMMANDS 15
static const char special_commands[NUM_SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = {
"/ban",
"/gaccept",
"/group",
"/ignore",
"/kick",
"/mod",
"/nick",
"/note",
"/passwd",
"/silence",
"/topic",
"/unignore",
"/unmod",
"/unsilence",
"/whois",
};
/* return true if input command is in the special_commands array. False otherwise.*/
static bool is_special_command(const char *input)
{
int s = char_find(0, input, ' ');
if (s == strlen(input))
return false;
int i;
for (i = 0; i < NUM_SPECIAL_COMMANDS; ++i) {
if (strncmp(input, special_commands[i], s) == 0)
return true;
}
return false;
}
/* Parses commands in the special_commands array which take exactly one argument that may contain spaces.
* Unlike parse_command, this function does not split the input string at spaces.
*
* Returns number of arguments on success
* Returns -1 on failure
*/
static int parse_special_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
{
int len = strlen(input);
int s = char_find(0, input, ' ');
if (s + 1 >= len)
return -1;
memcpy(args[0], input, s);
args[0][s++] = '\0'; /* increment to remove space after /command */
memcpy(args[1], input + s, len - s);
args[1][len - s] = '\0';
return 2;
}
/* Parses input command and puts args (split by spaces) into args array.
*
* Returns number of arguments on success
* Returns -1 on failure.
*/
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
{
if (is_special_command(input))
return parse_special_command(w, self, input, args);
char *cmd = strdup(input);
if (cmd == NULL)
@ -121,11 +224,19 @@ static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*a
strcpy(cmd, tmp); /* tmp will always fit inside cmd */
}
/* Ugly special case concatinates all args after arg1 for multi-word group passwords */
if (num_args > 2 && strcmp(args[0], "/join") == 0)
strcpy(args[2], input + strlen(args[0]) + 1 + strlen(args[1]) + 1);
free(cmd);
return num_args;
}
/* Matches command to respective function. Returns 0 on match, 1 on no match */
/* Matches command to respective function.
*
* Returns 0 on match,
* Returns -1 on no match
*/
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
char (*args)[MAX_STR_SIZE])
{
@ -138,7 +249,7 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct c
}
}
return 1;
return -1;
}
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
@ -164,6 +275,9 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
break;
case GROUPCHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, group_commands, args) == 0)
return;
break;
}

View File

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

View File

@ -1,291 +0,0 @@
/* file_senders.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "toxic.h"
#include "windows.h"
#include "friendlist.h"
#include "file_senders.h"
#include "line_info.h"
#include "misc_tools.h"
#include "notify.h"
FileSender file_senders[MAX_FILES];
uint8_t max_file_senders_index;
uint8_t num_active_file_senders;
extern _Friends Friends;
/* creates initial progress line that will be updated during file transfer.
Assumes progline is of size MAX_STR_SIZE */
void prep_prog_line(char *progline)
{
strcpy(progline, "0.0 B/s [");
int i;
for (i = 0; i < NUM_PROG_MARKS; ++i)
strcat(progline, "-");
strcat(progline, "] 0%");
}
/* prints a progress bar for file transfers.
if friendnum is -1 we're sending the file, otherwise we're receiving. */
void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_done)
{
double bps;
uint32_t line_id;
if (friendnum < 0) {
bps = file_senders[idx].bps;
line_id = file_senders[idx].line_id;
} else {
bps = Friends.list[friendnum].file_receiver[idx].bps;
line_id = Friends.list[friendnum].file_receiver[idx].line_id;
}
char msg[MAX_STR_SIZE];
bytes_convert_str(msg, sizeof(msg), bps);
strcat(msg, "/s [");
int n = pct_done / (100 / NUM_PROG_MARKS);
int i, j;
for (i = 0; i < n; ++i)
strcat(msg, "#");
for (j = i; j < NUM_PROG_MARKS; ++j)
strcat(msg, "-");
strcat(msg, "] ");
char pctstr[16];
const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%";
snprintf(pctstr, sizeof(pctstr), frmt, pct_done);
strcat(msg, pctstr);
line_info_set(self, line_id, msg);
}
/* refreshes active file receiver status bars */
static void refresh_recv_prog(Tox *m)
{
int i;
uint64_t curtime = get_unix_time();
for (i = 2; i < MAX_WINDOWS_NUM; ++i) {
ToxWindow *toxwin = get_window_ptr(i);
if (toxwin == NULL || !toxwin->is_chat)
continue;
int fnum = toxwin->num;
int j;
for (j = 0; j < MAX_FILES; ++j) {
if (!Friends.list[fnum].file_receiver[j].active)
continue;
int filenum = Friends.list[fnum].file_receiver[j].filenum;
double remain = (double) tox_file_data_remaining(m, fnum, filenum, 1);
/* must be called once per second */
if (timed_out(Friends.list[fnum].file_receiver[filenum].last_progress, curtime, 1)) {
Friends.list[fnum].file_receiver[filenum].last_progress = curtime;
uint64_t size = Friends.list[fnum].file_receiver[filenum].size;
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
print_progress_bar(toxwin, filenum, fnum, pct_done);
Friends.list[fnum].file_receiver[filenum].bps = 0;
}
}
}
}
/* refreshes active file sender status bars */
static void refresh_sender_prog(Tox *m)
{
int i;
uint64_t curtime = get_unix_time();
for (i = 0; i < max_file_senders_index; ++i) {
if (!file_senders[i].active || file_senders[i].finished)
continue;
int filenum = file_senders[i].filenum;
int32_t friendnum = file_senders[i].friendnum;
double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0);
/* must be called once per second */
if (timed_out(file_senders[i].last_progress, curtime, 1)) {
file_senders[i].last_progress = curtime;
double pct_done = remain > 0 ? (1 - (remain / file_senders[i].size)) * 100 : 100;
print_progress_bar(file_senders[i].toxwin, i, -1, pct_done);
file_senders[i].bps = 0;
}
}
}
static void set_max_file_senders_index(void)
{
int j;
for (j = max_file_senders_index; j > 0; --j) {
if (file_senders[j - 1].active)
break;
}
max_file_senders_index = j;
}
/* called whenever a file sender is opened or closed */
void reset_file_sender_queue(void)
{
int i;
int pos = 0;
for (i = 0; i < max_file_senders_index; ++i) {
if (file_senders[i].active)
file_senders[i].queue_pos = pos++;
}
}
/* set CTRL to -1 if we don't want to send a control signal.
set msg to NULL if we don't want to display a message */
void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, int32_t friendnum)
{
if (msg != NULL)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
if (CTRL > 0)
tox_file_send_control(m, friendnum, 0, filenum, CTRL, 0, 0);
fclose(file_senders[i].file);
memset(&file_senders[i], 0, sizeof(FileSender));
set_max_file_senders_index();
reset_file_sender_queue();
--num_active_file_senders;
}
void close_all_file_senders(Tox *m)
{
int i;
for (i = 0; i < max_file_senders_index; ++i) {
if (file_senders[i].active) {
fclose(file_senders[i].file);
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenum,
TOX_FILECONTROL_KILL, 0, 0);
memset(&file_senders[i], 0, sizeof(FileSender));
}
set_max_file_senders_index();
}
}
static void send_file_data(ToxWindow *self, Tox *m, int i, int32_t friendnum, int filenum, const char *filename)
{
FILE *fp = file_senders[i].file;
while (true) {
if (tox_file_send_data(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece,
file_senders[i].piecelen) == -1)
return;
file_senders[i].timestamp = get_unix_time();
file_senders[i].bps += file_senders[i].piecelen;
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
tox_file_data_size(m, friendnum), fp);
/* note: file sender is closed in chat_onFileControl callback after receiving reply */
if (file_senders[i].piecelen == 0) {
if (feof(fp) != 0) { /* make sure we're really at eof */
print_progress_bar(self, i, -1, 100.0);
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
file_senders[i].finished = true;
} else {
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Read error.", file_senders[i].filename);
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL);
if (self->active_box != -1)
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
else
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg);
}
return;
}
}
}
void do_file_senders(Tox *m)
{
int i;
for (i = 0; i < max_file_senders_index; ++i) {
if (!file_senders[i].active)
continue;
if (file_senders[i].queue_pos > 0) {
--file_senders[i].queue_pos;
continue;
}
ToxWindow *self = file_senders[i].toxwin;
char *filename = file_senders[i].filename;
int filenum = file_senders[i].filenum;
int32_t friendnum = file_senders[i].friendnum;
/* kill file transfer if chatwindow is closed */
if (self->chatwin == NULL) {
close_file_sender(self, m, i, NULL, TOX_FILECONTROL_KILL, filenum, friendnum);
continue;
}
/* If file transfer has timed out kill transfer and send kill control */
if (timed_out(file_senders[i].timestamp, get_unix_time(), TIMEOUT_FILESENDER)) {
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", filename);
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL);
if (self->active_box != -1)
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
else
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg);
continue;
}
if (!file_senders[i].noconnection && !file_senders[i].finished)
send_file_data(self, m, i, friendnum, filenum, filename);
file_senders[i].queue_pos = num_active_file_senders - 1;
}
refresh_sender_prog(m);
refresh_recv_prog(m);
}

View File

@ -1,75 +0,0 @@
/* file_senders.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _filesenders_h
#define _filesenders_h
#include "toxic.h"
#include "windows.h"
#define KiB 1024
#define MiB 1048576 /* 1024 ^ 2 */
#define GiB 1073741824 /* 1024 ^ 3 */
#define FILE_PIECE_SIZE 2048 /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */
#define MAX_FILES 32
#define TIMEOUT_FILESENDER 120
#define NUM_PROG_MARKS 50 /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
typedef struct {
FILE *file;
ToxWindow *toxwin;
int32_t friendnum;
bool active;
bool noconnection;
bool finished;
int filenum;
char nextpiece[FILE_PIECE_SIZE];
uint16_t piecelen;
char filename[MAX_STR_SIZE];
uint64_t timestamp;
uint64_t last_progress;
double bps;
uint64_t size;
uint32_t line_id;
uint8_t queue_pos;
} FileSender;
/* creates initial progress line that will be updated during file transfer.
Assumes progline is of size MAX_STR_SIZE */
void prep_prog_line(char *progline);
/* prints a progress bar for file transfers.
if friendnum is -1 we're sending the file, otherwise we're receiving. */
void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_remain);
/* set CTRL to -1 if we don't want to send a control signal.
set msg to NULL if we don't want to display a message */
void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, int32_t friendnum);
/* called whenever a file sender is opened or closed */
void reset_file_sender_queue(void);
void close_all_file_senders(Tox *m);
void do_file_senders(Tox *m);
#endif /* #define _filesenders_h */

277
src/file_transfers.c Normal file
View File

@ -0,0 +1,277 @@
/* file_transfers.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "toxic.h"
#include "windows.h"
#include "friendlist.h"
#include "file_transfers.h"
#include "line_info.h"
#include "misc_tools.h"
#include "notify.h"
extern FriendsList Friends;
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
#define NUM_PROG_MARKS 50
/* creates initial progress line that will be updated during file transfer.
Assumes progline has room for at least MAX_STR_SIZE bytes */
void init_progress_bar(char *progline)
{
strcpy(progline, "0% [");
int i;
for (i = 0; i < NUM_PROG_MARKS; ++i)
strcat(progline, "-");
strcat(progline, "] 0.0 B/s");
}
/* prints a progress bar for file transfers. */
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
{
if (bps < 0 || pct_done < 0 || pct_done > 100)
return;
char 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(prog_line, "=");
if (pct_done < 100)
strcpy(prog_line + n, ">");
for (j = i; j < NUM_PROG_MARKS - 1; ++j)
strcat(prog_line, "-");
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, full_line);
}
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
{
if (ft->state == FILE_TRANSFER_INACTIVE)
return;
/* Timeout must be set to 1 second to show correct bytes per second */
if (!timed_out(ft->last_line_progress, 1))
return;
double remain = ft->file_size - ft->position;
double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
print_progress_bar(self, ft->bps, pct_done, ft->line_id);
ft->bps = 0;
ft->last_line_progress = get_unix_time();
}
/* refreshes active file transfer status bars. */
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]);
refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]);
}
}
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
* Returns NULL if filenum is invalid.
*/
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum)
return ft_send;
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum)
return ft_recv;
}
return NULL;
}
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
* Returns NULL on failure.
*/
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction)
{
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
return NULL;
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
&Friends.list[friendnum].file_sender[i] :
&Friends.list[friendnum].file_receiver[i];
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
return ft;
}
return NULL;
}
/* Returns a pointer to an unused file sender.
* Returns NULL if all file senders are in use.
*/
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
if (ft->state == FILE_TRANSFER_INACTIVE) {
memset(ft, 0, sizeof(struct FileTransfer));
ft->window = window;
ft->index = i;
ft->friendnum = friendnum;
ft->filenum = filenum;
ft->file_type = type;
ft->last_keep_alive = get_unix_time();
ft->state = FILE_TRANSFER_PENDING;
ft->direction = FILE_TRANSFER_SEND;
return ft;
}
}
return NULL;
}
/* Returns a pointer to an unused file receiver.
* Returns NULL if all file receivers are in use.
*/
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i];
if (ft->state == FILE_TRANSFER_INACTIVE) {
memset(ft, 0, sizeof(struct FileTransfer));
ft->window = window;
ft->index = i;
ft->friendnum = friendnum;
ft->filenum = filenum;
ft->file_type = type;
ft->last_keep_alive = get_unix_time();
ft->state = FILE_TRANSFER_PENDING;
ft->direction = FILE_TRANSFER_RECV;
return ft;
}
}
return NULL;
}
/* Initializes an unused file transfer and returns its pointer.
* Returns NULL on failure.
*/
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
FILE_TRANSFER_DIRECTION direction, uint8_t type)
{
if (direction == FILE_TRANSFER_RECV)
return new_file_receiver(window, friendnum, filenum, type);
if (direction == FILE_TRANSFER_SEND)
return new_file_sender(window, friendnum, filenum, type);
return NULL;
}
/* Closes file transfer ft.
*
* Set CTRL to -1 if we don't want to send a control signal.
* Set message or self to NULL if we don't want to display a message.
*/
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
Notification sound_type)
{
if (!ft)
return;
if (ft->state == FILE_TRANSFER_INACTIVE)
return;
if (ft->file)
fclose(ft->file);
if (CTRL >= 0)
tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
if (message && self) {
if (self->active_box != -1 && sound_type != silent)
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
else
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
}
memset(ft, 0, sizeof(struct FileTransfer));
}
/* Kills all active file transfers for friendnum */
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
}
}
void kill_all_file_transfers(Tox *m)
{
size_t i;
for (i = 0; i < Friends.max_idx; ++i)
kill_all_file_transfers_friend(m, Friends.list[i].num);
}

111
src/file_transfers.h Normal file
View File

@ -0,0 +1,111 @@
/* file_transfers.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef FILE_TRANSFERS_H
#define FILE_TRANSFERS_H
#include <limits.h>
#include "toxic.h"
#include "windows.h"
#include "notify.h"
#define KiB 1024
#define MiB 1048576 /* 1024^2 */
#define GiB 1073741824 /* 1024^3 */
#define MAX_FILES 32
typedef enum FILE_TRANSFER_STATE {
FILE_TRANSFER_INACTIVE,
FILE_TRANSFER_PAUSED,
FILE_TRANSFER_PENDING,
FILE_TRANSFER_STARTED,
} FILE_TRANSFER_STATE;
typedef enum FILE_TRANSFER_DIRECTION {
FILE_TRANSFER_SEND,
FILE_TRANSFER_RECV
} FILE_TRANSFER_DIRECTION;
struct FileTransfer {
ToxWindow *window;
FILE *file;
FILE_TRANSFER_STATE state;
FILE_TRANSFER_DIRECTION direction;
uint8_t file_type;
char file_name[TOX_MAX_FILENAME_LENGTH + 1];
char file_path[PATH_MAX + 1]; /* Not used by senders */
double bps;
uint32_t filenum;
uint32_t friendnum;
size_t index;
uint64_t file_size;
uint64_t position;
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];
};
/* creates initial progress line that will be updated during file transfer.
progline must be at lesat MAX_STR_SIZE bytes */
void init_progress_bar(char *progline);
/* prints a progress bar for file transfers */
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
/* refreshes active file transfer status bars. */
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
* Returns NULL if filenum is invalid.
*/
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum);
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
* Returns NULL on failure.
*/
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction);
/* Initializes an unused file transfer and returns its pointer.
* Returns NULL on failure.
*/
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
FILE_TRANSFER_DIRECTION direction, uint8_t type);
/* Closes file transfer ft.
*
* Set CTRL to -1 if we don't want to send a control signal.
* Set message or self to NULL if we don't want to display a message.
*/
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
Notification sound_type);
/* Kills all active file transfers for friendnum */
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
void kill_all_file_transfers(Tox *m);
#endif /* #define FILE_TRANSFERS_H */

File diff suppressed because it is too large Load Diff

View File

@ -20,27 +20,14 @@
*
*/
#ifndef FRIENDLIST_H_53I41IM
#define FRIENDLIST_H_53I41IM
#ifndef FRIENDLIST_H
#define FRIENDLIST_H
#include <time.h>
#include "toxic.h"
#include "windows.h"
#include "file_senders.h"
struct FileReceiver {
char filename[MAX_STR_SIZE];
int filenum;
FILE *file;
bool pending;
bool active;
uint64_t size;
uint64_t bytes_recv;
double bps;
uint64_t last_progress; /* unix-time when we last updated progress */
uint32_t line_id;
};
#include "file_transfers.h"
struct LastOnline {
uint64_t last_on;
@ -48,51 +35,58 @@ struct LastOnline {
char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */
};
struct GroupInvite {
uint8_t *data;
uint16_t length;
};
typedef struct {
char name[TOXIC_MAX_NAME_LENGTH];
char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength;
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
uint16_t statusmsg_len;
char groupchat_key[TOX_CLIENT_ID_SIZE];
bool groupchat_pending;
char pub_key[TOX_CLIENT_ID_SIZE];
int32_t num;
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
size_t statusmsg_len;
char pub_key[TOX_PUBLIC_KEY_SIZE];
uint32_t num;
int chatwin;
bool active;
bool online;
uint8_t is_typing;
TOX_CONNECTION connection_status;
bool is_typing;
bool logging_on; /* saves preference for friend irrespective of global settings */
uint8_t status;
struct LastOnline last_online;
struct FileReceiver file_receiver[MAX_FILES];
uint8_t active_file_receivers;
struct GroupInvite group_invite;
struct FileTransfer file_receiver[MAX_FILES];
struct FileTransfer file_sender[MAX_FILES];
} ToxicFriend;
typedef struct {
char name[TOXIC_MAX_NAME_LENGTH];
char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength;
char pub_key[TOX_CLIENT_ID_SIZE];
int32_t num;
char pub_key[TOX_PUBLIC_KEY_SIZE];
uint32_t num;
bool active;
uint64_t last_on;
} BlockedFriend;
typedef struct {
int num_selected;
int max_idx; /* 1 + the index of the last friend in list */
int num_friends;
int *index;
size_t num_friends;
size_t num_online;
size_t max_idx; /* 1 + the index of the last friend in list */
uint32_t *index;
ToxicFriend *list;
} _Friends;
} FriendsList;
ToxWindow new_friendlist(void);
void disable_chatwin(int32_t f_num);
void disable_chatwin(uint32_t f_num);
int get_friendnum(uint8_t *name);
int load_blocklist(char *data);
void kill_friendlist(void);
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort);
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
/* sorts friendlist_index first by connection status then alphabetically */
void sort_friendlist_index(void);
#endif /* end of include guard: FRIENDLIST_H_53I41IM */
#endif /* end of include guard: FRIENDLIST_H */

View File

@ -22,7 +22,6 @@
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include "toxic.h"
#include "windows.h"
@ -30,15 +29,19 @@
#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 _Friends Friends;
extern _FriendRequests FriendRequests;
extern FriendsList Friends;
extern FriendRequests FrndRequests;
/* command functions */
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -48,80 +51,90 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
return;
}
int req = atoi(argv[1]);
long int req = strtol(argv[1], NULL, 10);
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
if (!FriendRequests.request[req].active) {
if (!FrndRequests.request[req].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
const char *msg;
int32_t friendnum = tox_add_friend_norequest(m, FriendRequests.request[req].key);
TOX_ERR_FRIEND_ADD err;
uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err);
if (friendnum == -1)
msg = "Failed to add friend.";
else {
msg = "Friend request accepted.";
if (err != TOX_ERR_FRIEND_ADD_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err);
return;
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
on_friendadded(m, friendnum, true);
}
memset(&FriendRequests.request[req], 0, sizeof(struct _friend_request));
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
int i;
for (i = FriendRequests.max_idx; i > 0; --i) {
if (FriendRequests.request[i - 1].active)
for (i = FrndRequests.max_idx; i > 0; --i) {
if (FrndRequests.request[i - 1].active)
break;
}
FriendRequests.max_idx = i;
--FriendRequests.num_requests;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
FrndRequests.max_idx = i;
--FrndRequests.num_requests;
}
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg)
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg)
{
const char *errmsg;
int32_t f_num = tox_add_friend(m, (uint8_t *) id_bin, (uint8_t *) msg, (uint16_t) strlen(msg));
switch (f_num) {
case TOX_FAERR_TOOLONG:
TOX_ERR_FRIEND_ADD err;
uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err);
switch (err) {
case TOX_ERR_FRIEND_ADD_TOO_LONG:
errmsg = "Message is too long.";
break;
case TOX_FAERR_NOMESSAGE:
case TOX_ERR_FRIEND_ADD_NO_MESSAGE:
errmsg = "Please add a message to your request.";
break;
case TOX_FAERR_OWNKEY:
case TOX_ERR_FRIEND_ADD_OWN_KEY:
errmsg = "That appears to be your own ID.";
break;
case TOX_FAERR_ALREADYSENT:
case TOX_ERR_FRIEND_ADD_ALREADY_SENT:
errmsg = "Friend request has already been sent.";
break;
case TOX_FAERR_UNKNOWN:
errmsg = "Undefined error when adding friend.";
break;
case TOX_FAERR_BADCHECKSUM:
case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM:
errmsg = "Bad checksum in address.";
break;
case TOX_FAERR_SETNEWNOSPAM:
case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM:
errmsg = "Nospam was different.";
break;
default:
case TOX_ERR_FRIEND_ADD_MALLOC:
errmsg = "Core memory allocation failed.";
break;
case TOX_ERR_FRIEND_ADD_OK:
errmsg = "Friend request sent.";
on_friendadded(m, f_num, true);
break;
case TOX_ERR_FRIEND_ADD_NULL:
/* fallthrough */
default:
errmsg = "Faile to add friend: Unknown error.";
break;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
@ -151,21 +164,23 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
snprintf(msg, sizeof(msg), "%s", tmp);
} else {
char selfname[TOX_MAX_NAME_LENGTH];
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfname);
tox_self_get_name(m, (uint8_t *) selfname);
size_t n_len = tox_self_get_name_size(m);
selfname[n_len] = '\0';
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
}
char id_bin[TOX_FRIEND_ADDRESS_SIZE] = {0};
char id_bin[TOX_ADDRESS_SIZE] = {0};
uint16_t id_len = (uint16_t) strlen(id);
/* try to add tox ID */
if (id_len == 2 * TOX_FRIEND_ADDRESS_SIZE) {
if (id_len == 2 * TOX_ADDRESS_SIZE) {
size_t i;
char xx[3];
uint32_t x;
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
xx[0] = id[2 * i];
xx[1] = id[2 * i + 1];
xx[2] = '\0';
@ -179,17 +194,52 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
}
cmd_add_helper(self, m, id_bin, msg);
} else { /* assume id is a username@domain address and do DNS lookup */
dns3_lookup(self, m, id_bin, id, msg);
} else { /* assume id is a username@domain address and do http name server lookup */
name_lookup(self, m, id_bin, id, msg);
}
}
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 2 || strlen(argv[1]) < 3) {
avatar_unset(m);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar is not set.");
return;
}
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes.");
return;
}
/* remove opening and closing quotes */
char path[MAX_STR_SIZE];
snprintf(path, sizeof(path), "%s", &argv[1][1]);
int len = strlen(path) - 1;
if (len <= 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
return;
}
path[len] = '\0';
char filename[MAX_STR_SIZE];
get_file_name(filename, sizeof(filename), path);
if (avatar_set(m, path, len) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.",
MAX_AVATAR_FILE_SIZE);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
}
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
line_info_clear(self->chatwin->hst);
wclear(window);
endwin();
refresh();
force_refresh(window);
}
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -200,17 +250,43 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
}
const char *ip = argv[1];
const char *port = argv[2];
const char *key = argv[3];
const char *port_str = argv[2];
const char *ascii_key = argv[3];
if (atoi(port) == 0) {
long int port = strtol(port_str, NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
return;
}
char *binary_string = hex_string_to_bin(key);
tox_bootstrap_from_address(m, ip, atoi(port), (uint8_t *) binary_string);
free(binary_string);
char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
return;
}
TOX_ERR_BOOTSTRAP err;
tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err);
tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err);
switch (err) {
case TOX_ERR_BOOTSTRAP_BAD_HOST:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP.");
break;
case TOX_ERR_BOOTSTRAP_BAD_PORT:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port.");
break;
case TOX_ERR_BOOTSTRAP_NULL:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
break;
default:
break;
}
}
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -220,29 +296,29 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
return;
}
int req = atoi(argv[1]);
long int req = strtol(argv[1], NULL, 10);
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
if (!FriendRequests.request[req].active) {
if (!FrndRequests.request[req].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
memset(&FriendRequests.request[req], 0, sizeof(struct _friend_request));
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
int i;
for (i = FriendRequests.max_idx; i > 0; --i) {
if (FriendRequests.request[i - 1].active)
for (i = FrndRequests.max_idx; i > 0; --i) {
if (FrndRequests.request[i - 1].active)
break;
}
FriendRequests.max_idx = i;
--FriendRequests.num_requests;
FrndRequests.max_idx = i;
--FrndRequests.num_requests;
}
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -252,20 +328,134 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
return;
}
int groupnum = tox_add_groupchat(m);
if (groupnum == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name required");
return;
}
if (init_groupchat_win(prompt, m, groupnum) == -1) {
const char *tmp_name = argv[1];
int len = strlen(tmp_name);
if (len == 0 || len > TOX_GROUP_MAX_GROUP_NAME_LENGTH) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group name.");
return;
}
char name[TOX_GROUP_MAX_GROUP_NAME_LENGTH];
if (argv[1][0] == '\"') { /* remove opening and closing quotes */
snprintf(name, sizeof(name), "%s", &argv[1][1]);
len -= 2;
name[len] = '\0';
} else {
snprintf(name, sizeof(name), "%s", argv[1]);
}
TOX_ERR_GROUP_NEW err;
uint32_t groupnum = tox_group_new(m, TOX_GROUP_PRIVACY_STATE_PUBLIC, (uint8_t *) name, len, &err);
if (err != TOX_ERR_GROUP_NEW_OK) {
switch (err) {
case TOX_ERR_GROUP_NEW_TOO_LONG: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name length cannot exceed %d.",
TOX_GROUP_MAX_GROUP_NAME_LENGTH);
break;
}
case TOX_ERR_GROUP_NEW_EMPTY: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name cannot be empty.");
break;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d).", err);
break;
}
}
return;
}
int init = init_groupchat_win(m, groupnum, name, len);
if (init == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
tox_group_leave(m, groupnum, NULL, 0, NULL);
} else if (init == -2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"You have been kicked from a group. Close the window and try again.");
tox_group_leave(m, groupnum, NULL, 0, NULL);
}
}
void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Chat ID is required.");
return;
}
const char *chat_id = argv[1];
if (strlen(chat_id) != TOX_GROUP_CHAT_ID_SIZE * 2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID");
return;
}
char id_bin[TOX_GROUP_CHAT_ID_SIZE] = {0};
size_t i;
char xch[3];
uint32_t x;
for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
xch[0] = chat_id[2 * i];
xch[1] = chat_id[2 * i + 1];
xch[2] = '\0';
if (sscanf(xch, "%02x", &x) != 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID.");
return;
}
id_bin[i] = x;
}
const char *passwd = NULL;
uint16_t passwd_len = 0;
if (argc > 1) {
passwd = argv[2];
passwd_len = strlen(passwd);
}
TOX_ERR_GROUP_JOIN err;
uint32_t groupnum = tox_group_join(m, (uint8_t *) id_bin, (uint8_t *) passwd, passwd_len, &err);
if (err != TOX_ERR_GROUP_JOIN_OK) {
if (err == TOX_ERR_GROUP_JOIN_TOO_LONG)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length cannot exceed %d.", TOX_GROUP_MAX_PASSWORD_SIZE);
else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group (error %d).", err);
return;
}
int init = init_groupchat_win(m, groupnum, NULL, 0);
if (init == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_group_leave(m, groupnum, NULL, 0, NULL);
} else if (init == -2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"You have been kicked from a group. Close the window and try again.");
tox_group_leave(m, groupnum, NULL, 0, NULL);
}
}
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -275,9 +465,9 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
if (argc == 0) {
if (log->log_on)
msg = "Logging for this window is ON. Type \"/log off\" to disable.";
msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)";
else
msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
msg = "Logging for this window is OFF; type \"/log on\" to enable.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
@ -286,19 +476,21 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
const char *swch = argv[1];
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
char myid[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) myid);
int log_ret = -1;
if (self->is_chat) {
Friends.list[self->num].logging_on = true;
log_enable(self->name, Friends.list[self->num].pub_key, log);
log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
} else if (self->is_prompt) {
char myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, (uint8_t *) myid);
log_enable(self->name, myid, log);
log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT);
} else if (self->is_groupchat) {
log_enable(self->name, NULL, log);
log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP);
}
msg = "Logging enabled";
msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
@ -307,7 +499,7 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
log_disable(log);
msg = "Logging disabled";
msg = "Logging disabled.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
}
@ -318,19 +510,74 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0};
char address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, (uint8_t *) address);
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
char bin_id[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) bin_id);
size_t i;
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
char xx[3];
snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff);
strcat(id, xx);
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID.");
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
}
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
char bin_id[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) bin_id);
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
return;
}
char nick[TOX_MAX_NAME_LENGTH];
tox_self_get_name(m, (uint8_t *) nick);
size_t nick_len = tox_self_get_name_size(m);
nick[nick_len] = '\0';
size_t data_file_len = strlen(DATA_FILE);
char dir[data_file_len + 1];
size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir);
#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 */
}
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -341,7 +588,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
}
char nick[MAX_STR_SIZE];
int len = 0;
size_t len = 0;
if (argv[1][0] == '\"') { /* remove opening and closing quotes */
snprintf(nick, sizeof(nick), "%s", &argv[1][1]);
@ -360,8 +607,9 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
nick[len] = '\0';
tox_set_name(m, (uint8_t *) nick, (uint16_t) len);
tox_self_set_name(m, (uint8_t *) nick, len, NULL);
prompt_update_nick(prompt, nick);
set_nick_all_groups(m, nick, len);
store_data(m, DATA_FILE);
}
@ -373,18 +621,32 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
return;
}
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
return;
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])
@ -399,7 +661,7 @@ void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (FriendRequests.num_requests == 0) {
if (FrndRequests.num_requests == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
return;
}
@ -407,64 +669,64 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
int i, j;
int count = 0;
for (i = 0; i < FriendRequests.max_idx; ++i) {
if (!FriendRequests.request[i].active)
for (i = 0; i < FrndRequests.max_idx; ++i) {
if (!FrndRequests.request[i].active)
continue;
char id[TOX_CLIENT_ID_SIZE * 2 + 1] = {0};
char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0};
for (j = 0; j < TOX_CLIENT_ID_SIZE; ++j) {
for (j = 0; j < TOX_PUBLIC_KEY_SIZE; ++j) {
char d[3];
snprintf(d, sizeof(d), "%02X", FriendRequests.request[i].key[j] & 0xff);
snprintf(d, sizeof(d), "%02X", FrndRequests.request[i].key[j] & 0xff);
strcat(id, d);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FriendRequests.request[i].msg);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg);
if (++count < FriendRequests.num_requests)
if (++count < FrndRequests.num_requests)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
}
}
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
{
bool have_note = false;
const char *errmsg;
lock_status ();
if (argc >= 2) {
have_note = true;
} else if (argc < 1) {
errmsg = "Require a status. Statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
return;
goto finish;
}
char status[MAX_STR_SIZE];
snprintf(status, sizeof(status), "%s", argv[1]);
str_to_lower(status);
const char *status_str = argv[1];
TOX_USER_STATUS status;
TOX_USERSTATUS status_kind;
if (!strcmp(status, "online"))
status_kind = TOX_USERSTATUS_NONE;
else if (!strcmp(status, "away"))
status_kind = TOX_USERSTATUS_AWAY;
else if (!strcmp(status, "busy"))
status_kind = TOX_USERSTATUS_BUSY;
if (!strcasecmp(status_str, "online"))
status = TOX_USER_STATUS_NONE;
else if (!strcasecmp(status_str, "away"))
status = TOX_USER_STATUS_AWAY;
else if (!strcasecmp(status_str, "busy"))
status = TOX_USER_STATUS_BUSY;
else {
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
return;
goto finish;
}
tox_set_user_status(m, status_kind);
prompt_update_status(prompt, status_kind);
tox_self_set_status(m, status);
prompt_update_status(prompt, status);
set_status_all_groups(m, status);
if (have_note) {
if (argv[2][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
return;
goto finish;
}
/* remove opening and closing quotes */
@ -475,4 +737,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
prompt_update_statusmessage(prompt, m, msg);
}
finish:
unlock_status ();
}

View File

@ -20,32 +20,41 @@
*
*/
#ifndef _global_commands_h
#define _global_commands_h
#ifndef GLOBAL_COMMANDS_H
#define GLOBAL_COMMANDS_H
#include "windows.h"
#include "toxic.h"
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg);
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg);
#ifdef _AUDIO
#ifdef AUDIO
void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* _AUDIO */
#endif /* AUDIO */
#endif /* #define _global_commands_h */
#ifdef VIDEO
void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* VIDEO */
#endif /* #define GLOBAL_COMMANDS_H */

727
src/group_commands.c Normal file
View File

@ -0,0 +1,727 @@
/* group_commands.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <stdlib.h>
#include "toxic.h"
#include "windows.h"
#include "line_info.h"
#include "misc_tools.h"
#include "log.h"
#include "groupchat.h"
extern GroupChat groupchats[MAX_GROUPCHAT_NUM];
void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char chatid[TOX_GROUP_CHAT_ID_SIZE * 2 + 1] = {0};
char chat_public_key[TOX_GROUP_CHAT_ID_SIZE];
TOX_ERR_GROUP_STATE_QUERIES err;
if (!tox_group_get_chat_id(m, self->num, (uint8_t *) chat_public_key, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve the Chat ID (error %d).", err);
return;
}
size_t i;
for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
char xx[3];
snprintf(xx, sizeof(xx), "%02X", chat_public_key[i] & 0xff);
strcat(chatid, xx);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", chatid);
}
void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_TOGGLE_IGNORE err;
if (!tox_group_toggle_ignore(m, self->num, peer_id, true, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to ignore %s (error %d).", nick, err);
return;
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, BLUE, "-!- Ignoring %s", nick);
}
static void cmd_kickban_helper(ToxWindow *self, Tox *m, const char *nick, bool set_ban)
{
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
const char *type_str = set_ban ? "ban" : "kick";
TOX_ERR_GROUP_MOD_REMOVE_PEER err;
tox_group_mod_remove_peer(m, self->num, peer_id, set_ban, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_REMOVE_PEER_OK: {
return;
}
case TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to %s %s.", type_str, nick);
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to %s %s from the group (error %d).", type_str, nick,
err);
return;
}
}
}
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
cmd_kickban_helper(self, m, argv[1], false);
}
void cmd_ban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
TOX_ERR_GROUP_BAN_QUERY err;
if (argc < 1) {
size_t num_banned = tox_group_ban_get_list_size(m, self->num, &err);
if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list size (error %d).", err);
return;
}
if (num_banned == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban list is empty.");
return;
}
uint32_t ban_list[num_banned];
if (!tox_group_ban_get_list(m, self->num, ban_list, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list (error %d).", err);
return;
}
uint16_t i;
for (i = 0; i < num_banned; ++i) {
uint32_t id = ban_list[i];
size_t len = tox_group_ban_get_name_size(m, self->num, id, &err);
if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve name length for ban %d (error %d).", id, err);
continue;
}
char tmp_nick[len];
if (!tox_group_ban_get_name(m, self->num, id, (uint8_t *) tmp_nick, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve name for ban %d (error %d).", id, err);
continue;
}
char nick[len + 1];
copy_tox_str(nick, sizeof(nick), tmp_nick, len);
uint64_t time_set = tox_group_ban_get_time_set(m, self->num, id, &err);
if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve timestamp for ban %d (error %d).", id, err);
continue;
}
struct tm tm_set = *localtime((const time_t *) &time_set);
char time_str[64];
strftime(time_str, sizeof(time_str), "%e %b %Y %H:%M:%S%p", &tm_set);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "ID %d : %s [Set:%s]", id, nick, time_str);
}
return;
}
cmd_kickban_helper(self, m, argv[1], true);
}
void cmd_unban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be specified.");
return;
}
int ban_id = atoi(argv[1]);
if (ban_id == 0 && strcmp(argv[1], "0")) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be a non-negative interger.");
return;
}
TOX_ERR_GROUP_MOD_REMOVE_BAN err;
tox_group_mod_remove_ban(m, self->num, ban_id, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_REMOVE_BAN_OK: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban list entry with id %d has been removed.", ban_id);
return;
}
case TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unban peers.");
return;
}
case TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID does not exist.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to remove ban list entry (error %d).", err);
return;
}
}
}
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_MODERATOR, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to promote moderators.");
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "This peer is already a moderator.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to promote peer to moderator (error %d).", err);
return;
}
}
}
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
if (tox_group_peer_get_role(m, self->num, peer_id, NULL) != TOX_GROUP_ROLE_MODERATOR) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s is not a moderator", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_USER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Nice try.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to revoke moderator powers from %s (error %d).", nick,
err);
return;
}
}
}
void cmd_mykey(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
char pk[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
TOX_ERR_GROUP_SELF_QUERY err;
if (!tox_group_self_get_public_key(m, self->num, (uint8_t *) pk, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch your public key (error %d)", err);
return;
}
size_t i;
for (i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) {
char d[3];
snprintf(d, sizeof(d), "%02X", pk[i] & 0xff);
strcat(pk_string, d);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", pk_string);
}
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *passwd = NULL;
size_t len = 0;
if (argc > 0) {
passwd = argv[1];
len = strlen(passwd);
}
TOX_ERR_GROUP_FOUNDER_SET_PASSWORD err;
tox_group_founder_set_password(m, self->num, (uint8_t *) passwd, len, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK: {
if (len > 0)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password has been set to %s.", passwd);
else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password has been unset.");
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length must not exceed %d.",
TOX_GROUP_MAX_PASSWORD_SIZE);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the password.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set password (error %d).", err);
return;
}
}
}
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
int maxpeers = 0;
if (argc < 1) {
TOX_ERR_GROUP_STATE_QUERIES err;
uint32_t maxpeers = tox_group_get_peer_limit(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve peer limit (error %d).", err);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit is set to %d", maxpeers);
return;
}
maxpeers = atoi(argv[1]);
if (maxpeers <= 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit must be a value greater than 0.");
return;
}
TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT err;
tox_group_founder_set_peer_limit(m, self->num, maxpeers, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit has been set to %d.", maxpeers);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the peer limit.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the peer limit (error %d).", err);
return;
}
}
}
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *pstate_str = NULL;
TOX_GROUP_PRIVACY_STATE privacy_state;
if (argc < 1) {
TOX_ERR_GROUP_STATE_QUERIES err;
privacy_state = tox_group_get_privacy_state(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve privacy state (error %d).", err);
return;
}
pstate_str = privacy_state == TOX_GROUP_PRIVACY_STATE_PRIVATE ? "private" : "public";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state is set to %s.", pstate_str);
return;
}
pstate_str = argv[1];
if (strcasecmp(pstate_str, "private") != 0 && strcasecmp(pstate_str, "public") != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state must be \"private\" or \"public\".");
return;
}
privacy_state = strcasecmp(pstate_str,
"private") == 0 ? TOX_GROUP_PRIVACY_STATE_PRIVATE : TOX_GROUP_PRIVACY_STATE_PUBLIC;
TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE err;
tox_group_founder_set_privacy_state(m, self->num, privacy_state, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state has been set to %s.", pstate_str);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the privacy state.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error setting privacy state (error %d).", err);
return;
}
}
}
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_OBSERVER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to silence %s.", nick);
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to silence %s (error %d).", nick, err);
return;
}
}
}
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
if (tox_group_peer_get_role(m, self->num, peer_id, NULL) != TOX_GROUP_ROLE_OBSERVER) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s is not silenced.", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_USER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unsilence %s.", nick);
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unsilence %s (error %d).", nick, err);
return;
}
}
}
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
TOX_ERR_GROUP_RECONNECT err;
if (!tox_group_reconnect(m, self->num, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to rejoin group (error %d).", err);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Reconnecting to group...");
}
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
TOX_ERR_GROUP_STATE_QUERIES err;
size_t tlen = tox_group_get_topic_size(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic length (error %d).", err);
return;
}
if (tlen > 0) {
char cur_topic[tlen];
if (!tox_group_get_topic(m, self->num, (uint8_t *) cur_topic, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic (error %d).", err);
return;
}
cur_topic[tlen] = '\0';
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic is set to: %s", cur_topic);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic is not set.");
}
return;
}
const char *topic = argv[1];
TOX_ERR_GROUP_TOPIC_SET err;
tox_group_set_topic(m, self->num, (uint8_t *) topic, strlen(topic), &err);
switch (err) {
case TOX_ERR_GROUP_TOPIC_SET_OK: {
/* handled below switch */
break;
}
case TOX_ERR_GROUP_TOPIC_SET_TOO_LONG: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic length must not exceed %d.", TOX_GROUP_MAX_TOPIC_LENGTH);
return;
}
case TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the topic.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the topic (error %d).", err);
return;
}
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
TOX_ERR_GROUP_SELF_QUERY sn_err;
size_t sn_len = tox_group_self_get_name_size(m, self->num, &sn_err);
char selfnick[sn_len];
if (!tox_group_self_get_name(m, self->num, (uint8_t *) selfnick, &sn_err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve your own name (error %d).", sn_err);
return;
}
selfnick[sn_len] = '\0';
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, MAGENTA, "-!- You set the topic to: %s", topic);
char tmp_event[MAX_STR_SIZE];
snprintf(tmp_event, sizeof(tmp_event), "set topic to %s", topic);
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
}
void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_TOGGLE_IGNORE err;
if (!tox_group_toggle_ignore(m, self->num, peer_id, false, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unignore %s (error %d).", nick, err);
return;
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, BLUE, "-!- You are no longer ignoring %s", nick);
}
void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified.");
return;
}
GroupChat *chat = &groupchats[self->num];
if (!chat) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois failed.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
int peer_index = get_peer_index(self->num, peer_id);
if (peer_index < 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois failed.");
return;
}
const char *status_str = "Online";
if (chat->peer_list[peer_index].status == TOX_USER_STATUS_BUSY)
status_str = "Busy";
else if (chat->peer_list[peer_index].status == TOX_USER_STATUS_AWAY)
status_str = "Away";
const char *role_str = "User";
if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_FOUNDER)
role_str = "Founder";
else if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_MODERATOR)
role_str = "Moderator";
else if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_OBSERVER)
role_str = "Observer";
char last_seen_str[128];
get_elapsed_time_str_2(last_seen_str, sizeof(last_seen_str), get_unix_time() - chat->peer_list[peer_index].last_active);
char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
size_t i;
for (i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) {
char d[3];
snprintf(d, sizeof(d), "%02X", chat->peer_list[peer_index].public_key[i] & 0xff);
strcat(pk_string, d);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois for %s", nick);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Role: %s", role_str);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Status: %s", status_str);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Last active: %s", last_seen_str);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Public key: %s", pk_string);
}

48
src/group_commands.h Normal file
View File

@ -0,0 +1,48 @@
/* group_commands.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 GROUP_COMMANDS_H
#define GROUP_COMMANDS_H
#include "windows.h"
#include "toxic.h"
void cmd_ban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mykey(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_prune(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* GROUP_COMMANDS_H */

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -27,7 +27,7 @@
#include "help.h"
#include "misc_tools.h"
#define HELP_MENU_HEIGHT 8
#define HELP_MENU_HEIGHT 9
#define HELP_MENU_WIDTH 26
void help_init_menu(ToxWindow *self)
@ -60,6 +60,9 @@ static void help_init_window(ToxWindow *self, int height, int width)
int y2, x2;
getmaxyx(stdscr, y2, x2);
if (y2 <= 0 || x2 <= 0)
return;
height = MIN(height, y2);
width = MIN(width, x2);
@ -86,6 +89,12 @@ static void help_draw_menu(ToxWindow *self)
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "hat commands\n");
wprintw(win, " g");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "r");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "oup commands\n");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " f");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
@ -138,27 +147,44 @@ static void help_draw_global(ToxWindow *self)
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
wprintw(win, " /accept <id> : Accept friend request\n");
wprintw(win, " /avatar <path> : Set an avatar (leave path empty to unset)\n");
wprintw(win, " /decline <id> : Decline friend request\n");
wprintw(win, " /requests : List pending friend requests\n");
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
wprintw(win, " /status <type> <msg> : Set status with optional note\n");
wprintw(win, " /note <msg> : Set a personal note\n");
wprintw(win, " /group : Create a group chat\n");
wprintw(win, " /join <chat id> <passwd> : Join a group chat with optional password\n");
wprintw(win, " /nick <nick> : Set your nickname\n");
wprintw(win, " /nospam <value> : Change part of your Tox ID to stop spam\n");
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
wprintw(win, " /groupchat : Create a group chat\n");
wprintw(win, " /myid : Print your Tox ID\n");
#ifdef QRPNG
wprintw(win, " /myqr <txt> or <png> : Print your Tox ID's QR code to a file.\n");
#else
wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n");
#endif /* QRPNG */
wprintw(win, " /clear : Clear window history\n");
wprintw(win, " /close : Close the current chat window\n");
wprintw(win, " /quit or /exit : Exit Toxic\n");
#ifdef _AUDIO
#ifdef AUDIO
wattron(win, A_BOLD);
wprintw(win, "\n Audio:\n");
wattroff(win, A_BOLD);
wprintw(win, " /lsdev <type> : List devices where type: in|out\n");
wprintw(win, " /sdev <type> <id> : Set active device\n");
#endif /* _AUDIO */
#endif /* AUDIO */
#ifdef VIDEO
wattron(win, A_BOLD);
wprintw(win, "\n Video:\n");
wattroff(win, A_BOLD);
wprintw(win, " /lsvdev <type> : List video devices where type: in|out\n");
wprintw(win, " /svdev <type> <id> : Set active video device\n");
#endif /* VIDEO */
help_draw_bottom_menu(win);
@ -176,13 +202,12 @@ static void help_draw_chat(ToxWindow *self)
wprintw(win, "Chat Commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " /invite <n> : Invite contact to a group chat\n");
wprintw(win, " /join : Join a pending group chat\n");
wprintw(win, " /gaccept <password> : Accept a group invite with optional password\n");
wprintw(win, " /sendfile <path> : Send a file\n");
wprintw(win, " /savefile <id> : Receive a file\n");
wprintw(win, " /cancel <type> <id> : Cancel file transfer where type: in|out\n");
#ifdef _AUDIO
#ifdef AUDIO
wattron(win, A_BOLD);
wprintw(win, "\n Audio:\n");
wattroff(win, A_BOLD);
@ -193,8 +218,15 @@ static void help_draw_chat(ToxWindow *self)
wprintw(win, " /hangup : Hangup active call\n");
wprintw(win, " /sdev <type> <id> : Change active device\n");
wprintw(win, " /mute <type> : Mute active device if in call\n");
wprintw(win, " /sense <n> : VAD sensitivity treshold\n");
#endif /* _AUDIO */
wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
#endif /* AUDIO */
#ifdef VIDEO
wattron(win, A_BOLD);
wprintw(win, "\n Video:\n");
wattroff(win, A_BOLD);
wprintw(win, " /video : Toggle video call\n");
#endif /* VIDEO */
help_draw_bottom_menu(win);
@ -212,11 +244,14 @@ static void help_draw_keys(ToxWindow *self)
wprintw(win, "Key bindings:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " Ctrl+O and Ctrl+P : Navigate through the tabs\n");
wprintw(win, " Ctrl+O and Ctrl+P : Navigate through the tabs\n");
wprintw(win, " Page Up and Page Down : Scroll window history one line\n");
wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\n");
wprintw(win, " Ctrl+H : Move to the bottom of window history\n");
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n\n");
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n");
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n");
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);
@ -225,6 +260,49 @@ static void help_draw_keys(ToxWindow *self)
wrefresh(win);
}
static void help_draw_group(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, "Group commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " /chatid : Print the group chat id to share with others\n");
wprintw(win, " /mykey : Print your group public key\n");
wprintw(win, " /ignore <nick> : Ignore peer\n");
wprintw(win, " /unignore <nick> : Unignore peer \n");
wprintw(win, " /rejoin : Rejoin the group\n");
wprintw(win, " /topic <msg> : Set group topic (show current topic if no msg)\n");
wprintw(win, " /whisper <nick> <msg> : Send private message to nick\n");
wprintw(win, " /whois <nick> : Display info about nick.\n");
wattron(win, A_BOLD);
wprintw(win, " Moderator commands:\n");
wattroff(win, A_BOLD);
wprintw(win, " /kick <nick> : Kick peer\n");
wprintw(win, " /ban <nick> : Ban peer (leave nick blank to see ban list)\n");
wprintw(win, " /unban <Ban ID> : Unban entry\n");
wprintw(win, " /silence <nick> : Silences peer for the entire group\n");
wprintw(win, " /unsilence <nick> : Unsilences peer\n");
wattron(win, A_BOLD);
wprintw(win, " Founder commands:\n");
wattroff(win, A_BOLD);
wprintw(win, " /mod <nick> : Promote peer to moderator\n");
wprintw(win, " /unmod <nick> : Demote moderator to normal user\n");
wprintw(win, " /passwd <password> : Set group password (leave blank to unset)\n");
wprintw(win, " /peerlimit <num> : Set group peer limit\n");
wprintw(win, " /privacy <state> : Set group privacy state: private|public\n");
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
}
static void help_draw_contacts(ToxWindow *self)
{
WINDOW *win = self->help->win;
@ -249,37 +327,46 @@ static void help_draw_contacts(ToxWindow *self)
void help_onKey(ToxWindow *self, wint_t key)
{
switch(key) {
switch (key) {
case 'x':
case T_KEY_ESC:
help_exit(self);
break;
case 'c':
#ifdef _AUDIO
help_init_window(self, 19, 80);
#ifdef VIDEO
help_init_window(self, 21, 80);
#elif AUDIO
help_init_window(self, 18, 80);
#else
help_init_window(self, 9, 80);
help_init_window(self, 10, 80);
#endif
self->help->type = HELP_CHAT;
break;
case 'g':
#ifdef _AUDIO
help_init_window(self, 23, 80);
#ifdef VIDEO
help_init_window(self, 30, 80);
#elif AUDIO
help_init_window(self, 26, 80);
#else
help_init_window(self, 19, 80);
help_init_window(self, 22, 80);
#endif
self->help->type = HELP_GLOBAL;
break;
case 'r':
help_init_window(self, 25, 80);
self->help->type = HELP_GROUP;
break;
case 'f':
help_init_window(self, 10, 80);
self->help->type = HELP_CONTACTS;
break;
case 'k':
help_init_window(self, 12, 80);
help_init_window(self, 15, 80);
self->help->type = HELP_KEYS;
break;
@ -294,7 +381,7 @@ void help_onDraw(ToxWindow *self)
{
curs_set(0);
switch(self->help->type) {
switch (self->help->type) {
case HELP_MENU:
help_draw_menu(self);
return;
@ -316,6 +403,7 @@ void help_onDraw(ToxWindow *self)
break;
case HELP_GROUP:
help_draw_group(self);
break;
}
}

View File

@ -20,8 +20,8 @@
*
*/
#ifndef _help_h
#define _help_h
#ifndef HELP_H
#define HELP_H
#include "toxic.h"
#include "windows.h"
@ -39,4 +39,4 @@ void help_onDraw(ToxWindow *self);
void help_init_menu(ToxWindow *self);
void help_onKey(ToxWindow *self, wint_t key);
#endif /* #define _help_h */
#endif /* #define HELP_H */

View File

@ -32,22 +32,29 @@
#include "toxic_strings.h"
#include "line_info.h"
#include "notify.h"
#include "groupchat.h"
#include "settings.h"
extern struct user_settings *user_settings;
/* add a char to input field and buffer */
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
{
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, error, 0, NULL);
sound_notify(self, notif_error, 0, NULL);
return;
}
if (add_char_to_buf(ctx, key) == -1) {
sound_notify(self, error, 0, NULL);
sound_notify(self, notif_error, 0, NULL);
return;
}
@ -63,7 +70,7 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
ChatContext *ctx = self->chatwin;
if (del_char_buf_bck(ctx) == -1) {
sound_notify(self, error, 0, NULL);
sound_notify(self, notif_error, 0, NULL);
return;
}
@ -80,21 +87,32 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
static void input_delete(ToxWindow *self)
{
if (del_char_buf_frnt(self->chatwin) == -1)
sound_notify(self, error, 0, NULL);
sound_notify(self, notif_error, 0, NULL);
}
/* delete last typed word */
static void input_del_word(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (del_word_buf(ctx) == -1) {
sound_notify(self, notif_error, 0, NULL);
return;
}
}
/* deletes entire line before cursor from input field and buffer */
static void input_discard(ToxWindow *self)
{
if (discard_buf(self->chatwin) == -1)
sound_notify(self, error, 0, NULL);
sound_notify(self, notif_error, 0, NULL);
}
/* deletes entire line after cursor from input field and buffer */
static void input_kill(ChatContext *ctx)
{
if (kill_buf(ctx) == -1)
sound_notify(NULL, error, NT_ALWAYS, NULL);
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
static void input_yank(ToxWindow *self, int x, int mx_x)
@ -102,7 +120,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
ChatContext *ctx = self->chatwin;
if (yank_buf(ctx) == -1) {
sound_notify(self, error, 0, NULL);
sound_notify(self, notif_error, 0, NULL);
return;
}
@ -110,7 +128,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
if (x + yank_cols >= mx_x) {
int rmdr = MAX(0, (x + yank_cols) - mx_x);
int s_len = wcswidth(&ctx->line[ctx->start], rmdr);
int s_len = MAX(0, wcswidth(&ctx->line[ctx->start], rmdr));
ctx->start += s_len + 1;
}
}
@ -122,7 +140,7 @@ static void input_mv_end(ToxWindow *self, int y, int mx_x)
ctx->pos = ctx->len;
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
}
@ -181,7 +199,7 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x)
ChatContext *ctx = self->chatwin;
fetch_hist_item(ctx, key);
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
ctx->start = wlen < mx_x ? 0 : wlen - mx_x + 1;
}
@ -213,6 +231,10 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
input_yank(self, x, mx_x);
break;
case T_KEY_C_W:
input_del_word(self, x, mx_x);
break;
case KEY_HOME:
case T_KEY_C_A:
input_mv_home(self);
@ -236,10 +258,30 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
input_history(self, key, mx_x);
break;
case T_KEY_C_L:
force_refresh(self->chatwin->history);
break;
default:
match = false;
break;
}
/* TODO: this special case is ugly.
maybe convert entire function to if/else and make them all customizable keys? */
if (!match) {
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;
}
}
return match;
}

View File

@ -20,8 +20,8 @@
*
*/
#ifndef _input_h
#define _input_h
#ifndef INPUT_H
#define INPUT_H
/* add a char to input field and buffer for given chatcontext */
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
@ -30,4 +30,4 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_
return true if key matches a function, false otherwise */
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
#endif /* #define _input_h */
#endif /* #define INPUT_H */

View File

@ -34,7 +34,7 @@
#include "message_queue.h"
#include "misc_tools.h"
extern struct user_settings *user_settings_;
extern struct user_settings *user_settings;
void line_info_init(struct history *hst)
{
@ -48,8 +48,8 @@ void line_info_init(struct history *hst)
hst->queue_sz = 0;
}
/* resets line_start (page end) */
static void line_info_reset_start(ToxWindow *self, struct history *hst)
/* resets line_start (moves to end of chat history) */
void line_info_reset_start(ToxWindow *self, struct history *hst)
{
struct line_info *line = hst->line_end;
@ -59,7 +59,7 @@ static void line_info_reset_start(ToxWindow *self, struct history *hst)
int y2, x2;
getmaxyx(self->window, y2, x2);
int side_offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;
int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;
int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
@ -111,13 +111,13 @@ static void line_info_root_fwd(struct history *hst)
hst->line_root = tmp;
}
/* returns ptr to queue item 0 and removes it from queue */
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
static struct line_info *line_info_ret_queue(struct history *hst)
{
if (hst->queue_sz <= 0)
return NULL;
struct line_info *ret = hst->queue[0];
struct line_info *line = hst->queue[0];
int i;
@ -126,16 +126,19 @@ static struct line_info *line_info_ret_queue(struct history *hst)
--hst->queue_sz;
return ret;
return line;
}
/* creates new line_info line and puts it in the queue. */
void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uint8_t type, uint8_t bold,
uint8_t colour, const char *msg, ...)
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...)
{
if (!self)
return;
struct history *hst = self->chatwin->hst;
if (hst->queue_sz >= MAX_QUEUE)
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
return;
struct line_info *new_line = calloc(1, sizeof(struct line_info));
@ -143,7 +146,7 @@ void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uin
if (new_line == NULL)
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
char frmt_msg[MAX_STR_SIZE] = {0};
char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0};
va_list args;
va_start(args, msg);
@ -155,22 +158,39 @@ void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uin
/* for type-specific formatting in print function */
switch (type) {
case IN_ACTION:
/* fallthrough */
case OUT_ACTION:
len += 3;
len += strlen(user_settings->line_normal) + 2;
break;
case IN_MSG:
/* fallthrough */
case OUT_MSG:
len += 2;
len += strlen(user_settings->line_normal) + 3;
break;
case IN_PRVT_MSG:
case OUT_PRVT_MSG:
len += strlen(user_settings->line_special) + 3;
break;
case CONNECTION:
len += 3;
len += strlen(user_settings->line_join) + 2;
break;
case DISCONNECTION:
len += strlen(user_settings->line_quit) + 2;
break;
case SYS_MSG:
break;
case NAME_CHANGE:
len += strlen(user_settings->line_alert) + 1;
break;
case PROMPT:
++len;
break;
@ -194,7 +214,7 @@ void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uin
if (timestr) {
snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr);
len += strlen(new_line->timestr);
len += strlen(new_line->timestr) + 1;
}
if (name1) {
@ -218,7 +238,7 @@ void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uin
}
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
static void line_info_check_queue(ToxWindow *self)
static void line_info_check_queue(ToxWindow *self)
{
struct history *hst = self->chatwin->hst;
struct line_info *line = line_info_ret_queue(hst);
@ -226,7 +246,7 @@ static void line_info_check_queue(ToxWindow *self)
if (line == NULL)
return;
if (hst->start_id > user_settings_->history_size)
if (hst->start_id > user_settings->history_size)
line_info_root_fwd(hst);
line->id = hst->line_end->id + 1;
@ -242,7 +262,7 @@ static void line_info_check_queue(ToxWindow *self)
if (x2 <= SIDEBAR_WIDTH)
return;
int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
int lines = 1 + line->newlines + (line->len / (x2 - offst));
int max_y = y2 - CHATBOX_HEIGHT;
@ -291,10 +311,16 @@ void line_info_print(ToxWindow *self)
switch (type) {
case OUT_MSG:
/* fallthrough */
case OUT_MSG_READ:
/* fallthrough */
case IN_MSG:
case IN_PRVT_MSG:
case OUT_PRVT_MSG:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestr);
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
int nameclr = GREEN;
@ -305,18 +331,35 @@ void line_info_print(ToxWindow *self)
nameclr = CYAN;
wattron(win, COLOR_PAIR(nameclr));
wprintw(win, "%s: ", line->name1);
wprintw(win, "%s %s: ", (type != OUT_PRVT_MSG && type != IN_PRVT_MSG) ?
user_settings->line_normal :
user_settings->line_special,
line->name1);
wattroff(win, COLOR_PAIR(nameclr));
if (line->msg[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
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));
if (line[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
else if (line[0] == '<')
wattron(win, COLOR_PAIR(RED));
if (type == OUT_MSG && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) {
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);
wattroff(win, COLOR_PAIR(RED));
@ -331,17 +374,21 @@ void line_info_print(ToxWindow *self)
break;
case OUT_ACTION_READ:
/* fallthrough */
case OUT_ACTION:
/* fallthrough */
case IN_ACTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestr);
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(YELLOW));
wprintw(win, "* %s %s", line->name1, line->msg);
wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg);
wattroff(win, COLOR_PAIR(YELLOW));
if (type == OUT_ACTION && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) {
if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
wattron(win, COLOR_PAIR(RED));
wprintw(win, " x", line->msg);
wattroff(win, COLOR_PAIR(RED));
@ -358,7 +405,7 @@ void line_info_print(ToxWindow *self)
case SYS_MSG:
if (line->timestr[0]) {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestr);
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
}
@ -391,13 +438,33 @@ void line_info_print(ToxWindow *self)
case CONNECTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestr);
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(line->colour));
wprintw(win, "%s ", user_settings->line_join);
wattron(win, A_BOLD);
wprintw(win, "* %s ", line->name1);
wprintw(win, "%s ", line->name1);
wattroff(win, A_BOLD);
wprintw(win, "%s\n", line->msg);
wattroff(win, COLOR_PAIR(line->colour));
break;
case DISCONNECTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(line->colour));
wprintw(win, "%s ", user_settings->line_quit);
wattron(win, A_BOLD);
wprintw(win, "%s ", line->name1);
wattroff(win, A_BOLD);
wprintw(win, "%s\n", line->msg);
wattroff(win, COLOR_PAIR(line->colour));
@ -405,12 +472,13 @@ void line_info_print(ToxWindow *self)
case NAME_CHANGE:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestr);
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(MAGENTA));
wprintw(win, "%s ", user_settings->line_alert);
wattron(win, A_BOLD);
wprintw(win, "* %s", line->name1);
wprintw(win, "%s", line->name1);
wattroff(win, A_BOLD);
wprintw(win, "%s", line->msg);
@ -455,14 +523,14 @@ static void line_info_scroll_up(struct history *hst)
{
if (hst->line_start->prev)
hst->line_start = hst->line_start->prev;
else sound_notify(NULL, error, NT_ALWAYS, NULL);
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
static void line_info_scroll_down(struct history *hst)
{
if (hst->line_start->next)
hst->line_start = hst->line_start->next;
else sound_notify(NULL, error, NT_ALWAYS, NULL);
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
static void line_info_page_up(ToxWindow *self, struct history *hst)
@ -494,24 +562,19 @@ bool line_info_onKey(ToxWindow *self, wint_t key)
struct history *hst = self->chatwin->hst;
bool match = true;
if (key == user_settings_->key_half_page_up) {
line_info_page_up(self, hst);
}
else if (key == user_settings_->key_half_page_down) {
line_info_page_down(self, hst);
}
else if (key == user_settings_->key_scroll_line_up) {
line_info_scroll_up(hst);
}
else if (key == user_settings_->key_scroll_line_down) {
line_info_scroll_down(hst);
}
else if (key == user_settings_->key_page_bottom) {
line_info_reset_start(self, hst);
}
else {
match = false;
}
if (key == user_settings->key_half_page_up) {
line_info_page_up(self, hst);
} else if (key == user_settings->key_half_page_down) {
line_info_page_down(self, hst);
} else if (key == user_settings->key_scroll_line_up) {
line_info_scroll_up(hst);
} else if (key == user_settings->key_scroll_line_down) {
line_info_scroll_down(hst);
} else if (key == user_settings->key_page_bottom) {
line_info_reset_start(self, hst);
} else {
match = false;
}
return match;
}

View File

@ -20,35 +20,39 @@
*
*/
#ifndef _line_info_h
#define _line_info_h
#ifndef LINE_INFO_H
#define LINE_INFO_H
#include "windows.h"
#include "toxic.h"
#define MAX_HISTORY 100000
#define MIN_HISTORY 40
#define MAX_QUEUE 128
#define MAX_LINE_INFO_QUEUE 1024
#define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* needs extra room for log loading */
enum {
SYS_MSG,
IN_MSG,
OUT_MSG,
OUT_MSG_READ, /* for sent messages that have received a read reply. don't set this with line_info_add */
OUT_MSG_READ, /* for sent messages that have received a read reply. */
IN_ACTION,
OUT_ACTION,
OUT_ACTION_READ, /* same as OUT_MSG_READ but for actions */
IN_PRVT_MSG, /* PRVT should only be used for groups */
OUT_PRVT_MSG,
PROMPT,
CONNECTION,
DISCONNECTION,
NAME_CHANGE,
} LINE_TYPE;
struct line_info {
char timestr[TIME_STR_SIZE];
char name1[TOXIC_MAX_NAME_LENGTH];
char name2[TOXIC_MAX_NAME_LENGTH];
char msg[TOX_MAX_MESSAGE_LENGTH];
uint64_t timestamp;
char name1[TOXIC_MAX_NAME_LENGTH + 1];
char name2[TOXIC_MAX_NAME_LENGTH + 1];
char msg[MAX_LINE_INFO_MSG_SIZE];
time_t timestamp;
uint8_t type;
uint8_t bold;
uint8_t colour;
@ -68,13 +72,13 @@ struct history {
struct line_info *line_end;
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
struct line_info *queue[MAX_QUEUE];
struct line_info *queue[MAX_LINE_INFO_QUEUE];
int queue_sz;
};
/* creates new line_info line and puts it in the queue. */
void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uint8_t type, uint8_t bold,
uint8_t colour, const char *msg, ...);
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...);
/* Prints a section of history starting at line_start */
void line_info_print(ToxWindow *self);
@ -88,7 +92,10 @@ void line_info_clear(struct history *hst);
/* puts msg in specified line_info msg buffer */
void line_info_set(ToxWindow *self, uint32_t id, char *msg);
/* resets line_start (moves to end of chat history) */
void line_info_reset_start(ToxWindow *self, struct history *hst);
void line_info_init(struct history *hst);
bool line_info_onKey(ToxWindow *self, wint_t key); /* returns true if key is a match */
#endif /* #define _line_info_h */
#endif /* #define LINE_INFO_H */

235
src/log.c
View File

@ -23,6 +23,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include "configdir.h"
#include "toxic.h"
@ -30,60 +31,88 @@
#include "misc_tools.h"
#include "log.h"
#include "settings.h"
#include "line_info.h"
extern struct user_settings *user_settings_;
extern struct user_settings *user_settings;
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
void init_logging_session(char *name, const char *key, struct chatlog *log)
/* There are three types of logs: chat logs, groupchat logs, and prompt logs (see LOG_TYPE in log.h)
A prompt log is in the format: LOGDIR/selfkey-home.log
A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log
A groupchat log is in the format: LOGDIR/selfkey-groupname-date[time].log
Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used.
Returns 0 on success, -1 if the path is too long */
static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype)
{
if (!log->log_on)
return;
if (!valid_nick(name))
name = UNKNOWN_NAME;
const char *set_path = user_settings_->chatlogs_path;
const char *namedash = logtype == LOG_PROMPT ? "" : "-";
const char *set_path = user_settings->chatlogs_path;
char *user_config_dir = get_user_config_dir();
int path_len = strlen(set_path) + strlen(name) ? *set_path
: strlen(user_config_dir) + strlen(LOGDIR) + strlen(name);
int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash);
path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR);
/* use first 4 digits of key as log ident. If no key use a timestamp */
char ident[32];
/* first 6 digits of selfkey */
char self_id[32];
path_len += KEY_IDENT_DIGITS * 2;
sprintf(&self_id[0], "%02X", selfkey[0] & 0xff);
sprintf(&self_id[2], "%02X", selfkey[1] & 0xff);
sprintf(&self_id[4], "%02X", selfkey[2] & 0xff);
self_id[KEY_IDENT_DIGITS * 2] = '\0';
if (key != NULL) {
path_len += (KEY_IDENT_DIGITS * 2 + 5);
sprintf(&ident[0], "%02X", key[0] & 0xff);
sprintf(&ident[2], "%02X", key[1] & 0xff);
ident[KEY_IDENT_DIGITS * 2 + 1] = '\0';
} else {
strftime(ident, sizeof(ident), "%Y-%m-%d[%H:%M:%S]", get_time());
path_len += strlen(ident) + 1;
char other_id[32] = {0};
switch (logtype) {
case LOG_CHAT:
path_len += KEY_IDENT_DIGITS * 2;
sprintf(&other_id[0], "%02X", otherkey[0] & 0xff);
sprintf(&other_id[2], "%02X", otherkey[1] & 0xff);
sprintf(&other_id[4], "%02X", otherkey[2] & 0xff);
other_id[KEY_IDENT_DIGITS * 2] = '\0';
break;
case LOG_GROUP:
strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time());
path_len += strlen(other_id);
break;
}
if (path_len >= MAX_STR_SIZE) {
log->log_on = false;
if (path_len >= destsize) {
free(user_config_dir);
return;
return -1;
}
char log_path[MAX_STR_SIZE];
if (*set_path)
snprintf(log_path, sizeof(log_path), "%s%s-%s.log", set_path, name, ident);
if (!string_is_empty(set_path))
snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id);
else
snprintf(log_path, sizeof(log_path), "%s%s%s-%s.log", user_config_dir, LOGDIR, name, ident);
snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id);
free(user_config_dir);
log->file = fopen(log_path, "a");
return 0;
}
if (log->file == NULL) {
log->log_on = false;
return;
}
/* Opens log file or creates a new one */
static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
{
if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL))
return -1;
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
char log_path[MAX_STR_SIZE];
if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1)
return -1;
log->file = fopen(log_path, "a+");
snprintf(log->path, sizeof(log->path), "%s", log_path);
if (log->file == NULL)
return -1;
return 0;
}
#define LOG_FLUSH_LIMIT 1 /* limits calls to fflush to a max of one per LOG_FLUSH_LIMIT seconds */
@ -105,33 +134,137 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
else
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
const char *t = user_settings_->time == TIME_12 ? "%Y/%m/%d [%I:%M:%S %p]" : "%Y/%m/%d [%H:%M:%S]";
const char *t = user_settings->log_timestamp_format;
char s[MAX_STR_SIZE];
strftime(s, MAX_STR_SIZE, t, get_time());
fprintf(log->file, "%s %s %s\n", s, name_frmt, msg);
uint64_t curtime = get_unix_time();
if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) {
if (timed_out(log->lastwrite, LOG_FLUSH_LIMIT)) {
fflush(log->file);
log->lastwrite = curtime;
log->lastwrite = get_unix_time();
}
}
void log_enable(char *name, const char *key, struct chatlog *log)
{
log->log_on = true;
if (log->file == NULL)
init_logging_session(name, key, log);
}
void log_disable(struct chatlog *log)
{
log->log_on = false;
if (log->file != NULL) {
if (log->file != NULL)
fclose(log->file);
log->file = NULL;
}
memset(log, 0, sizeof(struct chatlog));
}
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
{
log->log_on = true;
if (log->file != NULL)
return 0;
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
log_disable(log);
return -1;
}
return 0;
}
/* Loads previous history from chat log */
void load_chat_history(ToxWindow *self, struct chatlog *log)
{
if (log->file == NULL)
return;
off_t sz = file_size(log->path);
if (sz <= 0)
return;
char *hstbuf = malloc(sz + 1);
if (hstbuf == NULL)
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
if (fseek(log->file, 0L, SEEK_SET) == -1) {
free(hstbuf);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
return;
}
if (fread(hstbuf, sz, 1, log->file) != 1) {
free(hstbuf);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
return;
}
hstbuf[sz] = '\0';
/* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
int start, count = 0;
/* start at end and backtrace L lines or to the beginning of buffer */
for (start = sz - 1; start >= 0 && count < L; --start) {
if (hstbuf[start] == '\n')
++count;
}
const char *line = strtok(&hstbuf[start + 1], "\n");
if (line == NULL) {
free(hstbuf);
return;
}
while (line != NULL && count--) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
line = strtok(NULL, "\n");
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
free(hstbuf);
}
/* renames chatlog file replacing src with dest.
Returns 0 on success or if no log exists, -1 on failure. */
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum)
{
ToxWindow *toxwin = get_window_ptr(winnum);
struct chatlog *log = NULL;
bool log_on = false;
/* disable log if necessary and save its state */
if (toxwin != NULL) {
log = toxwin->chatwin->log;
log_on = log->log_on;
}
if (log_on)
log_disable(log);
char newpath[MAX_STR_SIZE];
char oldpath[MAX_STR_SIZE];
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1)
goto on_error;
if (!file_exists(oldpath))
return 0;
if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1)
goto on_error;
if (rename(oldpath, newpath) != 0)
goto on_error;
if (log_on)
log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
return 0;
on_error:
if (log_on)
log_enable(src, selfkey, otherkey, log, LOG_CHAT);
return -1;
}

View File

@ -20,25 +20,40 @@
*
*/
#ifndef _log_h
#define _log_h
#ifndef LOG_H
#define LOG_H
struct chatlog {
FILE *file;
uint64_t lastwrite;
time_t lastwrite;
char path[MAX_STR_SIZE];
bool log_on; /* specific to current chat window */
};
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
void init_logging_session(char *name, const char *key, struct chatlog *log);
enum {
LOG_GROUP,
LOG_PROMPT,
LOG_CHAT,
} LOG_TYPE;
/* formats/writes line to log file */
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event);
/* enables logging for specified log and creates/fetches file if necessary */
void log_enable(char *name, const char *key, struct chatlog *log);
/* enables logging for specified log and creates/fetches file if necessary.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype);
/* disables logging for specified log and closes file */
void log_disable(struct chatlog *log);
#endif /* #define _log_h */
/* Loads previous history from chat log */
void load_chat_history(ToxWindow *self, struct chatlog *log);
/* renames chatlog file replacing src with dest.
Returns 0 on success or if no log exists, -1 on failure. */
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
#endif /* #define LOG_H */

View File

@ -42,7 +42,7 @@ void cqueue_cleanup(struct chat_queue *q)
free(q);
}
void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id)
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id)
{
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
@ -103,7 +103,9 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
}
char selfname[TOX_MAX_NAME_LENGTH];
uint16_t len = tox_get_self_name(m, (uint8_t *) selfname);
tox_self_get_name(m, (uint8_t *) selfname);
size_t len = tox_self_get_name_size(m);
selfname[len] = '\0';
write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION);
@ -127,30 +129,26 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
}
}
#define CQUEUE_TRY_SEND_INTERVAL 10
#define CQUEUE_TRY_SEND_INTERVAL 60
/* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m)
{
struct chat_queue *q = self->chatwin->cqueue;
struct cqueue_msg *msg = q->root;
uint64_t curtime = get_unix_time();
while (msg) {
if (msg->receipt != 0 && !timed_out(msg->last_send_try, curtime, CQUEUE_TRY_SEND_INTERVAL)) {
msg = msg->next;
continue;
}
uint32_t receipt = 0;
if (msg->type == OUT_MSG)
receipt = tox_send_message(m, self->num, (uint8_t *) msg->message, msg->len);
else
receipt = tox_send_action(m, self->num, (uint8_t *) msg->message, msg->len);
msg->last_send_try = curtime;
msg->receipt = receipt;
if (!msg)
return;
}
if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL))
return;
uint32_t receipt = 0;
TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
msg->last_send_try = get_unix_time();
msg->receipt = receipt;
return;
}

View File

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

View File

@ -26,18 +26,22 @@
#include <time.h>
#include <limits.h>
#include <dirent.h>
#if defined(__FreeBSD__)
#include <netinet/in.h>
#include <sys/socket.h>
#else
#include <arpa/inet.h>
#endif
#include <sys/stat.h>
#include "toxic.h"
#include "windows.h"
#include "misc_tools.h"
#include "settings.h"
#include "file_senders.h"
#include "file_transfers.h"
extern ToxWindow *prompt;
extern struct user_settings *user_settings_;
static uint64_t current_unix_time;
extern struct user_settings *user_settings;
void hst_to_net(uint8_t *num, uint16_t numbytes)
{
@ -54,45 +58,40 @@ void hst_to_net(uint8_t *num, uint16_t numbytes)
return;
}
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 curtime, uint64_t timeout)
int timed_out(time_t timestamp, time_t timeout)
{
return timestamp + timeout <= curtime;
return timestamp + timeout <= get_unix_time();
}
/* Get the current local time */
struct tm *get_time(void)
{
struct tm *timeinfo;
uint64_t t = get_unix_time();
timeinfo = localtime((const time_t*) &t);
time_t t = get_unix_time();
timeinfo = localtime((const time_t *) &t);
return timeinfo;
}
/*Puts the current time in buf in the format of [HH:mm:ss] */
/* Puts the current time in buf in the format of [HH:mm:ss] */
void get_time_str(char *buf, int bufsize)
{
if (user_settings_->timestamps == TIMESTAMPS_OFF) {
if (user_settings->timestamps == TIMESTAMPS_OFF) {
buf[0] = '\0';
return;
}
const char *t = user_settings_->time == TIME_12 ? "[%-I:%M:%S] " : "[%H:%M:%S] ";
const char *t = user_settings->timestamp_format;
strftime(buf, bufsize, t, get_time());
}
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
void get_elapsed_time_str(char *buf, int bufsize, time_t secs)
{
if (!secs)
return;
@ -109,20 +108,42 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
}
char *hex_string_to_bin(const char *hex_string)
/* Converts seconds to string in format H hours, m minutes, s seconds */
void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs)
{
size_t len = strlen(hex_string);
char *val = malloc(len);
if (!secs)
return;
if (val == NULL)
exit_toxic_err("failed in hex_string_to_bin", FATALERR_MEMORY);
long int seconds = secs % 60;
long int minutes = (secs % 3600) / 60;
long int hours = secs / 3600;
size_t i;
if (!minutes && !hours)
snprintf(buf, bufsize, "%ld seconds", seconds);
else if (!hours)
snprintf(buf, bufsize, "%ld minutes, %ld seconds", minutes, seconds);
else
snprintf(buf, bufsize, "%ld hours, %ld minutes, %ld seconds", hours, minutes, seconds);
}
for (i = 0; i < len; ++i, hex_string += 2)
sscanf(hex_string, "%2hhx", &val[i]);
/*
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
* output_size must be exactly half of hex_len.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size)
{
if (output_size == 0 || hex_len != output_size * 2)
return -1;
return val;
for (size_t i = 0; i < output_size; ++i) {
sscanf(hex_string, "%2hhx", &output[i]);
hex_string += 2;
}
return 0;
}
int hex_string_to_bytes(char *buf, int size, const char *keystr)
@ -144,12 +165,42 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr)
return 0;
}
/* Converts a binary representation of a Tox ID into a string.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size)
{
if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1))
return -1;
size_t i;
for (i = 0; i < TOX_ADDRESS_SIZE; ++i)
snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
return 0;
}
/* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(const char *string)
{
if (!string)
return true;
return string[0] == '\0';
}
/* 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)
{
@ -158,7 +209,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
if (n < len)
return -1;
if ((len = mbstowcs(buf, string, n)) == (size_t) -1)
if ((len = mbstowcs(buf, string, n)) == (size_t) - 1)
return -1;
return len;
@ -172,7 +223,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
if (n < len)
return -1;
if ((len = wcstombs(buf, string, n)) == (size_t) -1)
if ((len = wcstombs(buf, string, n)) == (size_t) - 1)
return -1;
return len;
@ -188,8 +239,9 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2)
- cannot be empty
- cannot start with a space
- must not contain a forward slash (for logfile naming purposes)
- must not contain contiguous spaces */
int valid_nick(char *nick)
- must not contain contiguous spaces
- must not contain a newline or tab seqeunce */
int valid_nick(const char *nick)
{
if (!nick[0] || nick[0] == ' ')
return 0;
@ -197,27 +249,43 @@ int valid_nick(char *nick)
int i;
for (i = 0; nick[i]; ++i) {
if (nick[i] == ' ' && nick[i + 1] == ' ')
return 0;
if ((nick[i] == ' ' && nick[i + 1] == ' ')
|| nick[i] == '/'
|| nick[i] == '\n'
|| nick[i] == '\t'
|| nick[i] == '\v'
|| nick[i] == '\r')
if (nick[i] == '/')
return 0;
}
return 1;
}
/* gets base file name from path or original file name if no path is supplied */
void get_file_name(char *namebuf, int bufsize, const char *pathname)
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
void filter_str(char *str, size_t len)
{
int idx = strlen(pathname) - 1;
size_t i;
for (i = 0; i < len; ++i) {
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0')
str[i] = ' ';
}
}
/* gets base file name from path or original file name if no path is supplied.
* Returns the file name length
*/
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
{
int len = strlen(pathname) - 1;
char *path = strdup(pathname);
if (path == NULL)
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
while (idx >= 0 && pathname[idx] == '/')
path[idx--] = '\0';
while (len >= 0 && pathname[len] == '/')
path[len--] = '\0';
char *finalname = strdup(path);
@ -234,6 +302,29 @@ void get_file_name(char *namebuf, int bufsize, const char *pathname)
snprintf(namebuf, bufsize, "%s", finalname);
free(finalname);
free(path);
return strlen(namebuf);
}
/* Gets the base directory of path and puts it in dir.
* dir must have at least as much space as path_len + 1.
*
* Returns the length of the base directory.
*/
size_t get_base_dir(const char *path, size_t path_len, char *dir)
{
if (path_len == 0 || path == NULL)
return 0;
size_t dir_len = char_rfind(path, '/', path_len);
if (dir_len != 0 && dir_len < path_len)
++dir_len; /* Leave trailing slash */
memcpy(dir, path, dir_len);
dir[dir_len] = '\0';
return dir_len;
}
/* converts str to all lowercase */
@ -246,19 +337,72 @@ void str_to_lower(char *str)
}
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
Returns nick len on success, -1 on failure */
int get_nick_truncate(Tox *m, char *buf, int friendnum)
if toxcore API call fails, put UNKNOWN_NAME in buf
Returns nick len */
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
{
int len = tox_get_name(m, friendnum, (uint8_t *) buf);
size_t len = tox_friend_get_name_size(m, friendnum, NULL);
if (len == 0) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
} else {
tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL);
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
filter_str(buf, len);
return len;
}
/* same as get_nick_truncate but for groupchats */
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum)
{
TOX_ERR_GROUP_PEER_QUERY err;
size_t len = tox_group_peer_get_name_size(m, groupnum, peer_id, &err);
if (err != TOX_ERR_GROUP_PEER_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
} else {
tox_group_peer_get_name(m, groupnum, peer_id, (uint8_t *) buf, &err);
if (err != TOX_ERR_GROUP_PEER_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
}
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
filter_str(buf, len);
return len;
}
/* copies data to msg buffer.
returns length of msg.
returns 0 and nulls msg if length is too big for buffer size */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length)
{
if (length > size - 1) {
msg[0] = '\0';
return 0;
}
memcpy(msg, data, length);
msg[length] = '\0';
return length;
}
/* returns index of the first instance of ch in s starting at idx.
returns length of s if char not found */
returns length of s if char not found or 0 if s is NULL. */
int char_find(int idx, const char *s, char ch)
{
if (!s) {
return 0;
}
int i = idx;
for (i = idx; s[i]; ++i) {
@ -269,10 +413,14 @@ int char_find(int idx, const char *s, char ch)
return i;
}
/* returns index of the last instance of ch in s starting at len
returns 0 if char not found (skips 0th index) */
/* returns index of the last instance of ch in s starting at len.
returns 0 if char not found 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) {
@ -311,3 +459,84 @@ bool file_exists(const char *path)
struct stat s;
return stat(path, &s) == 0;
}
/* returns file size. If file doesn't exist returns 0. */
off_t file_size(const char *path)
{
struct stat st;
if (stat(path, &st) == -1)
return 0;
return st.st_size;
}
/* compares the first size bytes of fp to signature.
Returns 0 if they are the same, 1 if they differ, and -1 on error.
On success this function will seek back to the beginning of fp */
int check_file_signature(const char *signature, size_t size, FILE *fp)
{
char buf[size];
if (fread(buf, size, 1, fp) != 1)
return -1;
int ret = memcmp(signature, buf, size);
if (fseek(fp, 0L, SEEK_SET) == -1)
return -1;
return ret == 0 ? 0 : 1;
}
/* sets window title in tab bar. */
void set_window_title(ToxWindow *self, const char *title, int len)
{
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
if (self->is_groupchat) /* keep groupnumber in title */
snprintf(cpy, sizeof(cpy), "%d %s", self->num, title);
else
snprintf(cpy, sizeof(cpy), "%s", title);
if (len > MAX_WINDOW_NAME_LENGTH) {
strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "...");
cpy[MAX_WINDOW_NAME_LENGTH] = '\0';
}
snprintf(self->name, sizeof(self->name), "%s", cpy);
}
/* 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

@ -19,8 +19,10 @@
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _misc_tools_h
#define _misc_tools_h
#ifndef MISC_TOOLS_H
#define MISC_TOOLS_H
#include <sys/stat.h>
#include "windows.h"
#include "toxic.h"
@ -39,30 +41,46 @@
void hst_to_net(uint8_t *num, uint16_t numbytes);
/* convert a hex string to binary */
char *hex_string_to_bin(const char *hex_string);
/*
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
* output_size must be exactly half of hex_len.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size);
/* convert a hex string to bytes. returns 0 on success, -1 on failure */
int hex_string_to_bytes(char *buf, int size, const char *keystr);
/* get the current unix time */
uint64_t get_unix_time(void);
/* 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);
/* Puts the current time in buf in the format of [HH:mm:ss] */
/* get the current unix time (not thread safe) */
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 */
/* Converts seconds to string in format H hours, m minutes, s seconds */
void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs);
/* get the current local time (not thread safe) */
struct tm *get_time(void);
/* updates current unix time (should be run once per do_toxic loop) */
void update_unix_time(void);
/* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(const char *string);
/* Same as above but for wide character strings */
int wstring_is_empty(const wchar_t *string);
/* convert a multibyte string to a wide character string (must provide buffer) */
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
@ -73,7 +91,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
/* Returns 1 if connection has timed out, 0 otherwise */
int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
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);
@ -85,25 +103,45 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2);
- cannot be empty
- cannot start with a space
- must not contain a forward slash (for logfile naming purposes)
- must not contain contiguous spaces */
int valid_nick(char *nick);
- must not contain contiguous spaces
- must not contain a newline or tab seqeunce */
int valid_nick(const char *nick);
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
void filter_str(char *str, size_t len);
/* gets base file name from path or original file name if no path is supplied */
void get_file_name(char *namebuf, int bufsize, const char *pathname);
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname);
/* Gets the base directory of path and puts it in dir.
* dir must have at least as much space as path_len.
*
* Returns the length of the base directory on success.
* Returns -1 on failure.
*/
size_t get_base_dir(const char *path, size_t path_len, char *dir);
/* converts str to all lowercase */
void str_to_lower(char *str);
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
Returns nick len on success, -1 on failure */
int get_nick_truncate(Tox *m, char *buf, int friendnum);
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
/* same as get_nick_truncate but for groupchats */
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum);
/* copies data to msg buffer.
returns length of msg.
returns 0 and nulls msg if length is too big for buffer size */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length);
/* returns index of the first instance of ch in s starting at idx.
returns length of s if char not found */
returns length of s if char not found or 0 if s is NULL. */
int char_find(int idx, const char *s, char ch);
/* returns index of the last instance of ch in s
returns 0 if char not found */
/* returns index of the last instance of ch in s starting at len.
returns 0 if char not found or s is NULL (skips 0th index). */
int char_rfind(const char *s, char ch, int len);
/* Converts bytes to appropriate unit and puts in buf as a string */
@ -112,4 +150,28 @@ void bytes_convert_str(char *buf, int size, uint64_t bytes);
/* checks if a file exists. Returns true or false */
bool file_exists(const char *path);
#endif /* #define _misc_tools_h */
/* returns file size. If file doesn't exist returns 0. */
off_t file_size(const char *path);
/* compares the first size bytes of fp and signature.
Returns 0 if they are the same, 1 if they differ, and -1 on error.
On success this function will seek back to the beginning of fp */
int check_file_signature(const char *signature, size_t size, FILE *fp);
/* sets window title in tab bar. */
void set_window_title(ToxWindow *self, const char *title, int len);
/* Return true if address appears to be a valid ipv4 address. */
bool is_ip4_address(const char *address);
/* Return true if address roughly appears to be a valid ipv6 address.
*
* TODO: Improve this function (inet_pton behaves strangely with ipv6).
* for now the only guarantee is that it won't return true if the
* address is a domain or ipv4 address, and should only be used if you're
* reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
*/
bool is_ip6_address(const char *address);
#endif /* #define MISC_TOOLS_H */

401
src/name_lookup.c Normal file
View File

@ -0,0 +1,401 @@
/* name_lookup.c
*
*
* Copyright (C) 2015 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <string.h>
#include <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
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)
{
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");
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;
}

36
src/name_lookup.h Normal file
View File

@ -0,0 +1,36 @@
/* name_lookup.h
*
*
* Copyright (C) 2015 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NAME_LOOKUP
#define NAME_LOOKUP
/* Initializes http based name lookups. Note: This function must be called only once before additional
* threads are spawned.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int name_lookup_init(int curl_init_status);
int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message);
#endif /* NAME_LOOKUP */

View File

@ -1,5 +1,5 @@
/* notify.c
*
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
@ -20,12 +20,6 @@
*
*/
#include "notify.h"
#include "device.h"
#include "settings.h"
#include "line_info.h"
#include "misc_tools.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@ -36,72 +30,71 @@
#include <assert.h>
#include <sys/stat.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 */
#include "notify.h"
#include "audio_device.h"
#include "settings.h"
#include "line_info.h"
#include "misc_tools.h"
#include "xtra.h"
#ifdef _X11
#include <X11/Xlib.h>
#endif /* _X11 */
#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 _BOX_NOTIFY
#include <libnotify/notify.h>
#ifdef BOX_NOTIFY
#include <libnotify/notify.h>
#endif
#define MAX_BOX_MSG_LEN 127
#define SOUNDS_SIZE 10
#define ACTIVE_NOTIFS_MAX 50
extern struct user_settings *user_settings_;
extern struct user_settings *user_settings;
struct _Control {
struct Control {
time_t cooldown;
time_t notif_timeout;
#ifdef _X11
Display *display;
unsigned long this_window;
#endif /* _X11 */
#if defined(_SOUND_NOTIFY) || defined(_BOX_NOTIFY)
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_t poll_mutex[1];
bool poll_active;
#endif
#ifdef _SOUND_NOTIFY
#ifdef SOUND_NOTIFY
uint32_t device_idx; /* index of output device */
char* sounds[SOUNDS_SIZE];
#endif /* _SOUND_NOTIFY */
char *sounds[SOUNDS_SIZE];
#endif /* SOUND_NOTIFY */
} Control = {0};
struct _ActiveNotifications {
#ifdef _SOUND_NOTIFY
#ifdef SOUND_NOTIFY
uint32_t source;
uint32_t buffer;
bool looping;
#endif
bool active;
int *id_indicator;
#ifdef _BOX_NOTIFY
NotifyNotification* box;
#ifdef BOX_NOTIFY
NotifyNotification *box;
char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
char title[24];
char title[64];
size_t size;
time_t n_timeout;
#endif
} actives[ACTIVE_NOTIFS_MAX] = {{0}};
} actives[ACTIVE_NOTIFS_MAX];
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
@ -114,31 +107,22 @@ static void tab_notify(ToxWindow *self, uint64_t flags)
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;
}
#ifdef _X11
long unsigned int get_focused_window_id()
{
if (!Control.display) return 0;
Window focus;
int revert;
XGetInputFocus(Control.display, &focus, &revert);
return focus;
}
#endif /* _X11 */
static bool notifications_are_disabled(uint64_t flags)
{
bool res = flags & NT_RESTOL && Control.cooldown > get_unix_time();
#ifdef _X11
return res || (flags & NT_NOFOCUS && Control.this_window == get_focused_window_id());
if (user_settings->alerts != ALERTS_ENABLED)
return true;
bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time());
#ifdef X11
return res || ((flags & NT_NOFOCUS) && is_focused());
#else
return res;
#endif
@ -146,19 +130,19 @@ static bool notifications_are_disabled(uint64_t flags)
static void control_lock()
{
#if defined(_SOUND_NOTIFY) || defined(_BOX_NOTIFY)
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_lock(Control.poll_mutex);
#endif
}
static void control_unlock()
{
#if defined(_SOUND_NOTIFY) || defined(_BOX_NOTIFY)
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_unlock(Control.poll_mutex);
#endif
}
#ifdef _SOUND_NOTIFY
#ifdef SOUND_NOTIFY
bool is_playing(int source)
{
int ready;
@ -166,127 +150,205 @@ bool is_playing(int source)
return ready == AL_PLAYING;
}
/* TODO maybe find better way to do this */
/* cooldown is in seconds */
#define DEVICE_COOLDOWN 5 /* TODO perhaps load this from config? */
static bool device_opened = false;
time_t last_opened_update = 0;
/* Opens primary device. Returns true on succe*/
void m_open_device()
{
last_opened_update = get_unix_time();
if (device_opened) return;
/* Blah error check */
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
device_opened = true;
}
void m_close_device()
{
if (!device_opened) return;
close_device(output, Control.device_idx);
device_opened = false;
}
/* Terminate all sounds but wait for them to finish first */
void graceful_clear()
{
int i;
control_lock();
while (1) {
int i;
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) *actives[i].id_indicator = -1; // reset indicator value
#endif
if (actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
if ( actives[i].looping ) {
stop_sound(i);
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;
}
}
}
if (i == ACTIVE_NOTIFS_MAX) {
m_close_device(); /* In case it's opened */
control_unlock();
return;
}
usleep(1000);
}
control_unlock();
}
void* do_playing(void* _p)
void *do_playing(void *_p)
{
(void)_p;
int i;
while(Control.poll_active) {
while (true) {
control_lock();
if (!Control.poll_active) {
control_unlock();
break;
}
bool has_looping = false;
bool test_active_notify = false;
int i;
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].active && !actives[i].looping
#ifdef _BOX_NOTIFY
&& !actives[i].box
#endif
) {
if (actives[i].looping) has_looping = true;
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);
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) *actives[i].id_indicator = -1; // reset indicator value
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);
alDeleteBuffers(1, &actives[i].buffer);
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
#endif
#endif
}
/* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
if (device_opened && !has_looping &&
(time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
m_close_device();
}
has_looping = false;
control_unlock();
usleep(10000);
}
pthread_exit(NULL);
}
int play_source(uint32_t source, uint32_t buffer, bool looping)
{
{
int i = 0;
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
if ( i == ACTIVE_NOTIFS_MAX ) {
return -1; /* Full */
}
alSourcePlay(source);
actives[i].active = 1;
actives[i].source = source;
actives[i].buffer = buffer;
actives[i].looping = looping;
return i;
}
#elif _BOX_NOTIFY
void* do_playing(void* _p)
#elif BOX_NOTIFY
void *do_playing(void *_p)
{
(void)_p;
int i;
while(Control.poll_active) {
while (true) {
control_lock();
if (!Control.poll_active) {
control_unlock();
break;
}
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) *actives[i].id_indicator = -1; // reset indicator value
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);
}
@ -294,18 +356,20 @@ void graceful_clear()
{
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) *actives[i].id_indicator = -1; // reset indicator value
if (actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
control_unlock();
}
#endif
@ -321,62 +385,69 @@ void graceful_clear()
/* Opens primary device */
int init_notify(int login_cooldown, int notification_timeout)
{
#ifdef _SOUND_NOTIFY
#ifdef SOUND_NOTIFY
alutInitWithoutContext(NULL, NULL);
if (open_primary_device(output, &Control.device_idx, 48000, 20, 1) != de_None)
#endif /* SOUND_NOTIFY */
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0)
return -1;
#endif /* _SOUND_NOTIFY */
#if defined(_SOUND_NOTIFY) || defined(_BOX_NOTIFY)
pthread_mutex_init(Control.poll_mutex, NULL);
Control.poll_active = 1;
pthread_t thread;
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;
}
Control.poll_active = 1;
#endif
Control.cooldown = time(NULL) + login_cooldown;
#ifdef _X11
Control.display = XOpenDisplay(NULL);
Control.this_window = get_focused_window_id();
#endif /* _X11 */
#ifdef _BOX_NOTIFY
notify_init("toxic");
#ifdef BOX_NOTIFY
notify_init("Toxic");
#endif
Control.notif_timeout = notification_timeout;
return 1;
}
void terminate_notify()
{
#if defined(_SOUND_NOTIFY) || defined(_BOX_NOTIFY)
if ( !Control.poll_active ) return;
{
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
control_lock();
if ( !Control.poll_active ) {
control_unlock();
return;
}
Control.poll_active = 0;
control_unlock();
graceful_clear();
#endif
#ifdef _SOUND_NOTIFY
#ifdef SOUND_NOTIFY
int i = 0;
for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]);
close_device(output, Control.device_idx);
alutExit();
#endif /* _SOUND_NOTIFY */
#ifdef _BOX_NOTIFY
#endif /* SOUND_NOTIFY */
#ifdef BOX_NOTIFY
notify_uninit();
#endif
}
#ifdef _SOUND_NOTIFY
int set_sound(Notification sound, const char* value)
#ifdef SOUND_NOTIFY
int set_sound(Notification sound, const char *value)
{
if (sound == silent) return 0;
free(Control.sounds[sound]);
size_t len = strlen(value) + 1;
@ -388,24 +459,27 @@ int set_sound(Notification sound, const char* value)
}
int play_sound_internal(Notification what, bool loop)
{
{
uint32_t source;
uint32_t buffer;
m_open_device();
alGenSources(1, &source);
alGenBuffers(1, &buffer);
buffer = alutCreateBufferFromFile(Control.sounds[what]);
alSourcei(source, AL_BUFFER, buffer);
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;
}
return rc;
}
@ -414,7 +488,8 @@ int play_notify_sound(Notification notif, uint64_t flags)
int rc = -1;
if (flags & NT_BEEP) beep();
else if (notif != silent) {
if (notif != silent) {
if ( !Control.poll_active || !Control.sounds[notif] )
return -1;
@ -428,17 +503,22 @@ 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 ) {
#ifdef _BOX_NOTIFY
#ifdef BOX_NOTIFY
if (actives[id].box) {
GError* ignore;
GError *ignore;
notify_notification_close(actives[id].box, &ignore);
}
#endif
*actives[id].id_indicator = -1;
if (actives[id].id_indicator)
*actives[id].id_indicator = -1;
// alSourcei(actives[id].source, AL_LOOPING, false);
alSourceStop(actives[id].source);
alDeleteSources(1, &actives[id].source);
alDeleteBuffers(1,&actives[id].buffer);
alDeleteBuffers(1, &actives[id].buffer);
memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
}
}
@ -446,142 +526,158 @@ void stop_sound(int id)
static int m_play_sound(Notification notif, uint64_t flags)
{
#ifdef _SOUND_NOTIFY
#ifdef SOUND_NOTIFY
return play_notify_sound(notif, flags);
#else
if (notif != silent)
beep();
return -1;
#endif /* _SOUND_NOTIFY */
#endif /* SOUND_NOTIFY */
}
#ifdef _BOX_NOTIFY
void m_notify_action(NotifyNotification *box, char *action, void* data)
#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))
return -1;
int id = -1;
control_lock();
if (self && (!self->stb || self->stb->status != TOX_USERSTATUS_BUSY) && user_settings_->alerts == ALERTS_ENABLED)
if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY))
id = m_play_sound(notif, flags);
else if (flags & NT_ALWAYS)
id = m_play_sound(notif, flags);
#if defined(_BOX_NOTIFY) && !defined(_SOUND_NOTIFY)
#if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY)
if (id == -1) {
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id ++);
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
}
}
#endif
if ( id_indicator && id != -1 ) {
if ( id_indicator && id != -1 ) {
actives[id].id_indicator = id_indicator;
*id_indicator = id;
}
control_unlock();
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))
return -1;
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
#ifdef _SOUND_NOTIFY
#ifdef SOUND_NOTIFY
control_lock();
if (!actives[id].active || !Control.sounds[notif]) {
control_unlock();
return -1;
}
m_open_device();
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);
actives[id].buffer = alutCreateBufferFromFile(Control.sounds[notif]);
alSourcei(actives[id].source, AL_BUFFER, actives[id].buffer);
alSourcei(actives[id].source, AL_LOOPING, flags & NT_LOOP);
alSourcePlay(actives[id].source);
control_unlock();
return id;
#else
if (notif != silent)
beep();
return 0;
#endif /* _SOUND_NOTIFY */
#endif /* SOUND_NOTIFY */
}
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, char* title, const char* format, ...)
int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator, const char *title,
const char *format, ...)
{
if (notifications_are_disabled(flags)) {
tab_notify(self, flags);
return -1;
}
#ifdef _BOX_NOTIFY
#ifdef BOX_NOTIFY
int id = sound_notify(self, notif, flags, id_indicator);
control_lock();
#ifdef _SOUND_NOTIFY
#ifdef SOUND_NOTIFY
if (id == -1) { /* Could not play */
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
}
actives[id].active = 1;
actives[id].active = 1;
actives[id].id_indicator = id_indicator;
if (id_indicator) *id_indicator = id;
}
#endif
strncpy(actives[id].title, title, 24);
#else
if (id == -1)
return -1;
#endif /* SOUND_NOTIFY */
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
actives[id].size++;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
notify_notification_set_app_name(actives[id].box, "toxic");
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
@ -591,17 +687,17 @@ 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
#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);
return -1;
}
#ifdef _BOX_NOTIFY
#ifdef BOX_NOTIFY
if (sound_notify2(self, notif, flags, id) == -1)
return -1;
@ -613,20 +709,22 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
actives[id].size++;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
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");
}
@ -644,19 +742,21 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
#endif
}
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...)
{
tab_notify(self, flags);
if (notifications_are_disabled(flags))
return -1;
#ifdef _BOX_NOTIFY
#ifdef BOX_NOTIFY
control_lock();
int id;
int id;
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
@ -667,20 +767,22 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
*id_indicator = id;
}
strncpy(actives[id].title, title, 24);
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].active = 1;
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
notify_notification_set_app_name(actives[id].box, "toxic");
@ -694,14 +796,14 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
#endif
}
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...)
int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...)
{
tab_notify(self, flags);
if (notifications_are_disabled(flags))
return -1;
#ifdef _BOX_NOTIFY
#ifdef BOX_NOTIFY
control_lock();
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1 ) {
@ -710,7 +812,8 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form
}
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
@ -718,12 +821,13 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
for (; i < actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}

View File

@ -1,5 +1,5 @@
/* notify.h
*
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
@ -20,16 +20,15 @@
*
*/
#ifndef _notify_h
#define _notify_h
#ifndef NOTIFY_H
#define NOTIFY_H
#include <inttypes.h>
#include "windows.h"
typedef enum _Notification
{
typedef enum _Notification {
silent = -1,
error,
notif_error,
self_log_in,
self_log_out,
user_log_in,
@ -49,32 +48,33 @@ typedef enum _Flags {
NT_LOOP = 1 << 2, /* Loop sound. If this setting active, notify() will return id of the sound
* so it could be stopped. It will return 0 if error or NT_NATIVE flag is set and play \a instead
*/
NT_RESTOL = 1 << 3, /* Respect tolerance. Usually used to stop flood at toxic startup
NT_RESTOL = 1 << 3, /* Respect tolerance. Usually used to stop flood at toxic startup
* Only works if login_cooldown is true when calling init_notify()
*/
NT_NOTIFWND = 1 << 4, /* Pop notify window. NOTE: only works(/WILL WORK) if libnotify is present */
NT_WNDALERT_0 = 1 << 5, /* Alert toxic */
NT_WNDALERT_1 = 1 << 6, /* Alert toxic */
NT_WNDALERT_2 = 1 << 7, /* Alert toxic */
NT_ALWAYS = 1 << 8, /* Force sound to play */
} Flags;
int init_notify(int login_cooldown, int notification_timeout);
void terminate_notify();
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator);
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id);
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator);
int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id);
void stop_sound(int id);
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, 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);
#endif /* _SOUND_NOTIFY */
#ifdef SOUND_NOTIFY
int set_sound(Notification sound, const char *value);
#endif /* SOUND_NOTIFY */
#endif /* _notify_h */
#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();
/* 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 */

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