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

Compare commits

...

518 Commits

Author SHA1 Message Date
2ce42ab057 bump version to 0.5.0 2014-09-01 18:56:20 -04:00
934459dea8 show connection change message in chat windows 2014-08-31 14:17:33 -04:00
52bc874675 Merge pull request #234 from Ansa89/trivial-fix
Cosmetic fixes
2014-08-31 11:26:48 +02:00
511907fbc5 better way to check if files exist 2014-08-27 22:51:46 -04:00
155e194174 fix segfault on unknown long option 2014-08-27 14:30:06 -04:00
b1c7e21ca9 fix possible segfaults 2014-08-27 13:54:02 -04:00
7edcf6cb45 add setting to set path for chatlogs 2014-08-26 23:56:37 -04:00
9581940cfa Cosmetic fixes 2014-08-26 10:39:43 +02:00
f2aa57c4fa additional messages for startup options 2014-08-26 02:28:05 -04:00
8bf4405fd0 fix error code 2014-08-25 19:55:00 -04:00
21ef1788ca print init messages to prompt window intead of stderr 2014-08-25 19:26:41 -04:00
68f1dffba7 add command to decline friend requests 2014-08-25 14:08:01 -04:00
092df2c0e4 Merge pull request #231 from Ansa89/trivial-fix
Reworked manpage build system
2014-08-25 14:12:12 +02:00
691f94c75c README.md: second try 2014-08-25 12:59:34 +02:00
d6d4476e85 README.md: better look 2014-08-25 12:56:14 +02:00
924e8e0860 Reworked manpage build system 2014-08-25 12:54:44 +02:00
53193e933f Merge pull request #230 from louipc/manpage
Manpage
2014-08-25 00:49:31 -04:00
328587ad9c doc: Make asciidoc extra optional.
Completely separate man page generation from the main build to satisfy
travis-ci behaviour.

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

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

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

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

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

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-14 01:13:27 -04:00
35cc815cdb add option to specify DNSservers path 2014-08-12 22:32:36 -04:00
a318bdb034 add option to not connect to the DHT network 2014-08-12 18:59:27 -04:00
d6aaa95b25 ignore invalid file data callbacks 2014-08-12 15:27:42 -04:00
5718ad52db a few fixes 2014-08-12 03:01:18 -04:00
0f4cffbacc a few dns fixes 2014-08-11 01:59:01 -04:00
d9a861331f Use DNS server list instead of hard coding domains/keys 2014-08-10 21:40:19 -04:00
2f12a8d429 prevent statusmessage from wrapping on prompt screen 2014-08-10 00:37:26 -04:00
e75cf4f3ad fix bug where tab alert colours weren't being properly prioritized 2014-08-08 13:39:05 -04:00
bb85f31bb2 convert bytes for file transfer message, general fixes 2014-08-07 19:31:36 -04:00
396d08f0d2 refactor friend/blocklist, dynamically allocate memory 2014-08-07 13:24:29 -04:00
6ab184e7ce file transfer improvements and bug fixes 2014-08-05 17:38:33 -04:00
fd65fbfd0c temporarily disabling clang in travis script due to weird errors unrelated to toxic 2014-08-05 00:27:55 -04:00
637ea0ed55 attempt to fix travis with clang 2014-08-05 00:16:01 -04:00
dc2d20f4c4 bump version to 0.4.7 2014-08-04 22:28:12 -04:00
d712ccc17e Merge pull request #215 from mannol1/master
Fix ringing sounds
2014-08-05 01:41:21 +02:00
147030e06f Fix ringing sounds 2014-08-05 01:41:06 +02:00
09fd5cb69f small fix 2014-08-04 18:04:54 -04:00
64db9f73a2 update file transfers for core changes 2014-08-04 16:47:45 -04:00
28633be2dd a few fixes and improve error messages 2014-08-04 14:35:34 -04:00
6fdafceda8 add ability to cancel file transfers 2014-08-04 02:03:23 -04:00
38ed0c86ad use hangup cmd to cancel outgoing call requests 2014-08-03 23:11:19 -04:00
ad23816096 fix filetransfer bug 2014-08-03 16:44:21 -04:00
bc4a730e76 re-adding until better solution found 2014-08-03 15:42:14 -04:00
79372cc80d don't show full path when sending files 2014-08-03 15:12:55 -04:00
c9e4246ac5 Merge branch 'master' of https://github.com/Tox/toxic 2014-08-03 11:54:49 -04:00
dcd6a238b6 Merge pull request #213 from doughdemon/master
Add missing includes
2014-08-03 11:48:03 -04:00
c49de7733c Add missing includes
Fixes compilation with musl libc
2014-08-03 09:44:48 +02:00
3fc7c90630 stderr redirect doesn't work as intended, add invalid command message when no sound 2014-08-03 01:31:33 -04:00
53663a7832 try to end curses session gracefully on SIGSEGV 2014-08-03 00:26:44 -04:00
c14f2a3fcd put chatlogs in their own directory 2014-08-02 21:03:59 -04:00
3cbe61e111 fix a few formatting bugs and simplify code a bit 2014-08-02 15:35:57 -04:00
fa023c6a99 Merge pull request #211 from mannol1/master
Fix bug
2014-08-02 19:22:45 +02:00
f98e6bdcb4 Fix bug 2014-08-02 19:21:40 +02:00
0884954c84 fix data file init bug 2014-08-02 13:04:29 -04:00
43727c6730 Merge pull request #210 from mannol1/master
Fresh pack of backdoors
2014-08-02 17:31:36 +02:00
618b731d5a fix possible buffer overflow 2014-08-01 23:00:52 -04:00
46975bf38b Updated with latest core 2014-08-02 02:10:21 +02:00
8f3989000d Box notifications are like ready 2014-08-02 00:37:02 +02:00
9fe75fbc47 fix 2014-08-01 15:05:10 -04:00
c455e79604 dynamically adjust main loop sleep time according to recommended value 2014-08-01 13:56:49 -04:00
a223545853 close chatwindow if its associated contact is blocked/deleted 2014-07-31 22:05:09 -04:00
b243f7aa62 Merge branch 'master' of https://github.com/Tox/toxic 2014-07-31 15:13:10 -04:00
899452d7cd Merge branch 'block' 2014-07-31 15:04:05 -04:00
af68fa7ee0 endian correctness 2014-07-31 15:02:19 -04:00
5da789cc37 save last online data in blocked list 2014-07-31 14:53:02 -04:00
7e5b41c8e0 another apple include: <sys/syslimits.h>
for NAME_MAX
2014-07-31 10:52:36 -07:00
0254596c73 add help menu for friendlist 2014-07-31 13:49:15 -04:00
67c02404b7 alut is not part of OpenAL.framework on OS X
therefore the linux include path works fine
2014-07-31 10:38:04 -07:00
0b5ee7e2c7 Merge pull request #208 from Ansa89/makefile-update
Makefile: refactoring and adding desktop notifications support
2014-07-31 12:51:44 -04:00
fba0732faa implement contact blocking 2014-07-31 12:48:49 -04:00
d06086a656 Try to support older versions of OpenAL 2014-07-31 13:14:33 +02:00
e74b678739 check_features.mk: modify string comparison 2014-07-31 11:54:22 +02:00
b62787ce47 Makefile: refactoring and adding desktop notifications support 2014-07-30 14:14:13 +02:00
75708f7600 couple small fixes 2014-07-30 02:46:08 -04:00
476dec46b6 add settings to toggle typing notifications for self and others 2014-07-29 20:14:44 -04:00
973f6206ee enforce const correctess, fix undefined behaviour with string literals 2014-07-29 14:54:34 -04:00
cbe47b3660 Merge pull request #207 from Ansa89/manpage-update
Update toxic.conf manpage
2014-07-29 11:51:58 -04:00
1c58c339bb Update toxic.conf manpage 2014-07-29 12:07:24 +02:00
15e91cfa99 formatting, use case-insensitive string compare, use defines for keycodes 2014-07-28 21:47:35 -04:00
f4fb6ea4fc Merge branch 'gracchus163-master' 2014-07-28 20:45:52 -04:00
0d2ff2c0a8 Fixed toxic.conf.example mistakes 2014-07-29 01:28:10 +01:00
5275da5a6b Merge branch 'keybinds'
configurable keybinds finished and working, merging with master branch
2014-07-29 00:55:39 +01:00
e891b1281b Configurable keybinds implemented and example conf updated 2014-07-29 00:53:44 +01:00
ca7110b37c small visual adjustment to progress bar 2014-07-28 19:36:30 -04:00
8960eb98f4 Example conf updated 2014-07-28 22:47:33 +01:00
18610668b8 Human readable config parsing complete, few tests left to confirm 2014-07-28 22:44:12 +01:00
efe61e32e2 Merge pull request #204 from zetok/symbol
Fix ONLINE_CHAR being identical to OFFLINE_CHAR
2014-07-28 13:39:40 -04:00
7a7e4f573a Fix ONLINE_CHAR being identical to OFFLINE_CHAR 2014-07-28 18:38:15 +01:00
a7e6ab7758 reads correctly from config file now, need to go through and check each one and then look at usability 2014-07-28 15:19:50 +01:00
a0cde4ae8c Merge remote-tracking branch 'origin/master' into keybinds 2014-07-28 14:06:54 +01:00
7566aa9d26 Merge pull request #205 from loadedice/master
Lowered volume of sounds
2014-07-28 03:14:29 -04:00
d2332a5b77 Lowered volume of sounds 2014-07-28 16:42:57 +10:00
94a8ce5aa8 fix buggy path autocomplete behaviour 2014-07-28 01:33:12 -04:00
b18a67d656 put file senders in a round-robin queue so multiple transfers upload at a uniform pace 2014-07-27 22:27:27 -04:00
02708534c0 Merge pull request #200 from mannol1/master
Core adjustments
2014-07-28 01:35:58 +02:00
d5710d80e0 Upstream adaption 2014-07-28 01:35:40 +02:00
8dcba3219d Merge remote-tracking branch 'origin/master' into keybinds 2014-07-28 00:32:05 +01:00
9f01a45b1f Keybind defaults moved to settings.c; conf keybinds not reading correctly yet 2014-07-28 00:06:25 +01:00
dd2cb93ecc Merge pull request #203 from zetok/manpages
Put man pages in right place by default (#202)
2014-07-27 18:15:32 -04:00
49538a986c improve file transfer progress line 2014-07-27 18:00:33 -04:00
26c2331d0f Put man pages in right place by default (#202) 2014-07-27 21:06:37 +01:00
a0758643c2 fix blurry screenshot? 2014-07-27 03:36:06 -04:00
77e152ad36 fix security flaw where untrusted input wasn't being sanitized 2014-07-26 21:22:55 -04:00
4834642b80 Merge remote-tracking branch 'upstream/master' 2014-07-27 01:50:06 +02:00
18a6f621f0 Started adding support for popup notifications and adjustments to new core 2014-07-27 01:49:59 +02:00
3cae1d92cd change online/offline symbols 2014-07-26 19:16:07 -04:00
02b192d6ee fix bug and typo 2014-07-25 20:17:22 -04:00
f630a3e604 fix possible buffer overflows and undefined behaviour 2014-07-25 17:55:21 -04:00
fb5a9bc043 fix backspace bug, simplify a bit 2014-07-25 15:51:29 -04:00
26ad5a00a3 Fixed out of bounds read. 2014-07-25 15:17:46 -04:00
18e1f08e31 Merge branch 'master' of https://github.com/Tox/toxic 2014-07-24 23:04:07 -04:00
b68deef6db allow line_info_add to take formatted strings with variable # of args 2014-07-24 23:03:55 -04:00
30ec7debba Merge pull request #199 from mannol1/master
Fixed sounds not playing
2014-07-25 02:13:24 +02:00
3a1e23a3ff Fixed sounds not playing 2014-07-25 02:12:32 +02:00
0887bb7662 Merge pull request #198 from Ansa89/master
README.md: add precompiled binaries
2014-07-24 13:24:51 +02:00
5a55f738a9 README.md: add precompiled binaries 2014-07-24 13:05:56 +02:00
8d8df585ad sort directories and skip special symbols for path autocomplete 2014-07-23 18:34:32 -04:00
8660047ec1 fix small bug, update readme, bump version 2014-07-23 15:15:14 -04:00
9476db02a9 allow disabling of individual sounds 2014-07-23 14:59:36 -04:00
04fbf5f724 Merge pull request #195 from Ansa89/notify-fix
Add hardcoded path for sound notifications
2014-07-23 11:56:20 +02:00
3cc629cbc1 Add hardcoded path for sound notifications 2014-07-23 11:25:38 +02:00
50fca4cddf Merge pull request #193 from Ansa89/makefile-fix
Makefile: little refactoring
2014-07-23 03:51:52 +02:00
a047cead05 fix a few notification issues 2014-07-22 14:38:32 -04:00
1131b73299 Update toxic.conf.5 manpage 2014-07-22 16:38:20 +02:00
196af10d01 Move init and exit in right place 2014-07-22 12:30:35 +02:00
1e0e93e5c6 Makefile: little refactoring 2014-07-22 09:59:44 +02:00
ac01d6d316 fix config file loading, fix makefile compile errors 2014-07-21 18:57:16 -04:00
654e404e0e Merge pull request #190 from mannol1/master
Fixed some build errors
2014-07-21 23:49:08 +02:00
f0f1138c54 Fixed some build errors 2014-07-21 23:48:39 +02:00
bc94e08970 sendfile tab-complete for ~/ shortcut 2014-07-21 15:11:30 -04:00
42c3ede963 Merge pull request #188 from Ansa89/makefile-fix
Makefile fix
2014-07-21 13:02:25 -04:00
174568d769 README.md: add dependencies for sound notifications 2014-07-21 14:19:01 +02:00
4587d8ebbd Forgot libalut-dev dependency 2014-07-21 12:50:29 +02:00
98aae242fb Makefile fix 2014-07-21 12:33:19 +02:00
7abd8d5ee5 Merge pull request #187 from mannol1/master
Added sound notifications, libconfig support, and more...
2014-07-21 12:08:19 +02:00
98ac4d7983 Added sound notifications and prepared for later system notifications. Also, now using libconfig for configuration loading. 2014-07-21 03:25:21 +02:00
933d46553f Added sounds 2014-07-21 01:12:13 +02:00
7e667a8028 print multiple matches for all tab-complete operations, fix bug when tab-completing at end of line 2014-07-18 13:48:21 -04:00
d4e41d6053 autocomplete pathnames with spaces 2014-07-18 03:20:40 -04:00
f30dccf726 return correct value, fix comment 2014-07-18 01:52:33 -04:00
ea3fcd5b79 auto-completion for paths when sending file & improved auto-complete algorithm to do partial matches 2014-07-18 01:37:33 -04:00
e61d070def cleanup/error checks 2014-07-17 03:35:18 -04:00
b5f34f42a8 small fix 2014-07-16 21:34:50 -04:00
4426eaddd9 increase max friends 2014-07-16 15:59:54 -04:00
83f0720a39 another UB fix 2014-07-16 15:55:04 -04:00
ce4f293574 fix undefined behaviour with string literals 2014-07-16 12:51:07 -04:00
b23ae5a4c3 add buffer yanking, clean up input functions 2014-07-14 14:44:42 -04:00
190e1e73e8 change tab names: prompt = home, friends = contacts 2014-07-13 15:34:09 -04:00
ee67cf0bf1 use full name for log 2014-07-10 02:54:56 -04:00
aaeb47dc14 create dns loopup threads in detached state & add a few missing error descriptions 2014-07-09 23:40:46 -04:00
e19b0ed710 delete subwindows before parent window on resize to prevent memory leaks 2014-07-09 22:12:51 -04:00
a774121c13 Merge pull request #183 from mannol1/master
Fixes problems with upstream changes
2014-07-10 01:30:17 +02:00
df676423a7 Fixed conflicts 2014-07-10 01:29:18 +02:00
cf8dda6b0d Fixes problems with upstream changes 2014-07-10 01:24:14 +02:00
1ce731471d fix potential race conditions 2014-07-09 02:21:01 -04:00
f98c77432b exit toxic properly on ^C 2014-07-08 20:21:51 -04:00
9fa5a3fdb6 fix window cleanup bugs & give prompt its own cleanup func 2014-07-08 19:24:44 -04:00
5b9fd70f30 autosave every 60 seconds 2014-07-08 14:46:50 -04:00
442f68cd31 wrap tox_get_name in function that truncates name if it's too long 2014-07-08 14:31:59 -04:00
e74212cb9e Merge pull request #181 from Ansa89/master
Use long int instead uint64_t
2014-07-08 12:00:48 -04:00
57b52f35b4 Use long int instead uint64_t 2014-07-08 09:39:42 +02:00
27a31a8399 truncate long chat window names 2014-07-07 19:39:33 -04:00
f1a3ed379e fix nick truncating 2014-07-07 19:08:33 -04:00
60f9be7234 Merge pull request #180 from Ansa89/master
Forgot about help
2014-07-07 11:10:45 +02:00
dcfb90bc63 Forgot about help 2014-07-07 09:36:29 +02:00
74b84c4252 some code cleanup 2014-07-07 03:12:47 -04:00
00e6546f0c namelength should be signed, -Werror is probably overkill 2014-07-06 22:31:11 -04:00
a009fbf20c use char instead of uint8_t, fix compiler warnings so toxic compiles with -Wall and -Werror on gcc and clang 2014-07-06 22:16:20 -04:00
2ed9448b41 Merge pull request #174 from mannol1/master
Make closing window end call
2014-07-06 22:52:11 +02:00
1575a40d61 reduce inactive window refresh rate 2014-07-06 16:18:34 -04:00
a784fdf9d5 Merge pull request #179 from Ansa89/master
Add option to disable audio support
2014-07-06 16:16:15 -04:00
612c0e1131 Add freebsd flags for ncursesw 2014-07-06 21:23:58 +02:00
16a82e1897 Add option to disable audio support 2014-07-06 19:57:08 +02:00
ad14baf601 remove unnecessary memsets 2014-07-06 03:41:48 -04:00
8b6a5813e6 fix line_info_reset_start() bugs, a few other small fixes/cleanup 2014-07-06 01:48:50 -04:00
f4c76e12f4 A/V api change. 2014-07-05 16:19:38 -04:00
3fa8c4be0b small fix 2014-07-05 15:20:33 -04:00
2f904371ae Merge branch 'master' of https://github.com/Tox/toxic 2014-07-05 14:47:55 -04:00
455eba3bfd fix help popup flickering issue on some terminals 2014-07-05 14:46:16 -04:00
48f6a0cd5e fix typo 2014-07-04 14:25:22 -04:00
50a15d2289 update screenshot 2014-07-04 14:21:58 -04:00
de1e61bd5a code cleanup, improve welcome message 2014-07-04 14:19:20 -04:00
7fba5a59bf Fixed implicit declaration warning and merge conflict leftover 2014-07-04 18:14:06 +02:00
3a86ee923e Fixed merge conflicts 2014-07-04 18:05:50 +02:00
65e726a51a Now closing the window will end the call 2014-07-04 18:04:03 +02:00
91c4414889 update screenshot 2014-07-04 03:35:27 -04:00
6754741f37 improve /help system and add startup welcome message 2014-07-04 03:24:29 -04:00
10d0e99d72 core api const-correctness update 2014-07-02 17:30:31 -04:00
9696acc8bd remove obsolete unixtime patch 2014-07-02 16:20:03 -04:00
0f37e50419 groupchat keys can sometimes start with a 0 2014-07-02 14:13:35 -04:00
95d09e4b75 fix window resizing bug on some platforms and add -O1 optimization flag 2014-07-01 19:39:25 -04:00
7c71c35797 possibly fix segfault 2014-07-01 15:04:29 -04:00
893cfaa543 correctly reposition call infobox on resize 2014-07-01 11:17:52 -04:00
3e22c9b829 allow friendlist to be resized properly 2014-07-01 01:17:31 -04:00
a968ca2a2e update toxic version to 0.4.3 2014-07-01 00:11:40 -04:00
c271622670 fix resizing bugs 2014-06-30 23:56:47 -04:00
a126f9c1a6 don't excessively update percentage complete line during file transfers 2014-06-30 14:17:07 -04:00
44d524134f need mutexes 2014-06-30 11:42:11 -04:00
7ae807002e file sender thread was a failed experiment - do_file_senders should only be called once per tox_do iteration 2014-06-30 03:03:43 -04:00
a194f7ad87 add option to use the default locale 2014-06-29 18:14:28 -04:00
61d3f7e63e fix tab completion in prompt window 2014-06-29 16:54:27 -04:00
8715e9c41e Merge pull request #170 from Ansa89/patch-1
Manpage fix
2014-06-29 11:58:39 -04:00
5da69e7f56 Manpage fix 2014-06-29 11:48:02 +02:00
ebc5cd9285 manpage fixes/rm redundant comments & make alerts setting more intuitive 2014-06-28 22:05:05 -04:00
97536d2a72 add setting option to disable timestamps 2014-06-28 20:33:46 -04:00
a2e6a25fc8 make prompt window look like chat/groupchat windows (fixes various bugs, allows history scrolling) 2014-06-28 18:40:22 -04:00
f8998b5891 properly close file senders on exit 2014-06-28 12:55:05 -04:00
8d4f7fc32b Merge pull request #169 from Ansa89/make_help
Add help target and toxic.conf manpage
2014-06-28 12:51:42 -04:00
828b7fb505 Add help target and toxic.conf manpage 2014-06-28 18:40:59 +02:00
74525bcd14 rm Werror for clang 2014-06-28 12:24:42 -04:00
33e98fd720 put file senders in one thread due to weird issues & a few fixes 2014-06-28 12:14:43 -04:00
134e5873a9 Merge pull request #165 from mannol1/master
Fixed setting buffer to half of the size
2014-06-27 01:46:24 +02:00
89a95eca16 Fixed setting buffer to half of the size 2014-06-27 01:45:15 +02:00
d881312e3e should fix the high cpu usage issue 2014-06-26 19:35:12 -04:00
da65ba4e8d give file senders their own threads 2014-06-26 18:20:56 -04:00
e8a39e1722 man page fixes/additions, rm unneeded toxic.conf file from install 2014-06-26 13:22:22 -04:00
581261afca fix backspace issue on some platforms 2014-06-26 11:19:51 -04:00
8d68b5cc01 Merge pull request #164 from Ansa89/man-page
Add manpage
2014-06-26 14:09:44 +02:00
353be3a7a2 Add manpage 2014-06-26 14:06:22 +02:00
b9af1b3293 Merge pull request #5 from Tox/master
Sync with main project
2014-06-26 13:28:43 +02:00
09badaa9ee refactor chat window input code, misc fixes 2014-06-26 02:52:01 -04:00
587f6518f7 Merge pull request #4 from Tox/master
Sync with main project
2014-06-25 11:09:59 +02:00
6c38e72654 Merge pull request #161 from Ansa89/fix-autoconnect
Try to fix autoconnect
2014-06-25 11:00:45 +02:00
599c2119d5 README.md: better wording 2014-06-25 10:54:05 +02:00
dfd89f2b5c Makefile: fix typo...again 2014-06-25 09:56:31 +02:00
7db3dcbdf6 Makefile: fix typo 2014-06-25 09:53:05 +02:00
5b268a1a6a Try to fix autoconnect (Tox/toxic#158) 2014-06-25 09:13:57 +02:00
43c1140aa2 improve input scrolling, fix some unicode issues (WIP) 2014-06-24 21:02:16 -04:00
0bc9725b98 forgot makefile 2014-06-24 17:52:57 -04:00
ef097757f3 general cleanup: rm unused config.h, add license info to device.c/h, add a couple compiler options and fix warnings 2014-06-24 17:47:35 -04:00
6490fa598c Merge pull request #157 from Ansa89/widechar
Wide characters support
2014-06-24 17:04:21 -04:00
c41464c990 fix dns compatibility for apple 2014-06-24 13:20:12 -04:00
94d7e3199e Put back license 2014-06-24 17:48:49 +02:00
140dd5e5d3 Put back license 2014-06-24 17:46:52 +02:00
251a81ef43 Create cfg directory 2014-06-24 17:37:11 +02:00
76f81c4d33 Wide characters support and removed unused files 2014-06-24 17:35:23 +02:00
b14a1bb7b9 Merge pull request #3 from Tox/master
Sync with main project
2014-06-24 17:25:25 +02:00
5f6f021039 Merge pull request #155 from theGeekPirate/patch-1
Polishing README.md
2014-06-24 15:00:35 +02:00
9b69cecfb1 Polishing README.md
- Corrected English phrasing throughout
- Display screenshot on the page instead of a link
- Clarification as to what 'libtoxav' is
- Numeration for compilation steps instead of an unsorted list
- Separated compilation instructions into steps and notes
- Added links for dependencies
2014-06-24 05:54:14 -07:00
f75248d177 Merge pull request #153 from Ansa89/master
README.md: add build status
2014-06-24 14:02:01 +02:00
97f8d6c074 README.md: add build status 2014-06-24 13:20:21 +02:00
c29d5d1ca2 Merge pull request #1 from Tox/master
Sync with main project
2014-06-24 12:51:24 +02:00
48c272acf8 Merge pull request #152 from Ansa89/without-autotools
Update readme instructions
2014-06-24 12:48:53 +02:00
f66b9137e8 Update README.md 2014-06-24 12:46:18 +02:00
c3dfaa5935 Update README.md 2014-06-24 12:45:14 +02:00
9a9ae03e41 Update .gitignore: revert previous commit 2014-06-24 12:41:49 +02:00
2bbc47d3f7 Update .gitignore: remove autotools 2014-06-24 12:37:43 +02:00
f12b0ee472 Update .gitignore: remove autotools 2014-06-24 12:35:45 +02:00
46e4ddfaf1 Update README.md 2014-06-24 12:30:33 +02:00
449b6fa5ff Update README.md 2014-06-24 12:28:56 +02:00
3913adedb0 Update .travis.yml: cosmetic fixes 2014-06-24 12:06:13 +02:00
4cf545d334 Merge pull request #149 from Ansa89/without-autotools
Remove autotools dependency
2014-06-24 12:03:52 +02:00
9225af06b1 Update .travis.yml: moved makefile in "build/" 2014-06-24 11:53:10 +02:00
562483823c Makefile: move to "build/" 2014-06-24 11:53:02 +02:00
57742bcd87 Makefile: use <TAB> instead of <SPACE> 2014-06-24 11:01:32 +02:00
5066ea637b Update .travis.yml: remove autotools 2014-06-24 10:53:24 +02:00
7b8cf65218 Makefile: remove Windows support and bump version 2014-06-24 09:02:06 +02:00
7ac7713268 improve call duration string & remove a couple unused functions 2014-06-24 02:58:18 -04:00
490c80dae9 don't allow calls to offline friends 2014-06-23 22:15:39 -04:00
f324d2d34b some fixups 2014-06-23 21:56:58 -04:00
f3ee120c48 Merge branch 'master' of https://github.com/Tox/toxic 2014-06-23 18:54:48 -04:00
63ea6154f4 show info box for audio calls 2014-06-23 18:54:23 -04:00
eafa660dee Merge pull request #151 from mannol1/master
Forgot to set index in some callbacks
2014-06-24 00:30:37 +02:00
2a6a5b13d7 Forgot to set index in some callbacks 2014-06-24 00:30:11 +02:00
47b9648f85 Makefile: fix typo 2014-06-24 00:23:37 +02:00
55c05a4092 Merge pull request #150 from mannol1/master
Reverse call_idx and enable running call when devices fail to load
2014-06-24 00:21:22 +02:00
476b056ed0 make it dynamic 2014-06-24 00:20:44 +02:00
c53600b550 Makefile: add per-system defaults
This is only an empty structure, but I hope it will help for add per-system defaults in future
2014-06-24 00:17:14 +02:00
f47991e18e Reverse call_idx and enable running call when devices fail to load 2014-06-23 23:57:12 +02:00
773a3f4abf Remove autotools 2014-06-23 23:24:24 +02:00
809f472cb4 Remove autotools 2014-06-23 23:24:03 +02:00
e8ee3d694a Remove autotools 2014-06-23 23:23:41 +02:00
fec501801e Remove autotools 2014-06-23 23:23:17 +02:00
c52fe21237 Remove autotools 2014-06-23 23:23:05 +02:00
f00c218e56 Remove autotools 2014-06-23 23:22:32 +02:00
1daa4c5ca6 Makefile: add toxic version 2014-06-23 22:32:10 +02:00
717f8986cd Makefile: checks for libs 2014-06-23 21:55:32 +02:00
65aba6e77d Merge pull request #147 from Ansa89/cast-localtime
Cast localtime
2014-06-23 21:30:19 +02:00
9f8a6a8b6b Use pkg-config for toxcore and toxav 2014-06-23 15:34:32 +02:00
b0bfb13241 Update Makefile
Cosmetic fixes
2014-06-23 14:07:23 +02:00
34102f72a2 Hand-written makefile
Compile toxic without autotools
2014-06-23 11:09:01 +02:00
cb93c6ec65 Cast time to "time_t" 2014-06-23 10:58:24 +02:00
78af10fa1f Cast time to "time_t" 2014-06-23 10:57:27 +02:00
52b7719180 Merge pull request #146 from mannol1/master
Changed code a bit and added new features
2014-06-22 21:10:25 +02:00
48361a003e Merged upstream and other fixes 2014-06-22 21:07:11 +02:00
34bd4a1c7c fix a bunch of implicit declarations 2014-06-21 21:41:38 -04:00
94b271da5d wat 2014-06-22 02:31:24 +02:00
e47f2c05f3 Added VAD, changed device i/o, mute option, dynamic device changing and more 2014-06-22 02:18:23 +02:00
6b9ef7e6c9 refactor string funcs 2014-06-21 17:55:01 -04:00
62239a1fda fix backspace bug 2014-06-21 11:55:32 -04:00
bba81ac884 fix offset issue 2014-06-20 23:56:59 -04:00
9f4248b1e1 redesign text input field to scroll horizontally, increase max string size 2014-06-20 23:08:13 -04:00
e06f0ffb7e Missed device.* 2014-06-21 02:04:25 +02:00
29b283c176 VAD sort of works 2014-06-21 01:58:00 +02:00
3e797db16e various fixes 2014-06-20 14:27:33 -04:00
034a8f5d8b code cleanup, improve do_connection 2014-06-19 13:50:41 -04:00
eb7d910683 add toxme.se to dns3 server list 2014-06-18 20:22:26 -04:00
85af9f55ba improve key bindings (I think?) 2014-06-18 20:07:34 -04:00
ca98b49813 fix scrolling bug 2014-06-18 18:29:07 -04:00
0cff881d69 small fix 2014-06-18 18:07:51 -04:00
7eb82a0fe5 bump version to 0.4.1 2014-06-18 16:15:34 -04:00
58a131426a better handling of fatal errors 2014-06-18 15:54:05 -04:00
72e9e7d9c4 add str_to_lower function, ignore DNS domain name case 2014-06-18 13:12:39 -04:00
fc148be3e2 fix possible segfaults 2014-06-18 12:10:00 -04:00
bc51714148 exit dns threads with pthread_exit 2014-06-17 19:32:02 -04:00
b24325d879 threads for DNS lookups 2014-06-17 19:19:48 -04:00
b480e22a27 don't need this 2014-06-17 19:16:42 -04:00
afa4bc86e8 .. 2014-06-17 11:47:02 -04:00
6cd2411ec0 Merge pull request #142 from jin-eld/add-libresolv
Add libresolv
2014-06-17 11:39:02 -04:00
6f9ab56493 Remove redundant toxav header check
If pkg-config reports that toxav is there, it should be enough, no extra
checks needed.
2014-06-17 10:22:05 +02:00
485191d185 Add configure checks for libresolv 2014-06-17 10:20:42 +02:00
1456cef991 implement toxdns3 username lookups 2014-06-17 00:48:30 -04:00
53cb4b0248 ignore ^C 2014-06-15 15:36:29 -04:00
2c4f0d593d fix len values 2014-06-14 14:09:20 -04:00
499c66f411 include config header 2014-06-14 13:54:21 -04:00
1b5da956e5 more fixes 2014-06-14 03:00:04 -04:00
c76b541cb8 Merge branch 'master' of https://github.com/Tox/toxic 2014-06-14 01:55:15 -04:00
ce2d371c4b more fixes for line printing/scrolling 2014-06-14 01:45:51 -04:00
eefd981572 Merge pull request #141 from jin-eld/openal-sx
Search for OpenAL framework on OSX
2014-06-13 17:39:25 -07:00
9240295724 Search for OpenAL framework on OSX
if pkg-config does not find OpenAL on OSX, do a manual search for the
OpenAL framework.

references #140
2014-06-14 02:29:20 +02:00
a516724760 fix bug 2014-06-13 19:05:25 -04:00
a0ef4d752f small fix 2014-06-13 17:37:11 -04:00
65ad64bf42 fix some buggy behaviour with scrolling 2014-06-13 15:37:04 -04:00
b36680d767 fix bug 2014-06-13 03:47:18 -04:00
24a85df15f remove scroll mode, scroll windows via PageUp/PageDown 2014-06-13 02:10:30 -04:00
f10ce94f38 api changes 2014-06-11 20:04:20 -04:00
612c6c95eb fix openal includes on mac os
#140
2014-06-11 16:40:43 -07:00
31a2e648c5 code cleanup 2014-06-11 18:47:18 -04:00
31acdcada3 reorganize toxic_windows.h into two separate header files, rename main.c to toxic.c 2014-06-11 18:14:12 -04:00
12e33a1760 drop broken windows support 2014-06-11 14:37:36 -04:00
328e7f8d57 fix compile error when no audio support 2014-06-10 17:34:15 -04:00
33000598fc more intuitive option logic 2014-06-08 01:05:01 -04:00
6a2ef5cc6c terminate audio before core on exit 2014-06-07 17:59:17 -04:00
2a63e62aba --help message 2014-06-07 17:14:48 -04:00
935d8f8770 update dhtnodes 2014-06-07 14:50:21 -04:00
8c5ac1f77d proper commandline args 2014-06-07 14:36:37 -04:00
bfa266c604 refactor calling of toxic_string functions 2014-06-06 15:27:06 -04:00
901ffbc7c0 improve function 2014-06-04 14:41:36 -04:00
97dedd32fb better fix for trailing spaces issue 2014-06-04 01:19:20 -04:00
789c491c1e fix a few file transfer bugs 2014-06-03 16:04:57 -04:00
4b8dd3ad72 update example conf file 2014-06-03 02:06:37 -04:00
c2dce960b8 add setting to allow specified download path & some fixes 2014-06-03 02:02:24 -04:00
f9e15cd60b no trailing space for command tab-complete 2014-06-02 03:19:59 -04:00
4d249c5fe3 Bumped to 0.4.0 2014-06-01 14:53:57 -07:00
7206a9ea73 fix another possible buffer overflow
..
2014-06-01 13:42:42 -04:00
848b4e9a4c fix possible buffer overflow 2014-06-01 12:54:45 -04:00
d65d0a08aa fix chat scroll bug 2014-06-01 03:45:46 -04:00
7ccf4b6432 Little fix(tm) 2014-05-29 23:42:22 +02:00
d18ba78d03 Allow toxic to compile without av 2014-05-29 23:25:09 +02:00
ab1c97fb2b properly set default user settings 2014-05-29 13:58:37 -04:00
b0a66706bd Update README.md
Update default example path for Tox libs
2014-05-29 15:04:14 +01:00
f0962bd060 add setting to control history size 2014-05-25 19:54:34 -04:00
083ca2f3b7 missing format arg 2014-05-25 13:31:44 -04:00
8481b50f97 Merge pull request #132 from Impyy/master
Update audio_call.c
2014-05-25 18:55:40 +02:00
a04f7ee661 Merge pull request #130 from mannol1/master
Updated toxic to build against new core.
2014-05-25 18:54:19 +02:00
10d47d1ac6 Update audio_call.c
Fixed 2 small typos.
2014-05-24 15:35:10 +02:00
445f5aa1fd fix possible buffer overflow 2014-05-23 23:52:43 -04:00
1b49a89c8e correct inaccuracy 2014-05-20 20:59:43 -04:00
9bf92d1e48 Merge branch 'master' of https://github.com/Tox/toxic 2014-05-20 19:38:39 -04:00
da308b2253 fix file transfer bug 2014-05-20 19:36:53 -04:00
0c834b60f5 Not done yet. 2014-05-16 20:00:01 +02:00
2cf5430b85 Fixed a "bug" 2014-05-13 10:14:19 -07:00
30d2a5514e Yup 2014-05-13 10:11:20 -07:00
1d6a6efb81 Merge FreeBSD fixes upstream 2014-05-08 13:13:33 -07:00
af09961875 Merge pull request #128 from aitjcize/null-term
Fix file sender null terminator.
2014-05-05 15:41:09 -04:00
a46fe25283 Force terminate filename. 2014-05-06 03:39:09 +08:00
dfe3f1c4c2 Fix file sender null terminator. 2014-05-06 01:16:59 +08:00
b969079af5 Merge branch 'master' of https://github.com/Tox/toxic 2014-05-01 04:11:29 -04:00
2837c5697f small fix 2014-05-01 04:00:19 -04:00
820b619847 Update DHTnodes 2014-04-28 00:53:20 -07:00
6b72ef0720 change a few memcpy's to strcpy to fix possible segfaults 2014-04-26 04:09:51 -04:00
81125be971 show selected friend's ID at bottom of friendlist 2014-04-24 23:25:38 -04:00
decc585065 Merge branch 'master' of https://github.com/Tox/toxic 2014-04-22 21:17:28 -04:00
17ad66346e make cleaclear command refresh curses 2014-04-22 21:16:35 -04:00
e0330c984f Update README.md 2014-04-21 16:37:39 -07:00
3a9056745f Merge branch 'master' of https://github.com/Tox/toxic 2014-04-20 16:43:14 -04:00
9b997fbf01 fix bug 2014-04-20 16:42:37 -04:00
4cac797b40 General cleanup
Signed-off-by: Sean Qureshi <sean@tox.im>
2014-04-19 14:58:13 -07:00
7f5dc0a756 Removed useless line. 2014-04-19 17:49:19 -04:00
93d77fdeca load data before prompt init to fix bug with prompt logging init 2014-04-12 03:54:43 -04:00
5b3acf3f6b fix issue where log file cannot be created due to invalid name/path 2014-04-12 03:39:15 -04:00
a29136d6b7 use toxic settings for colour theme instead of config flag 2014-04-12 03:12:45 -04:00
4a8db6f098 Merge branch 'terminal_colors' of https://github.com/ooesili/toxic into ooesili-terminal_colors 2014-04-12 03:04:36 -04:00
766ae685c2 add setting to disable terminal alerts 2014-04-11 21:47:09 -04:00
e384f87a04 increase line message buffer size 2014-04-10 18:16:27 -04:00
688564cfc2 merge 2014-04-08 17:23:19 -04:00
4643996c3a add audio settings for conf file 2014-04-08 17:20:21 -04:00
eef5b4941f Merge pull request #121 from czarkoff/openbsd
Include "pthread.h"
2014-04-08 13:57:07 +02:00
4b83df3652 Merge pull request #122 from czarkoff/redeclarations
Drop typedef redeclarations
2014-04-08 13:56:39 +02:00
f54cd87abc Drop typedef redeclarations
C99 doesn't permit redeclaring typedefs in the same scope.
2014-04-08 12:11:10 +02:00
be8a0de38d Include "pthread.h"
It isn't implicit on OpenBSD.
2014-04-08 12:09:42 +02:00
9fbf7bd1c1 Merge branch 'master' of https://github.com/Tox/toxic 2014-04-08 02:51:22 -04:00
cd0bccfbeb remove leading 0 and am/pm for 12 hour timestamp in chat windows 2014-04-08 02:50:56 -04:00
c1cb367acf Update README.md 2014-04-08 02:17:19 -04:00
de3a28c6e6 create empty config file if none found && make default time 24 hours 2014-04-07 18:16:38 -04:00
5976d33fef settings_load needs to be before init_term 2014-04-07 06:22:51 -04:00
e17b62c98b Merge branch 'master' of https://github.com/Tox/toxic 2014-04-07 06:04:15 -04:00
92948abcf1 implement basic user settings 2014-04-07 06:03:45 -04:00
852c3e89d6 Merge pull request #119 from mannol1/master
Wow
2014-04-07 08:44:53 +02:00
8ce1a3d3e9 Codec settings and MORE! 2014-04-06 23:56:49 +02:00
cb9622136c Merge remote-tracking branch 'upstream/master' 2014-04-06 23:26:02 +02:00
46b57feb2f Codec settings 2014-04-06 23:26:00 +02:00
a9bcab4aee fix possible memory leak 2014-04-06 05:20:46 -04:00
0fdb01ff97 Turn off native colours by default 2014-04-05 16:20:35 -06:00
b9290c8a83 Let user disable native colours with ./configure 2014-04-05 13:31:53 -06:00
246a514e88 fix possible segfault 2014-04-02 18:57:11 -04:00
20f126e1d8 fix possible segfault 2014-04-02 17:27:07 -04:00
82027a5b4f Use default terminal fg/bg colors when we can. 2014-04-02 14:25:12 -06:00
3b2010200d possibly fix scrolling bug 2014-04-02 04:59:51 -04:00
24cd6d772f bigger history 2014-04-01 17:42:27 -04:00
c46676daa5 buffer needs to be empty 2014-04-01 16:32:53 -04:00
411ae8d0f5 off by 1 error 2014-04-01 04:43:52 -04:00
e419299487 use correct types 2014-04-01 03:53:12 -04:00
f3a8ba6ab3 small fix 2014-04-01 03:16:38 -04:00
6d98f38128 forgot a few string nulls 2014-04-01 02:49:35 -04:00
08f57de9e0 fix segfault and memory leak 2014-04-01 02:38:32 -04:00
3b7e161149 don't send null terminated strings, and null terminate all incoming strings 2014-03-31 22:34:05 -04:00
15815bf4bb split big function up 2014-03-31 21:26:09 -04:00
43a5ee2d4f esc isn't printable 2014-03-30 21:31:20 -04:00
bd817e77f0 Merge pull request #113 from graboy/master
Fixed support for wide characters
2014-03-30 17:22:41 -04:00
f8a4312fdd Repaired window switching after widechar fix 2014-03-30 16:42:27 -04:00
cce7892d94 Fixed character support 2014-03-30 16:40:13 -04:00
1420618eb0 small fix 2014-03-30 01:22:40 -04:00
52d6e8431f fix 2014-03-29 21:16:25 -04:00
1b89af9063 null fix 2014-03-29 05:44:20 -04:00
d873181306 fix scrolling bug 2014-03-29 04:40:35 -04:00
26640cda14 prevent screen from moving on input when in scroll mode 2014-03-28 19:52:26 -04:00
21c48bde5c string safety 2014-03-28 03:46:00 -04:00
8c071fb208 fix potential segfaults 2014-03-28 01:46:09 -04:00
b36a8fd8fa show percentage complete for file transfers 2014-03-27 23:05:50 -04:00
94e936575e fix memory leaks 2014-03-27 17:59:31 -04:00
1b3c40b539 put help message in chatwindow box when in scroll mode 2014-03-27 05:08:48 -04:00
bd5453002e a few more fixes 2014-03-26 19:14:28 -04:00
c218e104e7 a few fixes 2014-03-26 05:52:21 -04:00
f6db888808 Merge branch 'master' of https://github.com/stqism/toxic 2014-03-25 19:35:47 -07:00
3a804fefd1 Mention av
Signed-off-by: Sean Qureshi <sean@tox.im>
2014-03-25 19:34:11 -07:00
e0deda27da Merge branch 'master' of https://github.com/JFreegman/toxic 2014-03-25 19:27:14 -07:00
bb97f3e543 merge 2014-03-25 22:10:05 -04:00
92c0f737ac . 2014-03-25 22:02:48 -04:00
29b549e677 few fixes 2014-03-25 21:43:49 -04:00
3baa830afb move log struct to proper place 2014-03-25 08:25:10 -04:00
a5ce17f44e refactor groupchats to allow scrolling 2014-03-25 08:21:50 -04:00
2f981ecb12 make audio messages compatible with new printing method 2014-03-25 04:39:44 -04:00
5e941427d3 refactor prompt to allow scrolling 2014-03-25 03:17:22 -04:00
a40b6b1b1b replace prompt's PromptBuf struct with a ChatContext for compatibility 2014-03-24 07:55:28 -04:00
e5b6e0ad9f refactor chat history to allow scrolling 2014-03-24 07:18:58 -04:00
5956c6acaf Merge branch 'master' of https://github.com/mannol1/toxic 2014-03-23 15:32:51 -07:00
58f33fa1d6 Merge upstream 2014-03-23 22:54:56 +01:00
7384440a3d small fix 2014-03-21 04:03:07 -04:00
c1dfb741c9 typging change callback update 2014-03-20 20:59:54 -04:00
25b5545644 Update DHTnodes 2014-03-20 19:11:47 -04:00
d49e911fe4 type fixes 2014-03-19 03:14:08 -04:00
50a37495f8 API update 2014-03-18 21:48:26 -04:00
8bea44a44c properly handle invalid statuses 2014-03-18 19:34:02 -04:00
3ad82cf3b1 fix bug in issue #104 2014-03-17 22:18:04 -04:00
99e36195f7 make sure default statusmsg shows correct version 2014-03-17 20:38:30 -04:00
d03a661635 Merge branch 'master' of https://github.com/Tox/toxic 2014-03-17 19:58:08 -04:00
c5c12385ef Merge pull request #103 from mannol1/master
Open devices when call starts instead of keeping them opened all the time
2014-03-18 00:51:41 +01:00
705a55d1b5 Open devices when call starts instead of keeping them opened all the time 2014-03-18 00:50:15 +01:00
77db725822 Merge pull request #102 from graboy/master
Incorrectly handled error check for widechars
2014-03-16 20:40:26 -04:00
e6c68143bd Fixed incorrectly handled error check for longchars 2014-03-16 20:18:31 -04:00
b5cbd8e410 Merge pull request #100 from jin-eld/fix-noav
Fix toxic build when toxav is not available
2014-03-16 19:36:47 +00:00
8024757e57 Fix toxic build when toxav is not available 2014-03-16 20:28:46 +01:00
a5a7361370 Merge pull request #99 from jin-eld/issue#98
Add checks for pthreads to the build system
2014-03-16 17:01:24 +01:00
7f70345dae Add checks for pthreads to the build system
pthreads are now required, so add appropriate checks to the build system
and set the compile flags accordingly.

closes #98
2014-03-16 16:55:25 +01:00
33a4e806e2 handle last online stuff properly & update toxic version 2014-03-16 03:31:48 -04:00
1a9cd4cd2c Merge pull request #97 from mannol1/master
Fixes and stuff...
2014-03-15 14:12:02 +01:00
c055af7348 retain last status on load and some changes to statusbars 2014-03-15 07:40:13 -04:00
65eb185a9f attempt to make friendlist more visually appealing 2014-03-15 04:14:46 -04:00
a68fc671e5 show "last seen" time for offline friends 2014-03-14 23:46:24 -04:00
79fbf0a31f Fixes and stuff... 2014-03-14 23:08:08 +01:00
d29836845c get unix time more efficiently 2014-03-13 23:56:46 -04:00
c2d417c78b Merge branch 'master' of https://github.com/Tox/toxic 2014-03-13 23:31:58 -04:00
d8d198c81c rm unused argument for sort func 2014-03-13 23:30:44 -04:00
bc8b1f170e Revert "Remove all traces of autotools"
This reverts commit 514cf8e460.
2014-03-13 09:04:30 -07:00
514cf8e460 Remove all traces of autotools 2014-03-13 09:01:49 -07:00
ac82961bea fix bug and load previous status messages on boot 2014-03-13 07:34:14 -04:00
9d52b6ab5d forgot a lock 2014-03-13 06:43:53 -04:00
aeb70262e0 Merge branch 'master' of https://github.com/Tox/toxic 2014-03-13 06:27:57 -04:00
f6a85518bc implement multi-threading 2014-03-13 06:06:53 -04:00
27c5013697 ... 2014-03-12 23:18:46 -07:00
aa53076e11 Add joke unixtime patch 2014-03-12 22:19:21 -07:00
c37311ae36 an experiment gone horribly wrong 2014-03-12 18:48:25 -04:00
ce76896eb3 easier way to format timestamps 2014-03-12 03:08:13 -04:00
5e377639c8 Merge pull request #94 from lehitoskin/spellcheck
SPELLING IS FOR FOOLS
2014-03-11 22:57:33 -04:00
93fb9611f7 SPELLING IS FOR FOOLS 2014-03-11 19:54:09 -07:00
92 changed files with 10532 additions and 4877 deletions

13
.gitignore vendored
View File

@ -9,19 +9,10 @@
*.app
*.swp
*.la
m4/*
!m4/pkg.m4
configure
configure_aux
Makefile.in
aclocal.m4
config.h*
config.log
config.status
stamp-h1
autom4te.cache
.deps
.libs
*.orig
build/toxic
Makefile
build/*.o
build/*.d

View File

@ -1,59 +1,48 @@
language: c
compiler:
- gcc
- clang
# - clang # Fix me
before_script:
#installing libsodium, needed for Core
- git clone git://github.com/jedisct1/libsodium.git > /dev/null
# Installing yasm (needed for compiling vpx) and openal
- sudo apt-get -yq install yasm libopenal-dev libconfig-dev libalut-dev
# Installing libsodium, needed for toxcore
- git clone https://github.com/jedisct1/libsodium.git libsodium
- cd libsodium
- git checkout tags/0.4.2 > /dev/null
- ./autogen.sh > /dev/null
- ./configure > /dev/null
- make check -j3 > /dev/null
- sudo make install >/dev/null
- cd ..
#installing yasm, needed for compiling vpx
- sudo apt-get install yasm > /dev/null
#installing libconfig, needed for DHT_bootstrap_daemon
- wget http://www.hyperrealm.com/libconfig/libconfig-1.4.9.tar.gz > /dev/null
- tar -xvzf libconfig-1.4.9.tar.gz > /dev/null
- cd libconfig-1.4.9
- ./configure > /dev/null
- make -j3 > /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 > /dev/null
# 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 -j3 > /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 > /dev/null
# Installing vpx
- git clone http://git.chromium.org/webm/libvpx.git libvpx
- cd libvpx
- ./configure --enable-shared > /dev/null
- make -j3 >/dev/null
- make -j2 || make || exit 1 > /dev/null
- sudo make install > /dev/null
- cd ..
#creating libraries links and updating cache
# Creating libraries links and updating cache
- sudo ldconfig > /dev/null
# creating librarys' links and updating cache
- sudo ldconfig
- git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore
# 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 -j2 || make || exit 1
- sudo make install
- cd ..
- sudo apt-get install libopenal-dev -yq
script:
- autoreconf -i
- ./configure
- make -j2
- cd build
- make -j2 || make || exit 1
notifications:
email: false

View File

@ -1 +0,0 @@
See github contributors

View File

@ -672,3 +672,4 @@ may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

365
INSTALL
View File

@ -1,365 +0,0 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without warranty of any kind.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package, generally using the just-built uninstalled binaries.
4. Type `make install' to install the programs and any data files and
documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the `make install' phase executed with root
privileges.
5. Optionally, type `make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior `make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
7. Often, you can also type `make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide `make
distcheck', which can by used by developers to test that all other
targets like `make install' and `make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'. This
is known as a "VPATH" build.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple `-arch' options to the
compiler but only a single `-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the `lipo' tool if you have problems.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them. In general, the
default for these options is expressed in terms of `${prefix}', so that
specifying just `--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to `configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
`make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, `make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
`${prefix}'. Any directories that were specified during `configure',
but not in terms of `${prefix}', must each be overridden at install
time for the entire installation to be relocated. The approach of
makefile variable overrides for each directory variable is required by
the GNU Coding Standards, and ideally causes no recompilation.
However, some platforms have known limitations with the semantics of
shared libraries that end up requiring recompilation when using this
method, particularly noticeable in packages that use GNU Libtool.
The second method involves providing the `DESTDIR' variable. For
example, `make install DESTDIR=/alternate/directory' will prepend
`/alternate/directory' before all installation names. The approach of
`DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of `${prefix}'
at `configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of `make' will be. For these packages, running `./configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with `make V=1'; while running `./configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with `make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
CC is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
a workaround. If GNU CC is not installed, it is therefore recommended
to try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
in your `PATH', put it _after_ `/usr/bin'.
On Haiku, software installed for all users goes in `/boot/common',
not `/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of all of the options to `configure', and exit.
`--help=short'
`--help=recursive'
Print a summary of the options unique to this package's
`configure', and exit. The `short' variant lists options used
only in the top level, while the `recursive' variant lists options
also present in any nested packages.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names::
for more details, including other options available for fine-tuning
the installation locations.
`--no-create'
`-n'
Run the configure checks, but stop before creating any output
files.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

@ -1,3 +0,0 @@
SUBDIRS = build misc
ACLOCAL_AMFLAGS = -I m4

0
NEWS
View File

1
README
View File

@ -1 +0,0 @@

View File

@ -1,21 +1,68 @@
# Toxic
# Toxic [![Build Status](https://travis-ci.org/Tox/toxic.png?branch=master)](https://travis-ci.org/Tox/toxic)
Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
![Toxic Screenshot](https://i.imgur.com/ryaEmQZ.png "Home Screen")
Toxic is an ncurses based instant messaging client for [Tox](http://tox.im) which formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://i.imgur.com/hL7WhVl.png).
## Installation
* Generate the configure script by running the ```autoreconf -i``` command.
* Execute the configure script with ```./configure``` (you may need to pass it the location of your dependency libraries, i.e.):
```./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/toxcore --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/toxcore --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/ ```
### 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')
* Compile and install the program with ```make && sudo make install```
##### Audio
* libtoxav ([libtoxcore](https://github.com/irungentoo/toxcore) compiled with audio support)
* [openal](http://openal.org) (for Debian based systems, 'libopenal-dev')
#### Notes
If your default prefix is /usr/local and you get the error: "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run:
##### Sound notifications
* [openal](http://openal.org) (for Debian based systems, 'libopenal-dev')
* [openalut](http://openal.org) (for Debian based systems, 'libalut-dev')
##### Desktop notifications
* [libnotify](https://developer.gnome.org/libnotify) (for Debian based systems, 'libnotify-dev')
##### Documentation
* [Asciidoc](http://asciidoc.org/index.html) (only required for regenerating manpages)
* Run `make doc` after editing the asciidoc files to regenerate the manpages.
* **NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.
### Compiling
1. `cd build/`
2. `make PREFIX="/where/to/install"`
3. `sudo make install PREFIX="/where/to/install"`
### Compilation Notes
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
* `DISABLE_AV=1` → build toxic without audio call support
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support
### Packaging
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
### Troubleshooting
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
```
you can attempt to correct it by running `sudo ldconfig`. If that doesn't work, run:
```
echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
sudo ldconfig
```
If you dont already have them, you may need to install the ncurses libraries. For Debian based systems:
```
sudo apt-get install libncurses5-dev libncursesw5-dev
```
## 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).

67
build/Makefile Normal file
View File

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

View File

@ -1,60 +0,0 @@
#Don't change this unless needed, else you'll break stuff
bin_PROGRAMS = toxic
toxic_SOURCES = $(top_srcdir)/src/main.c \
$(top_srcdir)/src/chat.h \
$(top_srcdir)/src/chat.c \
$(top_srcdir)/src/configdir.h \
$(top_srcdir)/src/configdir.c \
$(top_srcdir)/src/prompt.h \
$(top_srcdir)/src/prompt.c \
$(top_srcdir)/src/friendlist.h \
$(top_srcdir)/src/friendlist.c \
$(top_srcdir)/src/toxic_windows.h \
$(top_srcdir)/src/windows.c \
$(top_srcdir)/src/groupchat.c \
$(top_srcdir)/src/groupchat.h \
$(top_srcdir)/src/global_commands.c \
$(top_srcdir)/src/global_commands.h \
$(top_srcdir)/src/chat_commands.c \
$(top_srcdir)/src/chat_commands.h \
$(top_srcdir)/src/execute.c \
$(top_srcdir)/src/execute.h \
$(top_srcdir)/src/misc_tools.c \
$(top_srcdir)/src/misc_tools.h \
$(top_srcdir)/src/toxic_strings.c \
$(top_srcdir)/src/toxic_strings.h \
$(top_srcdir)/src/log.c \
$(top_srcdir)/src/log.h \
$(top_srcdir)/src/file_senders.c \
$(top_srcdir)/src/file_senders.h
toxic_CFLAGS = -I$(top_srcdir) \
$(NCURSES_CFLAGS) \
$(LIBSODIUM_CFLAGS) \
$(LIBTOXCORE_CFLAGS)
toxic_CPPFLAGS = '-DTOXICVER="$(TOXIC_VERSION)"'
toxic_LDADD = $(LIBTOXCORE_LDFLAGS) \
$(LIBSODIUM_LDFLAGS) \
$(NCURSES_LIBS) \
$(LIBTOXCORE_LIBS) \
$(LIBSODIUM_LIBS) \
$(WINSOCK2_LIBS)
# For audio support
if BUILD_AV
toxic_SOURCES += $(top_srcdir)/src/audio_call.c \
$(top_srcdir)/src/audio_call.h
toxic_CFLAGS += $(LIBTOXAV_CFLAGS) \
$(OPENAL_CFLAGS)
toxic_LDADD += $(LIBTOXAV_LIBS) \
$(OPENAL_LIBS)
endif

23
cfg/checks/av.mk Normal file
View File

@ -0,0 +1,23 @@
# 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

@ -0,0 +1,41 @@
CHECKS_DIR = $(CFG_DIR)/checks
# Check if we can use X11
CHECK_X11_LIBS = $(shell pkg-config --exists x11 || echo -n "error")
ifneq ($(CHECK_X11_LIBS), error)
LIBS += x11
CFLAGS += -D_X11
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
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
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
endif
# Check if we can build Toxic
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
endif

View File

@ -0,0 +1,17 @@
# Variables for desktop notifications support
DESK_NOTIFY_LIBS = libnotify
DESK_NOTIFY_CFLAGS = -D_BOX_NOTIFY
# Check if we can build desktop notifications support
CHECK_DESK_NOTIFY_LIBS = $(shell pkg-config --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
endif

View File

@ -0,0 +1,23 @@
# Variables for sound notifications support
SND_NOTIFY_LIBS = openal freealut
SND_NOTIFY_CFLAGS = -D_SOUND_NOTIFY
ifneq (, $(findstring device.o, $(OBJ)))
SND_NOTIFY_OBJ =
else
SND_NOTIFY_OBJ = 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")
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
endif

23
cfg/global_vars.mk Normal file
View File

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

4
cfg/platforms/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
cfg/systems/FreeBSD.mk Normal file
View File

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

4
cfg/systems/Linux.mk Normal file
View File

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

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

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

20
cfg/targets/help.mk Normal file
View File

@ -0,0 +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
@echo "-- Variables --"
@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 " USER_CFLAGS: Add custom flags to default CFLAGS"
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
.PHONY: help

32
cfg/targets/install.mk Normal file
View File

@ -0,0 +1,32 @@
# Install target
install: toxic doc
mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
@echo "Installing toxic executable"
@install -m 0755 toxic $(abspath $(DESTDIR)/$(BINDIR))
@echo "Installing data files"
@for f in $(DATAFILES) ; do \
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)) ;\
file=$(abspath $(DESTDIR)/$(DATADIR))/$$f ;\
sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\
done
@for f in $(SNDFILES) ; do \
install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR))/sounds ;\
done
@echo "Installing man pages"
@for f in $(MANFILES) ; do \
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
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 ;\
gzip -f -9 $$file ;\
done
.PHONY: install

View File

@ -1,473 +0,0 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.65])
AC_INIT([toxic], [0.3.0], [https://tox.im/])
AC_CONFIG_AUX_DIR(configure_aux)
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([1.10 -Wall])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_CONFIG_MACRO_DIR([m4])
if test "x${prefix}" = "xNONE"; then
prefix="${ac_default_prefix}"
fi
DEPSEARCH=
LIBTOXCORE_SEARCH_HEADERS=
LIBTOXCORE_SEARCH_LIBS=
LIBSODIUM_SEARCH_HEADERS=
LIBSODIUM_SEARCH_LIBS=
LIBTOXCORE_FOUND="no"
NCURSES_FOUND="no"
NCURSES_WIDECHAR_SUPPORT="no"
AC_ARG_WITH(dependency-search,
AC_HELP_STRING([--with-dependency-search=DIR],
[search for dependencies in DIR, i.e. look for libraries in
DIR/lib and for headers in DIR/include]),
[
DEPSEARCH="$withval"
]
)
if test -n "$DEPSEARCH"; then
CFLAGS="$CFLAGS -I$DEPSEARCH/include"
CPPFLAGS="$CPPFLAGS -I$DEPSEARCH/include"
LDFLAGS="$LDFLAGS -L$DEPSEARCH/lib"
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$DEPSEARCH/lib/pkgconfig
fi
AC_ARG_WITH(libtoxcore-headers,
AC_HELP_STRING([--with-libtoxcore-headers=DIR],
[search for libtoxcore header files in DIR/tox]),
[
LIBTOXCORE_SEARCH_HEADERS="$withval"
AC_MSG_NOTICE([Will search for libtoxcore header files in $withval])
]
)
AC_ARG_WITH(libtoxcore-libs,
AC_HELP_STRING([--with-libtoxcore-libs=DIR],
[search for libtoxcore libraries in DIR]),
[
LIBTOXCORE_SEARCH_LIBS="$withval"
AC_MSG_NOTICE([Will search for libtoxcore libraries in $withval])
]
)
AC_ARG_WITH(libsodium-headers,
AC_HELP_STRING([--with-libsodium-headers=DIR],
[search for libsodium header files in DIR]),
[
LIBSODIUM_SEARCH_HEADERS="$withval"
AC_MSG_NOTICE([Will search for libsodium header files in $withval])
]
)
AC_ARG_WITH(libsodium-libs,
AC_HELP_STRING([--with-libsodium-libs=DIR],
[search for libsodium libraries in DIR]),
[
LIBSODIUM_SEARCH_LIBS="$withval"
AC_MSG_NOTICE([Will search for libsodium libraries in $withval])
]
)
WIN32=no
AC_CANONICAL_HOST
case $host_os in
*mingw*)
WIN32="yes"
;;
*freebsd*)
LDFLAGS="$LDFLAGS -L/usr/local/lib"
CFLAGS="$CFLAGS -I/usr/local/include"
CPPFLAGS="$CPPFLAGS -I/usr/local/include"
;;
esac
# Checks for programs.
AC_PROG_CC
AM_PROG_CC_C_O
AC_CHECK_HEADERS(
[limits.h locale.h stdint.h stdlib.h string.h unistd.h wchar.h wctype.h],
[],
[ AC_MSG_ERROR([required header is missing on your system]) ])
# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
AC_TYPE_SIZE_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT64_T
AC_TYPE_UINT8_T
# Checks for library functions.
AC_FUNC_MALLOC
AC_CHECK_FUNCS(
[iswprint memmove memset mkdir setlocale strchr strdup],
[],
[ AC_MSG_ERROR([required library function is missing on your system])])
# pkg-config based tests
PKG_PROG_PKG_CONFIG
if test -n "$PKG_CONFIG"; then
if test "x$WIN32" != "xyes"; then
PKG_CHECK_MODULES([NCURSES], [ncursesw],
[
NCURSES_FOUND="yes"
NCURSES_WIDECHAR_SUPPORT="yes"
],
[
NCURSES_WIDECHAR_SUPPORT="no"
PKG_CHECK_MODULES([NCURSES], [ncurses],
[
NCURSES_FOUND="yes"
],
[
AC_MSG_WARN([$NCURSES_PKG_ERRORS])
])
])
fi
else
AC_MSG_WARN([pkg-config was not found on your sytem])
fi
if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then
AC_PATH_PROG([CURSES_CONFIG], [ncursesw5-config], [no])
if test "x$CURSES_CONFIG" != "xno"; then
AC_MSG_CHECKING(ncurses cflags)
NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags`
AC_MSG_RESULT($NCURSES_CFLAGS)
AC_MSG_CHECKING(ncurses libraries)
NCURSES_LIBS=`${CURSES_CONFIG} --libs`
AC_MSG_RESULT($NCURSES_LIBS)
AC_SUBST(NCURSES_CFLAGS)
AC_SUBST(NCURSES_LIBS)
NCURSES_FOUND="yes"
NCURSES_WIDECHAR_SUPPORT="yes"
fi
fi
if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then
unset ac_cv_path_CURSES_CONFIG
AC_PATH_PROG([CURSES_CONFIG], [ncursesw5.4-config], [no])
if test "x$CURSES_CONFIG" != "xno"; then
AC_MSG_CHECKING(ncurses cflags)
NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags`
AC_MSG_RESULT($NCURSES_CFLAGS)
AC_MSG_CHECKING(ncurses libraries)
NCURSES_LIBS=`${CURSES_CONFIG} --libs`
AC_MSG_RESULT($NCURSES_LIBS)
AC_SUBST(NCURSES_CFLAGS)
AC_SUBST(NCURSES_LIBS)
NCURSES_FOUND="yes"
NCURSES_WIDECHAR_SUPPORT="yes"
fi
fi
if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then
unset ac_cv_path_CURSES_CONFIG
AC_PATH_PROG([CURSES_CONFIG], [ncurses5-config], [no])
if test "x$CURSES_CONFIG" != "xno"; then
AC_MSG_CHECKING(ncurses cflags)
NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags`
AC_MSG_RESULT($NCURSES_CFLAGS)
AC_MSG_CHECKING(ncurses libraries)
NCURSES_LIBS=`${CURSES_CONFIG} --libs`
AC_MSG_RESULT($NCURSES_LIBS)
AC_SUBST(NCURSES_CFLAGS)
AC_SUBST(NCURSES_LIBS)
NCURSES_FOUND="yes"
fi
fi
if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then
unset ac_cv_path_CURSES_CONFIG
AC_PATH_PROG([CURSES_CONFIG], [ncurses5.4-config], [no])
if test "x$CURSES_CONFIG" != "xno"; then
AC_MSG_CHECKING(ncurses cflags)
NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags`
AC_MSG_RESULT($NCURSES_CFLAGS)
AC_MSG_CHECKING(ncurses libraries)
NCURSES_LIBS=`${CURSES_CONFIG} --libs`
AC_MSG_RESULT($NCURSES_LIBS)
AC_SUBST(NCURSES_CFLAGS)
AC_SUBST(NCURSES_LIBS)
NCURSES_FOUND="yes"
fi
fi
if test "x$NCURSES_FOUND" = "xno"; then
AC_CHECK_HEADER([curses.h],
[],
[
AC_MSG_ERROR([headers for the ncurses library were not found on your system])
]
)
if test "x$WIN32" = "xyes"; then
dnl Check if pdcurses provides wide char support
NCURSES_WIDECHAR_SUPPORT="no"
AC_CHECK_LIB([pdcurses], [clear],
[],
[
AC_MSG_ERROR([required library pdcurses was not found on your system])
]
)
AC_CHECK_LIB(ws2_32, main,
[
WINSOCK2_LIBS="-lws2_32"
AC_SUBST(WINSOCK2_LIBS)
],
[
AC_MSG_ERROR([required library winsock2 was not found on the system, please check your MinGW installation])
]
)
AC_DEFINE([_WIN32_WINNT], [0x501],
[enable getaddrinfo/freeaddrinfo on XP and higher])
else
AC_CHECK_LIB([ncursesw], [wget_wch],
[
NCURSES_WIDECHAR_SUPPORT="yes"
],
[
unset ac_cv_lib_ncursesw_wget_wch
AC_CHECK_LIB([ncursesw], [wget_wch],
[
NCURSES_WIDECHAR_SUPPORT="yes"
],
[
NCURSES_WIDECHAR_SUPPORT="no"
AC_CHECK_LIB([ncurses], [clear],
[],
[
unset ac_cv_lib_ncurses_clear
AC_CHECK_LIB([ncurses], [clear],
[],
[
AC_MSG_ERROR([required library ncurses was not found on your system])
],
[
-ltinfo
]
)
]
)
],
[
-ltinfo
]
)
]
)
fi
fi
if test -n "$PKG_CONFIG"; then
PKG_CHECK_MODULES(LIBTOXCORE, [libtoxcore],
[
LIBTOXCORE_FOUND="yes"
],
[
AC_MSG_WARN([required library libsodium was not found in requested location $LIBSODIUM_SEARCH_LIBS])
])
fi
if test "x$LIBTOXCORE_FOUND" = "xno"; then
LIBSODIUM_LIBS=
LIBSODIUM_LDFLAGS=
LDFLAGS_SAVE="$LDFLAGS"
if test -n "$LIBSODIUM_SEARCH_LIBS"; then
LDFLAGS="$LDFLAGS -L$LIBSODIUM_SEARCH_LIBS"
AC_CHECK_LIB(sodium, randombytes_random,
[
LIBSODIUM_LDFLAGS="-L$LIBSODIUM_SEARCH_LIBS"
LIBSODIUM_LIBS="-lsodium"
],
[
AC_MSG_ERROR([required library libsodium was not found in requested location $LIBSODIUM_SEARCH_LIBS])
]
)
else
AC_CHECK_LIB(sodium, randombytes_random,
[],
[
AC_MSG_ERROR([required library libsodium was not found on your system, please check http://download.libsodium.org/libsodium/releases/])
]
)
fi
LDFLAGS="$LDFLAGS_SAVE"
AC_SUBST(LIBSODIUM_LIBS)
AC_SUBST(LIBSODIUM_LDFLAGS)
LIBTOXCORE_CFLAGS=
CFLAGS_SAVE="$CFLAGS"
CPPFLAGS_SAVE="$CPPFLAGS"
if test -n "$LIBTOXCORE_SEARCH_HEADERS"; then
CFLAGS="$CFLAGS -I$LIBTOXCORE_SEARCH_HEADERS"
CPPFLAGS="$CPPFLAGS -I$LIBTOXCORE_SEARCH_HEADERS"
AC_CHECK_HEADER([tox/tox.h],
[
LIBTOXCORE_CFLAGS="-I$LIBTOXCORE_SEARCH_HEADERS"
],
[
AC_MSG_ERROR([headers for the toxcore library were not found on your system])
]
)
else
AC_CHECK_HEADER([tox/tox.h],
[],
[
AC_MSG_ERROR([headers for the toxcore library were not found on your system])
],
)
fi
CFLAGS="$CFLAGS_SAVE"
CPPFLAGS="$CPPFLAGS_SAVE"
AC_SUBST(LIBTOXCORE_CFLAGS)
LIBTOXCORE_LIBS=
LIBTOXCORE_LDFLAGS=
LDFLAGS_SAVE="$LDFLAGS"
if test -n "$LIBTOXCORE_SEARCH_LIBS"; then
LDFLAGS="$LDFLAGS $LIBSODIUM_LDFLAGS -L$LIBTOXCORE_SEARCH_LIBS"
AC_CHECK_LIB([toxcore], [tox_new],
[
LIBTOXCORE_LDFLAGS="-L$LIBTOXCORE_SEARCH_LIBS"
LIBTOXCORE_LIBS="-ltoxcore"
],
[
AC_MSG_ERROR([required library toxcore was not found on your system])
],
[
$WINSOCK2_LIBS
$LIBSODIUM_LIBS
]
)
else
LDFLAGS="$LDFLAGS $LIBSODIUM_LDFLAGS"
AC_CHECK_LIB([toxcore], [tox_new],
[],
[
AC_MSG_ERROR([required library toxcore was not found on your system])
],
[
$WINSOCK2_LIBS
$LIBSODIUM_LIBS
]
)
fi
LDFLAGS="$LDFLAGS_SAVE"
AC_SUBST(LIBTOXCORE_LIBS)
AC_SUBST(LIBTOXCORE_LDFLAGS)
fi
####
#### A/V Stuff
AV_SEARCH_DIR=
BUILD_AV="yes"
AC_ARG_WITH(libtoxav-prefix,
AC_HELP_STRING([--with-libtoxav-prefix=DIR],
[search for libtoxav in DIR, i.e. look for libraries in
DIR/lib and for headers in DIR/include]),
[
AV_SEARCH_DIR="$withval"
]
)
if test -n "$AV_SEARCH_DIR"; then
CFLAGS="$CFLAGS -I$AV_SEARCH_DIR/include"
CPPFLAGS="$CPPFLAGS -I$AV_SEARCH_DIR/include"
LDFLAGS="$LDFLAGS -L$AV_SEARCH_DIR/lib"
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$AV_SEARCH_DIR/lib/pkgconfig
fi
# Check if specified enable
AC_ARG_ENABLE([av],
[AC_HELP_STRING([--disable-av], [build AV support libraries (default: auto)]) ],
[
if test "x$enableval" = "xno"; then
BUILD_AV="no"
elif test "x$enableval" = "xyes"; then
BUILD_AV="yes"
fi
]
)
# Check for A/V library
if test "x$BUILD_AV" = "xyes"; then
PKG_CHECK_MODULES([OPENAL], [openal],
[
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
PKG_CHECK_MODULES([LIBTOXAV], [libtoxav],
[
AC_CHECK_HEADER([tox/toxav.h],
[
# Place define for audio support
AC_DEFINE([_SUPPORT_AUDIO], [], [Is audio supported])
AC_MSG_NOTICE([Building with audio support])
],
[
AC_MSG_NOTICE([No A/V headers; disabling A/V support])
BUILD_AV="no"
],)
],
[
AC_MSG_NOTICE([No A/V library; disabling A/V support])
])
],
[
AC_MSG_NOTICE([No openal library; disabling A/V support])
])
fi
AM_CONDITIONAL(BUILD_AV, test "x$BUILD_AV" = "xyes")
TOXIC_VERSION="$PACKAGE_VERSION"
AC_PATH_PROG([GIT], [git], [no])
if test "x$GIT" != "xno"; then
if test -d ${srcdir}/.git; then
TOXIC_VERSION="${TOXIC_VERSION}_r`${GIT} rev-list HEAD --count`"
fi
fi
AC_SUBST(TOXIC_VERSION)
eval PACKAGE_DATADIR="${datadir}/${PACKAGE}"
eval PACKAGE_DATADIR="${PACKAGE_DATADIR}"
AC_DEFINE_UNQUOTED(PACKAGE_DATADIR, "$PACKAGE_DATADIR", [toxic data directory])
if test "x$NCURSES_WIDECHAR_SUPPORT" = "xyes"; then
AC_DEFINE([HAVE_WIDECHAR], [1], [ncurses wide char support available])
AC_DEFINE([_XOPEN_SOURCE_EXTENDED], [1],
[enable X/Open Portability Guide functionality])
fi
AC_CONFIG_FILES([Makefile
misc/Makefile
build/Makefile])
AC_OUTPUT

143
doc/toxic.1 Normal file
View File

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

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

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

271
doc/toxic.conf.5 Normal file
View File

@ -0,0 +1,271 @@
'\" t
.\" Title: toxic.conf
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2014-08-21
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC\&.CONF" "5" "2014\-08\-21" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
toxic.conf \- Configuration file for toxic
.SH "SYNOPSIS"
.sp
~/\&.config/tox/toxic\&.conf
.SH "DESCRIPTION"
.sp
The \fItoxic\&.conf\fR file is the main configuration file for \fBtoxic\fR(1) client\&. It uses syntax accepted by \fBlibconfig\fR\&. Lines starting with "//" are comments and will be ignored\&.
.SH "EXAMPLE"
.sp
.if n \{\
.RS 4
.\}
.nf
// Configuration for interface
ui = {
timestamps = true;
alerts = false;
};
// Configuration for audio
audio = {
input_device = 1;
};
.fi
.if n \{\
.RE
.\}
.SH "OPTIONS"
.PP
\fBui\fR
.RS 4
Configuration related to interface elements\&.
.PP
\fBtimestamps\fR
.RS 4
Enable or disable timestamps\&. true or false
.RE
.PP
\fBalerts\fR
.RS 4
Enable or disable terminal alerts on events\&. true or false
.RE
.PP
\fBnative_colors\fR
.RS 4
Select between native terminal colors and toxic color theme\&. true or false
.RE
.PP
\fBautolog\fR
.RS 4
Enable or disable autologging\&. true or false
.RE
.PP
\fBtime_format\fR
.RS 4
Select between 24 and 12 hour time\&. Specify 24 or 12
.RE
.PP
\fBshow_typing_other\fR
.RS 4
Show when others are typing in a 1\-on\-1 chat\&. true or false
.RE
.PP
\fBshow_typing_self\fR
.RS 4
Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false
.RE
.PP
\fBhistory_size\fR
.RS 4
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
.RE
.RE
.PP
\fBaudio\fR
.RS 4
Configuration related to audio devices\&.
.PP
\fBinput_device\fR
.RS 4
Audio input device\&. Integer value\&. Number corresponds to
/lsdev in
.RE
.PP
\fBoutput_device\fR
.RS 4
Audio output device\&. Integer value\&. Number corresponds to
/lsdev out
.RE
.PP
\fBVAD_treshold\fR
.RS 4
Voice Activity Detection treshold\&. Float value\&. Recommended values are around 40\&.0
.RE
.RE
.PP
\fBtox\fR
.RS 4
Configuration related to paths\&.
.PP
\fBdownload_path\fR
.RS 4
Default path for downloads\&. String value\&. Absolute path for downloaded files\&.
.RE
.PP
\fBchatlogs_path\fR
.RS 4
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
.RE
.RE
.PP
\fBsounds\fR
.RS 4
Configuration related to notification sounds\&. Special value "silent" can be used to disable a specific notification\&.
Each value is a string which corresponds to the absolute path of a wav sound file\&.
.PP
\fBerror\fR
.RS 4
Sound to play when an error occurs\&.
.RE
.PP
\fBself_log_in\fR
.RS 4
Sound to play when you log in\&.
.RE
.PP
\fBself_log_out\fR
.RS 4
Sound to play when you log out\&.
.RE
.PP
\fBuser_log_in\fR
.RS 4
Sound to play when a contact become online\&.
.RE
.PP
\fBuser_log_out\fR
.RS 4
Sound to play when a contact become offline\&.
.RE
.PP
\fBcall_incoming\fR
.RS 4
Sound to play when you receive an incoming call\&.
.RE
.PP
\fBcall_outgoing\fR
.RS 4
Sound to play when you start a call\&.
.RE
.PP
\fBgeneric_message\fR
.RS 4
Sound to play when an event occurs\&.
.RE
.PP
\fBtransfer_pending\fR
.RS 4
Sound to play when you receive a file transfer request\&.
.RE
.PP
\fBtransfer_completed\fR
.RS 4
Sound to play when a file transfer is completed\&.
.RE
.RE
.PP
\fBkeys\fR
.RS 4
Configuration related to user interface interaction\&. Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN\&.
Each value is a string which corresponds to a key combination\&.
.PP
\fBnext_tab\fR
.RS 4
Key combination to switch next tab\&.
.RE
.PP
\fBprev_tab\fR
.RS 4
Key combination to switch previous tab\&.
.RE
.PP
\fBscroll_line_up\fR
.RS 4
Key combination to scroll one line up\&.
.RE
.PP
\fBscroll_line_down\fR
.RS 4
Key combination to scroll one line down\&.
.RE
.PP
\fBhalf_page_up\fR
.RS 4
Key combination to scroll half page up\&.
.RE
.PP
\fBhalf_page_down\fR
.RS 4
Key combination to scroll half page down\&.
.RE
.PP
\fBpage_bottom\fR
.RS 4
Key combination to scroll to page bottom\&.
.RE
.PP
\fBpeer_list_up\fR
.RS 4
Key combination to scroll contacts list up\&.
.RE
.PP
\fBpeer_list_down\fR
.RS 4
Key combination to scroll contacts list down\&.
.RE
.RE
.SH "FILES"
.PP
~/\&.config/tox/toxic\&.conf
.RS 4
Main configuration file\&.
.RE
.PP
__DATADIR__/toxic\&.conf\&.example
.RS 4
Configuration example\&.
.RE
.SH "SEE ALSO"
.sp
\fBtoxic\fR(1)
.SH "RESOURCES"
.sp
Project page: https://github\&.com/Tox/toxic
.sp
IRC channel: chat\&.freenode\&.net#tox
.SH "AUTHORS"
.sp
JFreegman <JFreegman@gmail\&.com>

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

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

199
m4/pkg.m4
View File

@ -1,199 +0,0 @@
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
# serial 1 (pkg-config-0.24)
#
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# PKG_PROG_PKG_CONFIG([MIN-VERSION])
# ----------------------------------
AC_DEFUN([PKG_PROG_PKG_CONFIG],
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
fi
if test -n "$PKG_CONFIG"; then
_pkg_min_version=m4_default([$1], [0.9.0])
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
PKG_CONFIG=""
fi
fi[]dnl
])# PKG_PROG_PKG_CONFIG
# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
#
# Check to see whether a particular set of modules exists. Similar
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
#
# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
# only at the first occurence in configure.ac, so if the first place
# it's called might be skipped (such as if it is within an "if", you
# have to call PKG_CHECK_EXISTS manually
# --------------------------------------------------------------
AC_DEFUN([PKG_CHECK_EXISTS],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
if test -n "$PKG_CONFIG" && \
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
m4_default([$2], [:])
m4_ifvaln([$3], [else
$3])dnl
fi])
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
# ---------------------------------------------
m4_define([_PKG_CONFIG],
[if test -n "$$1"; then
pkg_cv_[]$1="$$1"
elif test -n "$PKG_CONFIG"; then
PKG_CHECK_EXISTS([$3],
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes ],
[pkg_failed=yes])
else
pkg_failed=untried
fi[]dnl
])# _PKG_CONFIG
# _PKG_SHORT_ERRORS_SUPPORTED
# -----------------------------
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi[]dnl
])# _PKG_SHORT_ERRORS_SUPPORTED
# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
# [ACTION-IF-NOT-FOUND])
#
#
# Note that if there is a possibility the first call to
# PKG_CHECK_MODULES might not happen, you should be sure to include an
# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
#
#
# --------------------------------------------------------------
AC_DEFUN([PKG_CHECK_MODULES],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
pkg_failed=no
AC_MSG_CHECKING([for $1])
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
and $1[]_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.])
if test $pkg_failed = yes; then
AC_MSG_RESULT([no])
_PKG_SHORT_ERRORS_SUPPORTED
if test $_pkg_short_errors_supported = yes; then
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
else
$1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
m4_default([$4], [AC_MSG_ERROR(
[Package requirements ($2) were not met:
$$1_PKG_ERRORS
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
_PKG_TEXT])[]dnl
])
elif test $pkg_failed = untried; then
AC_MSG_RESULT([no])
m4_default([$4], [AC_MSG_FAILURE(
[The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
_PKG_TEXT
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
])
else
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
AC_MSG_RESULT([yes])
$3
fi[]dnl
])# PKG_CHECK_MODULES
# PKG_INSTALLDIR(DIRECTORY)
# -------------------------
# Substitutes the variable pkgconfigdir as the location where a module
# should install pkg-config .pc files. By default the directory is
# $libdir/pkgconfig, but the default can be changed by passing
# DIRECTORY. The user can override through the --with-pkgconfigdir
# parameter.
AC_DEFUN([PKG_INSTALLDIR],
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
m4_pushdef([pkg_description],
[pkg-config installation directory @<:@]pkg_default[@:>@])
AC_ARG_WITH([pkgconfigdir],
[AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
[with_pkgconfigdir=]pkg_default)
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
m4_popdef([pkg_default])
m4_popdef([pkg_description])
]) dnl PKG_INSTALLDIR
# PKG_NOARCH_INSTALLDIR(DIRECTORY)
# -------------------------
# Substitutes the variable noarch_pkgconfigdir as the location where a
# module should install arch-independent pkg-config .pc files. By
# default the directory is $datadir/pkgconfig, but the default can be
# changed by passing DIRECTORY. The user can override through the
# --with-noarch-pkgconfigdir parameter.
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
m4_pushdef([pkg_description],
[pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
AC_ARG_WITH([noarch-pkgconfigdir],
[AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
[with_noarch_pkgconfigdir=]pkg_default)
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
m4_popdef([pkg_default])
m4_popdef([pkg_description])
]) dnl PKG_NOARCH_INSTALLDIR

View File

@ -1,2 +1,10 @@
192.254.75.98 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B
2607:5600:284::2 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B
192.254.75.98 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
37.187.46.132 33445 A9D98212B3F972BD11DA52BEB0658C326FCCC1BFD49F347F9C2D3D8B61E1B927
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
37.59.102.176 33445 B98A2CEAA6C6A2FADC2C3632D284318B60FE5375CCB41EFA081AB67F500C1B0B
95.85.13.245 33445 7187969BB10B54C98538BAE94C069CE5C84E650D54F7E596543D8FB1ECF4CF23
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
76.191.23.96 33445 4BA57660DE3E854C530EED601BF8D54B7EFAE960523B6CFC10210CC08E2CB808

2
misc/DNSservers Normal file
View File

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

View File

@ -1 +0,0 @@
dist_pkgdata_DATA = DHTnodes

76
misc/toxic.conf.example Normal file
View File

@ -0,0 +1,76 @@
// SAMPLE TOXIC CONFIGURATION
// USES LIBCONFIG-ACCEPTED SYNTAX
ui = {
// true to enable timestamps, false to disable
timestamps=true;
// true to enable terminal alerts on messages, false to disable
alerts=true;
// true to use native terminal colours, false to use toxic default colour theme
native_colors=false;
// true to enable autologging, false to disable
autolog=false;
// 24 or 12 hour time
time_format=24;
// true to show you when others are typing a message in 1-on-1 chats
show_typing_other=true;
// true to show others when you're typing a message in 1-on-1 chats
show_typing_self=true;
// maximum lines for chat window history
history_size=700;
};
audio = {
// preferred audio input device; numbers correspond to /lsdev in
input_device=2;
// 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;
};
tox = {
// Path for downloaded files
// download_path="/home/USERNAME/Downloads/";
// Path for chatlogs
// chatlogs_path="/home/USERNAME/toxic_chatlogs/";
};
// To disable a sound set the path to "silent"
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";
};
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
// Note: All printable keys register as input
keys = {
next_tab="Ctrl+P";
prev_tab="Ctrl+O";
scroll_line_up="PAGEUP";
scroll_line_down="PAGEDOWN";
half_page_up="Ctrl+F";
half_page_down="Ctrl+V";
page_bottom="Ctrl+H";
peer_list_up="Ctrl+[";
peer_list_down="Ctrl+]";
};

BIN
sounds/ContactLogsIn.wav Normal file

Binary file not shown.

BIN
sounds/ContactLogsOut.wav Normal file

Binary file not shown.

BIN
sounds/Error.wav Normal file

Binary file not shown.

BIN
sounds/IncomingCall.wav Normal file

Binary file not shown.

BIN
sounds/LogIn.wav Normal file

Binary file not shown.

BIN
sounds/LogOut.wav Normal file

Binary file not shown.

BIN
sounds/NewMessage.wav Normal file

Binary file not shown.

BIN
sounds/OutgoingCall.wav Normal file

Binary file not shown.

BIN
sounds/TransferComplete.wav Normal file

Binary file not shown.

BIN
sounds/TransferPending.wav Normal file

Binary file not shown.

1
sounds/license Normal file
View File

@ -0,0 +1 @@
Tox's sounds are licensed under the "Creative Commons Attribution 3.0 Unported", all credit attributed to Adam Reid.

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* audio_call.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 _audio_h
@ -7,24 +25,21 @@
#include <tox/toxav.h>
typedef struct ToxWindow ToxWindow;
#include "device.h"
typedef enum _AudioError
{
NoError = 0,
ErrorStartingCaptureDevice = 1 << 0,
ErrorStartingOutputDevice = 1 << 1,
ErrorStartingCoreAudio = 1 << 2
typedef enum _AudioError {
ae_None = 0,
ae_StartingCaptureDevice = 1 << 0,
ae_StartingOutputDevice = 1 << 1,
ae_StartingCoreAudio = 1 << 2
} AudioError;
/* You will have to pass pointer to first member of 'windows'
* declared in windows.c otherwise undefined behaviour will
/* You will have to pass pointer to first member of 'windows'
* declared in windows.c otherwise undefined behaviour will
*/
ToxAv* init_audio(ToxWindow* window, Tox* tox);
ToxAv *init_audio(ToxWindow *self, Tox *tox);
void terminate_audio();
int errors();
void stop_current_call(ToxWindow *self);
int start_transmission();
#endif /* _audio_h */
#endif /* _audio_h */

291
src/autocomplete.c Normal file
View File

@ -0,0 +1,291 @@
/* autocomplete.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 <limits.h>
#ifdef __APPLE__
#include <sys/types.h>
#include <sys/dir.h>
#else
#include <dirent.h>
#endif /* ifdef __APPLE__ */
#include "windows.h"
#include "toxic.h"
#include "misc_tools.h"
#include "line_info.h"
#include "execute.h"
#include "configdir.h"
static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items, int size)
{
if (m)
execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
const char *L = (char *) list;
int i;
for (i = 0; i < n_items; ++i)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); /* formatting */
}
/* puts match in match buffer. if more than one match, add first n chars that are identical.
e.g. if matches contains: [foo, foobar, foe] we put fo in matches. */
static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_SIZE], int n)
{
if (n == 1) {
strcpy(match, matches[0]);
return;
}
int i;
for (i = 0; i < MAX_STR_SIZE; ++i) {
char ch1 = matches[0][i];
int j;
for (j = 0; j < n; ++j) {
char ch2 = matches[j][i];
if (ch1 != ch2 || !ch1) {
strcpy(match, matches[0]);
match[i] = '\0';
return;
}
}
}
strcpy(match, matches[0]);
}
/* looks for all instances in list that begin with the last entered word in line according to pos,
then fills line with the complete word. e.g. "Hello jo" would complete the line
with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of line on success, -1 if error */
int complete_line(ToxWindow *self, const void *list, int n_items, int size)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
return -1;
const char *L = (char *) list;
const char *endchrs = " ";
char ubuf[MAX_STR_SIZE];
/* work with multibyte string copy of buf for simplicity */
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
return -1;
bool dir_search = strncmp(ubuf, "/sendfile", strlen("/sendfile")) == 0;
/* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", ubuf);
tmp[ctx->pos] = '\0';
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
char *sub = malloc(strlen(ubuf) + 1);
if (sub == NULL)
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
if (!s && !dir_search) {
strcpy(sub, tmp);
if (sub[0] != '/')
endchrs = ": ";
} else if (s) {
strcpy(sub, &s[1]);
if (dir_search) {
int sub_len = strlen(sub);
int si = char_rfind(sub, '/', sub_len);
if (si || *sub == '/')
memmove(sub, &sub[si + 1], sub_len - si);
}
}
if (string_is_empty(sub)) {
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];
if (strncasecmp(str, sub, s_len) == 0)
strcpy(matches[n_matches++], str);
}
free(sub);
if (!n_matches)
return -1;
if (!dir_search && n_matches > 1)
print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
char match[MAX_STR_SIZE];
get_str_match(self, match, matches, n_matches);
if (dir_search) {
if (n_matches == 1)
endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/";
else
endchrs = "";
} else if (n_matches > 1) {
endchrs = "";
}
/* put match in correct spot in buf and append endchars */
int n_endchrs = strlen(endchrs);
int m_len = strlen(match);
int strt = ctx->pos - s_len;
int diff = m_len - s_len + n_endchrs;
if (ctx->len + diff >= MAX_STR_SIZE)
return -1;
char tmpend[MAX_STR_SIZE];
strcpy(tmpend, &ubuf[ctx->pos]);
strcpy(&ubuf[strt], match);
strcpy(&ubuf[strt + m_len], endchrs);
strcpy(&ubuf[strt + m_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)
return -1;
wcscpy(ctx->line, newbuf);
ctx->len += diff;
ctx->pos += diff;
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)
{
ChatContext *ctx = self->chatwin;
char homedir[MAX_STR_SIZE];
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]);
wchar_t wline[MAX_STR_SIZE];
if (mbs_to_wcs_buf(wline, newline, sizeof(wline)) == -1)
return;
int newlen = wcslen(wline);
if (ctx->len + newlen >= MAX_STR_SIZE)
return;
wmemcpy(ctx->line, wline, newlen + 1);
ctx->pos = newlen;
ctx->len = ctx->pos;
}
/* attempts to match /sendfile "<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)
{
char b_path[MAX_STR_SIZE];
char b_name[MAX_STR_SIZE];
const wchar_t *tmpline = &line[11]; /* start after "/sendfile \"" */
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
return -1;
if (b_path[0] == '~')
complt_home_dir(self, b_path, sizeof(b_path));
int si = char_rfind(b_path, '/', strlen(b_path));
if (!b_path[0]) { /* list everything in pwd */
b_path[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);
}
strcpy(b_name, &b_path[si + 1]);
b_path[si + 1] = '\0';
int b_name_len = strlen(b_name);
DIR *dp = opendir(b_path);
if (dp == NULL)
return -1;
char dirnames[MAX_DIRS][NAME_MAX];
struct dirent *entry;
int dircount = 0;
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
if (strncmp(entry->d_name, b_name, b_name_len) == 0
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
++dircount;
}
}
if (dircount == 0)
return -1;
if (dircount > 1) {
qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr);
print_matches(self, m, dirnames, dircount, NAME_MAX);
}
return complete_line(self, dirnames, dircount, NAME_MAX);
}

42
src/autocomplete.h Normal file
View File

@ -0,0 +1,42 @@
/* autocomplete.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 _autocomplete_h
#define _autocomplete_h
/* looks for all instances in list that begin with the last entered word in line according to pos,
then fills line with the complete word. e.g. "Hello jo" would complete the line
with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of line on success, -1 if error */
int complete_line(ToxWindow *self, const void *list, int n_items, int size);
/* attempts to match /sendfile "<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);
#endif /* #define _autocomplete_h */

1257
src/chat.c

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -20,108 +20,125 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "toxic_windows.h"
#include "toxic.h"
#include "windows.h"
#include "misc_tools.h"
#include "friendlist.h"
#include "execute.h"
#include "line_info.h"
#include "groupchat.h"
#include "chat.h"
#include "file_senders.h"
extern ToxWindow *prompt;
extern ToxicFriend friends[MAX_FRIENDS_NUM];
extern _Friends Friends;
extern FileSender file_senders[MAX_FILES];
extern uint8_t max_file_senders_index;
extern uint8_t num_active_file_senders;
void cmd_chat_help(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])
{
if (argc == 1) {
if (!strcmp(argv[1], "global")) {
execute(window, self, m, "/help", GLOBAL_COMMAND_MODE);
return;
}
if (argc < 2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
return;
}
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, "Chat commands:\n");
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
const char *inoutstr = argv[1];
int filenum = atoi(argv[2]);
#ifdef _SUPPORT_AUDIO
if (filenum >= MAX_FILES || filenum < 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
wprintw(window, " /call : Audio call\n");
wprintw(window, " /cancel : Cancel call\n");
wprintw(window, " /answer : Answer incomming call\n");
wprintw(window, " /hangup : Hangup active call\n");
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;
}
#endif /* _SUPPORT_AUDIO */
wprintw(window, " /invite <n> : Invite friend to a group chat\n");
wprintw(window, " /join : Join a pending group chat\n");
wprintw(window, " /log <on> or <off> : Enable/disable logging\n");
wprintw(window, " /sendfile <filepath> : Send a file\n");
wprintw(window, " /savefile <n> : Receive a file\n");
wprintw(window, " /close : Close the current chat window\n");
wprintw(window, " /help : Print this message again\n");
wprintw(window, " /help global : Show a list of global commands\n");
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n\n");
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
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;
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
return;
}
}
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
wprintw(window, "Invalid syntax.\n");
return;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
return;
}
int groupnum = atoi(argv[1]);
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
wprintw(window, "Invalid syntax.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
return;
}
if (tox_invite_friend(m, self->num, groupnum) == -1) {
wprintw(window, "Failed to invite friend.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group.");
return;
}
wprintw(window, "Invited friend to group chat %d.\n", groupnum);
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) {
wattron(window, COLOR_PAIR(RED));
wprintw(window, " * Warning: Too many windows are open.\n");
wattron(window, COLOR_PAIR(RED));
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
uint8_t *groupkey = friends[self->num].pending_groupchat;
const char *groupkey = Friends.list[self->num].groupchat_key;
if (groupkey[0] == '\0') {
wprintw(window, "No pending group chat invite.\n");
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, groupkey);
int groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey);
if (groupnum == -1) {
wprintw(window, "Group chat instance failed to initialize.\n");
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) {
wprintw(window, "Group chat window failed to initialize.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
return;
}
@ -129,72 +146,81 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc != 1) {
wprintw(window, "Invalid syntax.\n");
return;
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
return;
}
uint8_t filenum = atoi(argv[1]);
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
wprintw(window, "No pending file transfers with that number.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
if (!friends[self->num].file_receiver.pending[filenum]) {
wprintw(window, "No pending file transfers with that number.\n");
if (!Friends.list[self->num].file_receiver[filenum].pending) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
uint8_t *filename = friends[self->num].file_receiver.filenames[filenum];
const char *filename = Friends.list[self->num].file_receiver[filenum].filename;
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
wprintw(window, "Accepted file transfer %u. Saving file as: '%s'\n", filenum, filename);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", filenum, filename);
if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) {
wattron(window, COLOR_PAIR(RED));
wprintw(window, "* Error writing to file.\n");
wattroff(window, COLOR_PAIR(RED));
/* 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 {
wprintw(window, "File transfer failed.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed.");
}
friends[self->num].file_receiver.pending[filenum] = false;
Friends.list[self->num].file_receiver[filenum].pending = false;
}
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (max_file_senders_index >= (MAX_FILES-1)) {
wprintw(window,"Please wait for some of your outgoing file transfers to complete.\n");
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;
}
if (argc < 1) {
wprintw(window, "Invalid syntax.\n");
return;
}
uint8_t *path = argv[1];
if (path[0] != '\"') {
wprintw(window, "File path must be enclosed in quotes.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
return;
}
path[strlen(++path)-1] = L'\0';
int path_len = strlen(path);
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes.");
return;
}
if (path_len > MAX_STR_SIZE) {
wprintw(window, "File path exceeds character limit.\n");
/* remove opening and closing quotes */
char path[MAX_STR_SIZE];
snprintf(path, sizeof(path), "%s", &argv[1][1]);
int path_len = strlen(path) - 1;
path[path_len] = '\0';
if (path_len >= MAX_STR_SIZE) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
return;
}
FILE *file_to_send = fopen(path, "r");
if (file_to_send == NULL) {
wprintw(window, "File '%s' not found.\n", path);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found.");
return;
}
@ -202,12 +228,13 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
uint64_t filesize = ftell(file_to_send);
fseek(file_to_send, 0, SEEK_SET);
uint8_t filename[MAX_STR_SIZE];
get_file_name(path, filename);
int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename) + 1);
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) {
wprintw(window, "Error sending file.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error sending file.");
return;
}
@ -215,22 +242,29 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
for (i = 0; i < MAX_FILES; ++i) {
if (!file_senders[i].active) {
memcpy(file_senders[i].pathname, path, path_len + 1);
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 = (uint8_t) filenum;
file_senders[i].filenum = filenum;
file_senders[i].friendnum = self->num;
file_senders[i].timestamp = (uint64_t) time(NULL);
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);
wprintw(window, "Sending file: '%s'\n", path);
char sizestr[32];
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"Sending file [%d]: '%s' (%s)", filenum, filename, sizestr);
++num_active_file_senders;
if (i == max_file_senders_index)
++max_file_senders_index;
reset_file_sender_queue();
return;
}
}
}
}

View File

@ -20,16 +20,27 @@
*
*/
void cmd_chat_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifndef _chat_commands_h
#define _chat_commands_h
#include "windows.h"
#include "toxic.h"
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 _SUPPORT_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]);
void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* _SUPPORT_AUDIO */
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 /* #define _chat_commands_h */

View File

@ -20,103 +20,76 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#ifdef _WIN32
#include <shlobj.h>
#include <direct.h>
#else /* WIN32 */
#include <unistd.h>
#include <pwd.h>
#endif /* WIN32 */
#include "toxic.h"
#include "configdir.h"
/* get the user's home directory */
void get_home_dir(char *home, int size)
{
struct passwd pwd;
struct passwd *pwdbuf;
const char *hmstr;
char buf[NSS_BUFLEN_PASSWD];
int rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
if (rc == 0) {
hmstr = pwd.pw_dir;
} else {
hmstr = getenv("HOME");
if (hmstr == NULL)
return;
snprintf(buf, sizeof(buf), "%s", hmstr);
hmstr = buf;
}
snprintf(home, size, "%s", hmstr);
}
/**
* @brief Get the users config directory.
* @brief Get the user's config directory.
*
* This is without a trailing slash.
* 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)
{
char home[NSS_BUFLEN_PASSWD];
get_home_dir(home, sizeof(home));
char *user_config_dir;
#ifdef _WIN32
#warning Please fix configdir for Win32
return NULL;
#if 0
char appdata[MAX_PATH];
BOOL ok;
ok = SHGetSpecialFolderPathA(NULL, appdata, CSIDL_PROFILE, TRUE);
if (!ok) {
return NULL;
}
user_config_dir = strdup(appdata);
return user_config_dir;
#endif
#else /* WIN32 */
#ifndef NSS_BUFLEN_PASSWD
#define NSS_BUFLEN_PASSWD 4096
#endif /* NSS_BUFLEN_PASSWD */
struct passwd pwd;
struct passwd *pwdbuf;
const char *home;
char buf[NSS_BUFLEN_PASSWD];
size_t len;
int rc;
rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
if (rc == 0) {
home = pwd.pw_dir;
} else {
home = getenv("HOME");
if (home == NULL) {
return NULL;
}
/* env variables can be tainted */
snprintf(buf, sizeof(buf), "%s", home);
home = buf;
}
# if defined(__APPLE__)
len = strlen(home) + strlen("/Library/Application Support") + 1;
user_config_dir = malloc(len);
if (user_config_dir == NULL) {
if (user_config_dir == NULL)
return NULL;
}
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
# else /* __APPLE__ */
const char *tmp;
if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
len = strlen(home) + strlen("/.config") + 1;
user_config_dir = malloc(len);
if (user_config_dir == NULL) {
if (user_config_dir == NULL)
return NULL;
}
snprintf(user_config_dir, len, "%s/.config", home);
} else {
@ -126,56 +99,48 @@ char *get_user_config_dir(void)
# endif /* __APPLE__ */
return user_config_dir;
#undef NSS_BUFLEN_PASSWD
#endif /* WIN32 */
}
/*
* Creates the config directory.
* Creates the config and chatlog directories.
*/
int create_user_config_dir(char *path)
int create_user_config_dirs(char *path)
{
#ifdef _WIN32
#warning Please fix configdir for Win32
return -1;
#if 0
char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1);
strcpy(fullpath, path);
strcat(fullpath, CONFIGDIR);
mkdir_err = _mkdir(fullpath);
struct __stat64 buf;
if (mkdir_err && (errno != EEXIST || _wstat64(fullpath, &buf) || !S_ISDIR(buf.st_mode))) {
free(fullpath);
return -1;
}
free(fullpath);
#endif
#else
int mkdir_err;
mkdir_err = mkdir(path, 0700);
struct stat buf;
int mkdir_err = mkdir(path, 0700);
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) {
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode)))
return -1;
}
char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1);
char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1);
if (fullpath == NULL || logpath == NULL)
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
strcpy(fullpath, path);
strcat(fullpath, CONFIGDIR);
strcpy(logpath, path);
strcat(logpath, LOGDIR);
mkdir_err = mkdir(fullpath, 0700);
if (mkdir_err && (errno != EEXIST || stat(fullpath, &buf) || !S_ISDIR(buf.st_mode))) {
free(fullpath);
free(logpath);
return -1;
}
mkdir_err = mkdir(logpath, 0700);
if (mkdir_err && (errno != EEXIST || stat(logpath, &buf) || !S_ISDIR(buf.st_mode))) {
free(fullpath);
free(logpath);
return -1;
}
free(logpath);
free(fullpath);
return 0;
#endif
}

View File

@ -20,16 +20,22 @@
*
*/
#ifdef _win32
#define CONFIGDIR "\\tox\\"
#else
#define CONFIGDIR "/tox/"
#ifndef _configdir_h
#define _configdir_h
#ifndef NSS_BUFLEN_PASSWD
#define NSS_BUFLEN_PASSWD 4096
#endif
#define CONFIGDIR "/tox/"
#define LOGDIR "/tox/chatlogs/"
#ifndef S_ISDIR
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
char *get_user_config_dir(void);
void get_home_dir(char *home, int size);
int create_user_config_dirs(char *path);
int create_user_config_dir(char *path);
#endif /* #define _configdir_h */

467
src/device.c Normal file
View File

@ -0,0 +1,467 @@
/* device.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 "device.h"
#ifdef _AUDIO
#include "audio_call.h"
#endif
#include "line_info.h"
#include "settings.h"
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif
#endif
#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_;
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 */
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
size_t ref_count;
int32_t selection;
_Bool enable_VAD;
_Bool muted;
pthread_mutex_t mutex[1];
uint32_t sample_rate;
uint32_t frame_duration;
int32_t sound_mode;
#ifdef _AUDIO
float VAD_treshold; /* 40 is usually recommended value */
#endif
} 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 */
uint32_t primary_device[2]; /* Primary device */
#ifdef _AUDIO
static ToxAv* av = NULL;
#endif /* _AUDIO */
/* q_mutex */
#define lock pthread_mutex_lock(&mutex)
#define unlock pthread_mutex_unlock(&mutex)
pthread_mutex_t mutex;
_Bool thread_running = _True,
thread_paused = _True; /* Thread control */
void* thread_poll(void*);
/* Meet devices */
#ifdef _AUDIO
DeviceError init_devices(ToxAv* av_)
#else
DeviceError init_devices()
#endif /* _AUDIO */
{
const char *stringed_device_list;
size[input] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
size[output] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) {
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
// Start poll thread
pthread_mutex_init(&mutex, NULL);
pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
return de_InternalError;
#ifdef _AUDIO
av = av_;
#endif /* _AUDIO */
return (DeviceError) de_None;
}
DeviceError terminate_devices()
{
/* Cleanup if needed */
thread_running = false;
usleep(20000);
pthread_mutex_destroy(&mutex);
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) {
unlock;
return de_DeviceNotActive;
}
device->muted = !device->muted;
unlock;
return de_None;
}
#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) {
unlock;
return de_DeviceNotActive;
}
device->VAD_treshold = value;
unlock;
return de_None;
}
#endif
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)
{
return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels);
}
// 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)
{
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);
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
}
else {
device->dhndl = alcOpenDevice(devices_names[type][selection]);
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);
}
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;
}
pthread_mutex_init(device->mutex, NULL);
unlock;
return de_None;
}
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) {
unlock;
return de_DeviceNotActive;
}
if ( !(device->ref_count--) ) {
running[type][device_idx] = NULL;
unlock;
DeviceError rc = de_None;
if (type == input) {
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
}
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);
}
free(device);
return rc;
}
unlock;
return de_None;
}
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD)
{
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
return de_InvalidSelection;
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;
unlock;
return de_None;
}
inline__ DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
Device* device = running[output][device_idx];
if (!device || device->muted) return de_DeviceNotActive;
pthread_mutex_lock(device->mutex);
ALuint bufid;
ALint processed, queued;
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
if(processed) {
ALuint bufids[processed];
alSourceUnqueueBuffers(device->source, processed, bufids);
alDeleteBuffers(processed - 1, bufids + 1);
bufid = bufids[0];
}
else if(queued < 16) alGenBuffers(1, &bufid);
else {
pthread_mutex_unlock(device->mutex);
return de_Busy;
}
alBufferData(bufid, device->sound_mode, data, lenght * 2 * channels, device->sample_rate);
alSourceQueueBuffers(device->source, 1, &bufid);
ALint state;
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
if(state != AL_PLAYING) alSourcePlay(device->source);
pthread_mutex_unlock(device->mutex);
return de_None;
}
void* thread_poll (void* arg) // TODO: maybe use thread for every input source
{
/*
* NOTE: We only need to poll input devices for data.
*/
(void)arg;
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 ++)
{
lock;
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) {
unlock;
continue;
}
Device* device = running[input][i];
int16_t frame[16000];
alcCaptureSamples(device->dhndl, frame, f_size);
if ( device->muted
#ifdef _AUDIO
|| (device->enable_VAD && !toxav_has_activity(av, device->call_idx, frame, f_size, device->VAD_treshold))
#endif /* _AUDIO */
)
{ unlock; continue; } /* Skip if no voice activity */
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
}
unlock;
}
usleep(5000);
}
}
pthread_exit(NULL);
}
void print_devices(ToxWindow* self, DeviceType type)
{
int i = 0;
for ( ; i < size[type]; i ++)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]);
return;
}
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)
{
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
return NULL;
return running[input][device_idx]->cb_data;
}

92
src/device.h Normal file
View File

@ -0,0 +1,92 @@
/* device.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/>.
*
*/
/*
* You can have multiple sources (Input devices) but only one output device.
* Pass buffers to output device via write();
* Read from running input device(s) via select()/callback combo.
*/
#ifndef _device_h
#define _device_h
#define MAX_DEVICES 32
#include <inttypes.h>
#include "windows.h"
#define _True 1
#define _False 0
typedef enum DeviceType {
input,
output,
} DeviceType;
typedef enum DeviceError {
de_None,
de_InternalError = -1,
de_InvalidSelection = -2,
de_FailedStart = -3,
de_Busy = -4,
de_AllDevicesBusy = -5,
de_DeviceNotActive = -6,
de_BufferError = -7,
de_UnsupportedMode = -8,
de_AlError = -9,
} DeviceError;
typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data);
#ifdef _AUDIO
DeviceError init_devices(ToxAv* av);
#else
DeviceError init_devices();
#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);
/* toggle device mute */
DeviceError device_mute(DeviceType type, uint32_t device_idx);
#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);
/* 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);
/* 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);
void print_devices(ToxWindow* self, DeviceType type);
DeviceError selection_valid(DeviceType type, int32_t selection);
#endif /* _device_h */

418
src/dns.c Normal file
View File

@ -0,0 +1,418 @@
/* 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);
}

32
src/dns.h Normal file
View File

@ -0,0 +1,32 @@
/* 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/>.
*
*/
/* Does DNS lookup for addr and puts resulting tox id in id_bin.
Return 0 on success, -1 on failure. */
#ifndef _dns_h
#define _dns_h
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg);
#endif /* #define _dns_h */

View File

@ -20,18 +20,18 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "toxic_windows.h"
#include "toxic.h"
#include "windows.h"
#include "execute.h"
#include "chat_commands.h"
#include "global_commands.h"
#include "line_info.h"
#include "misc_tools.h"
#include "notify.h"
struct cmd_func {
const char *name;
@ -43,6 +43,7 @@ static struct cmd_func global_commands[] = {
{ "/add", cmd_add },
{ "/clear", cmd_clear },
{ "/connect", cmd_connect },
{ "/decline", cmd_decline },
{ "/exit", cmd_quit },
{ "/groupchat", cmd_groupchat },
{ "/help", cmd_prompt_help },
@ -52,60 +53,75 @@ static struct cmd_func global_commands[] = {
{ "/note", cmd_note },
{ "/q", cmd_quit },
{ "/quit", cmd_quit },
{ "/requests", cmd_requests },
{ "/status", cmd_status },
#ifdef _SUPPORT_AUDIO
#ifdef _AUDIO
{ "/lsdev", cmd_list_devices },
{ "/sdev", cmd_change_device },
#endif /* _SUPPORT_AUDIO */
#endif /* _AUDIO */
};
static struct cmd_func chat_commands[] = {
{ "/help", cmd_chat_help },
{ "/cancel", cmd_cancelfile },
{ "/invite", cmd_groupinvite },
{ "/join", cmd_join_group },
{ "/savefile", cmd_savefile },
{ "/sendfile", cmd_sendfile },
#ifdef _SUPPORT_AUDIO
#ifdef _AUDIO
{ "/call", cmd_call },
{ "/cancel", cmd_cancel },
{ "/answer", cmd_answer },
{ "/reject", cmd_reject },
{ "/hangup", cmd_hangup },
#endif /* _SUPPORT_AUDIO */
{ "/sdev", cmd_ccur_device },
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
#endif /* _AUDIO */
};
/* Parses input command and puts args into arg array.
/* Parses input command and puts args into arg array.
Returns number of arguments on success, -1 on failure. */
static int parse_command(WINDOW *w, char *cmd, char (*args)[MAX_STR_SIZE])
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
{
char *cmd = strdup(input);
if (cmd == NULL)
exit_toxic_err("failed in parse_command", FATALERR_MEMORY);
int num_args = 0;
bool cmd_end = false; /* flags when we get to the end of cmd */
char *end; /* points to the end of the current arg */
int i = 0; /* index of last char in an argument */
/* characters wrapped in double quotes count as one arg */
while (!cmd_end && num_args < MAX_NUM_ARGS) {
if (*cmd == '\"') {
end = strchr(cmd+1, '\"');
while (num_args < MAX_NUM_ARGS) {
int qt_ofst = 0; /* set to 1 to offset index for quote char at end of arg */
if (end++ == NULL) { /* Increment past the end quote */
wprintw(w, "Invalid argument. Did you forget a closing \"?\n");
if (*cmd == '\"') {
qt_ofst = 1;
i = char_find(1, cmd, '\"');
if (cmd[i] == '\0') {
char *errmsg = "Invalid argument. Did you forget a closing \"?";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
free(cmd);
return -1;
}
cmd_end = *end == '\0';
} else {
end = strchr(cmd, ' ');
cmd_end = end == NULL;
i = char_find(0, cmd, ' ');
}
if (!cmd_end)
*end++ = '\0'; /* mark end of current argument */
memcpy(args[num_args], cmd, i + qt_ofst);
args[num_args++][i + qt_ofst] = '\0';
/* Copy from start of current arg to where we just inserted the null byte */
strcpy(args[num_args++], cmd);
cmd = end;
if (cmd[i] == '\0') /* no more args */
break;
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
strcpy(cmd, tmp);
}
free(cmd);
return num_args;
}
@ -117,7 +133,7 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_
for (i = 0; i < num_cmds; ++i) {
if (strcmp(args[0], commands[i].name) == 0) {
(commands[i].func)(w, self, m, num_args-1, args);
(commands[i].func)(w, self, m, num_args - 1, args);
return 0;
}
}
@ -125,33 +141,34 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_
return 1;
}
void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode)
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
{
if (string_is_empty(cmd))
if (string_is_empty(input))
return;
char args[MAX_NUM_ARGS][MAX_STR_SIZE] = {0};
int num_args = parse_command(w, cmd, args);
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
int num_args = parse_command(w, self, input, args);
if (num_args == -1)
return;
/* Try to match input command to command functions. If non-global command mode is specified,
try specified mode's commands first, then upon failure try global commands.
/* Try to match input command to command functions. If non-global command mode is specified,
try specified mode's commands first, then upon failure try global commands.
Note: Global commands must come last in case of duplicate command names */
switch (mode) {
case CHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
return;
break;
case CHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
return;
case GROUPCHAT_COMMAND_MODE:
break;
break;
case GROUPCHAT_COMMAND_MODE:
break;
}
if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0)
return;
wprintw(w, "Invalid command.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
}

View File

@ -20,15 +20,21 @@
*
*/
#ifndef _execute_h
#define _execute_h
#include "toxic.h"
#include "windows.h"
#define MAX_NUM_ARGS 4 /* Includes command */
#ifdef _SUPPORT_AUDIO
#define GLOBAL_NUM_COMMANDS 16
#define CHAT_NUM_COMMANDS 9
#else
#define GLOBAL_NUM_COMMANDS 14
#define CHAT_NUM_COMMANDS 5
#endif /* _SUPPORT_AUDIO */
#ifdef _AUDIO
#define GLOBAL_NUM_COMMANDS 18
#define CHAT_NUM_COMMANDS 12
#else
#define GLOBAL_NUM_COMMANDS 16
#define CHAT_NUM_COMMANDS 5
#endif /* _AUDIO */
enum {
GLOBAL_COMMAND_MODE,
@ -36,4 +42,6 @@ enum {
GROUPCHAT_COMMAND_MODE,
};
void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode);
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);
#endif /* #define _execute_h */

View File

@ -20,42 +20,224 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "toxic_windows.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;
static void close_file_sender(int i)
/* 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)
{
fclose(file_senders[i].file);
memset(&file_senders[i], 0, sizeof(FileSender));
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)
if (file_senders[j - 1].active)
break;
}
max_file_senders_index = j;
}
/* Should only be called on exit */
void close_all_file_senders(void)
/* 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)
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;
}
}
}
@ -67,47 +249,42 @@ void do_file_senders(Tox *m)
if (!file_senders[i].active)
continue;
uint8_t *pathname = file_senders[i].pathname;
uint8_t filenum = file_senders[i].filenum;
int friendnum = file_senders[i].friendnum;
FILE *fp = file_senders[i].file;
uint64_t current_time = (uint64_t) time(NULL);
/* If file transfer has timed out kill transfer and send kill control */
if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) {
ChatContext *ctx = file_senders[i].toxwin->chatwin;
if (ctx != NULL) {
wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname);
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
}
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0);
close_file_sender(i);
if (file_senders[i].queue_pos > 0) {
--file_senders[i].queue_pos;
continue;
}
while (true) {
if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece,
file_senders[i].piecelen) == -1)
break;
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;
file_senders[i].timestamp = current_time;
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
tox_file_data_size(m, friendnum), fp);
if (file_senders[i].piecelen == 0) {
ChatContext *ctx = file_senders[i].toxwin->chatwin;
if (ctx != NULL) {
wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname);
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
}
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
close_file_sender(i);
break;
}
/* 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

@ -20,7 +20,56 @@
*
*/
/* Should only be called on exit */
void close_all_file_senders(void);
#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 */

File diff suppressed because it is too large Load Diff

View File

@ -23,37 +23,76 @@
#ifndef FRIENDLIST_H_53I41IM
#define FRIENDLIST_H_53I41IM
#include "toxic_windows.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;
};
struct LastOnline {
uint64_t last_on;
struct tm tm;
char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */
};
typedef struct {
uint8_t name[TOX_MAX_NAME_LENGTH];
uint16_t namelength;
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
char name[TOXIC_MAX_NAME_LENGTH];
int namelength;
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
uint16_t statusmsg_len;
uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE];
uint8_t pub_key[TOX_CLIENT_ID_SIZE];
int num;
char groupchat_key[TOX_CLIENT_ID_SIZE];
bool groupchat_pending;
char pub_key[TOX_CLIENT_ID_SIZE];
int32_t num;
int chatwin;
bool active;
bool online;
bool is_typing;
bool logging_on; /* saves preference for friend irrespective of chat windows */
TOX_USERSTATUS status;
struct FileReceiver file_receiver;
uint8_t 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;
} ToxicFriend;
typedef struct {
int num;
char name[TOXIC_MAX_NAME_LENGTH];
int namelength;
char pub_key[TOX_CLIENT_ID_SIZE];
int32_t num;
bool active;
} PendingDel;
uint64_t last_on;
} BlockedFriend;
typedef struct {
int num_selected;
int max_idx; /* 1 + the index of the last friend in list */
int num_friends;
int *index;
ToxicFriend *list;
} _Friends;
ToxWindow new_friendlist(void);
void disable_chatwin(int f_num);
void disable_chatwin(int32_t f_num);
int get_friendnum(uint8_t *name);
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort);
int load_blocklist(char *data);
void kill_friendlist(void);
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort);
/* sorts friendlist_index first by connection status then alphabetically */
void sort_friendlist_index(Tox *m);
void sort_friendlist_index(void);
#endif /* end of include guard: FRIENDLIST_H_53I41IM */

View File

@ -20,278 +20,307 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include "toxic_windows.h"
#include "toxic.h"
#include "windows.h"
#include "misc_tools.h"
#include "friendlist.h"
#include "log.h"
#include "line_info.h"
#include "dns.h"
#include "groupchat.h"
#include "prompt.h"
#include "help.h"
extern char *DATA_FILE;
extern ToxWindow *prompt;
extern ToxicFriend friends[MAX_FRIENDS_NUM];
extern uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
extern uint8_t num_frnd_requests;
extern _Friends Friends;
extern _FriendRequests FriendRequests;
/* command functions */
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
/* check arguments */
if (argc != 1) {
wprintw(window, "Invalid syntax.\n");
return;
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
return;
}
int req = atoi(argv[1]);
if ((req == 0 && strcmp(argv[1], "0"))|| req >= MAX_FRIENDS_NUM) {
wprintw(window, "No pending friend request with that number.\n");
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
if (!strlen(pending_frnd_requests[req])) {
wprintw(window, "No pending friend request with that number.\n");
if (!FriendRequests.request[req].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
int friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]);
const char *msg;
int32_t friendnum = tox_add_friend_norequest(m, FriendRequests.request[req].key);
if (friendnum == -1)
wprintw(window, "Failed to add friend.\n");
msg = "Failed to add friend.";
else {
wprintw(window, "Friend request accepted.\n");
msg = "Friend request accepted.";
on_friendadded(m, friendnum, true);
}
memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE);
memset(&FriendRequests.request[req], 0, sizeof(struct _friend_request));
int i;
for (i = num_frnd_requests; i > 0; --i) {
if (!strlen(pending_frnd_requests[i-1]))
for (i = FriendRequests.max_idx; i > 0; --i) {
if (FriendRequests.request[i - 1].active)
break;
}
num_frnd_requests = i;
FriendRequests.max_idx = i;
--FriendRequests.num_requests;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
}
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, 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:
errmsg = "Message is too long.";
break;
case TOX_FAERR_NOMESSAGE:
errmsg = "Please add a message to your request.";
break;
case TOX_FAERR_OWNKEY:
errmsg = "That appears to be your own ID.";
break;
case TOX_FAERR_ALREADYSENT:
errmsg = "Friend request has already been sent.";
break;
case TOX_FAERR_UNKNOWN:
errmsg = "Undefined error when adding friend.";
break;
case TOX_FAERR_BADCHECKSUM:
errmsg = "Bad checksum in address.";
break;
case TOX_FAERR_SETNEWNOSPAM:
errmsg = "Nospam was different.";
break;
default:
errmsg = "Friend request sent.";
on_friendadded(m, f_num, true);
break;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
}
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
wprintw(window, "Invalid syntax.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
return;
}
char *id = argv[1];
uint8_t msg[MAX_STR_SIZE];
const char *id = argv[1];
char msg[MAX_STR_SIZE];
if (argc > 1) {
uint8_t *temp = argv[2];
if (temp[0] != '\"') {
wprintw(window, "Message must be enclosed in quotes.\n");
if (argv[2][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes.");
return;
}
temp[strlen(++temp)-1] = L'\0';
snprintf(msg, sizeof(msg), "%s", temp);
/* remove opening and closing quotes */
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", &argv[2][1]);
int len = strlen(tmp) - 1;
tmp[len] = '\0';
snprintf(msg, sizeof(msg), "%s", tmp);
} else {
uint8_t selfname[TOX_MAX_NAME_LENGTH];
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
char selfname[TOX_MAX_NAME_LENGTH];
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfname);
selfname[n_len] = '\0';
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
}
if (strlen(id) != 2 * TOX_FRIEND_ADDRESS_SIZE) {
wprintw(window, "Invalid ID length.\n");
return;
}
char id_bin[TOX_FRIEND_ADDRESS_SIZE] = {0};
uint16_t id_len = (uint16_t) strlen(id);
size_t i;
char xx[3];
uint32_t x;
uint8_t id_bin[TOX_FRIEND_ADDRESS_SIZE];
/* try to add tox ID */
if (id_len == 2 * TOX_FRIEND_ADDRESS_SIZE) {
size_t i;
char xx[3];
uint32_t x;
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
xx[0] = id[2 * i];
xx[1] = id[2 * i + 1];
xx[2] = '\0';
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
xx[0] = id[2 * i];
xx[1] = id[2 * i + 1];
xx[2] = '\0';
if (sscanf(xx, "%02x", &x) != 1) {
wprintw(window, "Invalid ID.\n");
return;
if (sscanf(xx, "%02x", &x) != 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID.");
return;
}
id_bin[i] = x;
}
id_bin[i] = x;
}
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
id[i] = toupper(id[i]);
}
int f_num = tox_add_friend(m, id_bin, msg, strlen(msg) + 1);
switch (f_num) {
case TOX_FAERR_TOOLONG:
wprintw(window, "Message is too long.\n");
break;
case TOX_FAERR_NOMESSAGE:
wprintw(window, "Please add a message to your request.\n");
break;
case TOX_FAERR_OWNKEY:
wprintw(window, "That appears to be your own ID.\n");
break;
case TOX_FAERR_ALREADYSENT:
wprintw(window, "Friend request has already been sent.\n");
break;
case TOX_FAERR_UNKNOWN:
wprintw(window, "Undefined error when adding friend.\n");
break;
case TOX_FAERR_BADCHECKSUM:
wprintw(window, "Bad checksum in address.\n");
break;
case TOX_FAERR_SETNEWNOSPAM:
wprintw(window, "Nospam was different (is this contact already added?)\n");
break;
default:
wprintw(window, "Friend request sent.\n");
on_friendadded(m, f_num, true);
break;
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);
}
}
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
line_info_clear(self->chatwin->hst);
wclear(window);
wprintw(window, "\n\n");
endwin();
refresh();
}
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
/* check arguments */
if (argc != 3) {
wprintw(window, "Invalid syntax.\n");
return;
}
tox_IP_Port dht;
char *ip = argv[1];
char *port = argv[2];
char *key = argv[3];
if (atoi(port) == 0) {
wprintw(window, "Invalid syntax.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
return;
}
uint8_t *binary_string = hex_string_to_bin(key);
tox_bootstrap_from_address(m, ip, TOX_ENABLE_IPV6_DEFAULT,
htons(atoi(port)), binary_string);
const char *ip = argv[1];
const char *port = argv[2];
const char *key = argv[3];
if (atoi(port) == 0) {
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);
}
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
return;
}
int req = atoi(argv[1]);
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
if (!FriendRequests.request[req].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
memset(&FriendRequests.request[req], 0, sizeof(struct _friend_request));
int i;
for (i = FriendRequests.max_idx; i > 0; --i) {
if (FriendRequests.request[i - 1].active)
break;
}
FriendRequests.max_idx = i;
--FriendRequests.num_requests;
}
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
wattron(window, COLOR_PAIR(RED));
wprintw(window, " * Warning: Too many windows are open.\n");
wattron(window, COLOR_PAIR(RED));
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
int groupnum = tox_add_groupchat(m);
if (groupnum == -1) {
wprintw(window, "Group chat instance failed to initialize.\n");
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) {
wprintw(window, "Group chat window failed to initialize.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
return;
}
wprintw(window, "Group chat created as %d.\n", groupnum);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum);
}
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *msg;
struct chatlog *log = self->chatwin->log;
if (argc == 0) {
bool on;
if (self->is_chat || self->is_groupchat)
on = self->chatwin->log->log_on;
else if (self->is_prompt)
on = self->promptbuf->log->log_on;
if (on) {
wprintw(window, "Logging for this window is ");
wattron(window, COLOR_PAIR(GREEN) | A_BOLD);
wprintw(window, "[on]");
wattroff(window, COLOR_PAIR(GREEN) | A_BOLD);
wprintw(window, ". Type \"/log off\" to disable.\n");
} else {
wprintw(window, "Logging for this window is ");
wattron(window, COLOR_PAIR(RED) | A_BOLD);
wprintw(window, "[off]");
wattroff(window, COLOR_PAIR(RED) | A_BOLD);
wprintw(window, ". Type \"/log on\" to enable.\n");
}
if (log->log_on)
msg = "Logging for this window is ON. Type \"/log off\" to disable.";
else
msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
}
uint8_t *swch = argv[1];
const char *swch = argv[1];
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
if (self->is_chat) {
friends[self->num].logging_on = true;
log_enable(self->name, friends[self->num].pub_key, self->chatwin->log);
Friends.list[self->num].logging_on = true;
log_enable(self->name, Friends.list[self->num].pub_key, log);
} else if (self->is_prompt) {
uint8_t myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, myid);
log_enable(self->name, &myid, self->promptbuf->log);
char myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, (uint8_t *) myid);
log_enable(self->name, myid, log);
} else if (self->is_groupchat) {
log_enable(self->name, NULL, self->chatwin->log);
log_enable(self->name, NULL, log);
}
wprintw(window, "Logging ");
wattron(window, COLOR_PAIR(GREEN) | A_BOLD);
wprintw(window, "[on]\n");
wattroff(window, COLOR_PAIR(GREEN) | A_BOLD);
msg = "Logging enabled";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
if (self->is_chat) {
friends[self->num].logging_on = false;
log_disable(self->chatwin->log);
} else if (self->is_prompt) {
log_disable(self->promptbuf->log);
} else if (self->is_groupchat) {
log_disable(self->chatwin->log);
}
if (self->is_chat)
Friends.list[self->num].logging_on = false;
wprintw(window, "Logging ");
wattron(window, COLOR_PAIR(RED) | A_BOLD);
wprintw(window, "[off]\n");
wattroff(window, COLOR_PAIR(RED) | A_BOLD);
log_disable(log);
msg = "Logging disabled";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
}
wprintw(window, "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.\n");
msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
}
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};
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, address);
char address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, (uint8_t *) address);
size_t i;
@ -301,38 +330,38 @@ void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
strcat(id, xx);
}
wprintw(window, "%s\n", id);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id);
}
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
/* check arguments */
if (argc < 1) {
wprintw(window, "Invalid name.\n");
return;
}
uint8_t *nick = argv[1];
int len = strlen(nick);
if (nick[0] == '\"') {
++nick;
len -= 2;
nick[len] = L'\0';
}
if (!valid_nick(nick)) {
wprintw(window, "Invalid name.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
return;
}
if (len > TOXIC_MAX_NAME_LENGTH) {
nick[TOXIC_MAX_NAME_LENGTH] = L'\0';
len = TOXIC_MAX_NAME_LENGTH;
char nick[MAX_STR_SIZE];
int len = 0;
if (argv[1][0] == '\"') { /* remove opening and closing quotes */
snprintf(nick, sizeof(nick), "%s", &argv[1][1]);
len = strlen(nick) - 1;
nick[len] = '\0';
} else {
snprintf(nick, sizeof(nick), "%s", argv[1]);
len = strlen(nick);
}
tox_set_name(m, nick, len+1);
prompt_update_nick(prompt, nick, len+1);
if (!valid_nick(nick)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
return;
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
nick[len] = '\0';
tox_set_name(m, (uint8_t *) nick, (uint16_t) len);
prompt_update_nick(prompt, nick);
store_data(m, DATA_FILE);
}
@ -340,104 +369,110 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
wprintw(window, "Wrong number of arguments.\n");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
return;
}
uint8_t *msg = argv[1];
if (msg[0] != '\"') {
wprintw(window, "Note must be enclosed in quotes.\n");
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
return;
}
msg[strlen(++msg)-1] = L'\0';
uint16_t len = strlen(msg) + 1;
tox_set_status_message(m, msg, len);
/* remove opening and closing quotes */
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "%s", &argv[1][1]);
int len = strlen(msg) - 1;
msg[len] = '\0';
prompt_update_statusmessage(prompt, msg, len);
prompt_update_statusmessage(prompt, m, msg);
}
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
wclear(window);
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, "\n\nGlobal commands:\n");
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, " /add <id> <msg> : Add friend with optional message\n");
wprintw(window, " /accept <n> : Accept friend request\n");
wprintw(window, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
wprintw(window, " /status <type> <msg> : Set status with optional note\n");
wprintw(window, " /note <msg> : Set a personal note\n");
wprintw(window, " /nick <nick> : Set your nickname\n");
wprintw(window, " /log <on> or <off> : Enable/disable logging\n");
wprintw(window, " /groupchat : Create a group chat\n");
wprintw(window, " /myid : Print your ID\n");
wprintw(window, " /help : Print this message again\n");
wprintw(window, " /clear : Clear the window\n");
wprintw(window, " /quit or /exit : Exit Toxic\n");
#ifdef _SUPPORT_AUDIO
wprintw(window, " /lsdev <type> : List devices where type: in|out\n");
wprintw(window, " /sdev <type> <id> : Set active device\n");
#endif /* _SUPPORT_AUDIO */
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n");
wprintw(window, " * Use ctrl-o and ctrl-p to navigate through the tabs.\n\n");
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
help_init_menu(self);
}
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
exit_toxic(m);
exit_toxic_success(m);
}
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t *msg = NULL;
if (argc >= 2) {
msg = argv[2];
if (msg[0] != '\"') {
wprintw(window, "Note must be enclosed in quotes.\n");
return;
}
} else if (argc != 1) {
wprintw(window, "Wrong number of arguments.\n");
if (FriendRequests.num_requests == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
return;
}
char *status = argv[1];
int len = strlen(status);
char l_status[len+1];
int i;
int i, j;
int count = 0;
for (i = 0; i <= len; ++i)
l_status[i] = tolower(status[i]);
for (i = 0; i < FriendRequests.max_idx; ++i) {
if (!FriendRequests.request[i].active)
continue;
char id[TOX_CLIENT_ID_SIZE * 2 + 1] = {0};
for (j = 0; j < TOX_CLIENT_ID_SIZE; ++j) {
char d[3];
snprintf(d, sizeof(d), "%02X", FriendRequests.request[i].key[j] & 0xff);
strcat(id, d);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FriendRequests.request[i].msg);
if (++count < FriendRequests.num_requests)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
}
}
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
bool have_note = false;
const char *errmsg;
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;
}
char status[MAX_STR_SIZE];
snprintf(status, sizeof(status), "%s", argv[1]);
str_to_lower(status);
TOX_USERSTATUS status_kind;
if (!strcmp(l_status, "online"))
if (!strcmp(status, "online"))
status_kind = TOX_USERSTATUS_NONE;
else if (!strcmp(l_status, "away"))
else if (!strcmp(status, "away"))
status_kind = TOX_USERSTATUS_AWAY;
else if (!strcmp(l_status, "busy"))
else if (!strcmp(status, "busy"))
status_kind = TOX_USERSTATUS_BUSY;
else {
wprintw(window, "Invalid status. Valid statuses are: online, busy and away.\n");
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
return;
}
tox_set_user_status(m, status_kind);
prompt_update_status(prompt, status_kind);
if (msg != NULL) {
msg[strlen(++msg)-1] = L'\0'; /* remove opening and closing quotes */
uint16_t len = strlen(msg) + 1;
tox_set_status_message(m, msg, len);
prompt_update_statusmessage(prompt, msg, len);
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;
}
/* remove opening and closing quotes */
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "%s", &argv[2][1]);
int len = strlen(msg) - 1;
msg[len] = '\0';
prompt_update_statusmessage(prompt, m, msg);
}
}

View File

@ -20,10 +20,17 @@
*
*/
#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_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
@ -31,9 +38,14 @@ void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef _SUPPORT_AUDIO
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg);
#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 /* _SUPPORT_AUDIO */
#endif /* _AUDIO */
#endif /* #define _global_commands_h */

View File

@ -20,33 +20,46 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* needed for strcasestr() and wcswidth() */
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <wchar.h>
#include "toxic_windows.h"
#include "windows.h"
#include "toxic.h"
#include "execute.h"
#include "misc_tools.h"
#include "groupchat.h"
#include "prompt.h"
#include "toxic_strings.h"
#include "log.h"
#include "line_info.h"
#include "settings.h"
#include "input.h"
#include "help.h"
#include "notify.h"
#include "autocomplete.h"
extern char *DATA_FILE;
extern int store_data(Tox *m, char *path);
static GroupChat groupchats[MAX_WINDOWS_NUM];
static GroupChat groupchats[MAX_GROUPCHAT_NUM];
static int max_groupchat_index = 0;
extern struct user_settings *user_settings_;
/* temporary until group chats have unique commands */
extern const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
extern const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
{
if (groupnum > MAX_GROUPCHAT_NUM)
return -1;
int i;
for (i = 0; i <= max_groupchat_index; ++i) {
@ -56,9 +69,11 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
groupchats[i].num_peers = 0;
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t));
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
/* temp fix */
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
set_active_window(groupchats[i].chatwin);
@ -77,25 +92,31 @@ void kill_groupchat_window(ToxWindow *self)
ChatContext *ctx = self->chatwin;
log_disable(ctx->log);
line_info_cleanup(ctx->hst);
delwin(ctx->linewin);
del_window(self);
delwin(ctx->history);
delwin(ctx->sidebar);
free(ctx->log);
free(ctx->hst);
free(ctx);
free(self->help);
del_window(self);
}
static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
{
set_active_window(0);
tox_del_groupchat(m, groupnum);
free(groupchats[groupnum].peer_names);
free(groupchats[groupnum].oldpeer_names);
free(groupchats[groupnum].peer_name_lengths);
free(groupchats[groupnum].oldpeer_name_lengths);
memset(&groupchats[groupnum], 0, sizeof(GroupChat));
int i;
for (i = max_groupchat_index; i > 0; --i) {
if (groupchats[i-1].active)
if (groupchats[i - 1].active)
break;
}
@ -103,78 +124,46 @@ static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
kill_groupchat_window(self);
}
static void print_groupchat_help(ChatContext *ctx)
{
wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(ctx->history, "Group chat commands:\n");
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(ctx->history, " /add <id> <msg> : Add friend with optional message\n");
wprintw(ctx->history, " /status <type> <msg>: Set your status with optional note\n");
wprintw(ctx->history, " /note <msg> : Set a personal note\n");
wprintw(ctx->history, " /nick <nick> : Set your nickname\n");
wprintw(ctx->history, " /groupchat : Create a group chat\n");
wprintw(ctx->history, " /log <on> or <off> : Enable/disable logging\n");
wprintw(ctx->history, " /close : Close the current group chat\n");
wprintw(ctx->history, " /help : Print this message again\n");
wprintw(ctx->history, " /help global : Show a list of global commands\n");
wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(ctx->history, " * Argument messages must be enclosed in quotation marks.\n");
wprintw(ctx->history, " * Scroll peer list with the Page Up/Page Down keys.\n\n");
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
wattron(ctx->history, COLOR_PAIR(WHITE) | A_BOLD);
wprintw(ctx->history, " Notice, some friends will be missing names while finding peers\n\n");
wattroff(ctx->history, COLOR_PAIR(WHITE) | A_BOLD);
}
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
uint8_t *msg, uint16_t len)
const char *msg, uint16_t len)
{
if (self->num != groupnum)
return;
ChatContext *ctx = self->chatwin;
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_group_peername(m, groupnum, peernum, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */
char nick[TOX_MAX_NAME_LENGTH];
int n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1); /* enforce client max name length */
nick[n_len] = '\0';
/* check if message contains own name and alert appropriately */
int alert_type = WINDOW_ALERT_1;
bool beep = false;
char selfnick[TOX_MAX_NAME_LENGTH];
uint16_t sn_len = tox_get_self_name(m, (uint8_t *) selfnick);
selfnick[sn_len] = '\0';
uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH);
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH);
/* Only play sound if mentioned */
if (strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH - 1)) {
sound_notify(self, generic_message, NT_WNDALERT_0, NULL);
if (self->active_box != -1)
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg);
else
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg);
if (nick_match) {
alert_type = WINDOW_ALERT_0;
beep = true;
nick_clr = RED;
}
else sound_notify(self, silent, NT_WNDALERT_1, NULL);
alert_window(self, alert_type, beep);
print_time(ctx->history);
wattron(ctx->history, COLOR_PAIR(nick_clr));
wprintw(ctx->history, "%s: ", nick);
wattroff(ctx->history, COLOR_PAIR(nick_clr));
if (msg[0] == '>') {
wattron(ctx->history, COLOR_PAIR(GREEN));
wprintw(ctx->history, "%s\n", msg);
wattroff(ctx->history, COLOR_PAIR(GREEN));
} else {
wprintw(ctx->history, "%s\n", msg);
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, nick, NULL, IN_MSG, 0, nick_clr, "%s", msg);
write_to_log(msg, nick, ctx->log, false);
}
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t *action,
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *action,
uint16_t len)
{
if (self->num != groupnum)
@ -182,64 +171,79 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
ChatContext *ctx = self->chatwin;
/* check if message contains own name and alert appropriately */
int alert_type = WINDOW_ALERT_1;
bool beep = false;
char selfnick[TOX_MAX_NAME_LENGTH];
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfnick);
selfnick[n_len] = '\0';
uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH);
bool nick_match = strcasestr(action, selfnick);
if (nick_match) {
alert_type = WINDOW_ALERT_0;
beep = true;
if (strcasestr(action, selfnick)) {
sound_notify(self, generic_message, NT_WNDALERT_0, NULL);
char nick[TOX_MAX_NAME_LENGTH];
int n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1); /* enforce client max name length */
nick[n_len] = '\0';
if (self->active_box != -1)
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
else
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action);
}
else sound_notify(self, silent, NT_WNDALERT_1, NULL);
alert_window(self, alert_type, beep);
char nick[TOX_MAX_NAME_LENGTH];
n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
nick[n_len] = '\0';
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_group_peername(m, groupnum, peernum, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */
print_time(ctx->history);
wattron(ctx->history, COLOR_PAIR(YELLOW));
wprintw(ctx->history, "* %s %s\n", nick, action);
wattroff(ctx->history, COLOR_PAIR(YELLOW));
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, nick, NULL, ACTION, 0, 0, "%s", action);
write_to_log(action, nick, ctx->log, true);
}
/* Puts two copies of peerlist in chat instance */
static void copy_peernames(int gnum, int npeers, uint8_t tmp_peerlist[][TOX_MAX_NAME_LENGTH])
/* Puts two copies of peerlist/lengths in chat instance */
static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], uint16_t lengths[], int npeers)
{
/* Assumes these are initiated in init_groupchat_win */
free(groupchats[gnum].peer_names);
free(groupchats[gnum].oldpeer_names);
free(groupchats[gnum].peer_name_lengths);
free(groupchats[gnum].oldpeer_name_lengths);
int N = TOX_MAX_NAME_LENGTH;
groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N);
groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N);
groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers);
groupchats[gnum].oldpeer_name_lengths = malloc(sizeof(uint16_t) * npeers);
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
|| groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
exit_toxic_err("failed in copy_peernames", FATALERR_MEMORY);
}
uint16_t unknown_len = (uint16_t) strlen(UNKNOWN_NAME);
int i;
for (i = 0; i < npeers; ++i) {
if (string_is_empty(tmp_peerlist[i])) {
memcpy(&groupchats[gnum].peer_names[i*N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
if (string_is_empty((char *) peerlist[i])) {
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
groupchats[gnum].peer_name_lengths[i] = unknown_len;
} else {
memcpy(&groupchats[gnum].peer_names[i*N], tmp_peerlist[i], N);
groupchats[gnum].peer_names[i*N+TOXIC_MAX_NAME_LENGTH] = '\0';
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], N);
uint16_t n_len = lengths[i];
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
groupchats[gnum].peer_names[i * N + n_len] = '\0';
groupchats[gnum].peer_name_lengths[i] = n_len;
}
}
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N*npeers);
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths,
sizeof(uint16_t) * npeers);
}
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum,
@ -248,325 +252,200 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
if (self->num != groupnum)
return;
if (groupnum > max_groupchat_index)
return;
groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum);
int num_peers = groupchats[groupnum].num_peers;
if (peernum > num_peers)
return;
/* get old peer name before updating name list */
uint8_t oldpeername[TOX_MAX_NAME_LENGTH] = {0};
uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
if (change != TOX_CHAT_CHANGE_PEER_ADD)
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum*TOX_MAX_NAME_LENGTH],
if (change != TOX_CHAT_CHANGE_PEER_ADD) {
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH],
sizeof(oldpeername));
uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
oldpeername[old_n_len] = '\0';
}
/* Update name lists */
/* Update name/len lists */
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
tox_group_get_names(m, groupnum, tmp_peerlist, num_peers);
copy_peernames(groupnum, num_peers, tmp_peerlist);
uint16_t tmp_peerlens[num_peers];
tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers);
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
/* get current peername then sort namelist */
uint8_t peername[TOX_MAX_NAME_LENGTH] = {0};
memcpy(peername, &groupchats[groupnum].peer_names[peernum*TOX_MAX_NAME_LENGTH], sizeof(peername));
uint8_t peername[TOX_MAX_NAME_LENGTH];
if (change != TOX_CHAT_CHANGE_PEER_DEL) {
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
peername[n_len] = '\0';
}
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
ChatContext *ctx = self->chatwin;
print_time(ctx->history);
uint8_t *event;
const char *event;
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
switch (change) {
case TOX_CHAT_CHANGE_PEER_ADD:
event = "has joined the room";
case TOX_CHAT_CHANGE_PEER_ADD:
event = "has joined the room";
line_info_add(self, timefrmt, (char *) peername, NULL, CONNECTION, 0, GREEN, event);
write_to_log(event, (char *) peername, ctx->log, true);
break;
wattron(ctx->history, COLOR_PAIR(GREEN));
wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "* %s", peername);
wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " %s\n", event);
wattroff(ctx->history, COLOR_PAIR(GREEN));
case TOX_CHAT_CHANGE_PEER_DEL:
event = "has left the room";
line_info_add(self, timefrmt, (char *) oldpeername, NULL, CONNECTION, 0, 0, event);
write_to_log(event, peername, ctx->log, true);
break;
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
case TOX_CHAT_CHANGE_PEER_DEL:
event = "has left the room";
write_to_log(event, (char *) oldpeername, ctx->log, true);
break;
wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "* %s", oldpeername);
wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " %s\n", event);
case TOX_CHAT_CHANGE_PEER_NAME:
event = " is now known as ";
line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, NAME_CHANGE, 0, 0, event);
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
write_to_log(event, oldpeername, ctx->log, true);
break;
case TOX_CHAT_CHANGE_PEER_NAME:
wattron(ctx->history, COLOR_PAIR(MAGENTA));
wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "* %s", oldpeername);
wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " is now known as ");
wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "%s\n", peername);
wattroff(ctx->history, A_BOLD);
wattroff(ctx->history, COLOR_PAIR(MAGENTA));
uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH + 32];
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", peername);
write_to_log(tmp_event, oldpeername, ctx->log, true);
break;
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername);
write_to_log(tmp_event, (char *) oldpeername, ctx->log, true);
break;
}
alert_window(self, WINDOW_ALERT_2, false);
sound_notify(self, silent, NT_WNDALERT_2, NULL);
}
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) {
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
{
if (action == NULL) {
wprintw(ctx->history, "Invalid syntax.\n");
return;
}
/* uint8_t selfname[TOX_MAX_NAME_LENGTH];
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
print_time(ctx->history);
wattron(ctx->history, COLOR_PAIR(YELLOW));
wprintw(ctx->history, "* %s %s\n", selfname, action);
wattroff(ctx->history, COLOR_PAIR(YELLOW)); */
if (tox_group_action_send(m, self->num, action, strlen(action) + 1) == -1) {
wattron(ctx->history, COLOR_PAIR(RED));
wprintw(ctx->history, " * Failed to send action\n");
wattroff(ctx->history, COLOR_PAIR(RED));
if (tox_group_action_send(m, self->num, (uint8_t *) action, strlen(action)) == -1) {
const char *errmsg = " * Failed to send action.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
}
}
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{
ChatContext *ctx = self->chatwin;
int x, y, y2, x2;
getyx(self->window, y, x);
getmaxyx(self->window, y2, x2);
int cur_len = 0;
if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */
if (ctx->pos > 0) {
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1]));
del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len);
if (x2 <= 0)
return;
if (x == 0)
wmove(self->window, y-1, x2 - cur_len);
else
wmove(self->window, y, x - cur_len);
} else {
beep();
}
}
else if (key == KEY_DC) { /* DEL key: Remove character at pos */
if (ctx->pos != ctx->len)
del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len);
else
beep();
if (self->help->active) {
help_onKey(self, key);
return;
}
else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */
if (ctx->pos > 0) {
discard_buf(ctx->line, &ctx->pos, &ctx->len);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
} else {
beep();
}
if (ltr) { /* char is printable */
input_new_char(self, key, x, y, x2, y2);
return;
}
else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */
if (ctx->pos != ctx->len)
kill_buf(ctx->line, &ctx->pos, &ctx->len);
else
beep();
}
if (line_info_onKey(self, key))
return;
else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (ctx->pos > 0) {
ctx->pos = 0;
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
}
if (input_handle(self, key, x, y, x2, y2))
return;
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
if (ctx->pos != ctx->len) {
ctx->pos = ctx->len;
mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2);
}
}
else if (key == KEY_LEFT) {
if (ctx->pos > 0) {
--ctx->pos;
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
if (x == 0)
wmove(self->window, y-1, x2 - cur_len);
else
wmove(self->window, y, x - cur_len);
} else {
beep();
}
}
else if (key == KEY_RIGHT) {
if (ctx->pos < ctx->len) {
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
++ctx->pos;
if (x == x2-1)
wmove(self->window, y+1, 0);
else
wmove(self->window, y, x + cur_len);
} else {
beep();
}
}
else if (key == KEY_UP) { /* fetches previous item in history */
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, LN_HIST_MV_UP);
mv_curs_end(self->window, ctx->len, y2, x2);
}
else if (key == KEY_DOWN) { /* fetches next item in history */
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, LN_HIST_MV_DWN);
mv_curs_end(self->window, ctx->len, y2, x2);
}
else if (key == '\t') { /* TAB key: completes peer name */
if (key == '\t') { /* TAB key: auto-completes peer name or command */
if (ctx->len > 0) {
int diff;
if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e'))
diff = complete_line(ctx->line, &ctx->pos, &ctx->len, groupchats[self->num].peer_names,
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
diff = complete_line(self, groupchats[self->num].peer_names,
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
else
diff = complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
MAX_CMDNAME_SIZE);
diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
if (diff != -1) {
if (x + diff > x2 - 1) {
int ofst = (x + diff - 1) - (x2 - 1);
wmove(self->window, y+1, ofst);
} else {
wmove(self->window, y, x+diff);
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
}
} else {
beep();
sound_notify(self, error, 0, NULL);
}
} else {
beep();
sound_notify(self, error, 0, NULL);
}
}
/* Scroll peerlist up and down one position if list overflows window */
else if (key == KEY_NPAGE) {
} else if (key == user_settings_->key_peer_list_down) { /* Scroll peerlist up and down one position */
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
++groupchats[self->num].side_pos;
}
else if (key == KEY_PPAGE) {
} else if (key == user_settings_->key_peer_list_up) {
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
}
} else if (key == '\n') {
rm_trailing_spaces_buf(ctx);
else
#if HAVE_WIDECHAR
if (iswprint(key))
#else
if (isprint(key))
#endif
{ /* prevents buffer overflows and strange behaviour when cursor goes past the window */
if ( (ctx->len < MAX_STR_SIZE-1) && (ctx->len < (x2 * (CHATBOX_HEIGHT - 1)-1)) ) {
add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key);
if (x == x2-1)
wmove(self->window, y+1, 0);
else
wmove(self->window, y, x + MAX(1, wcwidth(key)));
}
}
/* RETURN key: Execute command or print line */
else if (key == '\n') {
uint8_t line[MAX_STR_SIZE];
char line[MAX_STR_SIZE];
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
wclrtobot(self->window);
if (!string_is_empty(line))
add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos);
add_line_to_hist(ctx);
if (line[0] == '/') {
if (strcmp(line, "/close") == 0) {
close_groupchat(self, m, self->num);
return;
} else if (strcmp(line, "/help") == 0) {
if (strcmp(line, "help global") == 0)
execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE);
else
print_groupchat_help(ctx);
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_group_action(self, ctx, m, line + strlen("/me "));
} else {
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
}
} else if (!string_is_empty(line)) {
if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) {
wattron(ctx->history, COLOR_PAIR(RED));
wprintw(ctx->history, " * Failed to send message.\n");
wattroff(ctx->history, COLOR_PAIR(RED));
if (tox_group_message_send(m, self->num, (uint8_t *) line, strlen(line)) == -1) {
const char *errmsg = " * Failed to send message.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
}
}
reset_buf(ctx->line, &ctx->pos, &ctx->len);
wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
reset_buf(ctx);
}
}
static void groupchat_onDraw(ToxWindow *self, Tox *m)
{
curs_set(1);
int x2, y2;
getmaxyx(self->window, y2, x2);
ChatContext *ctx = self->chatwin;
line_info_print(self);
wclear(ctx->linewin);
if (ctx->len > 0) {
uint8_t line[MAX_STR_SIZE];
curs_set(1);
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
reset_buf(ctx->line, &ctx->pos, &ctx->len);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
} else {
mvwprintw(ctx->linewin, 1, 0, "%s", line);
}
}
if (ctx->len > 0)
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
wclear(ctx->sidebar);
mvwhline(ctx->linewin, 0, 0, ACS_HLINE, x2);
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2-CHATBOX_HEIGHT);
mvwaddch(ctx->sidebar, y2-CHATBOX_HEIGHT, 0, ACS_BTEE);
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
int num_peers = groupchats[self->num].num_peers;
@ -576,50 +455,62 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
wattroff(ctx->sidebar, A_BOLD);
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH-1);
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
int N = TOX_MAX_NAME_LENGTH;
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
int i;
for (i = 0; i < num_peers && i < maxlines; ++i) {
wmove(ctx->sidebar, i+2, 1);
wmove(ctx->sidebar, i + 2, 1);
int peer = i + groupchats[self->num].side_pos;
/* truncate nick to fit in side panel without modifying list */
uint8_t tmpnck[TOX_MAX_NAME_LENGTH];
memcpy(tmpnck, &groupchats[self->num].peer_names[peer*N], SIDEBAR_WIDTH-2);
tmpnck[SIDEBAR_WIDTH-2] = '\0';
char tmpnck[TOX_MAX_NAME_LENGTH];
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * N], SIDEBAR_WIDTH - 2);
tmpnck[SIDEBAR_WIDTH - 2] = '\0';
wprintw(ctx->sidebar, "%s\n", tmpnck);
}
int y, x;
getyx(self->window, y, x);
(void) x;
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
wmove(self->window, y + 1, new_x);
wrefresh(self->window);
if (self->help->active)
help_onDraw(self);
}
static void groupchat_onInit(ToxWindow *self, Tox *m)
{
int x, y;
getmaxyx(self->window, y, x);
int x2, y2;
getmaxyx(self->window, y2, x2);
ChatContext *ctx = self->chatwin;
ctx->history = subwin(self->window, y-CHATBOX_HEIGHT+1, x-SIDEBAR_WIDTH-1, 0, 0);
scrollok(ctx->history, 1);
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x, y-CHATBOX_HEIGHT, 0);
ctx->sidebar = subwin(self->window, y-CHATBOX_HEIGHT+1, SIDEBAR_WIDTH, 0, x-SIDEBAR_WIDTH);
ctx->log = malloc(sizeof(struct chatlog));
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
if (ctx->log == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
ctx->hst = calloc(1, sizeof(struct history));
ctx->log = calloc(1, sizeof(struct chatlog));
memset(ctx->log, 0, sizeof(struct chatlog));
if (ctx->log == NULL || ctx->hst == NULL)
exit_toxic_err("failed in groupchat_onInit", FATALERR_MEMORY);
line_info_init(ctx->hst);
if (user_settings_->autolog == AUTOLOG_ON)
log_enable(self->name, NULL, ctx->log);
print_groupchat_help(ctx);
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
wmove(self->window, y-CURS_Y_OFFSET, 0);
scrollok(ctx->history, 0);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
ToxWindow new_group_chat(Tox *m, int groupnum)
@ -637,19 +528,19 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
ret.onGroupAction = &groupchat_onGroupAction;
snprintf(ret.name, sizeof(ret.name), "Room #%d", groupnum);
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
Help *help = calloc(1, sizeof(Help));
if (chatwin != NULL)
ret.chatwin = chatwin;
else {
endwin();
fprintf(stderr, "calloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
if (chatwin == NULL || help == NULL)
exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY);
ret.chatwin = chatwin;
ret.help = help;
ret.num = groupnum;
ret.active_box = -1;
return ret;
}

View File

@ -20,18 +20,29 @@
*
*/
#ifndef _groupchat_h
#define _groupchat_h
#include "toxic.h"
#include "windows.h"
#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
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;
uint8_t *peer_names;
uint8_t *oldpeer_names;
uint16_t *peer_name_lengths;
uint16_t *oldpeer_name_lengths;
} 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);
#endif /* #define _groupchat_h */

321
src/help.c Normal file
View File

@ -0,0 +1,321 @@
/* help.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 "windows.h"
#include "toxic.h"
#include "help.h"
#include "misc_tools.h"
#define HELP_MENU_HEIGHT 8
#define HELP_MENU_WIDTH 26
void help_init_menu(ToxWindow *self)
{
if (self->help->win)
delwin(self->help->win);
int y2, x2;
getmaxyx(self->window, y2, x2);
if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH)
return;
self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3);
self->help->active = true;
self->help->type = HELP_MENU;
}
static void help_exit(ToxWindow *self)
{
delwin(self->help->win);
memset(self->help, 0, sizeof(Help));
}
static void help_init_window(ToxWindow *self, int height, int width)
{
if (self->help->win)
delwin(self->help->win);
int y2, x2;
getmaxyx(stdscr, y2, x2);
height = MIN(height, y2);
width = MIN(width, x2);
self->help->win = newwin(height, width, 0, 0);
}
static void help_draw_menu(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " Help Menu\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " g");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "lobal commands\n");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " c");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "hat commands\n");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " f");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "riendlist controls\n");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " k");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "ey bindings\n");
wprintw(win, " e");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "x");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "it menu\n");
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
}
static void help_draw_bottom_menu(WINDOW *win)
{
int y2, x2;
getmaxyx(win, y2, x2);
(void) x2;
wmove(win, y2 - 2, 1);
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " m");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "ain menu |");
wprintw(win, " e");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "x");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "it");
}
static void help_draw_global(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, "Global Commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
wprintw(win, " /accept <id> : Accept friend request\n");
wprintw(win, " /decline <id> : Decline friend request\n");
wprintw(win, " /requests : List pending friend requests\n");
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
wprintw(win, " /status <type> <msg> : Set status with optional note\n");
wprintw(win, " /note <msg> : Set a personal note\n");
wprintw(win, " /nick <nick> : Set your nickname\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");
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
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 */
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
}
static void help_draw_chat(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
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, " /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
wattron(win, A_BOLD);
wprintw(win, "\n Audio:\n");
wattroff(win, A_BOLD);
wprintw(win, " /call : Audio call\n");
wprintw(win, " /answer : Answer incoming call\n");
wprintw(win, " /reject : Reject incoming call\n");
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 */
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
}
static void help_draw_keys(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
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, " 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, " (Note: Custom keybindings override these defaults.)\n\n");
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
}
static void help_draw_contacts(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, "Friendlist controls:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " Up and Down arrows : Scroll through list\n");
wprintw(win, " Right and Left arrows : Switch between friendlist and blocked list\n");
wprintw(win, " Enter : Open a chat window with selected contact\n");
wprintw(win, " Delete : Permanently delete a contact\n");
wprintw(win, " B : Block or unblock a contact\n");
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
}
void help_onKey(ToxWindow *self, wint_t key)
{
switch(key) {
case 'x':
case T_KEY_ESC:
help_exit(self);
break;
case 'c':
#ifdef _AUDIO
help_init_window(self, 19, 80);
#else
help_init_window(self, 9, 80);
#endif
self->help->type = HELP_CHAT;
break;
case 'g':
#ifdef _AUDIO
help_init_window(self, 23, 80);
#else
help_init_window(self, 19, 80);
#endif
self->help->type = HELP_GLOBAL;
break;
case 'f':
help_init_window(self, 10, 80);
self->help->type = HELP_CONTACTS;
break;
case 'k':
help_init_window(self, 12, 80);
self->help->type = HELP_KEYS;
break;
case 'm':
help_init_menu(self);
self->help->type = HELP_MENU;
break;
}
}
void help_onDraw(ToxWindow *self)
{
curs_set(0);
switch(self->help->type) {
case HELP_MENU:
help_draw_menu(self);
return;
case HELP_CHAT:
help_draw_chat(self);
break;
case HELP_GLOBAL:
help_draw_global(self);
break;
case HELP_KEYS:
help_draw_keys(self);
break;
case HELP_CONTACTS:
help_draw_contacts(self);
break;
case HELP_GROUP:
break;
}
}

42
src/help.h Normal file
View File

@ -0,0 +1,42 @@
/* help.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 _help_h
#define _help_h
#include "toxic.h"
#include "windows.h"
typedef enum {
HELP_MENU,
HELP_GLOBAL,
HELP_CHAT,
HELP_GROUP,
HELP_KEYS,
HELP_CONTACTS,
} HELP_TYPES;
void help_onDraw(ToxWindow *self);
void help_init_menu(ToxWindow *self);
void help_onKey(ToxWindow *self, wint_t key);
#endif /* #define _help_h */

245
src/input.c Normal file
View File

@ -0,0 +1,245 @@
/* input.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/>.
*
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* needed for wcwidth() */
#endif
#include <wchar.h>
#include "toxic.h"
#include "windows.h"
#include "misc_tools.h"
#include "toxic_strings.h"
#include "line_info.h"
#include "notify.h"
/* 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;
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);
return;
}
if (add_char_to_buf(ctx, key) == -1) {
sound_notify(self, error, 0, NULL);
return;
}
if (x + cur_len >= mx_x) {
int s_len = wcwidth(ctx->line[ctx->start]);
ctx->start += 1 + MAX(0, cur_len - s_len);
}
}
/* delete a char via backspace key from input field and buffer */
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);
return;
}
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
if (ctx->start && (x >= mx_x - cur_len))
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
else if (ctx->start)
ctx->start = MAX(0, ctx->start - cur_len);
}
/* delete a char via delete key from input field and buffer */
static void input_delete(ToxWindow *self)
{
if (del_char_buf_frnt(self->chatwin) == -1)
sound_notify(self, error, 0, NULL);
}
/* 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);
}
/* 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);
}
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);
return;
}
int yank_cols = MAX(0, wcswidth(ctx->yank, ctx->yank_len));
if (x + yank_cols >= mx_x) {
int rmdr = MAX(0, (x + yank_cols) - mx_x);
int s_len = wcswidth(&ctx->line[ctx->start], rmdr);
ctx->start += s_len + 1;
}
}
/* moves cursor/line position to end of line in input field and buffer */
static void input_mv_end(ToxWindow *self, int y, int mx_x)
{
ChatContext *ctx = self->chatwin;
ctx->pos = ctx->len;
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
}
/* moves cursor/line position to start of line in input field and buffer */
static void input_mv_home(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0)
return;
ctx->pos = 0;
ctx->start = 0;
}
/* moves cursor/line position left in input field and buffer */
static void input_mv_left(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0)
return;
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
--ctx->pos;
if (ctx->start && (x >= mx_x - cur_len))
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
else if (ctx->start)
ctx->start = MAX(0, ctx->start - cur_len);
}
/* moves cursor/line position right in input field and buffer */
static void input_mv_right(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos >= ctx->len)
return;
++ctx->pos;
int cur_len = wcwidth(ctx->line[ctx->pos - 1]);
if (x + cur_len >= mx_x) {
int s_len = wcwidth(ctx->line[ctx->start]);
ctx->start += 1 + MAX(0, cur_len - s_len);
}
}
/* puts a line history item in input field and buffer */
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));
ctx->start = wlen < mx_x ? 0 : wlen - mx_x + 1;
}
/* Handles non-printable input keys that behave the same for all types of chat windows.
return true if key matches a function, false otherwise */
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
{
bool match = true;
switch (key) {
case 0x7f:
case KEY_BACKSPACE:
input_backspace(self, x, mx_x);
break;
case KEY_DC:
input_delete(self);
break;
case T_KEY_DISCARD:
input_discard(self);
break;
case T_KEY_KILL:
input_kill(self->chatwin);
break;
case T_KEY_C_Y:
input_yank(self, x, mx_x);
break;
case KEY_HOME:
case T_KEY_C_A:
input_mv_home(self);
break;
case KEY_END:
case T_KEY_C_E:
input_mv_end(self, y, mx_x);
break;
case KEY_LEFT:
input_mv_left(self, x, mx_x);
break;
case KEY_RIGHT:
input_mv_right(self, x, mx_x);
break;
case KEY_UP:
case KEY_DOWN:
input_history(self, key, mx_x);
break;
default:
match = false;
break;
}
return match;
}

33
src/input.h Normal file
View File

@ -0,0 +1,33 @@
/* input.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 _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);
/* Handles non-printable input keys that behave the same for all types of chat windows.
return true if key matches a function, false otherwise */
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
#endif /* #define _input_h */

484
src/line_info.c Normal file
View File

@ -0,0 +1,484 @@
/* line_info.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 <stdbool.h>
#include <stdarg.h>
#include "toxic.h"
#include "windows.h"
#include "line_info.h"
#include "groupchat.h"
#include "settings.h"
#include "notify.h"
extern struct user_settings *user_settings_;
void line_info_init(struct history *hst)
{
hst->line_root = calloc(1, sizeof(struct line_info));
if (hst->line_root == NULL)
exit_toxic_err("failed in line_info_init", FATALERR_MEMORY);
hst->line_start = hst->line_root;
hst->line_end = hst->line_start;
hst->queue_sz = 0;
}
/* resets line_start (page end) */
static void line_info_reset_start(ToxWindow *self, struct history *hst)
{
struct line_info *line = hst->line_end;
if (line->prev == NULL)
return;
int y2, x2;
getmaxyx(self->window, y2, x2);
int side_offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;
int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
int curlines = 0;
int nxtlines = line->newlines + (line->len / (x2 - side_offst));
do {
curlines += 1 + nxtlines;
line = line->prev;
nxtlines = line->newlines + (line->len / (x2 - side_offst));
} while (line->prev && curlines + nxtlines < max_y);
hst->line_start = line;
}
void line_info_cleanup(struct history *hst)
{
struct line_info *tmp1 = hst->line_root;
while (tmp1) {
struct line_info *tmp2 = tmp1->next;
free(tmp1);
tmp1 = tmp2;
}
int i;
for (i = 0; i < hst->queue_sz; ++i) {
if (hst->queue[i])
free(hst->queue[i]);
}
}
/* moves root forward and frees previous root */
static void line_info_root_fwd(struct history *hst)
{
struct line_info *tmp = hst->line_root->next;
tmp->prev = NULL;
if (hst->line_start->prev == NULL) { /* if line_start is root move it forward as well */
hst->line_start = hst->line_start->next;
hst->line_start->prev = NULL;
++hst->start_id;
}
free(hst->line_root);
hst->line_root = tmp;
}
/* adds a line_info line to queue */
static void line_info_add_queue(struct history *hst, struct line_info *line)
{
if (hst->queue_sz >= MAX_QUEUE)
return;
hst->queue[hst->queue_sz++] = line;
}
/* returns ptr to queue item 0 and removes it from queue */
static struct line_info *line_info_ret_queue(struct history *hst)
{
if (hst->queue_sz <= 0)
return NULL;
struct line_info *ret = hst->queue[0];
int i;
for (i = 0; i < hst->queue_sz; ++i)
hst->queue[i] = hst->queue[i + 1];
--hst->queue_sz;
return ret;
}
/* creates new line_info line and puts it in the queue.
SYS_MSG lines may contain an arbitrary number of arguments for string formatting */
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint8_t type, uint8_t bold,
uint8_t colour, const char *msg, ...)
{
struct history *hst = self->chatwin->hst;
struct line_info *new_line = calloc(1, sizeof(struct line_info));
if (new_line == NULL)
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
char frmt_msg[MAX_STR_SIZE] = {0};
va_list args;
va_start(args, msg);
vsnprintf(frmt_msg, sizeof(frmt_msg), msg, args);
va_end(args);
int len = 1; /* there will always be a newline */
/* for type-specific formatting in print function */
switch (type) {
case ACTION:
case CONNECTION:
len += 3;
break;
case SYS_MSG:
break;
case PROMPT:
++len;
break;
default:
len += 2;
break;
}
if (frmt_msg[0]) {
snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg);
len += strlen(new_line->msg);
int i;
for (i = 0; frmt_msg[i]; ++i) {
if (frmt_msg[i] == '\n')
++new_line->newlines;
}
}
if (tmstmp) {
snprintf(new_line->timestamp, sizeof(new_line->timestamp), "%s", tmstmp);
len += strlen(new_line->timestamp);
}
if (name1) {
snprintf(new_line->name1, sizeof(new_line->name1), "%s", name1);
len += strlen(new_line->name1);
}
if (name2) {
snprintf(new_line->name2, sizeof(new_line->name2), "%s", name2);
len += strlen(new_line->name2);
}
new_line->len = len;
new_line->type = type;
new_line->bold = bold;
new_line->colour = colour;
line_info_add_queue(hst, new_line);
}
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
static void line_info_check_queue(ToxWindow *self)
{
struct history *hst = self->chatwin->hst;
struct line_info *line = line_info_ret_queue(hst);
if (line == NULL)
return;
if (hst->start_id > user_settings_->history_size)
line_info_root_fwd(hst);
line->id = hst->line_end->id + 1;
line->prev = hst->line_end;
hst->line_end->next = line;
hst->line_end = line;
int y, y2, x, x2;
getmaxyx(self->window, y2, x2);
getyx(self->chatwin->history, y, x);
(void) x;
if (x2 <= SIDEBAR_WIDTH)
return;
int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
int lines = 1 + line->newlines + (line->len / (x2 - offst));
int max_y = y2 - CHATBOX_HEIGHT;
/* move line_start forward proportionate to the number of new lines */
if (y + lines - 1 >= max_y) {
while (lines > 0 && hst->line_start->next) {
lines -= 1 + hst->line_start->next->newlines + (hst->line_start->next->len / (x2 - offst));
hst->line_start = hst->line_start->next;
++hst->start_id;
}
}
}
void line_info_print(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
if (ctx == NULL)
return;
struct history *hst = ctx->hst;
/* Only allow one new item to be added to chat window per call to this function */
line_info_check_queue(self);
WINDOW *win = ctx->history;
wclear(win);
int y2, x2;
getmaxyx(self->window, y2, x2);
if (x2 <= SIDEBAR_WIDTH)
return;
if (self->is_groupchat)
wmove(win, 0, 0);
else
wmove(win, 2, 0);
struct line_info *line = hst->line_start->next;
int numlines = 0;
while (line && numlines++ <= y2) {
uint8_t type = line->type;
switch (type) {
case OUT_MSG:
case IN_MSG:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wattroff(win, COLOR_PAIR(BLUE));
int nameclr = GREEN;
if (line->colour)
nameclr = line->colour;
else if (type == IN_MSG)
nameclr = CYAN;
wattron(win, COLOR_PAIR(nameclr));
wprintw(win, "%s: ", line->name1);
wattroff(win, COLOR_PAIR(nameclr));
if (line->msg[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
wprintw(win, "%s\n", line->msg);
if (line->msg[0] == '>')
wattroff(win, COLOR_PAIR(GREEN));
break;
case ACTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(YELLOW));
wprintw(win, "* %s %s\n", line->name1, line->msg);
wattroff(win, COLOR_PAIR(YELLOW));
break;
case SYS_MSG:
if (line->timestamp[0]) {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wattroff(win, COLOR_PAIR(BLUE));
}
if (line->bold)
wattron(win, A_BOLD);
if (line->colour)
wattron(win, COLOR_PAIR(line->colour));
wprintw(win, "%s\n", line->msg);
if (line->bold)
wattroff(win, A_BOLD);
if (line->colour)
wattroff(win, COLOR_PAIR(line->colour));
break;
case PROMPT:
wattron(win, COLOR_PAIR(GREEN));
wprintw(win, "$ ");
wattroff(win, COLOR_PAIR(GREEN));
if (line->msg[0])
wprintw(win, "%s", line->msg);
wprintw(win, "\n");
break;
case CONNECTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(line->colour));
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));
break;
case NAME_CHANGE:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(MAGENTA));
wattron(win, A_BOLD);
wprintw(win, "* %s", line->name1);
wattroff(win, A_BOLD);
wprintw(win, "%s", line->msg);
wattron(win, A_BOLD);
wprintw(win, "%s\n", line->name2);
wattroff(win, A_BOLD);
wattroff(win, COLOR_PAIR(MAGENTA));
break;
}
line = line->next;
}
/* keep calling until queue is empty */
if (hst->queue_sz > 0)
line_info_print(self);
}
void line_info_set(ToxWindow *self, uint32_t id, char *msg)
{
struct line_info *line = self->chatwin->hst->line_end;
while (line) {
if (line->id == id) {
snprintf(line->msg, sizeof(line->msg), "%s", msg);
return;
}
line = line->prev;
}
}
/* static void line_info_goto_root(struct history *hst)
{
hst->line_start = hst->line_root;
} */
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);
}
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);
}
static void line_info_page_up(ToxWindow *self, struct history *hst)
{
int x2, y2;
getmaxyx(self->window, y2, x2);
(void) x2;
int jump_dist = y2 / 2;
int i;
for (i = 0; i < jump_dist && hst->line_start->prev; ++i)
hst->line_start = hst->line_start->prev;
}
static void line_info_page_down(ToxWindow *self, struct history *hst)
{
int x2, y2;
getmaxyx(self->window, y2, x2);
(void) x2;
int jump_dist = y2 / 2;
int i;
for (i = 0; i < jump_dist && hst->line_start->next; ++i)
hst->line_start = hst->line_start->next;
}
bool line_info_onKey(ToxWindow *self, wint_t key)
{
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;
}
return match;
}
void line_info_clear(struct history *hst)
{
hst->line_start = hst->line_end;
hst->start_id = hst->line_start->id;
}

90
src/line_info.h Normal file
View File

@ -0,0 +1,90 @@
/* line_info.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 _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
enum {
SYS_MSG,
IN_MSG,
OUT_MSG,
PROMPT,
ACTION,
CONNECTION,
NAME_CHANGE,
} LINE_TYPE;
struct line_info {
char timestamp[TIME_STR_SIZE];
char name1[TOXIC_MAX_NAME_LENGTH];
char name2[TOXIC_MAX_NAME_LENGTH];
char msg[TOX_MAX_MESSAGE_LENGTH];
uint8_t type;
uint8_t bold;
uint8_t colour;
uint32_t id;
uint16_t len; /* combined len of all strings */
uint8_t newlines;
struct line_info *prev;
struct line_info *next;
};
/* Linked list containing chat history lines */
struct history {
struct line_info *line_root;
struct line_info *line_start; /* the first line we want to start printing at */
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];
int queue_sz;
};
/* creates new line_info line and puts it in the queue.
SYS_MSG lines may contain an arbitrary number of arguments for string formatting */
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint8_t type, uint8_t bold,
uint8_t colour, const char *msg, ...);
/* Prints a section of history starting at line_start */
void line_info_print(ToxWindow *self);
/* frees all history lines */
void line_info_cleanup(struct history *hst);
/* clears the screen (does not delete anything) */
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);
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 */

View File

@ -20,53 +20,61 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "configdir.h"
#include "toxic_windows.h"
#include "toxic.h"
#include "windows.h"
#include "misc_tools.h"
#include "log.h"
#include "settings.h"
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(uint8_t *name, uint8_t *key, struct chatlog *log)
void init_logging_session(char *name, const char *key, struct chatlog *log)
{
if (!log->log_on)
return;
if (!valid_nick(name))
name = UNKNOWN_NAME;
const char *set_path = user_settings_->chatlogs_path;
char *user_config_dir = get_user_config_dir();
int path_len = strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(name);
int path_len = strlen(set_path) + strlen(name) ? *set_path
: strlen(user_config_dir) + strlen(LOGDIR) + strlen(name);
/* use first 4 digits of key as log ident. If no key use a timestamp */
uint8_t ident[32];
char ident[32];
if (key != NULL) {
path_len += (KEY_IDENT_DIGITS * 2 + 5);
sprintf(&ident[0], "%02X", key[0] & 0xff);
sprintf(&ident[2], "%02X", key[2] & 0xff);
ident[KEY_IDENT_DIGITS*2+1] = '\0';
sprintf(&ident[2], "%02X", key[1] & 0xff);
ident[KEY_IDENT_DIGITS * 2 + 1] = '\0';
} else {
struct tm *tminfo = get_time();
snprintf(ident, sizeof(ident),
"%04d-%02d-%02d[%d:%02d:%02d]", tminfo->tm_year+1900,tminfo->tm_mon+1, tminfo->tm_mday,
tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec);
strftime(ident, sizeof(ident), "%Y-%m-%d[%H:%M:%S]", get_time());
path_len += strlen(ident) + 1;
}
if (path_len > MAX_STR_SIZE) {
if (path_len >= MAX_STR_SIZE) {
log->log_on = false;
free(user_config_dir);
return;
}
uint8_t log_path[MAX_STR_SIZE];
char log_path[MAX_STR_SIZE];
snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
user_config_dir, CONFIGDIR, name, ident);
if (*set_path)
snprintf(log_path, sizeof(log_path), "%s%s-%s.log", set_path, name, ident);
else
snprintf(log_path, sizeof(log_path), "%s%s%s-%s.log", user_config_dir, LOGDIR, name, ident);
free(user_config_dir);
log->file = fopen(log_path, "a");
@ -76,10 +84,9 @@ void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log)
}
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
free(user_config_dir);
}
void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event)
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
{
if (!log->log_on)
return;
@ -89,19 +96,19 @@ void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event)
return;
}
uint8_t name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
char name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
if (event)
snprintf(name_frmt, sizeof(name_frmt), "* %s", name);
else
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
struct tm *tminfo = get_time();
fprintf(log->file,"%04d/%02d/%02d [%d:%02d:%02d] %s %s\n", tminfo->tm_year+1900,
tminfo->tm_mon+1, tminfo->tm_mday, tminfo->tm_hour, tminfo->tm_min,
tminfo->tm_sec, name_frmt, msg);
const char *t = user_settings_->time == TIME_12 ? "%Y/%m/%d [%I:%M:%S %p]" : "%Y/%m/%d [%H:%M:%S]";
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 = (uint64_t) time(NULL);
uint64_t curtime = get_unix_time();
if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) {
fflush(log->file);
@ -109,7 +116,7 @@ void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event)
}
}
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log)
void log_enable(char *name, const char *key, struct chatlog *log)
{
log->log_on = true;

View File

@ -20,14 +20,28 @@
*
*/
#ifndef _log_h
#define _log_h
#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */
struct chatlog {
FILE *file;
uint64_t lastwrite;
int pos;
bool log_on; /* specific to current chat window */
};
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log);
void init_logging_session(char *name, const char *key, struct chatlog *log);
/* formats/writes line to log file */
void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event);
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(uint8_t *name, uint8_t *key, struct chatlog *log);
void log_enable(char *name, const char *key, struct chatlog *log);
/* disables logging for specified log and closes file */
void log_disable(struct chatlog *log);
#endif /* #define _log_h */

View File

@ -1,530 +0,0 @@
/* main.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/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef SIGWINCH
#define SIGWINCH 28
#endif
#include <curses.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <signal.h>
#include <locale.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <direct.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#include <tox/tox.h>
#include "configdir.h"
#include "toxic_windows.h"
#include "friendlist.h"
#include "prompt.h"
#include "misc_tools.h"
#include "file_senders.h"
#ifdef _SUPPORT_AUDIO
#include "audio_call.h"
#endif /* _SUPPORT_AUDIO */
#ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "."
#endif
#ifdef _SUPPORT_AUDIO
ToxAv* av;
#endif /* _SUPPORT_AUDIO */
/* Export for use in Callbacks */
char *DATA_FILE = NULL;
ToxWindow *prompt = NULL;
static int f_loadfromfile; /* 1 if we want to load from/save the data file, 0 otherwise */
void on_window_resize(int sig)
{
endwin();
refresh();
clear();
}
static void init_term(void)
{
/* Setup terminal */
signal(SIGWINCH, on_window_resize);
#if HAVE_WIDECHAR
if (setlocale(LC_ALL, "") == NULL) {
fprintf(stderr, "Could not set your locale, plese check your locale settings or"
"disable wide char support\n");
exit(EXIT_FAILURE);
}
#endif
initscr();
cbreak();
keypad(stdscr, 1);
noecho();
timeout(100);
if (has_colors()) {
start_color();
init_pair(0, COLOR_WHITE, COLOR_BLACK);
init_pair(1, COLOR_GREEN, COLOR_BLACK);
init_pair(2, COLOR_CYAN, COLOR_BLACK);
init_pair(3, COLOR_RED, COLOR_BLACK);
init_pair(4, COLOR_BLUE, COLOR_BLACK);
init_pair(5, COLOR_YELLOW, COLOR_BLACK);
init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
init_pair(7, COLOR_BLACK, COLOR_BLACK);
init_pair(8, COLOR_BLACK, COLOR_WHITE);
}
refresh();
}
static Tox *init_tox(int ipv4)
{
/* Init core */
int ipv6 = !ipv4;
Tox *m = tox_new(ipv6);
/*
* TOX_ENABLE_IPV6_DEFAULT is always 1.
* Checking it is redundant, this *should* be doing ipv4 fallback
*/
if (ipv6 && m == NULL) {
fprintf(stderr, "IPv6 didn't initialize, trying IPv4\n");
m = tox_new(0);
}
if (m == NULL)
return NULL;
/* Callbacks */
tox_callback_connection_status(m, on_connectionchange, NULL);
tox_callback_typing_change(m, on_typing_change, NULL);
tox_callback_friend_request(m, on_request, NULL);
tox_callback_friend_message(m, on_message, NULL);
tox_callback_name_change(m, on_nickchange, NULL);
tox_callback_user_status(m, on_statuschange, NULL);
tox_callback_status_message(m, on_statusmessagechange, NULL);
tox_callback_friend_action(m, on_action, NULL);
tox_callback_group_invite(m, on_groupinvite, NULL);
tox_callback_group_message(m, on_groupmessage, NULL);
tox_callback_group_action(m, on_groupaction, NULL);
tox_callback_group_namelist_change(m, on_group_namelistchange, NULL);
tox_callback_file_send_request(m, on_file_sendrequest, NULL);
tox_callback_file_control(m, on_file_control, NULL);
tox_callback_file_data(m, on_file_data, NULL);
#ifdef __linux__
tox_set_name(m, (uint8_t *) "Cool guy", sizeof("Cool guy"));
#elif defined(_WIN32)
tox_set_name(m, (uint8_t *) "I should install GNU/Linux", sizeof("I should install GNU/Linux"));
#elif defined(__APPLE__)
tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); /* This used to users of other Unixes are hipsters */
#else
tox_set_name(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4"));
#endif
return m;
}
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
#define MAXNODES 50
#define NODELEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
static int linecnt = 0;
static char nodes[MAXNODES][NODELEN];
static uint16_t ports[MAXNODES];
static uint8_t keys[MAXNODES][TOX_CLIENT_ID_SIZE];
static int nodelist_load(char *filename)
{
if (!filename)
return 1;
FILE *fp = fopen(filename, "r");
if (fp == NULL)
return 1;
char line[MAXLINE];
while (fgets(line, sizeof(line), fp) && linecnt < MAXNODES) {
if (strlen(line) > MINLINE) {
char *name = strtok(line, " ");
char *port = strtok(NULL, " ");
char *key_ascii = strtok(NULL, " ");
/* invalid line */
if (name == NULL || port == NULL || key_ascii == NULL)
continue;
strncpy(nodes[linecnt], name, NODELEN);
nodes[linecnt][NODELEN - 1] = 0;
ports[linecnt] = htons(atoi(port));
uint8_t *key_binary = hex_string_to_bin(key_ascii);
memcpy(keys[linecnt], key_binary, TOX_CLIENT_ID_SIZE);
free(key_binary);
linecnt++;
}
}
if (linecnt < 1) {
fclose(fp);
return 2;
}
fclose(fp);
return 0;
}
int init_connection_helper(Tox *m, int line)
{
return tox_bootstrap_from_address(m, nodes[line], TOX_ENABLE_IPV6_DEFAULT,
ports[line], keys[line]);
}
/* Connects to a random DHT node listed in the DHTnodes file
*
* return codes:
* 1: failed to open node file
* 2: no line of sufficient length in node file
* 3: failed to resolve name to IP
* 4: nodelist file contains no acceptable line
*/
static bool srvlist_loaded = false;
#define NUM_INIT_NODES 5
int init_connection(Tox *m)
{
if (linecnt > 0) /* already loaded nodelist */
return init_connection_helper(m, rand() % linecnt) ? 0 : 3;
/* only once:
* - load the nodelist
* - connect to "everyone" inside
*/
if (!srvlist_loaded) {
srvlist_loaded = true;
int res = nodelist_load(PACKAGE_DATADIR "/DHTnodes");
if (linecnt < 1)
return res;
res = 3;
int i;
int n = MIN(NUM_INIT_NODES, linecnt);
for(i = 0; i < n; ++i)
if (init_connection_helper(m, rand() % linecnt))
res = 0;
return res;
}
/* empty nodelist file */
return 4;
}
static void do_connection(Tox *m, ToxWindow *prompt)
{
static int conn_try = 0;
static int conn_err = 0;
static bool dht_on = false;
bool is_connected = tox_isconnected(m);
if (!dht_on && !is_connected && !(conn_try++ % 100)) {
if (!conn_err) {
if ((conn_err = init_connection(m))) {
prep_prompt_win();
wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err);
}
}
} else if (!dht_on && is_connected) {
dht_on = true;
prompt_update_connectionstatus(prompt, dht_on);
prep_prompt_win();
wprintw(prompt->window, "DHT connected.\n");
} else if (dht_on && !is_connected) {
dht_on = false;
prompt_update_connectionstatus(prompt, dht_on);
prep_prompt_win();
wprintw(prompt->window, "\nDHT disconnected. Attempting to reconnect.\n");
}
}
static void load_friendlist(Tox *m)
{
int i;
uint32_t numfriends = tox_count_friendlist(m);
for (i = 0; i < numfriends; ++i)
friendlist_onFriendAdded(NULL, m, i, false);
}
/*
* Store Messenger to given location
* Return 0 stored successfully
* Return 1 file path is NULL
* Return 2 malloc failed
* Return 3 opening path failed
* Return 4 fwrite failed
*/
int store_data(Tox *m, char *path)
{
if (f_loadfromfile == 0) /*If file loading/saving is disabled*/
return 0;
if (path == NULL)
return 1;
FILE *fd;
size_t len;
uint8_t *buf;
len = tox_size(m);
buf = malloc(len);
if (buf == NULL)
return 2;
tox_save(m, buf);
fd = fopen(path, "wb");
if (fd == NULL) {
free(buf);
return 3;
}
if (fwrite(buf, len, 1, fd) != 1) {
free(buf);
fclose(fd);
return 4;
}
free(buf);
fclose(fd);
return 0;
}
static void load_data(Tox *m, char *path)
{
if (f_loadfromfile == 0) /*If file loading/saving is disabled*/
return;
FILE *fd;
size_t len;
uint8_t *buf;
if ((fd = fopen(path, "rb")) != NULL) {
fseek(fd, 0, SEEK_END);
len = ftell(fd);
fseek(fd, 0, SEEK_SET);
buf = malloc(len);
if (buf == NULL) {
fclose(fd);
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
if (fread(buf, len, 1, fd) != 1) {
free(buf);
fclose(fd);
endwin();
fprintf(stderr, "fread() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
tox_load(m, buf, len);
load_friendlist(m);
free(buf);
fclose(fd);
} else {
int st;
if ((st = store_data(m, path)) != 0) {
endwin();
fprintf(stderr, "Store messenger failed with return code: %d\n", st);
exit(EXIT_FAILURE);
}
}
}
void exit_toxic(Tox *m)
{
store_data(m, DATA_FILE);
close_all_file_senders();
kill_all_windows();
log_disable(prompt->promptbuf->log);
free(DATA_FILE);
free(prompt->stb);
free(prompt->promptbuf->log);
free(prompt->promptbuf);
tox_kill(m);
#ifdef _SUPPORT_AUDIO
terminate_audio(prompt, av);
#endif /* _SUPPORT_AUDIO */
endwin();
exit(EXIT_SUCCESS);
}
static void do_toxic(Tox *m, ToxWindow *prompt)
{
do_connection(m, prompt);
draw_active_window(m);
do_file_senders(m);
/* main tox-core loop */
tox_do(m);
}
int main(int argc, char *argv[])
{
char *user_config_dir = get_user_config_dir();
int config_err = 0;
f_loadfromfile = 1;
int f_flag = 0;
int i = 0;
int f_use_ipv4 = 0;
/* Make sure all written files are read/writeable only by the current user. */
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
for (i = 0; i < argc; ++i) {
if (argv[i] == NULL)
break;
else if (argv[i][0] == '-') {
if (argv[i][1] == 'f') {
if (argv[i + 1] != NULL)
DATA_FILE = strdup(argv[i + 1]);
else
f_flag = -1;
} else if (argv[i][1] == 'n') {
f_loadfromfile = 0;
} else if (argv[i][1] == '4') {
f_use_ipv4 = 1;
}
}
}
config_err = create_user_config_dir(user_config_dir);
if (DATA_FILE == NULL ) {
if (config_err) {
DATA_FILE = strdup("data");
} else {
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("data") + 1);
if (DATA_FILE != NULL) {
strcpy(DATA_FILE, user_config_dir);
strcat(DATA_FILE, CONFIGDIR);
strcat(DATA_FILE, "data");
} else {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
}
}
free(user_config_dir);
init_term();
Tox *m = init_tox(f_use_ipv4);
if (m == NULL) {
endwin();
fprintf(stderr, "Failed to initialize network. Aborting...\n");
exit(EXIT_FAILURE);
}
prompt = init_windows(m);
#ifdef _SUPPORT_AUDIO
attron(COLOR_PAIR(RED) | A_BOLD);
wprintw(prompt->window, "Starting audio...\n");
attroff(COLOR_PAIR(RED) | A_BOLD);
av = init_audio(prompt, m);
if ( errors() == NoError )
wprintw(prompt->window, "Audio started with no problems.\n");
else /* Get error code and stuff */
wprintw(prompt->window, "Error starting audio!\n");
#endif /* _SUPPORT_AUDIO */
if (f_loadfromfile)
load_data(m, DATA_FILE);
if (f_flag == -1) {
attron(COLOR_PAIR(RED) | A_BOLD);
wprintw(prompt->window, "You passed '-f' without giving an argument.\n"
"defaulting to 'data' for a keyfile...\n");
attroff(COLOR_PAIR(RED) | A_BOLD);
}
if (config_err) {
attron(COLOR_PAIR(RED) | A_BOLD);
wprintw(prompt->window, "Unable to determine configuration directory.\n"
"defaulting to 'data' for a keyfile...\n");
attroff(COLOR_PAIR(RED) | A_BOLD);
}
prompt_init_statusbar(prompt, m);
sort_friendlist_index(m);
while (true)
do_toxic(m, prompt);
return 0;
}

View File

@ -20,68 +20,138 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <dirent.h>
#include <sys/stat.h>
#include "toxic_windows.h"
#include "toxic.h"
#include "windows.h"
#include "misc_tools.h"
#include "settings.h"
#include "file_senders.h"
extern ToxWindow *prompt;
extern struct user_settings *user_settings_;
/* XXX: FIX */
unsigned char *hex_string_to_bin(char hex_string[])
static uint64_t current_unix_time;
void hst_to_net(uint8_t *num, uint16_t numbytes)
{
size_t len = strlen(hex_string);
unsigned char *val = malloc(len);
#ifndef WORDS_BIGENDIAN
uint32_t i;
uint8_t buff[numbytes];
if (val == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
for (i = 0; i < numbytes; ++i) {
buff[i] = num[numbytes - i - 1];
}
char *pos = hex_string;
size_t i;
memcpy(num, buff, numbytes);
#endif
return;
}
for (i = 0; i < len; ++i, pos += 2)
sscanf(pos, "%2hhx", &val[i]);
void update_unix_time(void)
{
current_unix_time = (uint64_t) time(NULL);
}
return val;
uint64_t get_unix_time(void)
{
return current_unix_time;
}
/* Returns 1 if connection has timed out, 0 otherwise */
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
{
return timestamp + timeout <= curtime;
}
/* Get the current local time */
struct tm *get_time(void)
{
struct tm *timeinfo;
time_t now;
time(&now);
timeinfo = localtime(&now);
uint64_t t = get_unix_time();
timeinfo = localtime((const time_t*) &t);
return timeinfo;
}
/* Prints the time to given window */
void print_time(WINDOW *window)
/*Puts the current time in buf in the format of [HH:mm:ss] */
void get_time_str(char *buf, int bufsize)
{
struct tm *timeinfo = get_time();
if (user_settings_->timestamps == TIMESTAMPS_OFF) {
buf[0] = '\0';
return;
}
wattron(window, COLOR_PAIR(BLUE));
wprintw(window, "[%d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
wattroff(window,COLOR_PAIR(BLUE));
const char *t = user_settings_->time == TIME_12 ? "[%-I:%M:%S] " : "[%H:%M:%S] ";
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)
{
if (!secs)
return;
long int seconds = secs % 60;
long int minutes = (secs % 3600) / 60;
long int hours = secs / 3600;
if (!minutes && !hours)
snprintf(buf, bufsize, "%.2ld", seconds);
else if (!hours)
snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds);
else
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
}
char *hex_string_to_bin(const char *hex_string)
{
size_t len = strlen(hex_string);
char *val = malloc(len);
if (val == NULL)
exit_toxic_err("failed in hex_string_to_bin", FATALERR_MEMORY);
size_t i;
for (i = 0; i < len; ++i, hex_string += 2)
sscanf(hex_string, "%2hhx", &val[i]);
return val;
}
int hex_string_to_bytes(char *buf, int size, const char *keystr)
{
if (size % 2 != 0)
return -1;
int i, res;
const char *pos = keystr;
for (i = 0; i < size; ++i) {
res = sscanf(pos, "%2hhx", &buf[i]);
pos += 2;
if (res == EOF || res < 1)
return -1;
}
return 0;
}
/* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(char *string)
int string_is_empty(const char *string)
{
return string[0] == '\0';
}
/* convert a multibyte string to a wide character string (must provide buffer) */
int mbs_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n)
/* convert a multibyte string to a wide character string and puts in buf. */
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
{
size_t len = mbstowcs(NULL, string, 0) + 1;
@ -94,9 +164,8 @@ int mbs_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n)
return len;
}
/* converts wide character string into a multibyte string.
Same thing as wcs_to_mbs() but caller must provide its own buffer */
int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
/* converts wide character string into a multibyte string and puts in buf. */
int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
{
size_t len = wcstombs(NULL, string, 0) + 1;
@ -109,91 +178,18 @@ int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
return len;
}
/* convert wide characters to multibyte string: string returned must be freed */
uint8_t *wcs_to_mbs(wchar_t *string)
{
uint8_t *ret = NULL;
size_t len = wcstombs(NULL, string, 0);
if (len != (size_t) -1) {
ret = malloc(++len);
if (ret != NULL) {
if (wcstombs(ret, string, len) == (size_t) -1)
return NULL;
}
} else {
ret = malloc(2);
if (ret != NULL) {
ret[0] = ' ';
ret[1] = '\0';
}
}
if (ret == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
return ret;
}
/* convert a wide char to multibyte string */
char *wc_to_char(wchar_t ch)
{
static char ret[MB_LEN_MAX + 1];
int len = wctomb(ret, ch);
if (len == -1) {
ret[0] = ' ';
ret[1] = '\0';
} else {
ret[len] = '\0';
}
return ret;
}
/* Returns 1 if connection has timed out, 0 otherwise */
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
{
return timestamp + timeout <= curtime;
}
/* Colours the window tab according to type. Beeps if is_beep is true */
void alert_window(ToxWindow *self, int type, bool is_beep)
{
switch (type) {
case WINDOW_ALERT_0:
self->alert0 = true;
break;
case WINDOW_ALERT_1:
self->alert1 = true;
break;
case WINDOW_ALERT_2:
self->alert2 = true;
break;
}
StatusBar *stb = prompt->stb;
if (is_beep && stb->status != TOX_USERSTATUS_BUSY)
beep();
}
/* case-insensitive string compare function for use with qsort */
int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2)
int qsort_strcasecmp_hlpr(const void *str1, const void *str2)
{
return strcasecmp((const char *) nick1, (const char *) nick2);
return strcasecmp((const char *) str1, (const char *) str2);
}
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
- 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(uint8_t *nick)
int valid_nick(char *nick)
{
if (!nick[0] || nick[0] == ' ')
return 0;
@ -201,40 +197,117 @@ int valid_nick(uint8_t *nick)
int i;
for (i = 0; nick[i]; ++i) {
if (nick[i] == ' ' && nick[i+1] == ' ')
if (nick[i] == ' ' && nick[i + 1] == ' ')
return 0;
if (nick[i] == '/')
return 0;
}
return 1;
}
/* Moves cursor to the end of the line in given window */
void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x)
{
int end_y = (len / max_x) + (max_y - CURS_Y_OFFSET);
int end_x = len % max_x;
wmove(w, end_y, end_x);
}
/* gets base file name from path or original file name if no path is supplied */
void get_file_name(uint8_t *pathname, uint8_t *namebuf)
void get_file_name(char *namebuf, int bufsize, const char *pathname)
{
int idx = 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] == '/')
pathname[idx--] = '\0';
path[idx--] = '\0';
uint8_t *filename = strrchr(pathname, '/'); /* Try unix style paths */
if (filename != NULL) {
if (!strlen(++filename))
filename = pathname;
} else {
filename = strrchr(pathname, '\\'); /* Try windows style paths */
char *finalname = strdup(path);
if (filename == NULL)
filename = pathname;
if (finalname == NULL)
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
const char *basenm = strrchr(path, '/');
if (basenm != NULL) {
if (basenm[1])
strcpy(finalname, &basenm[1]);
}
snprintf(namebuf, MAX_STR_SIZE, "%s", filename);
snprintf(namebuf, bufsize, "%s", finalname);
free(finalname);
free(path);
}
/* converts str to all lowercase */
void str_to_lower(char *str)
{
int i;
for (i = 0; str[i]; ++i)
str[i] = tolower(str[i]);
}
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
Returns nick len on success, -1 on failure */
int get_nick_truncate(Tox *m, char *buf, int friendnum)
{
int len = tox_get_name(m, friendnum, (uint8_t *) buf);
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
return len;
}
/* returns index of the first instance of ch in s starting at idx.
returns length of s if char not found */
int char_find(int idx, const char *s, char ch)
{
int i = idx;
for (i = idx; s[i]; ++i) {
if (s[i] == ch)
break;
}
return i;
}
/* returns index of the last instance of ch in s starting at len
returns 0 if char not found (skips 0th index) */
int char_rfind(const char *s, char ch, int len)
{
int i = 0;
for (i = len; i > 0; --i) {
if (s[i] == ch)
break;
}
return i;
}
/* Converts bytes to appropriate unit and puts in buf as a string */
void bytes_convert_str(char *buf, int size, uint64_t bytes)
{
double conv = bytes;
const char *unit;
if (conv < KiB) {
unit = "Bytes";
} else if (conv < MiB) {
unit = "KiB";
conv /= (double) KiB;
} else if (conv < GiB) {
unit = "MiB";
conv /= (double) MiB;
} else {
unit = "GiB";
conv /= (double) GiB;
}
snprintf(buf, size, "%.1f %s", conv, unit);
}
/* checks if a file exists. Returns true or false */
bool file_exists(const char *fp)
{
struct stat s;
return stat(fp, &s) == 0;
}

View File

@ -19,34 +19,58 @@
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _misc_tools_h
#define _misc_tools_h
#include "windows.h"
#include "toxic.h"
#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
#ifndef net_to_host
#define net_to_host(x, y) hst_to_net(x, y)
#endif
void hst_to_net(uint8_t *num, uint16_t numbytes);
/* convert a hex string to binary */
unsigned char *hex_string_to_bin(char hex_string[]);
char *hex_string_to_bin(const char *hex_string);
/* convert a hex string to bytes. returns 0 on success, -1 on failure */
int hex_string_to_bytes(char *buf, int size, const char *keystr);
/* get the current unix time */
uint64_t get_unix_time(void);
/* Puts the current time in buf in the format of [HH:mm:ss] */
void get_time_str(char *buf, int bufsize);
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs);
/* get the current local time */
struct tm *get_time(void);
/* Prints the time to given window */
void print_time(WINDOW *window);
/* 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(char *string);
int string_is_empty(const char *string);
/* convert a multibyte string to a wide character string (must provide buffer) */
int char_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n);
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
/* converts wide character string into a multibyte string.
Same thing as wcs_to_mbs() but caller must provide its own buffer */
int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n);
/* converts wide character string into a multibyte string and puts in buf. */
int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
/* convert wide characters to multibyte string: string returned must be free'd */
uint8_t *wcs_to_mbs(wchar_t *string);
/* convert a wide char to multibyte char */
char *wc_to_char(wchar_t ch);
/* 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);
/* Returns 1 if connection has timed out, 0 otherwise */
int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
@ -55,16 +79,37 @@ int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
void alert_window(ToxWindow *self, int type, bool is_beep);
/* case-insensitive string compare function for use with qsort */
int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2);
int qsort_strcasecmp_hlpr(const void *str1, const void *str2);
/* Returns true if nick is valid. A valid toxic nick:
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
- cannot be empty
- cannot start with a space
- must not contain a forward slash (for logfile naming purposes)
- must not contain contiguous spaces */
bool valid_nick(uint8_t *nick);
/* Moves the cursor to the end of the line in given window */
void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x);
int valid_nick(char *nick);
/* gets base file name from path or original file name if no path is supplied */
void get_file_name(uint8_t *pathname, uint8_t *namebuf);
void get_file_name(char *namebuf, int bufsize, const char *pathname);
/* 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);
/* returns index of the first instance of ch in s starting at idx.
returns length of s if char not found */
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 */
int char_rfind(const char *s, char ch, int len);
/* Converts bytes to appropriate unit and puts in buf as a string */
void bytes_convert_str(char *buf, int size, uint64_t bytes);
/* checks if a file exists. Returns true or false */
bool file_exists(const char *fp);
#endif /* #define _misc_tools_h */

743
src/notify.c Normal file
View File

@ -0,0 +1,743 @@
/* notify.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 "notify.h"
#include "device.h"
#include "settings.h"
#include "line_info.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>
#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 */
#ifdef _X11
#include <X11/Xlib.h>
#endif /* _X11 */
#ifdef _BOX_NOTIFY
#include <libnotify/notify.h>
#endif
#define SOUNDS_SIZE 10
#define ACTIVE_NOTIFS_MAX 50
extern struct user_settings *user_settings_;
struct _Control {
time_t cooldown;
time_t notif_timeout;
unsigned long this_window;
#ifdef _X11
Display *display;
#endif /* _X11 */
#if defined(_SOUND_NOTIFY) || defined(_BOX_NOTIFY)
pthread_mutex_t poll_mutex[1];
_Bool poll_active;
#endif
#ifdef _SOUND_NOTIFY
uint32_t device_idx; /* index of output device */
char* sounds[SOUNDS_SIZE];
#endif /* _SOUND_NOTIFY */
} Control = {0};
struct _ActiveNotifications {
#ifdef _SOUND_NOTIFY
uint32_t source;
uint32_t buffer;
_Bool looping;
#endif
_Bool active;
int *id_indicator;
#ifdef _BOX_NOTIFY
NotifyNotification* box;
char messages[128][128];
char title[24];
size_t size;
time_t n_timeout;
#endif
} actives[ACTIVE_NOTIFS_MAX] = {{0}};
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
long unsigned int get_focused_window_id()
{
#ifdef _X11
if (!Control.display) return 0;
Window focus;
int revert;
XGetInputFocus(Control.display, &focus, &revert);
return focus;
#else
return 0;
#endif /* _X11 */
}
static void control_lock()
{
#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)
pthread_mutex_unlock(Control.poll_mutex);
#endif
}
#ifdef _SOUND_NOTIFY
_Bool is_playing(int source)
{
int ready;
alGetSourcei(source, AL_SOURCE_STATE, &ready);
return ready == AL_PLAYING;
}
/* Terminate all sounds but wait them to finish first */
void graceful_clear()
{
int i;
control_lock();
while (1) {
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].active) {
#ifdef _BOX_NOTIFY
if (actives[i].box) {
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
if ( actives[i].looping ) {
stop_sound(i);
} else {
if (!is_playing(actives[i].source))
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
else break;
}
}
}
if (i == ACTIVE_NOTIFS_MAX) {
control_unlock();
return;
}
usleep(1000);
}
}
void* do_playing(void* _p)
{
(void)_p;
int i;
while(Control.poll_active) {
control_lock();
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].active && !actives[i].looping
#ifdef _BOX_NOTIFY
&& !actives[i].box
#endif
) {
if (!is_playing(actives[i].source)) {
/* Close */
alSourceStop(actives[i].source);
alDeleteSources(1, &actives[i].source);
alDeleteBuffers(1,&actives[i].buffer);
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
#ifdef _BOX_NOTIFY
else if (actives[i].box && time(NULL) >= actives[i].n_timeout)
{
GError* ignore;
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].looping && !is_playing(actives[i].source)) {
/* stop source if not looping or playing, just terminate box */
alSourceStop(actives[i].source);
alDeleteSources(1, &actives[i].source);
alDeleteBuffers(1,&actives[i].buffer);
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
#endif
}
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)
{
(void)_p;
int i;
while(Control.poll_active) {
control_lock();
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
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
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
control_unlock();
usleep(10000);
}
pthread_exit(NULL);
}
void graceful_clear()
{
int i;
control_lock();
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].box) {
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
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
control_unlock();
}
#endif
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/* Opens primary device */
int init_notify(int login_cooldown, int notification_timeout)
{
#ifdef _SOUND_NOTIFY
alutInitWithoutContext(NULL, NULL);
if (open_primary_device(output, &Control.device_idx, 48000, 20, 1) != de_None)
return -1;
#endif /* _SOUND_NOTIFY */
#if defined(_SOUND_NOTIFY) || defined(_BOX_NOTIFY)
pthread_mutex_init(Control.poll_mutex, NULL);
pthread_t thread;
if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) {
pthread_mutex_destroy(Control.poll_mutex);
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();
#else
Control.this_window = 1;
#endif /* _X11 */
#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;
Control.poll_active = 0;
graceful_clear();
#endif
#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
notify_uninit();
#endif
}
#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;
Control.sounds[sound] = calloc(len, 1);
memcpy(Control.sounds[sound], value, len);
struct stat buf;
return stat(value, &buf) == 0;
}
int play_sound_internal(Notification what, _Bool loop)
{
uint32_t source;
uint32_t buffer;
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);
return -1;
}
return rc;
}
int play_notify_sound(Notification notif, uint64_t flags)
{
int rc = -1;
if (flags & NT_BEEP) beep();
else if (notif != silent) {
if ( !Control.poll_active || !Control.sounds[notif] )
return -1;
rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0);
}
return rc;
}
void stop_sound(int id)
{
if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) {
#ifdef _BOX_NOTIFY
if (actives[id].box) {
GError* ignore;
notify_notification_close(actives[id].box, &ignore);
}
#endif
*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);
memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
}
}
#endif
static int m_play_sound(Notification notif, uint64_t flags)
{
#ifdef _SOUND_NOTIFY
return play_notify_sound(notif, flags);
#else
if (notif != silent)
beep();
return -1;
#endif /* _SOUND_NOTIFY */
}
#ifdef _BOX_NOTIFY
void m_notify_action(NotifyNotification *box, char *action, void* data)
{
}
#endif
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator)
{
/* Consider colored notify as primary */
if (self) {
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) ) self->alert = WINDOW_ALERT_1;
else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) ) self->alert = WINDOW_ALERT_2;
}
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
return -1;
int id = -1;
control_lock();
if (self && (!self->stb || self->stb->status != TOX_USERSTATUS_BUSY) && user_settings_->alerts == ALERTS_ENABLED)
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 (id == -1) {
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 ) {
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)
{
/* Consider colored notify as primary */
if (self) {
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) ) self->alert = WINDOW_ALERT_1;
else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) ) self->alert = WINDOW_ALERT_2;
}
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
return -1;
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
#ifdef _SOUND_NOTIFY
control_lock();
if (!actives[id].active || !Control.sounds[notif]) {
control_unlock();
return -1;
}
alSourceStop(actives[id].source);
alDeleteSources(1, &actives[id].source);
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 */
}
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, char* title, const char* format, ...)
{
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
return -1;
#ifdef _BOX_NOTIFY
int id = sound_notify(self, notif, flags, id_indicator);
control_lock();
#ifdef _SOUND_NOTIFY
if (id == -1) { /* Could not play */
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
}
actives[id].active = 1;
actives[id].id_indicator = id_indicator;
if (id_indicator) *id_indicator = id;
}
#endif
strncpy(actives[id].title, title, 24);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], 127, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[0]) > 124)
strcpy(actives[id].messages[0] + 124, "...");
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
notify_notification_set_app_name(actives[id].box, "toxic");
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
return sound_notify(self, notif, flags, id_indicator);
#endif
}
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...)
{
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
return -1;
#ifdef _BOX_NOTIFY
if (sound_notify2(self, notif, flags, id) == -1)
return -1;
control_lock();
if (!actives[id].box || actives[id].size >= 128 ) {
control_unlock();
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], 127, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > 124)
strcpy(actives[id].messages[actives[id].size] + 124, "...");
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}
formated[strlen(formated) - 1] = '\0';
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
return sound_notify2(self, notif, flags, id);
#endif
}
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
{
/* Always do colored notify */
if (self) {
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) ) self->alert = WINDOW_ALERT_1;
else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) ) self->alert = WINDOW_ALERT_2;
}
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
return -1;
#ifdef _BOX_NOTIFY
control_lock();
int id;
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
}
if (id_indicator) {
actives[id].id_indicator = id_indicator;
*id_indicator = id;
}
strncpy(actives[id].title, title, 24);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], 127, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[0]) > 124)
strcpy(actives[id].messages[0] + 124, "...");
actives[id].active = 1;
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
notify_notification_set_app_name(actives[id].box, "toxic");
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
return -1;
#endif
}
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...)
{
/* Always do colored notify */
if (self) {
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) ) self->alert = WINDOW_ALERT_1;
else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) ) self->alert = WINDOW_ALERT_2;
}
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
return -1;
#ifdef _BOX_NOTIFY
control_lock();
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= 128 ) {
control_unlock();
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], 127, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > 124)
strcpy(actives[id].messages[actives[id].size] + 124, "...");
actives[id].size ++;
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}
formated[strlen(formated) - 1] = '\0';
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
return -1;
#endif
}

80
src/notify.h Normal file
View File

@ -0,0 +1,80 @@
/* notify.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 _notify_h
#define _notify_h
#include <inttypes.h>
#include "windows.h"
typedef enum _Notification
{
silent = -1,
error,
self_log_in,
self_log_out,
user_log_in,
user_log_out,
call_incoming,
call_outgoing,
generic_message,
transfer_pending,
transfer_completed,
} Notification;
typedef enum _Flags {
NT_NOFOCUS = 1 << 0, /* Notify when focus is not on this terminal. NOTE: only works with x11,
* if no x11 present this flag is ignored
*/
NT_BEEP = 1 << 1, /* Play native sound instead: \a */
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
* 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);
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, ...);
#ifdef _SOUND_NOTIFY
int set_sound(Notification sound, const char* value);
#endif /* _SOUND_NOTIFY */
#endif /* _notify_h */

View File

@ -20,86 +20,102 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* needed for wcswidth() */
#endif
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "toxic_windows.h"
#include "toxic.h"
#include "windows.h"
#include "prompt.h"
#include "execute.h"
#include "misc_tools.h"
#include "toxic_strings.h"
#include "log.h"
#include "line_info.h"
#include "settings.h"
#include "input.h"
#include "help.h"
#include "notify.h"
#include "autocomplete.h"
uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE] = {0};
uint8_t num_frnd_requests = 0;
extern ToxWindow *prompt;
extern struct user_settings *user_settings_;
extern struct _Winthread Winthread;
_FriendRequests FriendRequests;
/* Array of global command names used for tab completion. */
const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/accept" },
{ "/add" },
{ "/clear" },
{ "/close" }, /* rm /close when groupchats gets its own list */
{ "/connect" },
{ "/decline" },
{ "/exit" },
{ "/groupchat" },
{ "/help" },
{ "/join" },
{ "/log" },
{ "/myid" },
{ "/nick" },
{ "/note" },
{ "/quit" },
{ "/requests" },
{ "/status" },
#ifdef _SUPPORT_AUDIO
#ifdef _AUDIO
{ "/lsdev" },
{ "/sdev" },
#endif /* _SUPPORT_AUDIO */
#endif /* _AUDIO */
};
/* prevents input string from eating system messages: call this prior to printing a prompt message
TODO: This is only a partial fix */
void prep_prompt_win(void)
void kill_prompt_window(ToxWindow *self)
{
PromptBuf *prt = prompt->promptbuf;
ChatContext *ctx = self->chatwin;
StatusBar *statusbar = self->stb;
if (prt->len <= 0)
return;
log_disable(ctx->log);
line_info_cleanup(ctx->hst);
wprintw(prompt->window, "\n");
delwin(ctx->linewin);
delwin(ctx->history);
delwin(statusbar->topline);
if (!prt->at_bottom) {
wmove(prompt->window, prt->orig_y - 1, X_OFST);
++prt->orig_y;
} else {
wmove(prompt->window, prt->orig_y - 2, X_OFST);
}
free(ctx->log);
free(ctx->hst);
free(ctx);
free(self->help);
free(statusbar);
del_window(self);
}
/* Updates own nick in prompt statusbar */
void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len)
void prompt_update_nick(ToxWindow *prompt, const char *nick)
{
StatusBar *statusbar = prompt->stb;
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
statusbar->nick_len = len;
statusbar->nick_len = strlen(statusbar->nick);
}
/* Updates own statusmessage in prompt statusbar */
void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len)
/* Updates own statusmessage */
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg)
{
StatusBar *statusbar = prompt->stb;
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
int len = strlen(statusbar->statusmsg);
statusbar->statusmsg_len = len;
tox_set_status_message(m, (uint8_t *) statusmsg, (uint64_t) len);
}
/* Updates own status in prompt statusbar */
void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status)
void prompt_update_status(ToxWindow *prompt, uint8_t status)
{
StatusBar *statusbar = prompt->stb;
statusbar->status = status;
@ -112,21 +128,25 @@ void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
statusbar->is_online = is_connected;
}
/* Adds friend request to pending friend requests.
Returns request number on success, -1 if queue is full or other error. */
static int add_friend_request(uint8_t *public_key)
/* Adds friend request to pending friend requests.
Returns request number on success, -1 if queue is full. */
static int add_friend_request(const char *public_key, const char *data)
{
if (num_frnd_requests >= MAX_FRIENDS_NUM)
if (FriendRequests.max_idx >= MAX_FRIEND_REQUESTS)
return -1;
int i;
for (i = 0; i <= num_frnd_requests; ++i) {
if (!strlen(pending_frnd_requests[i])) {
memcpy(pending_frnd_requests[i], public_key, TOX_CLIENT_ID_SIZE);
for (i = 0; i <= FriendRequests.max_idx; ++i) {
if (!FriendRequests.request[i].active) {
FriendRequests.request[i].active = true;
memcpy(FriendRequests.request[i].key, public_key, TOX_CLIENT_ID_SIZE);
snprintf(FriendRequests.request[i].msg, sizeof(FriendRequests.request[i].msg), "%s", data);
if (i == num_frnd_requests)
++num_frnd_requests;
if (i == FriendRequests.max_idx)
++FriendRequests.max_idx;
++FriendRequests.num_requests;
return i;
}
@ -135,357 +155,317 @@ static int add_friend_request(uint8_t *public_key)
return -1;
}
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key)
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{
PromptBuf *prt = self->promptbuf;
ChatContext *ctx = self->chatwin;
int x, y, y2, x2;
getyx(self->window, y, x);
getmaxyx(self->window, y2, x2);
/* BACKSPACE key: Remove one character from line */
if (key == 0x107 || key == 0x8 || key == 0x7f) {
if (prt->pos > 0) {
del_char_buf_bck(prt->line, &prt->pos, &prt->len);
wmove(self->window, y, x-1); /* not necessary but fixes a display glitch */
prt->scroll = false;
} else {
beep();
}
if (x2 <= 0)
return;
/* ignore non-menu related input if active */
if (self->help->active) {
help_onKey(self, key);
return;
}
else if (key == KEY_DC) { /* DEL key: Remove character at pos */
if (prt->pos != prt->len) {
del_char_buf_frnt(prt->line, &prt->pos, &prt->len);
prt->scroll = false;
} else {
beep();
}
if (ltr) { /* char is printable */
input_new_char(self, key, x, y, x2, y2);
return;
}
else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */
if (prt->pos > 0) {
wmove(self->window, prt->orig_y, X_OFST);
wclrtobot(self->window);
discard_buf(prt->line, &prt->pos, &prt->len);
} else {
beep();
}
}
if (line_info_onKey(self, key))
return;
else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */
if (prt->len != prt->pos)
kill_buf(prt->line, &prt->pos, &prt->len);
else
beep();
}
input_handle(self, key, x, y, x2, y2);
else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (prt->pos != 0)
prt->pos = 0;
}
if (key == '\t') { /* TAB key: auto-completes command */
if (ctx->len > 1 && ctx->line[0] == '/') {
int diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
if (prt->pos != prt->len)
prt->pos = prt->len;
}
else if (key == KEY_LEFT) {
if (prt->pos > 0)
--prt->pos;
else
beep();
}
else if (key == KEY_RIGHT) {
if (prt->pos < prt->len)
++prt->pos;
else
beep();
}
else if (key == KEY_UP) { /* fetches previous item in history */
wmove(self->window, prt->orig_y, X_OFST);
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot,
&prt->hst_pos, LN_HIST_MV_UP);
/* adjust line y origin appropriately when window scrolls down */
if (prt->at_bottom && prt->len >= x2 - X_OFST) {
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST;
int p_ofst = px2 != x2 ? 0 : X_OFST;
if (px2 <= 0)
return;
int k = prt->orig_y + ((prt->len + p_ofst) / px2);
if (k >= y2) {
wprintw(self->window, "\n");
--prt->orig_y;
if (diff != -1) {
if (x + diff > x2 - 1) {
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
}
} else {
sound_notify(self, error, 0, NULL);
}
}
}
else if (key == KEY_DOWN) { /* fetches next item in history */
wmove(self->window, prt->orig_y, X_OFST);
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot,
&prt->hst_pos, LN_HIST_MV_DWN);
}
else if (key == '\t') { /* TAB key: completes command */
if (prt->len > 1 && prt->line[0] == '/') {
if (complete_line(prt->line, &prt->pos, &prt->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
MAX_CMDNAME_SIZE) == -1)
beep();
} else {
beep();
sound_notify(self, error, 0, NULL);
}
}
} else if (key == '\n') {
rm_trailing_spaces_buf(ctx);
else
#if HAVE_WIDECHAR
if (iswprint(key))
#else
if (isprint(key))
#endif
{
if (prt->len < (MAX_STR_SIZE-1)) {
add_char_to_buf(prt->line, &prt->pos, &prt->len, key);
prt->scroll = true;
}
}
/* RETURN key: execute command */
else if (key == '\n') {
wprintw(self->window, "\n");
uint8_t line[MAX_STR_SIZE];
char line[MAX_STR_SIZE] = {0};
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1)
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
if (!string_is_empty(line))
add_line_to_hist(prt->line, prt->len, prt->ln_history, &prt->hst_tot, &prt->hst_pos);
add_line_to_hist(ctx);
execute(self->window, self, m, line, GLOBAL_COMMAND_MODE);
reset_buf(prt->line, &prt->pos, &prt->len);
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
reset_buf(ctx);
}
}
static void prompt_onDraw(ToxWindow *self, Tox *m)
{
PromptBuf *prt = self->promptbuf;
int x2, y2;
getmaxyx(self->window, y2, x2);
ChatContext *ctx = self->chatwin;
line_info_print(self);
wclear(ctx->linewin);
curs_set(1);
int x, y, x2, y2;
getyx(self->window, y, x);
getmaxyx(self->window, y2, x2);
wclrtobot(self->window);
/* if len is >= screen width offset max x by X_OFST to account for prompt char */
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST;
if (px2 <= 0)
return;
/* len offset to account for prompt char (0 if len is < width of screen) */
int p_ofst = px2 != x2 ? 0 : X_OFST;
if (prt->len > 0) {
uint8_t line[MAX_STR_SIZE];
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1)
reset_buf(prt->line, &prt->pos, &prt->len);
else
mvwprintw(self->window, prt->orig_y, X_OFST, line);
int k = prt->orig_y + ((prt->len + p_ofst) / px2);
prt->at_bottom = k == y2 - 1;
bool botm = k == y2;
bool edge = (prt->len + p_ofst) % px2 == 0;
/* move point of line origin up when input scrolls screen down */
if (prt->scroll && edge && botm) {
--prt->orig_y;
prt->scroll = false;
}
} else { /* Mark point of origin for new line */
prt->orig_y = y;
}
wattron(self->window, COLOR_PAIR(GREEN));
mvwprintw(self->window, prt->orig_y, 0, "$ ");
wattroff(self->window, COLOR_PAIR(GREEN));
if (ctx->len > 0)
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
StatusBar *statusbar = self->stb;
werase(statusbar->topline);
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
wmove(statusbar->topline, 0, 0);
if (statusbar->is_online) {
int colour = WHITE;
char *status_text = "Unknown";
const char *status_text = "Unknown";
switch (statusbar->status) {
case TOX_USERSTATUS_NONE:
status_text = "Online";
colour = GREEN;
break;
case TOX_USERSTATUS_AWAY:
status_text = "Away";
colour = YELLOW;
break;
case TOX_USERSTATUS_BUSY:
status_text = "Busy";
colour = RED;
break;
case TOX_USERSTATUS_NONE:
status_text = "Online";
colour = GREEN;
break;
case TOX_USERSTATUS_AWAY:
status_text = "Away";
colour = YELLOW;
break;
case TOX_USERSTATUS_BUSY:
status_text = "Busy";
colour = RED;
break;
case TOX_USERSTATUS_INVALID:
status_text = "ERROR";
colour = MAGENTA;
break;
}
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s ", statusbar->nick);
wattron(statusbar->topline, A_BOLD);
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
wprintw(statusbar->topline, "[%s]", status_text);
wprintw(statusbar->topline, " [%s]", status_text);
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s", statusbar->nick);
wattroff(statusbar->topline, A_BOLD);
} else {
wprintw(statusbar->topline, " [Offline]");
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s ", statusbar->nick);
wattroff(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, "[Offline]");
}
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
wattroff(statusbar->topline, A_BOLD);
/* Reset statusbar->statusmsg on window resize */
if (x2 != self->x) {
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {0};
/* put cursor back in correct spot */
int y_m = prt->orig_y + ((prt->pos + p_ofst) / px2);
int x_m = (prt->pos + X_OFST) % x2;
wmove(self->window, y_m, x_m);
}
pthread_mutex_lock(&Winthread.lock);
tox_get_self_status_message(m, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
pthread_mutex_unlock(&Winthread.lock);
static void prompt_onInit(ToxWindow *self, Tox *m)
{
scrollok(self->window, true);
PromptBuf *prt = self->promptbuf;
prt->log = malloc(sizeof(struct chatlog));
if (prt->log == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
}
memset(prt->log, 0, sizeof(struct chatlog));
self->x = x2;
execute(self->window, self, m, "/help", GLOBAL_COMMAND_MODE);
wclrtoeol(self->window);
/* Truncate note if it doesn't fit in statusbar */
uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3;
if (statusbar->statusmsg_len > maxlen) {
statusbar->statusmsg[maxlen - 3] = '\0';
strcat(statusbar->statusmsg, "...");
statusbar->statusmsg_len = maxlen;
}
if (statusbar->statusmsg[0])
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
int y, x;
getyx(self->window, y, x);
(void) x;
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
wmove(self->window, y + 1, new_x);
wrefresh(self->window);
if (self->help->active)
help_onDraw(self);
}
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , uint8_t status)
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum , uint8_t status)
{
if (friendnum < 0)
return;
PromptBuf *prt = self->promptbuf;
prep_prompt_win();
ChatContext *ctx = self->chatwin;
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
if (tox_get_name(m, friendnum, nick) == -1)
return;
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
get_nick_truncate(m, nick, friendnum);
if (!nick[0])
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
wprintw(self->window, "\n");
print_time(self->window);
uint8_t *msg;
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
const char *msg;
if (status == 1) {
msg = "has come online";
wattron(self->window, COLOR_PAIR(GREEN));
wattron(self->window, A_BOLD);
wprintw(self->window, "* %s ", nick);
wattroff(self->window, A_BOLD);
wprintw(self->window, "%s\n", msg);
wattroff(self->window, COLOR_PAIR(GREEN));
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
write_to_log(msg, nick, ctx->log, true);
write_to_log(msg, nick, prt->log, true);
alert_window(self, WINDOW_ALERT_2, false);
if (self->active_box != -1)
box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
"%s has come online", nick );
else
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has come online", nick );
} else {
msg = "has gone offline";
wattron(self->window, COLOR_PAIR(RED));
wattron(self->window, A_BOLD);
wprintw(self->window, "* %s ", nick);
wattroff(self->window, A_BOLD);
wprintw(self->window, "%s\n", msg);
wattroff(self->window, COLOR_PAIR(RED));
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, RED, msg);
write_to_log(msg, nick, ctx->log, true);
write_to_log(msg, nick, prt->log, true);
if (self->active_box != -1)
box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
"%s has gone offline", nick );
else
box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has gone offline", nick );
}
}
static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data, uint16_t length)
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data,
uint16_t length)
{
/* make sure message data is null-terminated */
data[length - 1] = 0;
PromptBuf *prt = self->promptbuf;
prep_prompt_win();
ChatContext *ctx = self->chatwin;
wprintw(self->window, "\n");
print_time(self->window);
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Friend request with the message '%s'\n", data);
wprintw(self->window, "%s", msg);
write_to_log(msg, "", prt->log, true);
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data);
write_to_log("Friend request with the message '%s'", "", ctx->log, true);
int n = add_friend_request(key);
int n = add_friend_request(key, data);
if (n == -1) {
uint8_t *errmsg = "Friend request queue is full. Discarding request.\n";
wprintw(self->window, "%s", errmsg);
write_to_log(errmsg, "", prt->log, true);
const char *errmsg = "Friend request queue is full. Discarding request.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
return;
}
wprintw(self->window, "Type \"/accept %d\" to accept it.\n", n);
alert_window(self, WINDOW_ALERT_1, true);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n);
sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL);
}
void prompt_init_statusbar(ToxWindow *self, Tox *m)
{
int x, y;
getmaxyx(self->window, y, x);
int x2, y2;
getmaxyx(self->window, y2, x2);
(void) y2;
/* Init statusbar info */
StatusBar *statusbar = self->stb;
statusbar->status = TOX_USERSTATUS_NONE;
statusbar->is_online = false;
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_get_self_name(m, nick, TOX_MAX_NAME_LENGTH);
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
char nick[TOX_MAX_NAME_LENGTH];
char statusmsg[MAX_STR_SIZE];
/* temporary until statusmessage saving works */
uint8_t ver[strlen(TOXICVER) + 1];
uint8_t statusmsg[MAX_STR_SIZE];
uint16_t n_len = tox_get_self_name(m, (uint8_t *) nick);
uint16_t s_len = tox_get_self_status_message(m, (uint8_t *) statusmsg, MAX_STR_SIZE);
uint8_t status = tox_get_self_user_status(m);
nick[n_len] = '\0';
statusmsg[s_len] = '\0';
/* load prev status message or show toxic version if it has never been set */
char ver[strlen(TOXICVER) + 1];
strcpy(ver, TOXICVER);
uint8_t *toxic_ver = strtok(ver, "_");
const char *toxic_ver = strtok(ver, "_");
if (toxic_ver != NULL)
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver);
else
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic hacker edition");
if ( (s_len <= 0 || !strncmp("Toxing on Toxic", statusmsg, strlen("Toxing on Toxic"))) && toxic_ver != NULL) {
snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic v.%s", toxic_ver);
s_len = strlen(statusmsg);
statusmsg[s_len] = '\0';
}
m_set_statusmessage(m, statusmsg, strlen(statusmsg) + 1);
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
prompt_update_statusmessage(prompt, m, statusmsg);
prompt_update_status(prompt, status);
prompt_update_nick(prompt, nick);
/* Init statusbar subwindow */
statusbar->topline = subwin(self->window, 2, x, 0, 0);
statusbar->topline = subwin(self->window, 2, x2, 0, 0);
}
static void print_welcome_msg(ToxWindow *self)
{
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " _____ _____ _____ ____ ");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_ _/ _ \\ \\/ /_ _/ ___|");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " | || | | \\ / | | | ");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " | || |_| / \\ | | |___ ");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_| \\___/_/\\_\\___\\____|");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging client.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
msg = "Type \"/help\" for assistance. Further help may be found via the man page.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
}
static void prompt_onInit(ToxWindow *self, Tox *m)
{
curs_set(1);
int y2, x2;
getmaxyx(self->window, y2, x2);
ChatContext *ctx = self->chatwin;
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
ctx->log = calloc(1, sizeof(struct chatlog));
ctx->hst = calloc(1, sizeof(struct history));
if (ctx->log == NULL || ctx->hst == NULL)
exit_toxic_err("failed in prompt_onInit", FATALERR_MEMORY);
line_info_init(ctx->hst);
if (user_settings_->autolog == AUTOLOG_ON) {
char myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, (uint8_t *) myid);
log_enable(self->name, myid, ctx->log);
}
scrollok(ctx->history, 0);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
print_welcome_msg(self);
}
ToxWindow new_prompt(void)
@ -502,19 +482,20 @@ ToxWindow new_prompt(void)
ret.onConnectionChange = &prompt_onConnectionChange;
ret.onFriendRequest = &prompt_onFriendRequest;
strcpy(ret.name, "prompt");
strcpy(ret.name, "home");
PromptBuf *promptbuf = calloc(1, sizeof(PromptBuf));
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
StatusBar *stb = calloc(1, sizeof(StatusBar));
Help *help = calloc(1, sizeof(Help));
if (stb != NULL && promptbuf != NULL) {
ret.promptbuf = promptbuf;
ret.stb = stb;
} else {
endwin();
fprintf(stderr, "calloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
if (stb == NULL || chatwin == NULL || help == NULL)
exit_toxic_err("failed in new_prompt", FATALERR_MEMORY);
ret.chatwin = chatwin;
ret.stb = stb;
ret.help = help;
ret.active_box = -1;
return ret;
}

View File

@ -23,21 +23,36 @@
#ifndef PROMPT_H_UZYGWFFL
#define PROMPT_H_UZYGWFFL
#define X_OFST 2 /* offset to account for prompt char */
#include "toxic.h"
#include "windows.h"
#ifdef _AUDIO
#define AC_NUM_GLOB_COMMANDS 18
#else
#define AC_NUM_GLOB_COMMANDS 16
#endif /* _AUDIO */
#ifdef _SUPPORT_AUDIO
#define AC_NUM_GLOB_COMMANDS 17
#else
#define AC_NUM_GLOB_COMMANDS 15
#endif /* _SUPPORT_AUDIO */
#define MAX_FRIEND_REQUESTS 32
struct _friend_request {
bool active;
char msg[MAX_STR_SIZE];
uint8_t key[TOX_CLIENT_ID_SIZE];
};
typedef struct {
int max_idx;
int num_requests;
struct _friend_request request[MAX_FRIEND_REQUESTS];
} _FriendRequests;
ToxWindow new_prompt(void);
void prep_prompt_win(void);
void prompt_init_statusbar(ToxWindow *self, Tox *m);
void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len);
void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len);
void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status);
void prompt_update_nick(ToxWindow *prompt, const char *nick);
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg);
void prompt_update_status(ToxWindow *prompt, uint8_t status);
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
void kill_prompt_window(ToxWindow *self);
#endif /* end of include guard: PROMPT_H_UZYGWFFL */

385
src/settings.c Normal file
View File

@ -0,0 +1,385 @@
/* settings.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 <libconfig.h>
#include <ctype.h>
#include "toxic.h"
#include "windows.h"
#include "configdir.h"
#include "notify.h"
#include "misc_tools.h"
#ifdef _AUDIO
#include "device.h"
#endif /* _AUDIO */
#include "settings.h"
#include "line_info.h"
#ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "."
#endif
#define NO_SOUND "silent"
static struct _ui_strings {
const char* self;
const char* timestamps;
const char* alerts;
const char* native_colors;
const char* autolog;
const char* time_format;
const char* history_size;
const char* show_typing_self;
const char* show_typing_other;
} ui_strings = {
"ui",
"timestamps",
"alerts",
"native_colors",
"autolog",
"time_format",
"history_size",
"show_typing_self",
"show_typing_other",
};
static void ui_defaults(struct user_settings* settings)
{
settings->timestamps = TIMESTAMPS_ON;
settings->time = TIME_24;
settings->autolog = AUTOLOG_OFF;
settings->alerts = ALERTS_ENABLED;
settings->colour_theme = DFLT_COLS;
settings->history_size = 700;
settings->show_typing_self = SHOW_TYPING_ON;
settings->show_typing_other = SHOW_TYPING_ON;
}
static const struct _keys_strings {
const char* self;
const char* next_tab;
const char* prev_tab;
const char* scroll_line_up;
const char* scroll_line_down;
const char* half_page_up;
const char* half_page_down;
const char* page_bottom;
const char* peer_list_up;
const char* peer_list_down;
} key_strings = {
"keys",
"next_tab",
"prev_tab",
"scroll_line_up",
"scroll_line_down",
"half_page_up",
"half_page_down",
"page_bottom",
"peer_list_up",
"peer_list_down"
};
/* defines from toxic.h */
static void key_defaults(struct user_settings* settings)
{
settings->key_next_tab = T_KEY_NEXT;
settings->key_prev_tab = T_KEY_PREV;
settings->key_scroll_line_up = KEY_PPAGE;
settings->key_scroll_line_down = KEY_NPAGE;
settings->key_half_page_up = T_KEY_C_F;
settings->key_half_page_down = T_KEY_C_V;
settings->key_page_bottom = T_KEY_C_H;
settings->key_peer_list_up = T_KEY_C_LB;
settings->key_peer_list_down = T_KEY_C_RB;
}
static const struct _tox_strings {
const char* self;
const char* download_path;
const char* chatlogs_path;
} tox_strings = {
"tox",
"download_path",
"chatlogs_path",
};
static void tox_defaults(struct user_settings* settings)
{
strcpy(settings->download_path, "");
strcpy(settings->chatlogs_path, "");
}
#ifdef _AUDIO
static const struct _audio_strings {
const char* self;
const char* input_device;
const char* output_device;
const char* VAD_treshold;
} audio_strings = {
"audio",
"input_device",
"output_device",
"VAD_treshold",
};
static void audio_defaults(struct user_settings* settings)
{
settings->audio_in_dev = 0;
settings->audio_out_dev = 0;
settings->VAD_treshold = 40.0;
}
#endif
#ifdef _SOUND_NOTIFY
static const struct _sound_strings {
const char* self;
const char* error;
const char* self_log_in;
const char* self_log_out;
const char* user_log_in;
const char* user_log_out;
const char* call_incoming;
const char* call_outgoing;
const char* generic_message;
const char* transfer_pending;
const char* transfer_completed;
} sound_strings = {
"sounds",
"error",
"self_log_in",
"self_log_out",
"user_log_in",
"user_log_out",
"call_incoming",
"call_outgoing",
"generic_message",
"transfer_pending",
"transfer_completed",
};
#endif
static int key_parse(const char** bind){
int len = strlen(*bind);
if (len > 5) {
if(strncasecmp(*bind, "ctrl+", 5) == 0)
return toupper(bind[0][5]) - 'A' + 1;
}
if (strncasecmp(*bind, "tab", 3) == 0)
return T_KEY_TAB;
if (strncasecmp(*bind, "page", 4) == 0)
return len == 6 ? KEY_PPAGE : KEY_NPAGE;
return -1;
}
int settings_load(struct user_settings *s, const char *patharg)
{
config_t cfg[1];
config_setting_t *setting;
const char *str = NULL;
/* Load default settings */
ui_defaults(s);
tox_defaults(s);
key_defaults(s);
#ifdef _AUDIO
audio_defaults(s);
#endif
config_init(cfg);
char path[MAX_STR_SIZE];
/* use default config file path */
if (patharg == NULL) {
char *user_config_dir = get_user_config_dir();
snprintf(path, sizeof(path), "%s%stoxic.conf", user_config_dir, CONFIGDIR);
free(user_config_dir);
/* make sure path exists or is created on first time running */
if (!file_exists(path)) {
FILE *fp = fopen(path, "w");
if (fp == NULL)
return -1;
fclose(fp);
}
} else {
snprintf(path, sizeof(path), "%s", patharg);
}
if (!config_read_file(cfg, path)) {
config_destroy(cfg);
return -1;
}
/* ui */
if ((setting = config_lookup(cfg, ui_strings.self)) != NULL) {
config_setting_lookup_bool(setting, ui_strings.timestamps, &s->timestamps);
config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts);
config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog);
config_setting_lookup_bool(setting, ui_strings.native_colors, &s->colour_theme);
config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size);
config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self);
config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other);
config_setting_lookup_int(setting, ui_strings.time_format, &s->time);
s->time = s->time == TIME_24 || s->time == TIME_12 ? s->time : TIME_24; /* Check defaults */
}
/* paths */
if ((setting = config_lookup(cfg, tox_strings.self)) != NULL) {
if ( config_setting_lookup_string(setting, tox_strings.download_path, &str) ) {
snprintf(s->download_path, sizeof(s->download_path), "%s", str);
int len = strlen(s->download_path);
/* make sure path ends with a '/' */
if (len >= sizeof(s->download_path) - 2)
s->download_path[0] = '\0';
else if (s->download_path[len - 1] != '/')
strcat(&s->download_path[len - 1], "/");
}
if ( config_setting_lookup_string(setting, tox_strings.chatlogs_path, &str) ) {
snprintf(s->chatlogs_path, sizeof(s->chatlogs_path), "%s", str);
int len = strlen(s->chatlogs_path);
if (len >= sizeof(s->chatlogs_path) - 2)
s->chatlogs_path[0] = '\0';
else if (s->chatlogs_path[len - 1] != '/')
strcat(&s->chatlogs_path[len - 1], "/");
}
}
/* keys */
if ((setting = config_lookup(cfg, key_strings.self)) != NULL) {
const char* tmp = NULL;
if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) s->key_next_tab = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) s->key_prev_tab = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) s->key_scroll_line_up = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) s->key_scroll_line_down= key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) s->key_half_page_up = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) s->key_half_page_down = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) s->key_page_bottom = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) s->key_peer_list_up = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp)) s->key_peer_list_down = key_parse(&tmp);
}
#ifdef _AUDIO
if ((setting = config_lookup(cfg, audio_strings.self)) != NULL) {
config_setting_lookup_int(setting, audio_strings.input_device, &s->audio_in_dev);
s->audio_in_dev = s->audio_in_dev < 0 || s->audio_in_dev > MAX_DEVICES ? 0 : s->audio_in_dev;
config_setting_lookup_int(setting, audio_strings.output_device, &s->audio_out_dev);
s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev;
config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold);
}
#endif
#ifdef _SOUND_NOTIFY
if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) {
if ( (config_setting_lookup_string(setting, sound_strings.error, &str) != CONFIG_TRUE) ||
!set_sound(error, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(error, PACKAGE_DATADIR "/sounds/Error.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.user_log_in, &str) ||
!set_sound(user_log_in, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ContactLogsIn.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.self_log_in, &str) ||
!set_sound(self_log_in, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(self_log_in, PACKAGE_DATADIR "/sounds/LogIn.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.user_log_out, &str) ||
!set_sound(user_log_out, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ContactLogsOut.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.self_log_out, &str) ||
!set_sound(self_log_out, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(self_log_out, PACKAGE_DATADIR "/sounds/LogOut.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.call_incoming, &str) ||
!set_sound(call_incoming, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/IncomingCall.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) ||
!set_sound(call_outgoing, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/OutgoingCall.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.generic_message, &str) ||
!set_sound(generic_message, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(generic_message, PACKAGE_DATADIR "/sounds/NewMessage.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) ||
!set_sound(transfer_pending, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/TransferPending.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) ||
!set_sound(transfer_completed, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/TransferComplete.wav");
}
}
else {
set_sound(error, PACKAGE_DATADIR "/sounds/Error.wav");
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ContactLogsIn.wav");
set_sound(self_log_in, PACKAGE_DATADIR "/sounds/LogIn.wav");
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ContactLogsOut.wav");
set_sound(self_log_out, PACKAGE_DATADIR "/sounds/LogOut.wav");
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/IncomingCall.wav");
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/OutgoingCall.wav");
set_sound(generic_message, PACKAGE_DATADIR "/sounds/NewMessage.wav");
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/TransferPending.wav");
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/TransferComplete.wav");
}
#endif
config_destroy(cfg);
return 0;
}

81
src/settings.h Normal file
View File

@ -0,0 +1,81 @@
/* settings.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 _settings_h
#define _settings_h
#include <limits.h>
/* holds user setting values */
struct user_settings {
int autolog; /* boolean */
int alerts; /* boolean */
int time; /* 12 or 24 */
int timestamps; /* boolean */
int colour_theme; /* boolean (0 for default toxic colours) */
int history_size; /* int between MIN_HISTORY and MAX_HISTORY */
int show_typing_self; /* boolean */
int show_typing_other; /* boolean */
char download_path[PATH_MAX];
char chatlogs_path[PATH_MAX];
int key_next_tab; /* character code */
int key_prev_tab; /* character code */
int key_scroll_line_up;
int key_scroll_line_down;
int key_half_page_up;
int key_half_page_down;
int key_page_bottom;
int key_peer_list_up;
int key_peer_list_down;
#ifdef _AUDIO
int audio_in_dev;
int audio_out_dev;
double VAD_treshold;
#endif
};
enum {
AUTOLOG_OFF = 0,
AUTOLOG_ON = 1,
TIME_24 = 24,
TIME_12 = 12,
TIMESTAMPS_OFF = 0,
TIMESTAMPS_ON = 1,
ALERTS_DISABLED = 0,
ALERTS_ENABLED = 1,
DFLT_COLS = 0,
NATIVE_COLS = 1,
SHOW_TYPING_OFF = 0,
SHOW_TYPING_ON = 1,
DFLT_HST_SIZE = 700,
} settings_values;
int settings_load(struct user_settings *s, const char *patharg);
#endif /* #define _settings_h */

870
src/toxic.c Normal file
View File

@ -0,0 +1,870 @@
/* main.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 <curses.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <signal.h>
#include <locale.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <getopt.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <limits.h>
#include <tox/tox.h>
#include "configdir.h"
#include "toxic.h"
#include "windows.h"
#include "friendlist.h"
#include "prompt.h"
#include "misc_tools.h"
#include "file_senders.h"
#include "line_info.h"
#include "settings.h"
#include "log.h"
#include "notify.h"
#include "device.h"
#ifdef _AUDIO
#include "audio_call.h"
#endif /* _AUDIO */
#ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "."
#endif
#ifdef _AUDIO
ToxAv *av;
#endif /* _AUDIO */
/* Export for use in Callbacks */
char *DATA_FILE = NULL;
char *BLOCK_FILE = NULL;
ToxWindow *prompt = NULL;
#define AUTOSAVE_FREQ 60
struct _Winthread Winthread;
struct arg_opts arg_opts;
struct user_settings *user_settings_ = NULL;
static void catch_SIGINT(int sig)
{
Winthread.sig_exit_toxic = true;
}
static void catch_SIGSEGV(int sig)
{
freopen("/dev/tty", "w", stderr);
endwin();
fprintf(stderr, "Caught SIGSEGV: Aborting toxic session.\n");
exit(EXIT_FAILURE);
}
static void flag_window_resize(int sig)
{
Winthread.flag_resize = true;
}
static void init_signal_catchers(void)
{
signal(SIGWINCH, flag_window_resize);
signal(SIGINT, catch_SIGINT);
signal(SIGSEGV, catch_SIGSEGV);
}
void exit_toxic_success(Tox *m)
{
store_data(m, DATA_FILE);
close_all_file_senders(m);
kill_all_windows(m);
free(DATA_FILE);
free(BLOCK_FILE);
free(user_settings_);
#ifdef _SOUND_NOTIFY
// sound_notify(NULL, self_log_out, NT_ALWAYS, NULL);
#endif /* _SOUND_NOTIFY */
terminate_notify();
#ifdef _AUDIO
terminate_audio();
#endif /* _AUDIO */
tox_kill(m);
endwin();
exit(EXIT_SUCCESS);
}
void exit_toxic_err(const char *errmsg, int errcode)
{
if (errmsg == NULL)
errmsg = "No error message";
freopen("/dev/tty", "w", stderr);
endwin();
fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg);
exit(EXIT_FAILURE);
}
static void init_term(void)
{
#if HAVE_WIDECHAR
if (!arg_opts.default_locale) {
if (setlocale(LC_ALL, "") == NULL)
exit_toxic_err("Could not set your locale, please check your locale settings or "
"disable unicode support with the -d flag.", FATALERR_LOCALE_SET);
}
#endif
initscr();
cbreak();
keypad(stdscr, 1);
noecho();
timeout(100);
if (has_colors()) {
short bg_color = COLOR_BLACK;
start_color();
if (user_settings_->colour_theme == NATIVE_COLS) {
if (assume_default_colors(-1, -1) == OK)
bg_color = -1;
}
init_pair(0, COLOR_WHITE, COLOR_BLACK);
init_pair(1, COLOR_GREEN, bg_color);
init_pair(2, COLOR_CYAN, bg_color);
init_pair(3, COLOR_RED, bg_color);
init_pair(4, COLOR_BLUE, bg_color);
init_pair(5, COLOR_YELLOW, bg_color);
init_pair(6, COLOR_MAGENTA, bg_color);
init_pair(7, COLOR_BLACK, COLOR_BLACK);
init_pair(8, COLOR_BLACK, COLOR_WHITE);
}
refresh();
}
static struct _init_messages {
char **msgs;
int num;
} init_messages;
/* One-time queue for messages created during init. Do not use after program init. */
static void queue_init_message(const char *msg)
{
int i = init_messages.num;
++init_messages.num;
char **new_msgs = realloc(init_messages.msgs, sizeof(char *) * init_messages.num);
if (new_msgs == NULL)
exit_toxic_err("Failed in queue_init_message", FATALERR_MEMORY);
new_msgs[i] = malloc(MAX_STR_SIZE);
if (new_msgs[i] == NULL)
exit_toxic_err("Failed in queue_init_message", FATALERR_MEMORY);
snprintf(new_msgs[i], MAX_STR_SIZE, "%s", msg);
init_messages.msgs = new_msgs;
}
/* called after messages have been printed to console and are no longer needed */
static void cleanup_init_messages(void)
{
if (init_messages.num <= 0)
return;
int i;
for (i = 0; i < init_messages.num; ++i)
free(init_messages.msgs[i]);
free(init_messages.msgs);
}
static void print_init_messages(ToxWindow *toxwin)
{
int i;
for (i = 0; i < init_messages.num; ++i)
line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]);
}
static Tox *init_tox(void)
{
Tox_Options tox_opts;
tox_opts.ipv6enabled = !arg_opts.use_ipv4;
tox_opts.udp_disabled = arg_opts.force_tcp;
tox_opts.proxy_enabled = arg_opts.use_proxy;
if (tox_opts.proxy_enabled) {
tox_opts.proxy_port = arg_opts.proxy_port;
snprintf(tox_opts.proxy_address, sizeof(tox_opts.proxy_address), "%s", arg_opts.proxy_address);
char tmp[48];
snprintf(tmp, sizeof(tmp), "Using proxy %s : %d",
arg_opts.proxy_address, arg_opts.proxy_port);
queue_init_message(tmp);
}
if (tox_opts.udp_disabled) {
queue_init_message("UDP disabled");
} else if (tox_opts.proxy_enabled) {
const char *msg = "WARNING: Using a proxy without disabling UDP may leak your real IP address.";
queue_init_message(msg);
msg = "Use the -t option to disable UDP.";
queue_init_message(msg);
}
/* Init core */
Tox *m = tox_new(&tox_opts);
if (tox_opts.ipv6enabled && m == NULL) {
queue_init_message("IPv6 failed to initialize");
tox_opts.ipv6enabled = 0;
m = tox_new(&tox_opts);
}
if (!tox_opts.ipv6enabled)
queue_init_message("Forcing IPv4 connection");
if (tox_opts.proxy_enabled && m == NULL)
exit_toxic_err("Proxy error", FATALERR_PROXY);
if (m == NULL)
return NULL;
/* Callbacks */
tox_callback_connection_status(m, on_connectionchange, NULL);
tox_callback_typing_change(m, on_typing_change, NULL);
tox_callback_friend_request(m, on_request, NULL);
tox_callback_friend_message(m, on_message, NULL);
tox_callback_name_change(m, on_nickchange, NULL);
tox_callback_user_status(m, on_statuschange, NULL);
tox_callback_status_message(m, on_statusmessagechange, NULL);
tox_callback_friend_action(m, on_action, NULL);
tox_callback_group_invite(m, on_groupinvite, NULL);
tox_callback_group_message(m, on_groupmessage, NULL);
tox_callback_group_action(m, on_groupaction, NULL);
tox_callback_group_namelist_change(m, on_group_namelistchange, NULL);
tox_callback_file_send_request(m, on_file_sendrequest, NULL);
tox_callback_file_control(m, on_file_control, NULL);
tox_callback_file_data(m, on_file_data, NULL);
#ifdef __linux__
tox_set_name(m, (uint8_t *) "Cool dude", strlen("Cool dude"));
#elif defined(__FreeBSD__)
tox_set_name(m, (uint8_t *) "Nerd", strlen("Nerd"));
#elif defined(__APPLE__)
tox_set_name(m, (uint8_t *) "Hipster", strlen("Hipster")); /* This used to users of other Unixes are hipsters */
#else
tox_set_name(m, (uint8_t *) "Registered Minix user #4", strlen("Registered Minix user #4"));
#endif
return m;
}
#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */
#define MAXNODES 50
#define NODELEN (MAX_NODE_LINE - TOX_CLIENT_ID_SIZE - 7)
static struct _toxNodes {
int lines;
char nodes[MAXNODES][NODELEN];
uint16_t ports[MAXNODES];
char keys[MAXNODES][TOX_CLIENT_ID_SIZE];
} toxNodes;
static int load_nodelist(const char *filename)
{
if (!filename)
return 1;
FILE *fp = fopen(filename, "r");
if (fp == NULL)
return 1;
char line[MAX_NODE_LINE];
while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) {
if (strlen(line) > MIN_NODE_LINE) {
const char *name = strtok(line, " ");
const char *port = strtok(NULL, " ");
const char *key_ascii = strtok(NULL, " ");
/* invalid line */
if (name == NULL || port == NULL || key_ascii == NULL)
continue;
snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name);
toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0;
toxNodes.ports[toxNodes.lines] = atoi(port);
char *key_binary = hex_string_to_bin(key_ascii);
memcpy(toxNodes.keys[toxNodes.lines], key_binary, TOX_CLIENT_ID_SIZE);
free(key_binary);
toxNodes.lines++;
}
}
fclose(fp);
if (toxNodes.lines < 1)
return 1;
return 0;
}
int init_connection_helper(Tox *m, int line)
{
return tox_bootstrap_from_address(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line]);
}
/* Connects to a random DHT node listed in the DHTnodes file
*
* return codes:
* 1: failed to open node file
* 2: no line of sufficient length in node file
* 3: failed to resolve name to IP
* 4: nodelist file contains no acceptable line
*/
static bool srvlist_loaded = false;
#define NUM_INIT_NODES 5
int init_connection(Tox *m)
{
if (toxNodes.lines > 0) /* already loaded nodelist */
return init_connection_helper(m, rand() % toxNodes.lines) ? 0 : 3;
/* only once:
* - load the nodelist
* - connect to "everyone" inside
*/
if (!srvlist_loaded) {
srvlist_loaded = true;
int res;
if (!arg_opts.nodes_path[0])
res = load_nodelist(PACKAGE_DATADIR "/DHTnodes");
else
res = load_nodelist(arg_opts.nodes_path);
if (res != 0)
return res;
res = 3;
int i;
int n = MIN(NUM_INIT_NODES, toxNodes.lines);
for (i = 0; i < n; ++i) {
if (init_connection_helper(m, rand() % toxNodes.lines))
res = 0;
}
return res;
}
/* empty nodelist file */
return 4;
}
#define TRY_CONNECT 10 /* Seconds between connection attempts when DHT is not connected */
static void do_connection(Tox *m, ToxWindow *prompt)
{
if (arg_opts.no_connect == 1)
return;
char msg[MAX_STR_SIZE] = {0};
static int conn_err = 0;
static bool was_connected = false;
static uint64_t last_conn_try = 0;
uint64_t curtime = get_unix_time();
bool is_connected = tox_isconnected(m);
if (was_connected && is_connected)
return;
if (!was_connected && is_connected) {
was_connected = true;
prompt_update_connectionstatus(prompt, was_connected);
snprintf(msg, sizeof(msg), "DHT connected");
} else if (was_connected && !is_connected) {
was_connected = false;
prompt_update_connectionstatus(prompt, was_connected);
snprintf(msg, sizeof(msg), "DHT disconnected. Attempting to reconnect.");
} else if (!was_connected && !is_connected && timed_out(last_conn_try, curtime, TRY_CONNECT)) {
/* if autoconnect has already failed there's no point in trying again */
if (conn_err == 0) {
last_conn_try = curtime;
if ((conn_err = init_connection(m)) != 0)
snprintf(msg, sizeof(msg), "Auto-connect failed with error code %d", conn_err);
}
}
if (msg[0])
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
}
static void load_friendlist(Tox *m)
{
uint32_t i;
uint32_t numfriends = tox_count_friendlist(m);
for (i = 0; i < numfriends; ++i)
friendlist_onFriendAdded(NULL, m, i, false);
sort_friendlist_index();
}
/*
* Store Messenger to given location
* Return 0 stored successfully or ignoring data file
* Return -1 file path is NULL
* Return -2 malloc failed
* Return -3 opening path failed
* Return -4 fwrite failed
*/
int store_data(Tox *m, char *path)
{
if (arg_opts.ignore_data_file)
return 0;
if (path == NULL)
return -1;
int len = tox_size(m);
char *buf = malloc(len);
if (buf == NULL)
return -2;
tox_save(m, (uint8_t *) buf);
FILE *fd = fopen(path, "wb");
if (fd == NULL) {
free(buf);
return -3;
}
if (fwrite(buf, len, 1, fd) != 1) {
free(buf);
fclose(fd);
return -4;
}
free(buf);
fclose(fd);
return 0;
}
static void load_data(Tox *m, char *path)
{
if (arg_opts.ignore_data_file)
return;
FILE *fd;
if ((fd = fopen(path, "rb")) != NULL) {
fseek(fd, 0, SEEK_END);
int len = ftell(fd);
fseek(fd, 0, SEEK_SET);
char *buf = malloc(len);
if (buf == NULL) {
fclose(fd);
exit_toxic_err("failed in load_data", FATALERR_MEMORY);
}
if (fread(buf, len, 1, fd) != 1) {
free(buf);
fclose(fd);
exit_toxic_err("failed in load_data", FATALERR_FREAD);
}
tox_load(m, (uint8_t *) buf, len);
load_friendlist(m);
load_blocklist(BLOCK_FILE);
free(buf);
fclose(fd);
} else {
if (store_data(m, path) != 0)
exit_toxic_err("failed in load_data", FATALERR_STORE_DATA);
}
}
static void do_toxic(Tox *m, ToxWindow *prompt)
{
pthread_mutex_lock(&Winthread.lock);
do_connection(m, prompt);
do_file_senders(m);
if (arg_opts.no_connect == 0)
tox_do(m); /* main tox-core loop */
pthread_mutex_unlock(&Winthread.lock);
}
#define INACTIVE_WIN_REFRESH_RATE 10
void *thread_winref(void *data)
{
Tox *m = (Tox *) data;
uint8_t draw_count = 0;
while (true) {
draw_active_window(m);
draw_count++;
if (Winthread.flag_resize) {
on_window_resize();
Winthread.flag_resize = false;
} else if (draw_count >= INACTIVE_WIN_REFRESH_RATE) {
refresh_inactive_windows();
draw_count = 0;
}
if (Winthread.sig_exit_toxic) {
pthread_mutex_lock(&Winthread.lock);
exit_toxic_success(m);
}
}
}
static void print_usage(void)
{
fprintf(stderr, "usage: toxic [OPTION] [FILE ...]\n");
fprintf(stderr, " -4, --ipv4 Force IPv4 connection\n");
fprintf(stderr, " -b --debug Enable stderr for debugging\n");
fprintf(stderr, " -c, --config Use specified config file\n");
fprintf(stderr, " -d, --default-locale Use default POSIX locale\n");
fprintf(stderr, " -f, --file Use specified data file\n");
fprintf(stderr, " -h, --help Show this message and exit\n");
fprintf(stderr, " -n, --nodes Use specified DHTnodes file\n");
fprintf(stderr, " -o, --noconnect Do not connect to the DHT network\n");
fprintf(stderr, " -p, --proxy Use proxy: Requires [IP] [port]\n");
fprintf(stderr, " -r, --dnslist Use specified DNSservers file\n");
fprintf(stderr, " -t, --force-tcp Force TCP connection (use this with proxies)\n");
fprintf(stderr, " -x, --nodata Ignore data file\n");
}
static void set_default_opts(void)
{
arg_opts.use_ipv4 = 0;
arg_opts.ignore_data_file = 0;
arg_opts.debug = 0;
arg_opts.default_locale = 0;
arg_opts.use_custom_data = 0;
arg_opts.no_connect = 0;
arg_opts.force_tcp = 0;
arg_opts.use_proxy = 0;
}
static void parse_args(int argc, char *argv[])
{
set_default_opts();
static struct option long_opts[] = {
{"file", required_argument, 0, 'f'},
{"nodata", no_argument, 0, 'x'},
{"ipv4", no_argument, 0, '4'},
{"debug", no_argument, 0, 'b'},
{"default-locale", no_argument, 0, 'd'},
{"config", required_argument, 0, 'c'},
{"nodes", required_argument, 0, 'n'},
{"help", no_argument, 0, 'h'},
{"noconnect", no_argument, 0, 'o'},
{"dnslist", required_argument, 0, 'r'},
{"force-tcp", no_argument, 0, 't'},
{"proxy", required_argument, 0, 'p'},
{NULL, no_argument, NULL, 0},
};
const char *opts_str = "4bdhotxc:f:n:r:p:";
int opt, indexptr;
while ((opt = getopt_long(argc, argv, opts_str, long_opts, &indexptr)) != -1) {
switch (opt) {
case '4':
arg_opts.use_ipv4 = 1;
break;
case 'b':
arg_opts.debug = 1;
queue_init_message("stderr enabled");
break;
case 'c':
snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg);
if (!file_exists(arg_opts.config_path))
queue_init_message("Config file not found");
break;
case 'd':
arg_opts.default_locale = 1;
queue_init_message("Using default POSIX locale");
break;
case 'f':
arg_opts.use_custom_data = 1;
DATA_FILE = strdup(optarg);
BLOCK_FILE = malloc(strlen(optarg) + strlen("-blocklist") + 1);
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
exit_toxic_err("failed in parse_args", FATALERR_MEMORY);
strcpy(BLOCK_FILE, optarg);
strcat(BLOCK_FILE, "-blocklist");
char tmp[PATH_MAX];
snprintf(tmp, sizeof(tmp), "Using '%s' data file", DATA_FILE);
queue_init_message(tmp);
break;
case 'n':
snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg);
if (!file_exists(arg_opts.nodes_path))
queue_init_message("DHTnodes file not found");
break;
case 'o':
arg_opts.no_connect = 1;
queue_init_message("DHT disabled");
break;
case 'p':
arg_opts.use_proxy = 1;
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
if (++optind > argc || argv[optind-1][0] == '-')
exit_toxic_err("Proxy error", FATALERR_PROXY);
arg_opts.proxy_port = (uint16_t) atoi(argv[optind-1]);
break;
case 'r':
snprintf(arg_opts.dns_path, sizeof(arg_opts.dns_path), "%s", optarg);
if (!file_exists(arg_opts.dns_path))
queue_init_message("DNSservers file not found");
break;
case 't':
arg_opts.force_tcp = 1;
break;
case 'x':
arg_opts.ignore_data_file = 1;
queue_init_message("Ignoring data file");
break;
case 'h':
default:
print_usage();
exit(EXIT_SUCCESS);
}
}
}
#define DATANAME "data"
#define BLOCKNAME "data-blocklist"
static int init_data_files(void)
{
if (arg_opts.use_custom_data)
return 0;
char *user_config_dir = get_user_config_dir();
int config_err = create_user_config_dirs(user_config_dir);
if (DATA_FILE == NULL ) {
if (config_err) {
DATA_FILE = strdup(DATANAME);
BLOCK_FILE = strdup(BLOCKNAME);
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
} else {
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1);
BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1);
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
strcpy(DATA_FILE, user_config_dir);
strcat(DATA_FILE, CONFIGDIR);
strcat(DATA_FILE, DATANAME);
strcpy(BLOCK_FILE, user_config_dir);
strcat(BLOCK_FILE, CONFIGDIR);
strcat(BLOCK_FILE, BLOCKNAME);
}
}
free(user_config_dir);
return config_err;
}
#define REC_TOX_DO_LOOPS_PER_SEC 25
/* Adjusts usleep value so that tox_do runs close to the recommended number of times per second */
static useconds_t optimal_msleepval(uint64_t *looptimer, uint64_t *loopcount, uint64_t cur_time, useconds_t msleepval)
{
useconds_t new_sleep = msleepval;
++(*loopcount);
if (*looptimer == cur_time)
return new_sleep;
if (*loopcount != REC_TOX_DO_LOOPS_PER_SEC)
new_sleep *= (double) *loopcount / REC_TOX_DO_LOOPS_PER_SEC;
*looptimer = cur_time;
*loopcount = 0;
return new_sleep;
}
int main(int argc, char *argv[])
{
init_signal_catchers();
parse_args(argc, argv);
/* Make sure all written files are read/writeable only by the current user. */
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
int config_err = init_data_files();
/* init user_settings struct and load settings from conf file */
user_settings_ = calloc(1, sizeof(struct user_settings));
if (user_settings_ == NULL)
exit_toxic_err("failed in main", FATALERR_MEMORY);
char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
int settings_err = settings_load(user_settings_, p);
Tox *m = init_tox();
init_term();
/* enable stderr for debugging */
if (!arg_opts.debug)
freopen("/dev/null", "w", stderr);
if (m == NULL)
exit_toxic_err("failed in main", FATALERR_NETWORKINIT);
if (!arg_opts.ignore_data_file)
load_data(m, DATA_FILE);
prompt = init_windows(m);
prompt_init_statusbar(prompt, m);
/* thread for ncurses stuff */
if (pthread_mutex_init(&Winthread.lock, NULL) != 0)
exit_toxic_err("failed in main", FATALERR_MUTEX_INIT);
if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0)
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
#ifdef _AUDIO
av = init_audio(prompt, m);
set_primary_device(input, user_settings_->audio_in_dev);
set_primary_device(output, user_settings_->audio_out_dev);
#elif _SOUND_NOTIFY
if ( init_devices() == de_InternalError )
queue_init_message("Failed to init audio devices");
#endif /* _AUDIO */
init_notify(60, 3000);
#ifdef _SOUND_NOTIFY
// sound_notify(prompt, self_log_in, 0, NULL);
#endif /* _SOUND_NOTIFY */
const char *msg;
if (config_err) {
msg = "Unable to determine configuration directory. Defaulting to 'data' for data file...";
queue_init_message(msg);
}
if (settings_err == -1)
queue_init_message("Failed to load user settings");
print_init_messages(prompt);
cleanup_init_messages();
uint64_t last_save = (uint64_t) time(NULL);
uint64_t looptimer = last_save;
useconds_t msleepval = 40000;
uint64_t loopcount = 0;
while (true) {
update_unix_time();
do_toxic(m, prompt);
uint64_t cur_time = get_unix_time();
if (timed_out(last_save, cur_time, AUTOSAVE_FREQ)) {
pthread_mutex_lock(&Winthread.lock);
store_data(m, DATA_FILE);
pthread_mutex_unlock(&Winthread.lock);
last_save = cur_time;
}
msleepval = optimal_msleepval(&looptimer, &loopcount, cur_time, msleepval);
usleep(msleepval);
}
return 0;
}

112
src/toxic.h Normal file
View File

@ -0,0 +1,112 @@
/* toxic.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 _toxic_h
#define _toxic_h
#ifndef TOXICVER
#define TOXICVER "NOVER_" /* Use the -D flag to set this */
#endif
#ifndef SIGWINCH
#define SIGWINCH 28
#endif
#ifndef SIGINT
#define SIGINT 2
#endif
#include <stdbool.h>
#include <curses.h>
#include <tox/tox.h>
#define UNKNOWN_NAME "Anonymous"
#define MAX_STR_SIZE TOX_MAX_MESSAGE_LENGTH
#define MAX_CMDNAME_SIZE 64
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */
#define KEY_IDENT_DIGITS 2 /* number of hex digits to display for the pub-key based identifier */
#define TIME_STR_SIZE 16
/* ASCII key codes */
#define T_KEY_ESC 0x1B /* ESC key */
#define T_KEY_KILL 0x0B /* ctrl-k */
#define T_KEY_DISCARD 0x15 /* ctrl-u */
#define T_KEY_NEXT 0x10 /* ctrl-p */
#define T_KEY_PREV 0x0F /* ctrl-o */
#define T_KEY_C_E 0x05 /* ctrl-e */
#define T_KEY_C_A 0x01 /* ctrl-a */
#define T_KEY_C_RB 0x1D /* ctrl-] */
#define T_KEY_C_LB 0x1B /* ctrl-[ */
#define T_KEY_C_V 0x16 /* ctrl-v */
#define T_KEY_C_F 0x06 /* ctrl-f */
#define T_KEY_C_H 0x08 /* ctrl-h */
#define T_KEY_C_Y 0x19 /* ctrl-y */
#define T_KEY_TAB 0x09 /* TAB key */
#define ONLINE_CHAR "*"
#define OFFLINE_CHAR "o"
typedef enum _FATAL_ERRS {
FATALERR_MEMORY = -1, /* heap memory allocation failed */
FATALERR_FREAD = -2, /* fread() failed on critical read */
FATALERR_THREAD_CREATE = -3, /* thread creation failed */
FATALERR_MUTEX_INIT = -4, /* mutex init failed */
FATALERR_THREAD_ATTR = -5, /* thread attr object init failed */
FATALERR_LOCALE_SET = -6, /* system locale not set */
FATALERR_STORE_DATA = -7, /* store_data failed in critical section */
FATALERR_NETWORKINIT = -8, /* Tox network failed to init */
FATALERR_INFLOOP = -9, /* infinite loop detected */
FATALERR_WININIT = -10, /* window init failed */
FATALERR_PROXY = -11, /* Tox network failed to init using a proxy */
} FATAL_ERRS;
/* Fixes text color problem on some terminals.
Uncomment if necessary */
/* #define URXVT_FIX */
void exit_toxic_success(Tox *m);
void exit_toxic_err(const char *errmsg, int errcode);
int store_data(Tox *m, char *path);
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata);
void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata);
void on_message(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata);
void on_action(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata);
void on_nickchange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata);
void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata);
void on_statusmessagechange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata);
void on_friendadded(Tox *m, int32_t friendnumber, bool sort);
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length, void *userdata);
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, void *userdata);
void on_groupinvite(Tox *m, int32_t friendnumber, const uint8_t *group_pub_key, void *userdata);
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata);
void on_file_sendrequest(Tox *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, const uint8_t *pathname,
uint16_t pathname_length, void *userdata);
void on_file_control(Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type,
const uint8_t *data, uint16_t length, void *userdata);
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata);
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata);
#endif /* #define _toxic_h */

View File

@ -20,232 +20,192 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "toxic_windows.h"
#include "toxic.h"
#include "windows.h"
#include "misc_tools.h"
#include "toxic_strings.h"
#include "notify.h"
/* Adds char to buffer at pos */
void add_char_to_buf(wchar_t *buf, size_t *pos, size_t *len, wint_t ch)
/* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */
int add_char_to_buf(ChatContext *ctx, wint_t ch)
{
if (*pos < 0 || *len >= MAX_STR_SIZE)
return;
if (ctx->len >= MAX_STR_SIZE - 1)
return -1;
/* move all chars including null in front of pos one space forward and insert char in pos */
int i;
wmemmove(&ctx->line[ctx->pos + 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
ctx->line[ctx->pos++] = ch;
ctx->line[++ctx->len] = L'\0';
for (i = *len; i >= *pos && i >= 0; --i)
buf[i+1] = buf[i];
buf[(*pos)++] = ch;
++(*len);
return 0;
}
/* Deletes the character before pos */
void del_char_buf_bck(wchar_t *buf, size_t *pos, size_t *len)
/* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */
int del_char_buf_bck(ChatContext *ctx)
{
if (*pos <= 0)
if (ctx->pos <= 0)
return -1;
wmemmove(&ctx->line[ctx->pos - 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
--ctx->pos;
ctx->line[--ctx->len] = L'\0';
return 0;
}
/* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */
int del_char_buf_frnt(ChatContext *ctx)
{
if (ctx->pos >= ctx->len)
return -1;
wmemmove(&ctx->line[ctx->pos], &ctx->line[ctx->pos + 1], ctx->len - ctx->pos - 1);
ctx->line[--ctx->len] = L'\0';
return 0;
}
/* Deletes the line from beginning to pos and puts discarded portion in yank buffer.
Return 0 on success, -1 if noting to discard. */
int discard_buf(ChatContext *ctx)
{
if (ctx->pos <= 0)
return -1;
ctx->yank_len = ctx->pos;
wmemcpy(ctx->yank, ctx->line, ctx->yank_len);
ctx->yank[ctx->yank_len] = L'\0';
wmemmove(ctx->line, &ctx->line[ctx->pos], ctx->len - ctx->pos);
ctx->len -= ctx->pos;
ctx->pos = 0;
ctx->start = 0;
ctx->line[ctx->len] = L'\0';
return 0;
}
/* Deletes the line from pos to len and puts killed portion in yank buffer.
Return 0 on success, -1 if nothing to kill. */
int kill_buf(ChatContext *ctx)
{
if (ctx->len <= ctx->pos)
return -1;
ctx->yank_len = ctx->len - ctx->pos;
wmemcpy(ctx->yank, &ctx->line[ctx->pos], ctx->yank_len);
ctx->yank[ctx->yank_len] = L'\0';
ctx->line[ctx->pos] = L'\0';
ctx->len = ctx->pos;
return 0;
}
/* Inserts string in ctx->yank into line at pos.
Return 0 on success, -1 if yank buffer is empty or too long */
int yank_buf(ChatContext *ctx)
{
if (!ctx->yank[0])
return -1;
if (ctx->yank_len + ctx->len >= MAX_STR_SIZE)
return -1;
wmemmove(&ctx->line[ctx->pos + ctx->yank_len], &ctx->line[ctx->pos], ctx->len - ctx->pos);
wmemcpy(&ctx->line[ctx->pos], ctx->yank, ctx->yank_len);
ctx->pos += ctx->yank_len;
ctx->len += ctx->yank_len;
ctx->line[ctx->len] = L'\0';
return 0;
}
/* nulls line and sets pos, len and start to 0 */
void reset_buf(ChatContext *ctx)
{
ctx->line[0] = L'\0';
ctx->pos = 0;
ctx->len = 0;
ctx->start = 0;
}
/* Removes trailing spaces from line. */
void rm_trailing_spaces_buf(ChatContext *ctx)
{
if (ctx->len <= 0)
return;
if (ctx->line[ctx->len - 1] != ' ')
return;
int i;
/* similar to add_char_to_buf but deletes a char */
for (i = *pos-1; i <= *len; ++i)
buf[i] = buf[i+1];
for (i = ctx->len - 1; i >= 0; --i) {
if (ctx->line[i] != ' ')
break;
}
--(*pos);
--(*len);
}
/* Deletes the character at pos */
void del_char_buf_frnt(wchar_t *buf, size_t *pos, size_t *len)
{
if (*pos < 0 || *pos >= *len)
return;
int i;
for (i = *pos; i < *len; ++i)
buf[i] = buf[i+1];
--(*len);
}
/* Deletes the line from beginning to pos */
void discard_buf(wchar_t *buf, size_t *pos, size_t *len)
{
if (*pos <= 0)
return;
int i;
int c = 0;
for (i = *pos; i <= *len; ++i)
buf[c++] = buf[i];
*pos = 0;
*len = c - 1;
}
/* Deletes the line from pos to len */
void kill_buf(wchar_t *buf, size_t *pos, size_t *len)
{
if (*len == *pos)
return;
buf[*pos] = L'\0';
*len = *pos;
}
/* nulls buf and sets pos and len to 0 */
void reset_buf(wchar_t *buf, size_t *pos, size_t *len)
{
buf[0] = L'\0';
*pos = 0;
*len = 0;
ctx->len = i + 1;
ctx->pos = MIN(ctx->pos, ctx->len);
ctx->line[ctx->len] = L'\0';
}
#define HIST_PURGE MAX_LINE_HIST / 4
/* shifts hist items back and makes room for HIST_PURGE new entries */
static void shift_hist_back(wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot)
static void shift_hist_back(ChatContext *ctx)
{
int i;
int n = MAX_LINE_HIST - HIST_PURGE;
for (i = 0; i < n; ++i)
wmemcpy(hst[i], hst[i+HIST_PURGE], MAX_STR_SIZE);
wmemcpy(ctx->ln_history[i], ctx->ln_history[i + HIST_PURGE], MAX_STR_SIZE);
*hst_tot = n;
ctx->hst_tot = n;
}
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. */
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
int *hst_pos)
void add_line_to_hist(ChatContext *ctx)
{
if (len > MAX_STR_SIZE)
if (ctx->len >= MAX_STR_SIZE)
return;
if (*hst_tot >= MAX_LINE_HIST)
shift_hist_back(hst, hst_tot);
if (ctx->hst_tot >= MAX_LINE_HIST)
shift_hist_back(ctx);
++(*hst_tot);
*hst_pos = *hst_tot;
++ctx->hst_tot;
ctx->hst_pos = ctx->hst_tot;
wmemcpy(hst[*hst_tot-1], buf, len + 1);
wmemcpy(ctx->ln_history[ctx->hst_tot - 1], ctx->line, ctx->len + 1);
}
/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item.
/* copies history item at hst_pos to line. Sets pos and len to the len of the history item.
hst_pos is decremented or incremented depending on key_dir.
resets buffer if at end of history */
void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, wchar_t (*hst)[MAX_STR_SIZE],
int hst_tot, int *hst_pos, int key_dir)
resets line if at end of history */
void fetch_hist_item(ChatContext *ctx, int key_dir)
{
if (key_dir == LN_HIST_MV_UP) {
if (--(*hst_pos) < 0) {
*hst_pos = 0;
beep();
if (key_dir == KEY_UP) {
if (--ctx->hst_pos < 0) {
ctx->hst_pos = 0;
sound_notify(NULL, error, NT_ALWAYS, NULL);
}
} else {
if (++(*hst_pos) >= hst_tot) {
*hst_pos = hst_tot;
reset_buf(buf, pos, len);
if (++ctx->hst_pos >= ctx->hst_tot) {
ctx->hst_pos = ctx->hst_tot;
reset_buf(ctx);
return;
}
}
const wchar_t *hst_line = hst[*hst_pos];
const wchar_t *hst_line = ctx->ln_history[ctx->hst_pos];
size_t h_len = wcslen(hst_line);
wmemcpy(buf, hst_line, h_len + 1);
*pos = h_len;
*len = h_len;
}
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
with "Hello john".
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of buf on success, -1 if error */
int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int n_items, int size)
{
if (*pos <= 0 || *len <= 0 || *len >= MAX_STR_SIZE)
return -1;
const uint8_t *L = (uint8_t *) list;
uint8_t ubuf[MAX_STR_SIZE];
/* work with multibyte string copy of buf for simplicity */
if (wcs_to_mbs_buf(ubuf, buf, MAX_STR_SIZE) == -1)
return -1;
/* isolate substring from space behind pos to pos */
uint8_t tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", ubuf);
tmp[*pos] = '\0';
uint8_t *sub = strrchr(tmp, ' ');
int n_endchrs = 1; /* 1 = append space to end of match, 2 = append ": " */
if (!sub++) {
sub = tmp;
if (sub[0] != '/') /* make sure it's not a command */
n_endchrs = 2;
}
if (string_is_empty(sub))
return -1;
int s_len = strlen(sub);
const uint8_t *match;
bool is_match = false;
int i;
/* look for a match in list */
for (i = 0; i < n_items; ++i) {
match = &L[i*size];
if (is_match = strncasecmp(match, sub, s_len) == 0)
break;
}
if (!is_match)
return -1;
/* put match in correct spot in buf and append endchars (space or ": ") */
const uint8_t *endchrs = n_endchrs == 1 ? " " : ": ";
int m_len = strlen(match);
int strt = (int) *pos - s_len;
int diff = m_len - s_len + n_endchrs;
if (*len + diff > MAX_STR_SIZE)
return -1;
uint8_t tmpend[MAX_STR_SIZE];
strcpy(tmpend, &ubuf[*pos]);
strcpy(&ubuf[strt], match);
strcpy(&ubuf[strt+m_len], endchrs);
strcpy(&ubuf[strt+m_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)
return -1;
wcscpy(buf, newbuf);
*len += (size_t) diff;
*pos += (size_t) diff;
return diff;
wmemcpy(ctx->line, hst_line, h_len + 1);
ctx->pos = h_len;
ctx->len = h_len;
}

View File

@ -20,44 +20,45 @@
*
*/
/* Adds char to buffer at pos */
void add_char_to_buf(wchar_t *buf, size_t *pos, size_t *len, wint_t ch);
#ifndef _toxic_strings_h
#define _toxic_strings_h
/* Deletes the character before pos */
void del_char_buf_bck(wchar_t *buf, size_t *pos, size_t *len);
#include "windows.h"
/* Deletes the character at pos */
void del_char_buf_frnt(wchar_t *buf, size_t *pos, size_t *len);
/* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */
int add_char_to_buf(ChatContext *ctx, wint_t ch);
/* Deletes the line from beginning to pos */
void discard_buf(wchar_t *buf, size_t *pos, size_t *len);
/* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */
int del_char_buf_bck(ChatContext *ctx);
/* Deletes the line from pos to len */
void kill_buf(wchar_t *buf, size_t *pos, size_t *len);
/* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */
int del_char_buf_frnt(ChatContext *ctx);
/* nulls buf and sets pos and len to 0 */
void reset_buf(wchar_t *buf, size_t *pos, size_t *len);
/* Deletes the line from beginning to pos and puts discarded portion in yank buffer.
Return 0 on success, -1 if noting to discard */
int discard_buf(ChatContext *ctx);
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
with "Hello john".
/* Deletes the line from pos to len and puts killed portion in yank buffer.
Return 0 on success, -1 if nothing to kill. */
int kill_buf(ChatContext *ctx);
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
/* nulls line and sets pos, len and start to 0 */
void reset_buf(ChatContext *ctx);
Returns the difference between the old len and new len of buf on success, -1 if error */
int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int n_items, int size);
/* Inserts string in ctx->yank into line at pos.
Return 0 on success, -1 if yank buffer is empty or too long */
int yank_buf(ChatContext *ctx);
enum {
LN_HIST_MV_UP,
LN_HIST_MV_DWN,
};
/* Removes trailing spaces from line. */
void rm_trailing_spaces_buf(ChatContext *ctx);
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to last history item. */
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
int *hst_pos);
void add_line_to_hist(ChatContext *ctx);
/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item.
hst_pos is decremented or incremented depending on key_dir. */
void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, wchar_t (*hst)[MAX_STR_SIZE],
int hst_tot, int *hst_pos, int key_dir);
/* copies history item at hst_pos to line. Sets pos and len to the len of the history item.
hst_pos is decremented or incremented depending on key_dir.
resets line if at end of history */
void fetch_hist_item(ChatContext *ctx, int key_dir);
#endif /* #define _toxic_strings_h */

View File

@ -1,261 +0,0 @@
/* toxic_windows.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 _windows_h
#define _windows_h
#ifndef TOXICVER
#define TOXICVER "NOVER_" /* Use the -D flag to set this */
#endif
#include <curses.h>
#include <wctype.h>
#include <wchar.h>
#include <tox/tox.h>
#ifdef _SUPPORT_AUDIO
#include <tox/toxav.h>
#endif /* _SUPPORT_AUDIO */
#define UNKNOWN_NAME "Anonymous"
#define MAX_WINDOWS_NUM 32
#define MAX_FRIENDS_NUM 100
#define MAX_STR_SIZE 256
#define MAX_CMDNAME_SIZE 64
#define KEY_SIZE_BYTES 32
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */
#define N_DEFAULT_WINS 2 /* number of permanent default windows */
#define CURS_Y_OFFSET 3 /* y-axis cursor offset for chat contexts */
#define CHATBOX_HEIGHT 4
#define KEY_IDENT_DIGITS 2 /* number of hex digits to display for the pub-key based identifier */
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
/* ASCII key codes */
#define T_KEY_KILL 0xB /* ctrl-k */
#define T_KEY_DISCARD 0x15 /* ctrl-u */
#define T_KEY_NEXT 0x10 /* ctrl-p */
#define T_KEY_PREV 0x0F /* ctrl-o */
#define T_KEY_C_E 0x05 /* ctrl-e */
#define T_KEY_C_A 0x01 /* ctrl-a */
/* Curses foreground colours (background is black) */
enum {
WHITE,
GREEN,
CYAN,
RED,
BLUE,
YELLOW,
MAGENTA,
BLACK,
};
/* tab alert types: lower types take priority */
enum {
WINDOW_ALERT_0,
WINDOW_ALERT_1,
WINDOW_ALERT_2,
};
/* Fixes text color problem on some terminals.
Uncomment if necessary */
/* #define URXVT_FIX */
typedef struct ToxWindow ToxWindow;
typedef struct StatusBar StatusBar;
typedef struct PromptBuf PromptBuf;
typedef struct ChatContext ChatContext;
struct ToxWindow {
void(*onKey)(ToxWindow *, Tox *, wint_t);
void(*onDraw)(ToxWindow *, Tox *);
void(*onInit)(ToxWindow *, Tox *);
void(*onFriendRequest)(ToxWindow *, uint8_t *, uint8_t *, uint16_t);
void(*onFriendAdded)(ToxWindow *, Tox *, int, bool);
void(*onConnectionChange)(ToxWindow *, Tox *, int, uint8_t);
void(*onMessage)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
void(*onNickChange)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
void(*onStatusChange)(ToxWindow *, Tox *, int, TOX_USERSTATUS);
void(*onStatusMessageChange)(ToxWindow *, int, uint8_t *, uint16_t);
void(*onAction)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t);
void(*onGroupAction)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t);
void(*onGroupInvite)(ToxWindow *, Tox *, int, uint8_t *);
void(*onGroupNamelistChange)(ToxWindow *, Tox*, int, int, uint8_t);
void(*onFileSendRequest)(ToxWindow *, Tox *, int, uint8_t, uint64_t, uint8_t *, uint16_t);
void(*onFileControl)(ToxWindow *, Tox *, int, uint8_t, uint8_t, uint8_t, uint8_t *, uint16_t);
void(*onFileData)(ToxWindow *, Tox *, int, uint8_t, uint8_t *, uint16_t);
void(*onTypingChange)(ToxWindow *, Tox *, int, int);
#ifdef _SUPPORT_AUDIO
void(*onInvite)(ToxWindow *, ToxAv *);
void(*onRinging)(ToxWindow *, ToxAv *);
void(*onStarting)(ToxWindow *, ToxAv *);
void(*onEnding)(ToxWindow *, ToxAv *);
void(*onError)(ToxWindow *, ToxAv *);
void(*onStart)(ToxWindow *, ToxAv *);
void(*onCancel)(ToxWindow *, ToxAv *);
void(*onReject)(ToxWindow *, ToxAv *);
void(*onEnd)(ToxWindow *, ToxAv *);
void(*onTimeout)(ToxWindow *, ToxAv *);
#endif /* _SUPPORT_AUDIO */
char name[TOX_MAX_NAME_LENGTH];
int num;
bool active;
int x;
/* window type identifiers */
bool is_chat;
bool is_groupchat;
bool is_prompt;
bool alert0;
bool alert1;
bool alert2;
ChatContext *chatwin;
PromptBuf *promptbuf;
StatusBar *stb;
WINDOW *popup;
WINDOW *window;
};
/* statusbar info holder */
struct StatusBar {
WINDOW *topline;
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
uint16_t statusmsg_len;
uint8_t nick[TOX_MAX_NAME_LENGTH];
uint16_t nick_len;
TOX_USERSTATUS status;
bool is_online;
};
#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */
struct chatlog {
FILE *file;
uint64_t lastwrite;
int pos;
bool log_on; /* specific to current chat window */
};
#define MAX_LINE_HIST 128
/* chat and groupchat window/buffer holder */
struct ChatContext {
wchar_t line[MAX_STR_SIZE];
size_t pos;
size_t len;
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE];
int hst_pos;
int hst_tot;
bool self_is_typing;
struct chatlog *log;
WINDOW *history;
WINDOW *linewin;
WINDOW *sidebar;
};
/* prompt window/buffer holder */
struct PromptBuf {
wchar_t line[MAX_STR_SIZE];
size_t pos;
size_t len;
bool at_bottom; /* true if line end is at bottom of window */
int orig_y; /* y axis point of line origin */
bool scroll; /* used for prompt window hack to determine when to scroll down */
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE];
int hst_pos;
int hst_tot;
struct chatlog *log;
WINDOW *linewin;
};
/* Start file transfer code */
#define MAX_FILES 256
#define FILE_PIECE_SIZE 1024
#define TIMEOUT_FILESENDER 300
typedef struct {
FILE *file;
ToxWindow *toxwin;
int friendnum;
bool active;
uint8_t filenum;
uint8_t nextpiece[FILE_PIECE_SIZE];
uint16_t piecelen;
uint8_t pathname[MAX_STR_SIZE];
uint64_t timestamp;
} FileSender;
struct FileReceiver {
uint8_t filenames[MAX_FILES][MAX_STR_SIZE];
FILE *files[MAX_FILES];
bool pending[MAX_FILES];
};
/* End file transfer code */
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata);
void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata);
void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *userdata);
void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_friendadded(Tox *m, int friendnumber, bool sort);
void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message, uint16_t length, void *userdata);
void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, uint16_t length, void *userdata);
void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *userdata);
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata);
void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *pathname, uint16_t pathname_length, void *userdata);
void on_file_control(Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type, uint8_t *data, uint16_t length, void *userdata);
void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata);
void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata);
ToxWindow *init_windows(Tox *m);
void draw_active_window(Tox *m);
int add_window(Tox *m, ToxWindow w);
void del_window(ToxWindow *w);
void set_active_window(int ch);
int get_num_active_windows(void);
/* cleans up all chat and groupchat windows (should only be called on shutdown) */
void kill_all_windows(void);
#endif

View File

@ -20,156 +20,157 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <ctype.h>
#include "friendlist.h"
#include "prompt.h"
#include "toxic_windows.h"
#include "toxic.h"
#include "windows.h"
#include "groupchat.h"
#include "chat.h"
#include "line_info.h"
#include "settings.h"
extern char *DATA_FILE;
extern struct _Winthread Winthread;
static ToxWindow windows[MAX_WINDOWS_NUM];
static ToxWindow *active_window;
extern ToxWindow *prompt;
extern struct user_settings *user_settings_;
static int num_active_windows;
/* CALLBACKS START */
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFriendRequest != NULL)
windows[i].onFriendRequest(&windows[i], public_key, data, length);
windows[i].onFriendRequest(&windows[i], m, (const char *) public_key, (const char *) data, length);
}
}
void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata)
void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onConnectionChange != NULL)
windows[i].onConnectionChange(&windows[i], m, friendnumber, status);
}
}
void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata)
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata)
{
if (user_settings_->show_typing_other == SHOW_TYPING_OFF)
return;
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onTypingChange != NULL)
windows[i].onTypingChange(&windows[i], m, friendnumber, is_typing);
}
}
void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata)
void on_message(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onMessage != NULL)
windows[i].onMessage(&windows[i], m, friendnumber, string, length);
windows[i].onMessage(&windows[i], m, friendnumber, (const char *) string, length);
}
}
void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata)
void on_action(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onAction != NULL)
windows[i].onAction(&windows[i], m, friendnumber, string, length);
windows[i].onAction(&windows[i], m, friendnumber, (const char *) string, length);
}
}
void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata)
void on_nickchange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
{
if (friendnumber < 0 || friendnumber > MAX_FRIENDS_NUM)
return;
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onNickChange != NULL)
windows[i].onNickChange(&windows[i], m, friendnumber, string, length);
windows[i].onNickChange(&windows[i], m, friendnumber, (const char *) string, length);
}
if (store_data(m, DATA_FILE))
wprintw(prompt->window, "\nCould not store Tox data\n");
store_data(m, DATA_FILE);
}
void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata)
void on_statusmessagechange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onStatusMessageChange != NULL)
windows[i].onStatusMessageChange(&windows[i], friendnumber, string, length);
windows[i].onStatusMessageChange(&windows[i], friendnumber, (const char *) string, length);
}
}
void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *userdata)
void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onStatusChange != NULL)
windows[i].onStatusChange(&windows[i], m, friendnumber, status);
}
}
void on_friendadded(Tox *m, int friendnumber, bool sort)
void on_friendadded(Tox *m, int32_t friendnumber, bool sort)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFriendAdded != NULL)
windows[i].onFriendAdded(&windows[i], m, friendnumber, sort);
}
if (store_data(m, DATA_FILE))
wprintw(prompt->window, "\nCould not store Tox data\n");
store_data(m, DATA_FILE);
}
void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message, uint16_t length,
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length,
void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupMessage != NULL)
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, message, length);
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, (const char *) message, length);
}
}
void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, uint16_t length,
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length,
void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupAction != NULL)
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, action, length);
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, (const char *) action, length);
}
}
void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *userdata)
void on_groupinvite(Tox *m, int32_t friendnumber, const uint8_t *group_pub_key, void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupInvite != NULL)
windows[i].onGroupInvite(&windows[i], m, friendnumber, group_pub_key);
windows[i].onGroupInvite(&windows[i], m, friendnumber, (const char *) group_pub_key);
}
}
@ -177,44 +178,44 @@ void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t ch
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupNamelistChange != NULL)
windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change);
}
}
void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize,
uint8_t *filename, uint16_t filename_length, void *userdata)
void on_file_sendrequest(Tox *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
const uint8_t *filename, uint16_t filename_length, void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileSendRequest != NULL)
windows[i].onFileSendRequest(&windows[i], m, friendnumber, filenumber, filesize,
filename, filename_length);
(const char *) filename, filename_length);
}
}
void on_file_control (Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber,
uint8_t control_type, uint8_t *data, uint16_t length, void *userdata)
void on_file_control (Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
uint8_t control_type, const uint8_t *data, uint16_t length, void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileControl != NULL)
windows[i].onFileControl(&windows[i], m, friendnumber, receive_send, filenumber,
control_type, data, length);
control_type, (const char *) data, length);
}
}
void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length,
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length,
void *userdata)
{
int i;
for (i = 0; i < num_active_windows; ++i) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileData != NULL)
windows[i].onFileData(&windows[i], m, friendnumber, filenumber, data, length);
windows[i].onFileData(&windows[i], m, friendnumber, filenumber, (const char *) data, length);
}
}
@ -235,12 +236,15 @@ int add_window(Tox *m, ToxWindow w)
if (w.window == NULL)
return -1;
#ifdef URXVT_FIX
/* Fixes text color problem on some terminals. */
wbkgd(w.window, COLOR_PAIR(6));
#endif
windows[i] = w;
w.onInit(&w, m);
if (w.onInit)
w.onInit(&w, m);
++num_active_windows;
@ -250,43 +254,6 @@ int add_window(Tox *m, ToxWindow w)
return -1;
}
/* Deletes window w and cleans up */
void del_window(ToxWindow *w)
{
active_window = windows; /* Go to prompt screen */
delwin(w->window);
memset(w, 0, sizeof(ToxWindow));
clear();
refresh();
--num_active_windows;
}
/* Shows next window when tab or back-tab is pressed */
void set_next_window(int ch)
{
ToxWindow *end = windows + MAX_WINDOWS_NUM - 1;
ToxWindow *inf = active_window;
while (true) {
if (ch == T_KEY_NEXT) {
if (++active_window > end)
active_window = windows;
} else if (--active_window < windows)
active_window = end;
if (active_window->window)
return;
if (active_window == inf) { /* infinite loop check */
endwin();
fprintf(stderr, "set_next_window() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
}
}
void set_active_window(int index)
{
if (index < 0 || index >= MAX_WINDOWS_NUM)
@ -295,15 +262,46 @@ void set_active_window(int index)
active_window = windows + index;
}
/* Shows next window when tab or back-tab is pressed */
void set_next_window(int ch)
{
ToxWindow *end = windows + MAX_WINDOWS_NUM - 1;
ToxWindow *inf = active_window;
while (true) {
if (ch == user_settings_->key_next_tab) {
if (++active_window > end)
active_window = windows;
} else if (--active_window < windows)
active_window = end;
if (active_window->window)
return;
if (active_window == inf) /* infinite loop check */
exit_toxic_err("failed in set_next_window", FATALERR_INFLOOP);
}
}
/* Deletes window w and cleans up */
void del_window(ToxWindow *w)
{
set_active_window(0); /* Go to prompt screen */
delwin(w->window);
memset(w, 0, sizeof(ToxWindow));
clear();
refresh();
--num_active_windows;
}
ToxWindow *init_windows(Tox *m)
{
int n_prompt = add_window(m, new_prompt());
if (n_prompt == -1 || add_window(m, new_friendlist()) == -1) {
endwin();
fprintf(stderr, "add_window() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
if (n_prompt == -1 || add_window(m, new_friendlist()) == -1)
exit_toxic_err("failed in init_windows", FATALERR_WININIT);
prompt = &windows[n_prompt];
active_window = prompt;
@ -311,25 +309,71 @@ ToxWindow *init_windows(Tox *m)
return prompt;
}
static void draw_window_tab(ToxWindow toxwin)
void on_window_resize(void)
{
/* alert0 takes priority */
if (toxwin.alert0)
attron(COLOR_PAIR(GREEN));
else if (toxwin.alert1)
attron(COLOR_PAIR(RED));
else if (toxwin.alert2)
attron(COLOR_PAIR(MAGENTA));
endwin();
refresh();
clear();
/* equivalent to LINES and COLS */
int x2, y2;
getmaxyx(stdscr, y2, x2);
y2 -= 2;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (!windows[i].active)
continue;
ToxWindow *w = &windows[i];
if (windows[i].is_friendlist) {
delwin(w->window);
w->window = newwin(y2, x2, 0, 0);
continue;
}
if (w->help->active)
wclear(w->help->win);
if (w->is_groupchat)
delwin(w->chatwin->sidebar);
else
delwin(w->stb->topline);
delwin(w->chatwin->linewin);
delwin(w->chatwin->history);
delwin(w->window);
w->window = newwin(y2, x2, 0, 0);
w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
if (w->is_groupchat) {
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
} else {
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
w->stb->topline = subwin(w->window, 2, x2, 0, 0);
}
#ifdef _AUDIO
if (w->chatwin->infobox.active) {
delwin(w->chatwin->infobox.win);
w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH);
}
#endif /* _AUDIO */
scrollok(w->chatwin->history, 0);
}
}
static void draw_window_tab(ToxWindow *toxwin)
{
if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert));
clrtoeol();
printw(" [%s]", toxwin.name);
if (toxwin.alert0)
attroff(COLOR_PAIR(GREEN));
else if (toxwin.alert1)
attroff(COLOR_PAIR(RED));
else if (toxwin.alert2)
attroff(COLOR_PAIR(MAGENTA));
printw(" [%s]", toxwin->name);
if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert));
}
static void draw_bar(void)
@ -347,25 +391,28 @@ static void draw_bar(void)
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].active) {
if (windows + i == active_window) {
#ifdef URXVT_FIX
attron(A_BOLD | COLOR_PAIR(GREEN));
} else {
#endif
attron(A_BOLD);
}
if (!windows[i].active)
continue;
draw_window_tab(windows[i]);
if (windows + i == active_window)
if (windows + i == active_window) {
#ifdef URXVT_FIX
attroff(A_BOLD | COLOR_PAIR(GREEN));
} else {
attron(A_BOLD | COLOR_PAIR(GREEN));
else
#endif
attroff(A_BOLD);
}
}
attron(A_BOLD);
draw_window_tab(&windows[i]);
if (windows + i == active_window)
#ifdef URXVT_FIX
attroff(A_BOLD | COLOR_PAIR(GREEN));
else
#endif
attroff(A_BOLD);
}
refresh();
@ -374,33 +421,70 @@ static void draw_bar(void)
void draw_active_window(Tox *m)
{
ToxWindow *a = active_window;
a->alert0 = false;
a->alert1 = false;
a->alert2 = false;
a->alert = WINDOW_ALERT_NONE;
wint_t ch = 0;
draw_bar();
touchwin(a->window);
#ifndef WIN32
wresize(a->window, LINES - 2, COLS);
#endif
a->onDraw(a, m);
wrefresh(a->window);
/* Handle input */
bool ltr;
#ifdef HAVE_WIDECHAR
wget_wch(stdscr, &ch);
int status = wget_wch(stdscr, &ch);
if (status == ERR)
return;
if (status == OK)
ltr = iswprint(ch);
else /* if (status == KEY_CODE_YES) */
ltr = false;
#else
ch = getch();
#endif
if (ch == T_KEY_NEXT || ch == T_KEY_PREV)
if (ch == ERR)
return;
/* TODO verify if this works */
ltr = isprint(ch);
#endif /* HAVE_WIDECHAR */
if (!ltr && (ch == user_settings_->key_next_tab || ch == user_settings_->key_prev_tab)) {
set_next_window((int) ch);
else if (ch != ERR)
a->onKey(a, m, ch);
} else {
pthread_mutex_lock(&Winthread.lock);
a->onKey(a, m, ch, ltr);
pthread_mutex_unlock(&Winthread.lock);
}
}
/* refresh inactive windows to prevent scrolling bugs.
call at least once per second */
void refresh_inactive_windows(void)
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
ToxWindow *a = &windows[i];
if (a->active && a != active_window && !a->is_friendlist)
line_info_print(a);
}
}
/* returns a pointer to the ToxWindow in the ith index. Returns NULL if no ToxWindow exists */
ToxWindow *get_window_ptr(int i)
{
ToxWindow *toxwin = NULL;
if (i >= 0 && i <= MAX_WINDOWS_NUM && windows[i].active)
toxwin = &windows[i];
return toxwin;
}
int get_num_active_windows(void)
@ -409,14 +493,17 @@ int get_num_active_windows(void)
}
/* destroys all chat and groupchat windows (should only be called on shutdown) */
void kill_all_windows(void)
void kill_all_windows(Tox *m)
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].is_chat)
kill_chat_window(&windows[i]);
kill_chat_window(&windows[i], m);
else if (windows[i].is_groupchat)
kill_groupchat_window(&windows[i]);
}
kill_prompt_window(prompt);
kill_friendlist();
}

243
src/windows.h Normal file
View File

@ -0,0 +1,243 @@
/* windows.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 _windows_h
#define _windows_h
#include <pthread.h>
#include <wctype.h>
#include <wchar.h>
#include <tox/tox.h>
#ifdef _AUDIO
#include <tox/toxav.h>
#endif /* _AUDIO */
#include "toxic.h"
#define MAX_WINDOWS_NUM 32
#define MAX_WINDOW_NAME_LENGTH 16
#define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */
#define CHATBOX_HEIGHT 2
/* Curses foreground colours (background is black) */
enum {
WHITE,
GREEN,
CYAN,
RED,
BLUE,
YELLOW,
MAGENTA,
BLACK,
} C_COLOURS;
/* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */
typedef enum {
WINDOW_ALERT_NONE = 0,
WINDOW_ALERT_0 = GREEN,
WINDOW_ALERT_1 = RED,
WINDOW_ALERT_2 = MAGENTA,
} WINDOW_ALERTS;
/* Fixes text color problem on some terminals.
Uncomment if necessary */
/* #define URXVT_FIX */
struct _Winthread {
pthread_t tid;
pthread_mutex_t lock;
bool sig_exit_toxic;
bool flag_resize;
};
struct arg_opts {
int ignore_data_file;
int use_ipv4;
int force_tcp;
int debug;
int default_locale;
int use_custom_data;
int no_connect;
char dns_path[MAX_STR_SIZE];
char config_path[MAX_STR_SIZE];
char nodes_path[MAX_STR_SIZE];
int use_proxy;
char proxy_address[256];
uint16_t proxy_port;
};
typedef struct ToxWindow ToxWindow;
typedef struct StatusBar StatusBar;
typedef struct PromptBuf PromptBuf;
typedef struct ChatContext ChatContext;
typedef struct Help Help;
struct ToxWindow {
void(*onKey)(ToxWindow *, Tox *, wint_t, bool);
void(*onDraw)(ToxWindow *, Tox *);
void(*onInit)(ToxWindow *, Tox *);
void(*onFriendRequest)(ToxWindow *, Tox *, const char *, const char *, uint16_t);
void(*onFriendAdded)(ToxWindow *, Tox *, int32_t, bool);
void(*onConnectionChange)(ToxWindow *, Tox *, int32_t, uint8_t);
void(*onMessage)(ToxWindow *, Tox *, int32_t, const char *, uint16_t);
void(*onNickChange)(ToxWindow *, Tox *, int32_t, const char *, uint16_t);
void(*onStatusChange)(ToxWindow *, Tox *, int32_t, uint8_t);
void(*onStatusMessageChange)(ToxWindow *, int32_t, const char *, uint16_t);
void(*onAction)(ToxWindow *, Tox *, int32_t, const char *, uint16_t);
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
void(*onGroupAction)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, const char *);
void(*onGroupNamelistChange)(ToxWindow *, Tox *, int, int, uint8_t);
void(*onFileSendRequest)(ToxWindow *, Tox *, int32_t, uint8_t, uint64_t, const char *, uint16_t);
void(*onFileControl)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t, uint8_t, const char *, uint16_t);
void(*onFileData)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t);
#ifdef _AUDIO
void(*onInvite)(ToxWindow *, ToxAv *, int);
void(*onRinging)(ToxWindow *, ToxAv *, int);
void(*onStarting)(ToxWindow *, ToxAv *, int);
void(*onEnding)(ToxWindow *, ToxAv *, int);
void(*onError)(ToxWindow *, ToxAv *, int);
void(*onStart)(ToxWindow *, ToxAv *, int);
void(*onCancel)(ToxWindow *, ToxAv *, int);
void(*onReject)(ToxWindow *, ToxAv *, int);
void(*onEnd)(ToxWindow *, ToxAv *, int);
void(*onRequestTimeout)(ToxWindow *, ToxAv *, int);
void(*onPeerTimeout)(ToxWindow *, ToxAv *, int);
int call_idx; /* If in a call will have this index set, otherwise it's -1.
* Don't modify outside av callbacks. */
int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */
int ringing_sound;
#endif /* _AUDIO */
int active_box; /* For box notify */
char name[TOXIC_MAX_NAME_LENGTH];
int32_t num; /* corresponds to friendnumber in chat windows */
bool active;
int x;
bool is_chat;
bool is_groupchat;
bool is_prompt;
bool is_friendlist;
WINDOW_ALERTS alert;
ChatContext *chatwin;
StatusBar *stb;
Help *help;
WINDOW *window;
};
/* statusbar info holder */
struct StatusBar {
WINDOW *topline;
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
uint16_t statusmsg_len;
char nick[TOXIC_MAX_NAME_LENGTH];
int nick_len;
uint8_t status;
bool is_online;
};
#ifdef _AUDIO
#define INFOBOX_HEIGHT 7
#define INFOBOX_WIDTH 21
/* holds display info for audio calls */
struct infobox {
float vad_lvl;
bool in_is_muted;
bool out_is_muted;
bool hide;
bool active;
uint64_t lastupdate;
uint64_t starttime;
char timestr[TIME_STR_SIZE];
WINDOW *win;
};
#endif /* _AUDIO */
#define MAX_LINE_HIST 128
/* chat and groupchat window/buffer holder */
struct ChatContext {
wchar_t line[MAX_STR_SIZE];
int pos;
int len;
int start; /* the position to start printing line at */
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE]; /* history for input lines/commands */
int hst_pos;
int hst_tot;
wchar_t yank[MAX_STR_SIZE]; /* contains last killed/discarded line */
int yank_len;
struct history *hst;
struct chatlog *log;
#ifdef _AUDIO
struct infobox infobox;
#endif
uint8_t self_is_typing;
WINDOW *history;
WINDOW *linewin;
WINDOW *sidebar;
};
struct Help {
WINDOW *win;
int type;
bool active;
};
ToxWindow *init_windows(Tox *m);
void draw_active_window(Tox *m);
int add_window(Tox *m, ToxWindow w);
void del_window(ToxWindow *w);
void set_active_window(int ch);
int get_num_active_windows(void);
void kill_all_windows(Tox *m); /* should only be called on shutdown */
void on_window_resize(void);
ToxWindow *get_window_ptr(int i);
/* refresh inactive windows to prevent scrolling bugs.
call at least once per second */
void refresh_inactive_windows(void);
#endif /* #define _windows_h */