mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-27 21:06:46 +02:00
Compare commits
686 Commits
Author | SHA1 | Date | |
---|---|---|---|
6d3fbfee59 | |||
be5e7906da | |||
369f26932e | |||
22ea522baf | |||
4f60d546e6 | |||
76d1eafdc0 | |||
37912f2d88 | |||
09710327c5 | |||
acee4615f8 | |||
5ed26eda9b | |||
6d2b90ac9f | |||
02ea0fac44 | |||
7d3d129624 | |||
b3ed8bc35c | |||
90210daca7 | |||
0e13a1f1bc | |||
09e2690211 | |||
e65e3af274 | |||
c6c60d018e | |||
451d4ced80 | |||
7a7402ff86 | |||
600e013adc | |||
1d71e2eb18 | |||
f858714edd | |||
4df44a7274 | |||
a26ed9d28f | |||
2bd5083b8f | |||
8805f694b9 | |||
71040355fd | |||
6bc5d8c543 | |||
abb39ea6b5 | |||
15846d2b50 | |||
958df9f2e8 | |||
2fd43aebee | |||
34c29745cc | |||
da6fe41d75 | |||
e17fa89d8f | |||
f056f13329 | |||
3515623159 | |||
2194b9e259 | |||
c24e1bd2b8 | |||
38ec96e96a | |||
d2b572ede1 | |||
703d5419a3 | |||
221d761ff4 | |||
151f5f0c51 | |||
4f6c603543 | |||
a009f11c0c | |||
1f8c11a33a | |||
5e20e6b279 | |||
1f02bb2be5 | |||
98154b3cba | |||
379ad9e116 | |||
cb21672600 | |||
4019395f44 | |||
ee084c572c | |||
41a8401ac5 | |||
d8a3f7de4c | |||
c425aa2f27 | |||
94e026d114 | |||
f89638635a | |||
402b86687f | |||
5b1b420ac0 | |||
62ec514f17 | |||
f893dd755f | |||
9aedcf7753 | |||
d3effa26b5 | |||
2ec180789b | |||
9f74d3a3a8 | |||
9fcbc3bde0 | |||
cf16849b37 | |||
32442b6286 | |||
50f227418b | |||
fc06a625a6 | |||
70bd39eb74 | |||
4e0e322e32 | |||
e73ac9b6a4 | |||
bcda6e476e | |||
5b29ce7132 | |||
f43f644451 | |||
d6fdac9739 | |||
c6a2bb8a90 | |||
04576fea7e | |||
e6f839f9ac | |||
eb02424f8a | |||
2e609c46f6 | |||
a474e3bf39 | |||
93835f0455 | |||
ac6d8ff89c | |||
88e74224ed | |||
deccaec40e | |||
4419be36e8 | |||
b34b51e8c1 | |||
74416b4b58 | |||
675712cea0 | |||
36feebfe8d | |||
3fe9abd84d | |||
fd6432c727 | |||
1feffcc2f0 | |||
6bba3cb9e2 | |||
3cb6db3d60 | |||
77238eeadf | |||
88270827a9 | |||
aade65bfe1 | |||
b24c5d8cf8 | |||
9f0feb7223 | |||
74c1eef1d1 | |||
65c07a57db | |||
ab99c1ac73 | |||
05f5f16af3 | |||
d16be574f3 | |||
7e0b8b4870 | |||
39c4b7ecdd | |||
c5d9aca3e1 | |||
fa0e645a79 | |||
14a8bdb874 | |||
93a73cbef2 | |||
6aab9a79d8 | |||
dfff777283 | |||
a95fc7824c | |||
f707dce2da | |||
0d07d14b13 | |||
6cc1525daa | |||
49f5efaab0 | |||
a5e5e98afc | |||
4ab99c73a0 | |||
e02cf1bb7d | |||
9751cfc407 | |||
36963a5b38 | |||
7cf9c37aef | |||
2b4b8c0289 | |||
368a1465ec | |||
fea317ee24 | |||
8584feab80 | |||
f7e48d294e | |||
928f25bd89 | |||
941ac1d951 | |||
7af9327b37 | |||
6b97df2615 | |||
cea5f1fe04 | |||
abfdbfe468 | |||
462cfca175 | |||
db410cb01e | |||
a920f3edfe | |||
2c3921a9fb | |||
f295352495 | |||
ffcc804efe | |||
69be1bc398 | |||
b4464eda4d | |||
28dd43608d | |||
11701d22a1 | |||
4e2db756be | |||
19cfe3d393 | |||
c546df3917 | |||
ed0a4fb3b8 | |||
271ca08eb2 | |||
0e79b8a076 | |||
1606d01158 | |||
c8a9ac21f3 | |||
e91aaf6c73 | |||
619fdc1098 | |||
b7e613de32 | |||
929fca3de1 | |||
b67792f9f2 | |||
96162bf254 | |||
8a66c3fa4c | |||
2cdcbc07a7 | |||
6e0d19b01d | |||
ad04fa4dcd | |||
c2c612b85a | |||
d359ba6a54 | |||
54e2fe8d6f | |||
53353825e2 | |||
fcdc8e8b67 | |||
9b6efb65de | |||
c8ea02376e | |||
2369b5e9e2 | |||
8f28f1d748 | |||
a33e5f4bec | |||
e0a35a6569 | |||
9863dfc2ae | |||
c755247434 | |||
879b2b236e | |||
c6b9a288b6 | |||
e9e5b5af8d | |||
d175ff2480 | |||
750258adef | |||
60b4d62657 | |||
ea78dca756 | |||
3cb412632b | |||
8301ab1bc2 | |||
b6e90d2ebb | |||
06c268417f | |||
1458a6bbc5 | |||
737d29864b | |||
0a2ad23c15 | |||
a455c80a1f | |||
43bda5f7d9 | |||
f2121fae74 | |||
3241551cfb | |||
6e90072fb8 | |||
ca1fca5aa5 | |||
ef1068b6aa | |||
72982cee97 | |||
9a4eaa8693 | |||
64e7553fb0 | |||
dd8df1df76 | |||
c8d102b02d | |||
a3fa7fd524 | |||
b2ed8c0ead | |||
bbdf4c96b9 | |||
5496890b34 | |||
fd85d8f87b | |||
37e7b4c3d3 | |||
92d76c7f99 | |||
2a787c1097 | |||
327259c4c8 | |||
f173f4275e | |||
48eaf8a14f | |||
083611f18e | |||
48ffae68a9 | |||
c39f8909cd | |||
32e541bd1c | |||
f559bdabfe | |||
0047ba0e9f | |||
ecefc19b23 | |||
e83b397494 | |||
688ea927f8 | |||
904f58c0e8 | |||
035420e5c7 | |||
444d8e7a74 | |||
84a0276668 | |||
312d0c3f42 | |||
d8eca8393c | |||
374b78c763 | |||
409e4ddd96 | |||
1beb35025b | |||
51a1c660b4 | |||
85d3c18ba6 | |||
d0a7ca17d2 | |||
36640224af | |||
231078b6b9 | |||
414f58d896 | |||
4d73f8b241 | |||
82e76a3b5b | |||
0bc610e18d | |||
02e6d2db3c | |||
5a2c341259 | |||
1a7eaeddba | |||
f656d0a722 | |||
09c1ad4566 | |||
8b9e34db75 | |||
dd9186e834 | |||
5ff1517b28 | |||
bbb639c5aa | |||
860db2f612 | |||
523f205646 | |||
e998c8a866 | |||
eaea68c33e | |||
4780cfeafc | |||
bdb0951c84 | |||
e3130c92c0 | |||
12c880ab51 | |||
522aabd4e4 | |||
bf09b3b6c4 | |||
2d3c5c9450 | |||
ae87b2eb2d | |||
c34ecc8dfd | |||
2c2938c647 | |||
22dd883f28 | |||
dfbb1338a5 | |||
f8dc82516e | |||
59b16f7760 | |||
a11289de79 | |||
a64b8cae89 | |||
470eaeb97b | |||
6503349ed3 | |||
ec23cedc3a | |||
ce1ae1d7dc | |||
6669d5632f | |||
59e1114997 | |||
c20510e5aa | |||
2b2e746549 | |||
dc5a8d44e5 | |||
090d3339a0 | |||
442d9e22b4 | |||
0c39e7b158 | |||
3e3f2614b5 | |||
53e4b2c971 | |||
d6c57c7aa3 | |||
3efc984ae9 | |||
7618a0871a | |||
b87f73cc47 | |||
785d39d78f | |||
38a0f6fae4 | |||
710be51cad | |||
50438425e5 | |||
4b4bfe8876 | |||
67da4bdd5b | |||
e230badb47 | |||
d48d9ed4dc | |||
b9fe6bfa10 | |||
5cbbb62ce2 | |||
3369c9d808 | |||
dfc594f949 | |||
d714cda145 | |||
428e1e4100 | |||
2b19f56e63 | |||
4badc983ea | |||
c8a4f88ff3 | |||
eeeaf8d707 | |||
37b3b5a5ed | |||
a561ef8c49 | |||
89f200e870 | |||
2823115a6c | |||
5599d73760 | |||
bac66cb5ad | |||
61c50972e3 | |||
ff0fbbc379 | |||
4a124dea43 | |||
e722b665d1 | |||
4365b8d5ad | |||
e55e5f3f7e | |||
d1036c8538 | |||
9240f62829 | |||
1b89bc9051 | |||
7700ef83e0 | |||
9922b3a4f3 | |||
d561d2e1ac | |||
7865cea284 | |||
424189cad9 | |||
82aa64e8f7 | |||
a0418520a2 | |||
f451d961bc | |||
a4da9fd49e | |||
0a0891fa98 | |||
9464b369a4 | |||
47692edee4 | |||
73c94b25ba | |||
ab2ea5936d | |||
90985af007 | |||
5aad8764b1 | |||
5a175f374a | |||
4acfe84171 | |||
e995a1cb69 | |||
867f041c23 | |||
7e49ba92b2 | |||
2fa53c0531 | |||
673800c947 | |||
835b821b75 | |||
78587ad20b | |||
09bbec79cf | |||
751b5f9943 | |||
4a337ae3cb | |||
1baeb15073 | |||
141b36af04 | |||
4238c20e72 | |||
4e177d60b0 | |||
1bd880708b | |||
76c21c8b34 | |||
dd8c2caac7 | |||
7a7e8a7f8d | |||
43f45d67a4 | |||
adb36a39bb | |||
5a0d9f5e9f | |||
a4e8cb3971 | |||
e22b7a336c | |||
317fa42b20 | |||
884f8dda37 | |||
f5090532fb | |||
ae04dd8fa5 | |||
00e4075937 | |||
d65f3ea58d | |||
008163bf6c | |||
f09002a49a | |||
431290d47a | |||
b66874b7b3 | |||
1b9fd7f936 | |||
0a1457acde | |||
d2008aa92f | |||
e83356faef | |||
fd4161832b | |||
0a132b0eb5 | |||
9c7ac9043a | |||
b86c0d357f | |||
75dd26518f | |||
2cc261c619 | |||
f7d46f1100 | |||
be264528d2 | |||
c825adc1e3 | |||
4e9f125e95 | |||
d26c8fe447 | |||
e42d635195 | |||
a32cd1c19b | |||
14dc02ac83 | |||
e4a28d1839 | |||
0908920b51 | |||
8a882916fb | |||
55ada8ad2f | |||
2094186c0f | |||
d3a489b756 | |||
e6b18231c0 | |||
cc0145d561 | |||
9f0daca5eb | |||
1f2bd44dce | |||
6492bd12f9 | |||
43552161f9 | |||
e5d45fdf1d | |||
bd69c8da18 | |||
8c3e3ebe24 | |||
7eebbd982d | |||
5c66f5c161 | |||
40f70fc1e3 | |||
9cf4427375 | |||
23cf9686cb | |||
70add920fe | |||
c56c6cc219 | |||
e9a0296851 | |||
de30433e26 | |||
fa64fa8b93 | |||
50c7942cb5 | |||
e7697b8fba | |||
b46a77f977 | |||
38909afa89 | |||
5af561c811 | |||
ae984d4f04 | |||
97a8ecd115 | |||
64d782569a | |||
6248baf98b | |||
33cfca7ecc | |||
a01cc35368 | |||
b10eebd77e | |||
05661ca9b6 | |||
784883f773 | |||
a9e5723ca4 | |||
95dfea8d29 | |||
e574af7d68 | |||
2918ca45a2 | |||
6ad744f770 | |||
5b394e6f35 | |||
940af2c711 | |||
544c402f78 | |||
cee9e624b8 | |||
2e65ee3609 | |||
3a176e1cab | |||
00cccad22c | |||
a432d733d7 | |||
893e88294b | |||
b071a9e992 | |||
48cf4ebf02 | |||
773a75b948 | |||
133c0e8d63 | |||
bf54cb36ef | |||
032853b5c9 | |||
650c13ca7a | |||
9d5fe03285 | |||
c3f68b60d3 | |||
1c16467eb9 | |||
3c74385f5c | |||
c07c0028bb | |||
dc3b2e04ab | |||
7fd3aa9164 | |||
4c60312e2d | |||
16e29aa4e0 | |||
bd7b073155 | |||
f25cf870e6 | |||
13291d0365 | |||
9ee7a48910 | |||
daf4614ba6 | |||
919d36369c | |||
618704df76 | |||
9a70dd9651 | |||
e2d310b10f | |||
a1015a366a | |||
913ec7b3fe | |||
4a52b06954 | |||
89f9c07b9e | |||
388d78d11e | |||
a2a23b3932 | |||
f405ae8b42 | |||
cf3f6750eb | |||
4de22d067a | |||
51e274ea38 | |||
0a6ce62363 | |||
13c5de5531 | |||
21f8e7f398 | |||
bcf4a5af90 | |||
09f90d095b | |||
416ebc9ab8 | |||
3ca22aa714 | |||
8dd25e1f0b | |||
5b9bd603ea | |||
3c2c1f15ce | |||
6876df4a45 | |||
1ff97161fb | |||
667410e879 | |||
a862874740 | |||
79bde4e5bf | |||
833b724e9f | |||
96b68058bb | |||
e823233149 | |||
3ac22fafe4 | |||
71f2ac170c | |||
0ef888eea3 | |||
a9b0028a15 | |||
b18e6cff5a | |||
424a1c94d9 | |||
009095af24 | |||
2ce42ab057 | |||
934459dea8 | |||
52bc874675 | |||
511907fbc5 | |||
155e194174 | |||
b1c7e21ca9 | |||
7edcf6cb45 | |||
9581940cfa | |||
f2aa57c4fa | |||
8bf4405fd0 | |||
21ef1788ca | |||
68f1dffba7 | |||
092df2c0e4 | |||
691f94c75c | |||
d6d4476e85 | |||
924e8e0860 | |||
53193e933f | |||
328587ad9c | |||
1a8fdb1b99 | |||
690f0221b5 | |||
e117bd3985 | |||
15cc87bffd | |||
97d4c97c52 | |||
c8b22d7e8a | |||
f48ec4f49b | |||
d4ce697bd9 | |||
bd20513493 | |||
fd3f4eb724 | |||
75e8486061 | |||
bd216709fc | |||
a3a8f7608a | |||
affc88d0a8 | |||
eca4882ce2 | |||
58b0a04019 | |||
b870679f2c | |||
ae83725cb6 | |||
595e42b587 | |||
f5401df2c7 | |||
3e79a5ca8b | |||
327081945e | |||
68ec484a58 | |||
1d6ccf56a8 | |||
67f637a1e1 | |||
5e175d5319 | |||
778db0fece | |||
72010dd2e1 | |||
39556b36f3 | |||
dc9ffa6e56 | |||
1e92bb3c2b | |||
edbdf2966a | |||
3f6fd734d3 | |||
919a06d282 | |||
35cc815cdb | |||
a318bdb034 | |||
d6aaa95b25 | |||
5718ad52db | |||
0f4cffbacc | |||
d9a861331f | |||
2f12a8d429 | |||
e75cf4f3ad | |||
bb85f31bb2 | |||
396d08f0d2 | |||
6ab184e7ce | |||
fd65fbfd0c | |||
637ea0ed55 | |||
dc2d20f4c4 | |||
d712ccc17e | |||
147030e06f | |||
09fd5cb69f | |||
64db9f73a2 | |||
28633be2dd | |||
6fdafceda8 | |||
38ed0c86ad | |||
ad23816096 | |||
bc4a730e76 | |||
79372cc80d | |||
c9e4246ac5 | |||
dcd6a238b6 | |||
c49de7733c | |||
3fc7c90630 | |||
53663a7832 | |||
c14f2a3fcd | |||
3cbe61e111 | |||
fa023c6a99 | |||
f98e6bdcb4 | |||
0884954c84 | |||
43727c6730 | |||
618b731d5a | |||
46975bf38b | |||
8f3989000d | |||
9fe75fbc47 | |||
c455e79604 | |||
a223545853 | |||
b243f7aa62 | |||
899452d7cd | |||
af68fa7ee0 | |||
5da789cc37 | |||
7e5b41c8e0 | |||
0254596c73 | |||
67c02404b7 | |||
0b5ee7e2c7 | |||
fba0732faa | |||
d06086a656 | |||
e74b678739 | |||
b62787ce47 | |||
75708f7600 | |||
476dec46b6 | |||
973f6206ee | |||
cbe47b3660 | |||
1c58c339bb | |||
15e91cfa99 | |||
f4fb6ea4fc | |||
0d2ff2c0a8 | |||
5275da5a6b | |||
e891b1281b | |||
ca7110b37c | |||
8960eb98f4 | |||
18610668b8 | |||
efe61e32e2 | |||
7a7e4f573a | |||
a7e6ab7758 | |||
a0cde4ae8c | |||
7566aa9d26 | |||
d2332a5b77 | |||
94a8ce5aa8 | |||
b18a67d656 | |||
02708534c0 | |||
d5710d80e0 | |||
8dcba3219d | |||
9f01a45b1f | |||
dd2cb93ecc | |||
49538a986c | |||
26c2331d0f | |||
a0758643c2 | |||
77e152ad36 | |||
4834642b80 | |||
18a6f621f0 | |||
3cae1d92cd | |||
02b192d6ee | |||
f630a3e604 | |||
fb5a9bc043 | |||
26ad5a00a3 | |||
18e1f08e31 | |||
b68deef6db | |||
30ec7debba | |||
3a1e23a3ff | |||
0887bb7662 | |||
5a55f738a9 | |||
8d8df585ad | |||
8660047ec1 | |||
9476db02a9 | |||
04fbf5f724 | |||
3cc629cbc1 | |||
50fca4cddf | |||
a047cead05 | |||
1131b73299 | |||
196af10d01 | |||
1e0e93e5c6 | |||
ac01d6d316 | |||
654e404e0e | |||
f0f1138c54 | |||
bc94e08970 | |||
42c3ede963 | |||
174568d769 | |||
4587d8ebbd | |||
98aae242fb | |||
7abd8d5ee5 | |||
98ac4d7983 | |||
933d46553f | |||
7e667a8028 | |||
d4e41d6053 | |||
f30dccf726 | |||
ea3fcd5b79 | |||
e61d070def | |||
b5f34f42a8 | |||
4426eaddd9 | |||
83f0720a39 | |||
ce4f293574 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,3 +16,4 @@ stamp-h1
|
|||||||
build/toxic
|
build/toxic
|
||||||
build/*.o
|
build/*.o
|
||||||
build/*.d
|
build/*.d
|
||||||
|
apidoc/python/build
|
||||||
|
53
.travis.yml
53
.travis.yml
@ -1,53 +0,0 @@
|
|||||||
language: c
|
|
||||||
compiler:
|
|
||||||
- gcc
|
|
||||||
- clang
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
# Installing yasm (needed for compiling vpx) and openal
|
|
||||||
- sudo apt-get -yq install yasm libopenal-dev
|
|
||||||
# Installing libsodium, needed for toxcore
|
|
||||||
- git clone https://github.com/jedisct1/libsodium.git libsodium
|
|
||||||
- cd libsodium
|
|
||||||
- git checkout tags/0.4.2 > /dev/null
|
|
||||||
- ./autogen.sh > /dev/null
|
|
||||||
- ./configure > /dev/null
|
|
||||||
- make check -j2 || make check || exit 1 > /dev/null
|
|
||||||
- sudo make install > /dev/null
|
|
||||||
- cd ..
|
|
||||||
# Installing libopus, needed for audio encoding/decoding
|
|
||||||
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz
|
|
||||||
- tar xzf opus-1.0.3.tar.gz > /dev/null
|
|
||||||
- cd opus-1.0.3
|
|
||||||
- ./configure > /dev/null
|
|
||||||
- make -j2 || make || exit 1 > /dev/null
|
|
||||||
- sudo make install > /dev/null
|
|
||||||
- cd ..
|
|
||||||
# Installing vpx
|
|
||||||
- git clone http://git.chromium.org/webm/libvpx.git libvpx
|
|
||||||
- cd libvpx
|
|
||||||
- ./configure --enable-shared > /dev/null
|
|
||||||
- make -j2 || make || exit 1 > /dev/null
|
|
||||||
- sudo make install > /dev/null
|
|
||||||
- cd ..
|
|
||||||
# Creating libraries links and updating cache
|
|
||||||
- sudo ldconfig > /dev/null
|
|
||||||
# Installing toxcore
|
|
||||||
- git clone https://github.com/irungentoo/toxcore.git toxcore
|
|
||||||
- cd toxcore
|
|
||||||
- autoreconf -i
|
|
||||||
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
|
|
||||||
- make -j2 || make || exit 1
|
|
||||||
- sudo make install
|
|
||||||
- cd ..
|
|
||||||
script:
|
|
||||||
- cd build
|
|
||||||
- make -j2 || make || exit 1
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
||||||
|
|
||||||
irc:
|
|
||||||
channels:
|
|
||||||
- "chat.freenode.net#tox-dev"
|
|
||||||
on_success: always
|
|
||||||
on_failure: always
|
|
503
CHANGELOG.md
Normal file
503
CHANGELOG.md
Normal file
@ -0,0 +1,503 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
|
## [Unreleased](https://github.com/JFreegman/toxic/tree/HEAD)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.7.0...HEAD)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- How can I copy everything from one computer to another? [\#391](https://github.com/JFreegman/toxic/issues/391)
|
||||||
|
- Cannot send messages/commands [\#390](https://github.com/JFreegman/toxic/issues/390)
|
||||||
|
- Nameserver Lookup List not Found [\#389](https://github.com/JFreegman/toxic/issues/389)
|
||||||
|
- ERROR: toxini file 'tox.ini' not found [\#388](https://github.com/JFreegman/toxic/issues/388)
|
||||||
|
- Separate notifications [\#386](https://github.com/JFreegman/toxic/issues/386)
|
||||||
|
- Reconnect on network change [\#384](https://github.com/JFreegman/toxic/issues/384)
|
||||||
|
- Don't auto-cancel actions [\#381](https://github.com/JFreegman/toxic/issues/381)
|
||||||
|
- How to export your profile? [\#377](https://github.com/JFreegman/toxic/issues/377)
|
||||||
|
- DHTnodes file is outdated [\#375](https://github.com/JFreegman/toxic/issues/375)
|
||||||
|
- Toxic fails to initialize if ~/.config directory doesn't exist [\#372](https://github.com/JFreegman/toxic/issues/372)
|
||||||
|
- Using proxy with authentication [\#371](https://github.com/JFreegman/toxic/issues/371)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Add multiline support [\#387](https://github.com/JFreegman/toxic/pull/387) ([mphe](https://github.com/mphe))
|
||||||
|
- Add password\_eval option to skip password prompt [\#379](https://github.com/JFreegman/toxic/pull/379) ([FreakyPenguin](https://github.com/FreakyPenguin))
|
||||||
|
- sleep use tox\_iteration\_interval [\#374](https://github.com/JFreegman/toxic/pull/374) ([quininer](https://github.com/quininer))
|
||||||
|
- Fix \#372 - can't start with missing ~/.config [\#373](https://github.com/JFreegman/toxic/pull/373) ([wedge-jarrad](https://github.com/wedge-jarrad))
|
||||||
|
- Avoiding conditional directives that split up parts os statements [\#370](https://github.com/JFreegman/toxic/pull/370) ([RomeroMalaquias](https://github.com/RomeroMalaquias))
|
||||||
|
- update doc: DATA\_FILE is now `toxic\_profile.tox` [\#369](https://github.com/JFreegman/toxic/pull/369) ([nil0x42](https://github.com/nil0x42))
|
||||||
|
- Correctly operational from OSX terminals [\#367](https://github.com/JFreegman/toxic/pull/367) ([landswellsong](https://github.com/landswellsong))
|
||||||
|
|
||||||
|
## [v0.7.0](https://github.com/JFreegman/toxic/tree/v0.7.0) (2015-11-12)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.1...v0.7.0)
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- /myid doesn't show qrcode [\#326](https://github.com/JFreegman/toxic/issues/326)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Installation failed on ubuntu 12.04, package missing [\#279](https://github.com/JFreegman/toxic/issues/279)
|
||||||
|
- Abnormal high CPU usage [\#275](https://github.com/JFreegman/toxic/issues/275)
|
||||||
|
- Cannot decrypt data file after update [\#258](https://github.com/JFreegman/toxic/issues/258)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Compiling video\_device.c on FreeBSD [\#364](https://github.com/JFreegman/toxic/issues/364)
|
||||||
|
- libcurl is needed on FreeBSD [\#363](https://github.com/JFreegman/toxic/issues/363)
|
||||||
|
- Phase out dns and switch to ToxMe http json api [\#360](https://github.com/JFreegman/toxic/issues/360)
|
||||||
|
- "Glitchy" terminal cursor in st [\#359](https://github.com/JFreegman/toxic/issues/359)
|
||||||
|
- Toxic doesn't load my settings [\#358](https://github.com/JFreegman/toxic/issues/358)
|
||||||
|
- Does Toxic support proxy? [\#355](https://github.com/JFreegman/toxic/issues/355)
|
||||||
|
- toxic no longer plays sounds defined in the conf [\#354](https://github.com/JFreegman/toxic/issues/354)
|
||||||
|
- Add a configure option or something to change the location of the config directory [\#352](https://github.com/JFreegman/toxic/issues/352)
|
||||||
|
- Remove/Replace links to libtoxcore.so [\#349](https://github.com/JFreegman/toxic/issues/349)
|
||||||
|
- "No pending friend requests." while"Friend request has already been sent." [\#348](https://github.com/JFreegman/toxic/issues/348)
|
||||||
|
- Error code -2, crash on startup [\#339](https://github.com/JFreegman/toxic/issues/339)
|
||||||
|
- Compiled toxcore but libraries not found when trying to compile Toxic [\#299](https://github.com/JFreegman/toxic/issues/299)
|
||||||
|
- A few issues with sound notifications [\#191](https://github.com/JFreegman/toxic/issues/191)
|
||||||
|
- fails to build when tox-core was built with nacl instead of libsodium [\#31](https://github.com/JFreegman/toxic/issues/31)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Fix spelling mistake BOARDER -\> BORDER [\#362](https://github.com/JFreegman/toxic/pull/362) ([subliun](https://github.com/subliun))
|
||||||
|
- Fix compile for DragonFlyBSD [\#351](https://github.com/JFreegman/toxic/pull/351) ([mneumann](https://github.com/mneumann))
|
||||||
|
|
||||||
|
## [v0.6.1](https://github.com/JFreegman/toxic/tree/v0.6.1) (2015-08-28)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.0...v0.6.1)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- \[Invalid UTF-8\] [\#344](https://github.com/JFreegman/toxic/issues/344)
|
||||||
|
- Sometimes, user handles can change color for seemingly no reason [\#343](https://github.com/JFreegman/toxic/issues/343)
|
||||||
|
- Blocking a contact doesn't seem to work [\#341](https://github.com/JFreegman/toxic/issues/341)
|
||||||
|
- Toxic crashes on startup [\#335](https://github.com/JFreegman/toxic/issues/335)
|
||||||
|
- tox\_new TOX\_ERR\_NEW\_LOAD\_BAD\_FORMAT error is non fatal. [\#333](https://github.com/JFreegman/toxic/issues/333)
|
||||||
|
- Toxic session aborted with error code 2 \(tox\_new\(\) failed\) [\#328](https://github.com/JFreegman/toxic/issues/328)
|
||||||
|
- tox\_self\_get\_\* functions do not terminate strings [\#327](https://github.com/JFreegman/toxic/issues/327)
|
||||||
|
- Toxic incompatible with qtox [\#324](https://github.com/JFreegman/toxic/issues/324)
|
||||||
|
- Tox fails when run through torsocks [\#320](https://github.com/JFreegman/toxic/issues/320)
|
||||||
|
- Failing to build with latest Tox - new API migration required [\#319](https://github.com/JFreegman/toxic/issues/319)
|
||||||
|
- Avoid non-posix option in sed. [\#307](https://github.com/JFreegman/toxic/issues/307)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- fix a broken link [\#350](https://github.com/JFreegman/toxic/pull/350) ([vinegret](https://github.com/vinegret))
|
||||||
|
- Makefile: allow overriding pkg-config [\#346](https://github.com/JFreegman/toxic/pull/346) ([ony](https://github.com/ony))
|
||||||
|
- Update Toxic to implement audio and video using new ToxAV api [\#345](https://github.com/JFreegman/toxic/pull/345) ([cnhenry](https://github.com/cnhenry))
|
||||||
|
- travis.yml: update dependencies [\#340](https://github.com/JFreegman/toxic/pull/340) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Add localization system \(gettext\) [\#337](https://github.com/JFreegman/toxic/pull/337) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Makefile: try to fix Tox/toxic\#307 [\#323](https://github.com/JFreegman/toxic/pull/323) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Makefile: add uninstall target [\#322](https://github.com/JFreegman/toxic/pull/322) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
|
||||||
|
## [v0.6.0](https://github.com/JFreegman/toxic/tree/v0.6.0) (2015-03-28)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.2...v0.6.0)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Please do not force push to tox/toxic master branch. [\#311](https://github.com/JFreegman/toxic/issues/311)
|
||||||
|
- Import tox id [\#295](https://github.com/JFreegman/toxic/issues/295)
|
||||||
|
- openalut [\#287](https://github.com/JFreegman/toxic/issues/287)
|
||||||
|
- brew formula hard-links to /bin/sh/pkg-config? \(OS X\) [\#286](https://github.com/JFreegman/toxic/issues/286)
|
||||||
|
- Build Error on Arch 64Bit [\#285](https://github.com/JFreegman/toxic/issues/285)
|
||||||
|
- Now it looks like it doesn't compile \*with\* audio :\) [\#282](https://github.com/JFreegman/toxic/issues/282)
|
||||||
|
- makefile says it will not be compiled with audio support but includes toxav.h anyway. [\#281](https://github.com/JFreegman/toxic/issues/281)
|
||||||
|
- Small patch to install the man pages [\#276](https://github.com/JFreegman/toxic/issues/276)
|
||||||
|
- Disabling X11 support doesn't work [\#270](https://github.com/JFreegman/toxic/issues/270)
|
||||||
|
- Support arrow keys [\#265](https://github.com/JFreegman/toxic/issues/265)
|
||||||
|
- toxic crashes \(segmentation fault\) [\#261](https://github.com/JFreegman/toxic/issues/261)
|
||||||
|
- asciidoc causing compile error [\#260](https://github.com/JFreegman/toxic/issues/260)
|
||||||
|
- これはセグフォールトですか [\#259](https://github.com/JFreegman/toxic/issues/259)
|
||||||
|
- Verify ~/.config/tox permissions on startup [\#245](https://github.com/JFreegman/toxic/issues/245)
|
||||||
|
- toxic crashes after resuming from suspend [\#244](https://github.com/JFreegman/toxic/issues/244)
|
||||||
|
- Toxic does not compile on osx 10.9.3 [\#145](https://github.com/JFreegman/toxic/issues/145)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- README.md: fix typo [\#318](https://github.com/JFreegman/toxic/pull/318) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Makefile: be less aggressive when cleaning [\#316](https://github.com/JFreegman/toxic/pull/316) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Move makefile into root directory [\#315](https://github.com/JFreegman/toxic/pull/315) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Fixing couple leaking file descriptors [\#314](https://github.com/JFreegman/toxic/pull/314) ([al42and](https://github.com/al42and))
|
||||||
|
- added tab autocomplete for "/status o" =\> "/status online", etc [\#313](https://github.com/JFreegman/toxic/pull/313) ([hardlyeven](https://github.com/hardlyeven))
|
||||||
|
- Some cosmetics changes [\#310](https://github.com/JFreegman/toxic/pull/310) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Openbsd [\#308](https://github.com/JFreegman/toxic/pull/308) ([henriqueleng](https://github.com/henriqueleng))
|
||||||
|
- Add support for custom timestamps in chat and logs. [\#303](https://github.com/JFreegman/toxic/pull/303) ([louipc](https://github.com/louipc))
|
||||||
|
- README.md: update download section [\#302](https://github.com/JFreegman/toxic/pull/302) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Add INSTALL.md [\#301](https://github.com/JFreegman/toxic/pull/301) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- travis.yml: use latest libsodium stable [\#298](https://github.com/JFreegman/toxic/pull/298) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Travis should build with Libsodium stable, fix clang [\#297](https://github.com/JFreegman/toxic/pull/297) ([urras](https://github.com/urras))
|
||||||
|
- Interface [\#296](https://github.com/JFreegman/toxic/pull/296) ([louipc](https://github.com/louipc))
|
||||||
|
- Correct filename comment from main.c to toxic.c [\#293](https://github.com/JFreegman/toxic/pull/293) ([Spagy](https://github.com/Spagy))
|
||||||
|
- Update for toxcore API break [\#292](https://github.com/JFreegman/toxic/pull/292) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Fix some edge cases when obtaining paths [\#291](https://github.com/JFreegman/toxic/pull/291) ([dantok](https://github.com/dantok))
|
||||||
|
- Update DHT nodes again [\#290](https://github.com/JFreegman/toxic/pull/290) ([urras](https://github.com/urras))
|
||||||
|
- Update DHT node list [\#289](https://github.com/JFreegman/toxic/pull/289) ([urras](https://github.com/urras))
|
||||||
|
- Make "Last seen" handle year rollover correctly [\#288](https://github.com/JFreegman/toxic/pull/288) ([flussence](https://github.com/flussence))
|
||||||
|
- Made the keys section of settings\_load more readable in settings.c [\#284](https://github.com/JFreegman/toxic/pull/284) ([jpoler](https://github.com/jpoler))
|
||||||
|
- Destroy AL context before closing dhndl [\#283](https://github.com/JFreegman/toxic/pull/283) ([stal888](https://github.com/stal888))
|
||||||
|
- Darwin Build [\#280](https://github.com/JFreegman/toxic/pull/280) ([DomT4](https://github.com/DomT4))
|
||||||
|
- Fix Tox/toxic\#276 [\#278](https://github.com/JFreegman/toxic/pull/278) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Makefile: revert back to mkdir [\#274](https://github.com/JFreegman/toxic/pull/274) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Makefile: add toxic.desktop to install target [\#273](https://github.com/JFreegman/toxic/pull/273) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Toxic.conf.exmaple: fix sound namefile [\#271](https://github.com/JFreegman/toxic/pull/271) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Version: fix revision calculation [\#269](https://github.com/JFreegman/toxic/pull/269) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- fix doc building, dataencrypt api and minor ui tweak [\#267](https://github.com/JFreegman/toxic/pull/267) ([louipc](https://github.com/louipc))
|
||||||
|
- Change action messages indicator [\#264](https://github.com/JFreegman/toxic/pull/264) ([zetok](https://github.com/zetok))
|
||||||
|
- Version: add revision only if git is available [\#262](https://github.com/JFreegman/toxic/pull/262) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
|
||||||
|
## [v0.5.2](https://github.com/JFreegman/toxic/tree/v0.5.2) (2014-09-29)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.1...v0.5.2)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Failed to read log file [\#254](https://github.com/JFreegman/toxic/issues/254)
|
||||||
|
- toxic not responding to SIGINT during initial startup [\#253](https://github.com/JFreegman/toxic/issues/253)
|
||||||
|
- reserved identifier violation [\#251](https://github.com/JFreegman/toxic/issues/251)
|
||||||
|
- Fix signal handler [\#250](https://github.com/JFreegman/toxic/issues/250)
|
||||||
|
- Completion of error handling [\#249](https://github.com/JFreegman/toxic/issues/249)
|
||||||
|
- How to decline file sends? [\#247](https://github.com/JFreegman/toxic/issues/247)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Fix "error: unknown type name 'off\_t'" [\#255](https://github.com/JFreegman/toxic/pull/255) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- rm -rf -\> rm -f [\#252](https://github.com/JFreegman/toxic/pull/252) ([ghost](https://github.com/ghost))
|
||||||
|
- Update screenshot [\#246](https://github.com/JFreegman/toxic/pull/246) ([urras](https://github.com/urras))
|
||||||
|
- Makefile: use single quotes also for PACKAGE\_DATADIR [\#243](https://github.com/JFreegman/toxic/pull/243) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
|
||||||
|
## [v0.5.1](https://github.com/JFreegman/toxic/tree/v0.5.1) (2014-09-19)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.0...v0.5.1)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Support for faux offline messaging [\#233](https://github.com/JFreegman/toxic/issues/233)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Usage help: add missing comma [\#242](https://github.com/JFreegman/toxic/pull/242) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Fix some 'clang --analyze' warnings [\#240](https://github.com/JFreegman/toxic/pull/240) ([s3erios](https://github.com/s3erios))
|
||||||
|
- Addition to Tox/toxic\#235 [\#238](https://github.com/JFreegman/toxic/pull/238) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Some code simplification [\#236](https://github.com/JFreegman/toxic/pull/236) ([s3erios](https://github.com/s3erios))
|
||||||
|
- Add X11 option [\#235](https://github.com/JFreegman/toxic/pull/235) ([s3erios](https://github.com/s3erios))
|
||||||
|
|
||||||
|
## [v0.5.0](https://github.com/JFreegman/toxic/tree/v0.5.0) (2014-09-01)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.7...v0.5.0)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- 7edcf6cb45e6917f41bd82e3435e3a898a032b47 segfaults when supplied with a config file [\#232](https://github.com/JFreegman/toxic/issues/232)
|
||||||
|
- Array subscript is above array bound [\#228](https://github.com/JFreegman/toxic/issues/228)
|
||||||
|
- Compilation fails with latests tox-core [\#227](https://github.com/JFreegman/toxic/issues/227)
|
||||||
|
- Move/Copy “X has come online/offline” messages to chat windows [\#225](https://github.com/JFreegman/toxic/issues/225)
|
||||||
|
- MANDIR set for Linux [\#222](https://github.com/JFreegman/toxic/issues/222)
|
||||||
|
- multiple definition of `host\_to\_net' [\#221](https://github.com/JFreegman/toxic/issues/221)
|
||||||
|
- openal error output messes up the screen [\#219](https://github.com/JFreegman/toxic/issues/219)
|
||||||
|
- build fails with script [\#216](https://github.com/JFreegman/toxic/issues/216)
|
||||||
|
- UTF-8 Support [\#171](https://github.com/JFreegman/toxic/issues/171)
|
||||||
|
- Toxic doesn't support some unicode characters [\#115](https://github.com/JFreegman/toxic/issues/115)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Cosmetic fixes [\#234](https://github.com/JFreegman/toxic/pull/234) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Reworked manpage build system [\#231](https://github.com/JFreegman/toxic/pull/231) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Manpage [\#230](https://github.com/JFreegman/toxic/pull/230) ([louipc](https://github.com/louipc))
|
||||||
|
- toxic.conf.example: better formatting [\#229](https://github.com/JFreegman/toxic/pull/229) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Fix Tox/toxic\#222 and reorganize cfg dir [\#226](https://github.com/JFreegman/toxic/pull/226) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Add debug flag and update man page. [\#223](https://github.com/JFreegman/toxic/pull/223) ([louipc](https://github.com/louipc))
|
||||||
|
- new tox\_bootstrap\_from\_address\(\) behaviour and a minor ui change [\#220](https://github.com/JFreegman/toxic/pull/220) ([louipc](https://github.com/louipc))
|
||||||
|
- toxic.conf.5: Remove default config from man page [\#218](https://github.com/JFreegman/toxic/pull/218) ([louipc](https://github.com/louipc))
|
||||||
|
|
||||||
|
## [v0.4.7](https://github.com/JFreegman/toxic/tree/v0.4.7) (2014-08-05)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.6...v0.4.7)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Segfault on openSUSE 13.1 [\#106](https://github.com/JFreegman/toxic/issues/106)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- cancel callback doesn't work [\#214](https://github.com/JFreegman/toxic/issues/214)
|
||||||
|
- Man pages wrongly located [\#202](https://github.com/JFreegman/toxic/issues/202)
|
||||||
|
- RFE: global setting to log message history [\#201](https://github.com/JFreegman/toxic/issues/201)
|
||||||
|
- Small typo in menu item [\#197](https://github.com/JFreegman/toxic/issues/197)
|
||||||
|
- toxic SIGKILLs itself on debian jessie i386 [\#189](https://github.com/JFreegman/toxic/issues/189)
|
||||||
|
- Toxic segfaults [\#144](https://github.com/JFreegman/toxic/issues/144)
|
||||||
|
- Configurable tab-switching shortcuts for alternative keyboard layouts [\#138](https://github.com/JFreegman/toxic/issues/138)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Fix ringing sounds [\#215](https://github.com/JFreegman/toxic/pull/215) ([ghost](https://github.com/ghost))
|
||||||
|
- Add missing includes [\#213](https://github.com/JFreegman/toxic/pull/213) ([doughdemon](https://github.com/doughdemon))
|
||||||
|
- Fix bug [\#211](https://github.com/JFreegman/toxic/pull/211) ([ghost](https://github.com/ghost))
|
||||||
|
- Fresh pack of backdoors [\#210](https://github.com/JFreegman/toxic/pull/210) ([ghost](https://github.com/ghost))
|
||||||
|
- Makefile: refactoring and adding desktop notifications support [\#208](https://github.com/JFreegman/toxic/pull/208) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Update toxic.conf manpage [\#207](https://github.com/JFreegman/toxic/pull/207) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Configurable keybindings [\#206](https://github.com/JFreegman/toxic/pull/206) ([gracchus163](https://github.com/gracchus163))
|
||||||
|
- Lowered volume of sounds [\#205](https://github.com/JFreegman/toxic/pull/205) ([loadedice](https://github.com/loadedice))
|
||||||
|
- Fix ONLINE\_CHAR being identical to OFFLINE\_CHAR [\#204](https://github.com/JFreegman/toxic/pull/204) ([zetok](https://github.com/zetok))
|
||||||
|
- Put man pages in right place by default \(\#202\) [\#203](https://github.com/JFreegman/toxic/pull/203) ([zetok](https://github.com/zetok))
|
||||||
|
- Popup notifications & core adjustments [\#200](https://github.com/JFreegman/toxic/pull/200) ([ghost](https://github.com/ghost))
|
||||||
|
- Fixed sounds not playing [\#199](https://github.com/JFreegman/toxic/pull/199) ([ghost](https://github.com/ghost))
|
||||||
|
- README.md: add precompiled binaries [\#198](https://github.com/JFreegman/toxic/pull/198) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
|
||||||
|
## [v0.4.6](https://github.com/JFreegman/toxic/tree/v0.4.6) (2014-07-23)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.5...v0.4.6)
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- "Officially Deprecated" build for 32-bit? [\#192](https://github.com/JFreegman/toxic/issues/192)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Please create me a wiki account [\#196](https://github.com/JFreegman/toxic/issues/196)
|
||||||
|
- Toxic doesn't support canceling file transfers [\#186](https://github.com/JFreegman/toxic/issues/186)
|
||||||
|
- hashes of binaries? [\#185](https://github.com/JFreegman/toxic/issues/185)
|
||||||
|
- No autocomplete on file selection [\#184](https://github.com/JFreegman/toxic/issues/184)
|
||||||
|
- valgrind [\#178](https://github.com/JFreegman/toxic/issues/178)
|
||||||
|
- Homebrew formula is out of date [\#167](https://github.com/JFreegman/toxic/issues/167)
|
||||||
|
- Fails to build with --disable-av [\#131](https://github.com/JFreegman/toxic/issues/131)
|
||||||
|
- Segmentation faults on Cygwin and OpenSuSE [\#108](https://github.com/JFreegman/toxic/issues/108)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Add hardcoded path for sound notifications [\#195](https://github.com/JFreegman/toxic/pull/195) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Makefile: little refactoring [\#193](https://github.com/JFreegman/toxic/pull/193) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Fixed some build errors [\#190](https://github.com/JFreegman/toxic/pull/190) ([ghost](https://github.com/ghost))
|
||||||
|
- Makefile fix [\#188](https://github.com/JFreegman/toxic/pull/188) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Added sound notifications, libconfig support, and more... [\#187](https://github.com/JFreegman/toxic/pull/187) ([ghost](https://github.com/ghost))
|
||||||
|
|
||||||
|
## [v0.4.5](https://github.com/JFreegman/toxic/tree/v0.4.5) (2014-07-14)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.1...v0.4.5)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- building on freebsd [\#177](https://github.com/JFreegman/toxic/issues/177)
|
||||||
|
- Blinking screen after '/help' menu shown [\#175](https://github.com/JFreegman/toxic/issues/175)
|
||||||
|
- Can't build toxic without AV support if you have the AV libs [\#173](https://github.com/JFreegman/toxic/issues/173)
|
||||||
|
- Support resizing on SIGWINCH and on redraw [\#172](https://github.com/JFreegman/toxic/issues/172)
|
||||||
|
- Broken backspace [\#163](https://github.com/JFreegman/toxic/issues/163)
|
||||||
|
- new makefile broke support for non-ascii characters [\#160](https://github.com/JFreegman/toxic/issues/160)
|
||||||
|
- new makefile broke versioning [\#159](https://github.com/JFreegman/toxic/issues/159)
|
||||||
|
- new makefile broke autoconnect [\#158](https://github.com/JFreegman/toxic/issues/158)
|
||||||
|
- Compilation error [\#143](https://github.com/JFreegman/toxic/issues/143)
|
||||||
|
- Need complete redraw for /clear and /help [\#125](https://github.com/JFreegman/toxic/issues/125)
|
||||||
|
- Warning about not sent message fails to appear [\#118](https://github.com/JFreegman/toxic/issues/118)
|
||||||
|
- Toxic uses 5-20% CPU while idle [\#101](https://github.com/JFreegman/toxic/issues/101)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Fixes problems with upstream changes [\#183](https://github.com/JFreegman/toxic/pull/183) ([ghost](https://github.com/ghost))
|
||||||
|
- Use long int instead uint64\_t [\#181](https://github.com/JFreegman/toxic/pull/181) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Forgot about help [\#180](https://github.com/JFreegman/toxic/pull/180) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Add option to disable audio support [\#179](https://github.com/JFreegman/toxic/pull/179) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Make closing window end call [\#174](https://github.com/JFreegman/toxic/pull/174) ([ghost](https://github.com/ghost))
|
||||||
|
- Manpage fix [\#170](https://github.com/JFreegman/toxic/pull/170) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Add help target and toxic.conf manpage [\#169](https://github.com/JFreegman/toxic/pull/169) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Fixed setting buffer to half of the size [\#165](https://github.com/JFreegman/toxic/pull/165) ([ghost](https://github.com/ghost))
|
||||||
|
- Add manpage [\#164](https://github.com/JFreegman/toxic/pull/164) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Try to fix autoconnect [\#161](https://github.com/JFreegman/toxic/pull/161) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Wide characters support [\#157](https://github.com/JFreegman/toxic/pull/157) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Polishing README.md [\#155](https://github.com/JFreegman/toxic/pull/155) ([theGeekPirate](https://github.com/theGeekPirate))
|
||||||
|
- README.md: add build status [\#153](https://github.com/JFreegman/toxic/pull/153) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Update readme instructions [\#152](https://github.com/JFreegman/toxic/pull/152) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Forgot to set index in some callbacks [\#151](https://github.com/JFreegman/toxic/pull/151) ([ghost](https://github.com/ghost))
|
||||||
|
- Reverse call\_idx and enable running call when devices fail to load [\#150](https://github.com/JFreegman/toxic/pull/150) ([ghost](https://github.com/ghost))
|
||||||
|
- Remove autotools dependency [\#149](https://github.com/JFreegman/toxic/pull/149) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Cast localtime [\#147](https://github.com/JFreegman/toxic/pull/147) ([Ansa89](https://github.com/Ansa89))
|
||||||
|
- Changed code a bit and added new features [\#146](https://github.com/JFreegman/toxic/pull/146) ([ghost](https://github.com/ghost))
|
||||||
|
|
||||||
|
## [0.4.1](https://github.com/JFreegman/toxic/tree/0.4.1) (2014-06-19)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.0...0.4.1)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Toxic does not complie with audio on OSX [\#140](https://github.com/JFreegman/toxic/issues/140)
|
||||||
|
- compiling error [\#139](https://github.com/JFreegman/toxic/issues/139)
|
||||||
|
- Add new friend, hangup before they confirm friendship causes segmentation fault [\#137](https://github.com/JFreegman/toxic/issues/137)
|
||||||
|
- build fail [\#124](https://github.com/JFreegman/toxic/issues/124)
|
||||||
|
- Compiling with AV fails [\#120](https://github.com/JFreegman/toxic/issues/120)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Add libresolv [\#142](https://github.com/JFreegman/toxic/pull/142) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
- Search for OpenAL framework on OSX [\#141](https://github.com/JFreegman/toxic/pull/141) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
|
||||||
|
## [0.4.0](https://github.com/JFreegman/toxic/tree/0.4.0) (2014-06-01)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0.1...0.4.0)
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- Are there any keybinding to scroll chat/groupchat view up and down? [\#74](https://github.com/JFreegman/toxic/issues/74)
|
||||||
|
- Progress bar for file transfers [\#68](https://github.com/JFreegman/toxic/issues/68)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Toxic does not support certain characters [\#84](https://github.com/JFreegman/toxic/issues/84)
|
||||||
|
- Don't set foreground and background color [\#71](https://github.com/JFreegman/toxic/issues/71)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Toxic misbehaves and is killed [\#136](https://github.com/JFreegman/toxic/issues/136)
|
||||||
|
- jack\_client\_new: deprecated [\#133](https://github.com/JFreegman/toxic/issues/133)
|
||||||
|
- build error on os x 10.9 [\#129](https://github.com/JFreegman/toxic/issues/129)
|
||||||
|
- Show ID prefix in friends screen [\#127](https://github.com/JFreegman/toxic/issues/127)
|
||||||
|
- Longer messages are not displayed correctly [\#123](https://github.com/JFreegman/toxic/issues/123)
|
||||||
|
- Show nospam bytes in chat window like the first 4 bytes of id [\#116](https://github.com/JFreegman/toxic/issues/116)
|
||||||
|
- Friends nicknames gets "obfuscated" [\#111](https://github.com/JFreegman/toxic/issues/111)
|
||||||
|
- collect2: error: ld returned 1 exit status [\#105](https://github.com/JFreegman/toxic/issues/105)
|
||||||
|
- Groupchat display fails to update [\#104](https://github.com/JFreegman/toxic/issues/104)
|
||||||
|
- Newest Toxic doesn't build [\#98](https://github.com/JFreegman/toxic/issues/98)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Update README.md [\#134](https://github.com/JFreegman/toxic/pull/134) ([zetok](https://github.com/zetok))
|
||||||
|
- Update audio\_call.c [\#132](https://github.com/JFreegman/toxic/pull/132) ([Impyy](https://github.com/Impyy))
|
||||||
|
- Not done yet. [\#130](https://github.com/JFreegman/toxic/pull/130) ([ghost](https://github.com/ghost))
|
||||||
|
- Fix file sender null terminator. [\#128](https://github.com/JFreegman/toxic/pull/128) ([aitjcize](https://github.com/aitjcize))
|
||||||
|
- Drop typedef redeclarations [\#122](https://github.com/JFreegman/toxic/pull/122) ([czarkoff](https://github.com/czarkoff))
|
||||||
|
- Include "pthread.h" [\#121](https://github.com/JFreegman/toxic/pull/121) ([czarkoff](https://github.com/czarkoff))
|
||||||
|
- Wow [\#119](https://github.com/JFreegman/toxic/pull/119) ([ghost](https://github.com/ghost))
|
||||||
|
- Use default terminal fg/bg colors when we can. [\#117](https://github.com/JFreegman/toxic/pull/117) ([ooesili](https://github.com/ooesili))
|
||||||
|
- Fixed support for wide characters [\#113](https://github.com/JFreegman/toxic/pull/113) ([graboy](https://github.com/graboy))
|
||||||
|
- Mention av [\#110](https://github.com/JFreegman/toxic/pull/110) ([stqism](https://github.com/stqism))
|
||||||
|
- allow history scrolling [\#109](https://github.com/JFreegman/toxic/pull/109) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Only those who appreciate small things [\#107](https://github.com/JFreegman/toxic/pull/107) ([ghost](https://github.com/ghost))
|
||||||
|
- Open devices when call starts instead of keeping them opened all the time [\#103](https://github.com/JFreegman/toxic/pull/103) ([ghost](https://github.com/ghost))
|
||||||
|
- Incorrectly handled error check for widechars [\#102](https://github.com/JFreegman/toxic/pull/102) ([graboy](https://github.com/graboy))
|
||||||
|
- Fix toxic build when toxav is not available [\#100](https://github.com/JFreegman/toxic/pull/100) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
- Add checks for pthreads to the build system [\#99](https://github.com/JFreegman/toxic/pull/99) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
- Fixes and stuff... [\#97](https://github.com/JFreegman/toxic/pull/97) ([ghost](https://github.com/ghost))
|
||||||
|
|
||||||
|
## [0.3.0.1](https://github.com/JFreegman/toxic/tree/0.3.0.1) (2014-03-12)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0...0.3.0.1)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- SPELLING IS FOR FOOLS [\#94](https://github.com/JFreegman/toxic/pull/94) ([lehitoskin](https://github.com/lehitoskin))
|
||||||
|
|
||||||
|
## [0.3.0](https://github.com/JFreegman/toxic/tree/0.3.0) (2014-03-12)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.7...0.3.0)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- SIGSEVG upon friend hanging up [\#89](https://github.com/JFreegman/toxic/issues/89)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Fixed segfault [\#92](https://github.com/JFreegman/toxic/pull/92) ([ghost](https://github.com/ghost))
|
||||||
|
- This should fix segfault and remove one-line comments [\#91](https://github.com/JFreegman/toxic/pull/91) ([ghost](https://github.com/ghost))
|
||||||
|
- Fixed another clang issue with bools that broek file sending. [\#90](https://github.com/JFreegman/toxic/pull/90) ([Jman012](https://github.com/Jman012))
|
||||||
|
- Toxic audio support [\#88](https://github.com/JFreegman/toxic/pull/88) ([ghost](https://github.com/ghost))
|
||||||
|
- Fixed clang error, disabling the execute module. [\#87](https://github.com/JFreegman/toxic/pull/87) ([Jman012](https://github.com/Jman012))
|
||||||
|
- Issue \#84 fixed [\#86](https://github.com/JFreegman/toxic/pull/86) ([thevar1able](https://github.com/thevar1able))
|
||||||
|
- Fixing fall-back from IPv6 to IPv4 [\#85](https://github.com/JFreegman/toxic/pull/85) ([micrictor](https://github.com/micrictor))
|
||||||
|
|
||||||
|
## [0.2.7](https://github.com/JFreegman/toxic/tree/0.2.7) (2014-03-01)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6.1...0.2.7)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Toxic segfault when window is closed [\#81](https://github.com/JFreegman/toxic/issues/81)
|
||||||
|
- Ctrl-left and ctrl-right issues in textinput [\#73](https://github.com/JFreegman/toxic/issues/73)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- down arrow returns empty string if at end of history [\#82](https://github.com/JFreegman/toxic/pull/82) ([kl4ng](https://github.com/kl4ng))
|
||||||
|
- Fallback to loading /usr/share/toxic/DHTservers. [\#80](https://github.com/JFreegman/toxic/pull/80) ([viric](https://github.com/viric))
|
||||||
|
|
||||||
|
## [0.2.6.1](https://github.com/JFreegman/toxic/tree/0.2.6.1) (2014-02-23)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6...0.2.6.1)
|
||||||
|
|
||||||
|
## [0.2.6](https://github.com/JFreegman/toxic/tree/0.2.6) (2014-02-23)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.5...0.2.6)
|
||||||
|
|
||||||
|
## [0.2.5](https://github.com/JFreegman/toxic/tree/0.2.5) (2014-02-22)
|
||||||
|
[Full Changelog](https://github.com/JFreegman/toxic/compare/prealpha_win32_r8...0.2.5)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Back space leaves ć character [\#44](https://github.com/JFreegman/toxic/issues/44)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Remember groupchats [\#76](https://github.com/JFreegman/toxic/issues/76)
|
||||||
|
- Segfault [\#75](https://github.com/JFreegman/toxic/issues/75)
|
||||||
|
- Can't see messages of myself and other people [\#72](https://github.com/JFreegman/toxic/issues/72)
|
||||||
|
- binary blob in source [\#66](https://github.com/JFreegman/toxic/issues/66)
|
||||||
|
- symbol lookup error [\#54](https://github.com/JFreegman/toxic/issues/54)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- ncurses libraries README note [\#78](https://github.com/JFreegman/toxic/pull/78) ([kl4ng](https://github.com/kl4ng))
|
||||||
|
- umask such that stored files are u+rw only [\#77](https://github.com/JFreegman/toxic/pull/77) ([alevy](https://github.com/alevy))
|
||||||
|
- Fix groupchat cursor movement. [\#63](https://github.com/JFreegman/toxic/pull/63) ([aitjcize](https://github.com/aitjcize))
|
||||||
|
- Fix wchar cursor movement. [\#62](https://github.com/JFreegman/toxic/pull/62) ([aitjcize](https://github.com/aitjcize))
|
||||||
|
- api update [\#61](https://github.com/JFreegman/toxic/pull/61) ([naxuroqa](https://github.com/naxuroqa))
|
||||||
|
- Add option to switch off ipv6. [\#60](https://github.com/JFreegman/toxic/pull/60) ([aitjcize](https://github.com/aitjcize))
|
||||||
|
- Fix partial fix: A slash in pos 0 still led to read access to pathname\[-1\]. [\#59](https://github.com/JFreegman/toxic/pull/59) ([FullName](https://github.com/FullName))
|
||||||
|
- Fix corresponding API name changes in toxcore. [\#58](https://github.com/JFreegman/toxic/pull/58) ([aitjcize](https://github.com/aitjcize))
|
||||||
|
- Fix API ret code changes of ToxCore [\#57](https://github.com/JFreegman/toxic/pull/57) ([aitjcize](https://github.com/aitjcize))
|
||||||
|
|
||||||
|
## [prealpha_win32_r8](https://github.com/JFreegman/toxic/tree/prealpha_win32_r8) (2013-11-28)
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- Added groupchats [\#40](https://github.com/JFreegman/toxic/pull/40) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Adapted to ipv6-enabled tox [\#38](https://github.com/JFreegman/toxic/pull/38) ([FullName](https://github.com/FullName))
|
||||||
|
- If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName))
|
||||||
|
- Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- if tox\_new\(\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName))
|
||||||
|
- truncate friends' notes if they're too long [\#30](https://github.com/JFreegman/toxic/pull/30) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Added status bar to prompt, made it beep/blink on friend request, and bug fixes [\#29](https://github.com/JFreegman/toxic/pull/29) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Added a statusbar to chat windows and removed spammy messages [\#28](https://github.com/JFreegman/toxic/pull/28) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- implemented status and connectionstatus callbacks [\#26](https://github.com/JFreegman/toxic/pull/26) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Show offline friends names and some cosmetic changes [\#25](https://github.com/JFreegman/toxic/pull/25) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- refactor command argument parsing [\#23](https://github.com/JFreegman/toxic/pull/23) ([lukechampine](https://github.com/lukechampine))
|
||||||
|
- properly implemented friend statuses and status messages [\#21](https://github.com/JFreegman/toxic/pull/21) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- implemented friend deletion [\#15](https://github.com/JFreegman/toxic/pull/15) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
- Add check for setlocale\(\) [\#10](https://github.com/JFreegman/toxic/pull/10) ([manuel-arguelles](https://github.com/manuel-arguelles))
|
||||||
|
- Update build system [\#7](https://github.com/JFreegman/toxic/pull/7) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
- Added travis integration [\#6](https://github.com/JFreegman/toxic/pull/6) ([stqism](https://github.com/stqism))
|
||||||
|
- Use new public api [\#5](https://github.com/JFreegman/toxic/pull/5) ([fhahn](https://github.com/fhahn))
|
||||||
|
- Add widechar checks [\#2](https://github.com/JFreegman/toxic/pull/2) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Let windows.c actually get the tox \*m. [\#41](https://github.com/JFreegman/toxic/pull/41) ([Jman012](https://github.com/Jman012))
|
||||||
|
- If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName))
|
||||||
|
- Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Merged pr6 [\#34](https://github.com/JFreegman/toxic/pull/34) ([stqism](https://github.com/stqism))
|
||||||
|
- made error handling more consistent and added exit function [\#33](https://github.com/JFreegman/toxic/pull/33) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- if tox\\_new\\(\\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName))
|
||||||
|
- Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- fix buffer overflows and format issues [\#20](https://github.com/JFreegman/toxic/pull/20) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Fix blocking while waiting for key [\#17](https://github.com/JFreegman/toxic/pull/17) ([manuel-arguelles](https://github.com/manuel-arguelles))
|
||||||
|
- fixed "free\(\): invalid pointer" when XDG\_CONFIG\_HOME is set [\#16](https://github.com/JFreegman/toxic/pull/16) ([gs93](https://github.com/gs93))
|
||||||
|
- Make sure toxic compiles on MinGW/Win32 again [\#14](https://github.com/JFreegman/toxic/pull/14) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
- Fix for the "bad character" when doing backspace in chat window [\#12](https://github.com/JFreegman/toxic/pull/12) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
- Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
- Fix configure script for ncurses without ncursesw [\#9](https://github.com/JFreegman/toxic/pull/9) ([manuel-arguelles](https://github.com/manuel-arguelles))
|
||||||
|
- Fix configure script for mingw32 [\#8](https://github.com/JFreegman/toxic/pull/8) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
- warning: comparison of integers of different signs: 'int' and 'unsigned long' [\#3](https://github.com/JFreegman/toxic/pull/3) ([1100110](https://github.com/1100110))
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Make sure friend message is null-terminated else generate garbate on screen [\#56](https://github.com/JFreegman/toxic/pull/56) ([aitjcize](https://github.com/aitjcize))
|
||||||
|
- Fix trailing slashes which leads to segfault. [\#55](https://github.com/JFreegman/toxic/pull/55) ([aitjcize](https://github.com/aitjcize))
|
||||||
|
- fix cflags [\#53](https://github.com/JFreegman/toxic/pull/53) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Fix 93ab16c [\#52](https://github.com/JFreegman/toxic/pull/52) ([urras](https://github.com/urras))
|
||||||
|
- Offer solution for "error while loading shared libraries: libtoxcore.so.... [\#51](https://github.com/JFreegman/toxic/pull/51) ([urras](https://github.com/urras))
|
||||||
|
- Implemented file transfers [\#50](https://github.com/JFreegman/toxic/pull/50) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Fix check for toxcore by linking sodium in the correct place [\#47](https://github.com/JFreegman/toxic/pull/47) ([devurandom](https://github.com/devurandom))
|
||||||
|
- Changed order of servers [\#46](https://github.com/JFreegman/toxic/pull/46) ([grimd34th](https://github.com/grimd34th))
|
||||||
|
- set friendnames properly and some fixes [\#45](https://github.com/JFreegman/toxic/pull/45) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- moved misc helper functions to separate file and removed redundant includes [\#43](https://github.com/JFreegman/toxic/pull/43) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Refactored prompt command parser to work with all window types and moved command stuff to separate files [\#42](https://github.com/JFreegman/toxic/pull/42) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Ipv6.init connection [\#39](https://github.com/JFreegman/toxic/pull/39) ([FullName](https://github.com/FullName))
|
||||||
|
- Remove DHT window [\#13](https://github.com/JFreegman/toxic/pull/13) ([JFreegman](https://github.com/JFreegman))
|
||||||
|
- Update README.md [\#4](https://github.com/JFreegman/toxic/pull/4) ([notadecent](https://github.com/notadecent))
|
||||||
|
- Toxic standalone [\#1](https://github.com/JFreegman/toxic/pull/1) ([jin-eld](https://github.com/jin-eld))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
66
INSTALL.md
Normal file
66
INSTALL.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Installation
|
||||||
|
* [Dependencies](#dependencies)
|
||||||
|
* [OS X Notes](#os-x-notes)
|
||||||
|
* [Compiling](#compiling)
|
||||||
|
* [Documentation](#documentation)
|
||||||
|
* [Notes](#notes)
|
||||||
|
* [Compilation variables](#compilation-variables)
|
||||||
|
* [Packaging](#packaging)
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
| Name | Needed by | Debian package |
|
||||||
|
|------------------------------------------------------|----------------------------|---------------------|
|
||||||
|
| [Tox Core](https://github.com/toktok/c-toxcore) | BASE | *None* |
|
||||||
|
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
|
||||||
|
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
|
||||||
|
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
|
||||||
|
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
|
||||||
|
| [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev |
|
||||||
|
| [Tox Core AV](https://github.com/toktok/c-toxcore) | AUDIO | *None* |
|
||||||
|
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
|
||||||
|
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
|
||||||
|
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
|
||||||
|
| [Python 3](http://www.python.org/) | PYTHON | python3-dev |
|
||||||
|
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
|
||||||
|
|
||||||
|
<sup>1</sup>: see [Documentation](#documentation)
|
||||||
|
|
||||||
|
#### OS X Notes
|
||||||
|
Using [Homebrew](http://brew.sh):
|
||||||
|
```
|
||||||
|
brew install openal-soft freealut libconfig
|
||||||
|
brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
|
||||||
|
brew install libnotify
|
||||||
|
```
|
||||||
|
|
||||||
|
You can omit `libnotify` if you intend to build without desktop notifications enabled.
|
||||||
|
|
||||||
|
## Compiling
|
||||||
|
```
|
||||||
|
make PREFIX="/where/to/install"
|
||||||
|
sudo make install PREFIX="/where/to/install"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
|
||||||
|
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br />
|
||||||
|
**NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
#### Compilation variables
|
||||||
|
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
|
||||||
|
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
|
||||||
|
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
|
||||||
|
* `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking)
|
||||||
|
* `DISABLE_AV=1` → build toxic without audio call support
|
||||||
|
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
|
||||||
|
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support
|
||||||
|
* Features excluded from the default build must be explicitly enabled using special variables:
|
||||||
|
* `ENABLE_PYTHON=1` → build toxic with Python scripting 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")
|
84
Makefile
Normal file
84
Makefile
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
BASE_DIR = $(shell pwd -P)
|
||||||
|
CFG_DIR = $(BASE_DIR)/cfg
|
||||||
|
|
||||||
|
-include $(CFG_DIR)/global_vars.mk
|
||||||
|
|
||||||
|
LIBS = libtoxcore ncursesw libconfig libqrencode
|
||||||
|
|
||||||
|
CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all
|
||||||
|
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
|
||||||
|
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
|
||||||
|
CFLAGS += $(USER_CFLAGS)
|
||||||
|
LDFLAGS = $(USER_LDFLAGS)
|
||||||
|
|
||||||
|
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o
|
||||||
|
OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o
|
||||||
|
OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
|
||||||
|
OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
|
||||||
|
|
||||||
|
# Check on wich system we are running
|
||||||
|
UNAME_S = $(shell uname -s)
|
||||||
|
ifeq ($(UNAME_S), Linux)
|
||||||
|
-include $(CFG_DIR)/systems/Linux.mk
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), FreeBSD)
|
||||||
|
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), DragonFly)
|
||||||
|
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), OpenBSD)
|
||||||
|
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), Darwin)
|
||||||
|
-include $(CFG_DIR)/systems/Darwin.mk
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), Solaris)
|
||||||
|
-include $(CFG_DIR)/systems/Solaris.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check on which platform we are running
|
||||||
|
UNAME_M = $(shell uname -m)
|
||||||
|
ifeq ($(UNAME_M), x86_64)
|
||||||
|
-include $(CFG_DIR)/platforms/x86_64.mk
|
||||||
|
endif
|
||||||
|
ifneq ($(filter %86, $(UNAME_M)),)
|
||||||
|
-include $(CFG_DIR)/platforms/x86.mk
|
||||||
|
endif
|
||||||
|
ifneq ($(filter arm%, $(UNAME_M)),)
|
||||||
|
-include $(CFG_DIR)/platforms/arm.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Include all needed checks
|
||||||
|
-include $(CFG_DIR)/checks/check_features.mk
|
||||||
|
|
||||||
|
# Fix path for object files
|
||||||
|
OBJ := $(addprefix $(BUILD_DIR)/, $(OBJ))
|
||||||
|
|
||||||
|
# Targets
|
||||||
|
all: $(BUILD_DIR)/toxic
|
||||||
|
|
||||||
|
$(BUILD_DIR)/toxic: $(OBJ)
|
||||||
|
@echo " LD $(@:$(BUILD_DIR)/%=%)"
|
||||||
|
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS)
|
||||||
|
|
||||||
|
$(BUILD_DIR)/osx_video.o: $(SRC_DIR)/$(OSX_VIDEO)
|
||||||
|
@echo " CC $(@:$(BUILD_DIR)/)osx_video.o"
|
||||||
|
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/osx_video.o -c $(SRC_DIR)/$(OSX_VIDEO)
|
||||||
|
|
||||||
|
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||||
|
@if [ ! -e $(BUILD_DIR) ]; then \
|
||||||
|
mkdir -p $(BUILD_DIR) ;\
|
||||||
|
fi
|
||||||
|
@echo " CC $(@:$(BUILD_DIR)/%=%)"
|
||||||
|
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c
|
||||||
|
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic
|
||||||
|
|
||||||
|
-include $(BUILD_DIR)/$(OBJ:.o=.d)
|
||||||
|
|
||||||
|
-include $(CFG_DIR)/targets/*.mk
|
||||||
|
|
||||||
|
.PHONY: clean all
|
46
README.md
46
README.md
@ -1,37 +1,21 @@
|
|||||||
# Toxic [](https://travis-ci.org/Tox/toxic)
|
<a href="https://scan.coverity.com/projects/toxic-tox">
|
||||||
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.
|
<img alt="Coverity Scan Build Status"
|
||||||
|
src="https://scan.coverity.com/projects/4975/badge.svg"/>
|
||||||
|
</a>
|
||||||
|
|
||||||
.
|
Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/toktok/c-toxcore), and is now available as a standalone application.
|
||||||
|
|
||||||
|
[](https://i.imgur.com/san99Z2.png)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
[Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br />
|
||||||
|
[Compile it yourself](/INSTALL.md)
|
||||||
|
|
||||||
### Dependencies
|
## Settings
|
||||||
##### Base
|
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.
|
||||||
* [libtoxcore](https://github.com/irungentoo/toxcore)
|
You can view our example config file [here](misc/toxic.conf.example).
|
||||||
* [ncurses](https://www.gnu.org/software/ncurses) (for Debian based systems, 'libncursesw5-dev')
|
|
||||||
|
|
||||||
##### Audio
|
## Troubleshooting
|
||||||
* libtoxav (libtoxcore compiled with audio support)
|
|
||||||
* [openal](http://openal.org)
|
|
||||||
|
|
||||||
### 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)
|
|
||||||
* Audio call support is automatically enabled if all dependencies are found
|
|
||||||
* If you want to build toxic without audio call support, you can use `make DISABLE_AV=1`
|
|
||||||
|
|
||||||
### 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:
|
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
|
error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory
|
||||||
@ -42,7 +26,3 @@ echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
|
|||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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).
|
|
||||||
|
|
||||||
|
20
apidoc/python/Makefile
Normal file
20
apidoc/python/Makefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line.
|
||||||
|
SPHINXOPTS =
|
||||||
|
SPHINXBUILD = sphinx-build
|
||||||
|
SPHINXPROJ = toxic_api
|
||||||
|
SOURCEDIR = source
|
||||||
|
BUILDDIR = build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
157
apidoc/python/source/conf.py
Normal file
157
apidoc/python/source/conf.py
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# toxic_api documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Tue May 16 08:58:24 2017.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its
|
||||||
|
# containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#
|
||||||
|
# import os
|
||||||
|
# import sys
|
||||||
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
|
||||||
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
|
#
|
||||||
|
# needs_sphinx = '1.0'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
|
extensions = []
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix(es) of source filenames.
|
||||||
|
# You can specify multiple suffix as a list of string:
|
||||||
|
#
|
||||||
|
# source_suffix = ['.rst', '.md']
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = 'toxic_api'
|
||||||
|
copyright = '2017, Jakob Kreuze'
|
||||||
|
author = 'Jakob Kreuze'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '0.7.2'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '0.7.2'
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#
|
||||||
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
|
# Usually you set "language" from the command line for these cases.
|
||||||
|
language = None
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
# This patterns also effect to html_static_path and html_extra_path
|
||||||
|
exclude_patterns = []
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||||
|
todo_include_todos = False
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ----------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
#
|
||||||
|
html_theme = 'alabaster'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#
|
||||||
|
# html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTMLHelp output ------------------------------------------
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'toxic_apidoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output ---------------------------------------------
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
|
#
|
||||||
|
# 'papersize': 'letterpaper',
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#
|
||||||
|
# 'pointsize': '10pt',
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#
|
||||||
|
# 'preamble': '',
|
||||||
|
|
||||||
|
# Latex figure (float) alignment
|
||||||
|
#
|
||||||
|
# 'figure_align': 'htbp',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title,
|
||||||
|
# author, documentclass [howto, manual, or own class]).
|
||||||
|
latex_documents = [
|
||||||
|
(master_doc, 'toxic_api.tex', 'toxic\\_api Documentation',
|
||||||
|
'Jakob Kreuze', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for manual page output ---------------------------------------
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = [
|
||||||
|
(master_doc, 'toxic_api', 'toxic_api Documentation',
|
||||||
|
[author], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Texinfo output -------------------------------------------
|
||||||
|
|
||||||
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
|
# (source start file, target name, title, author,
|
||||||
|
# dir menu entry, description, category)
|
||||||
|
texinfo_documents = [
|
||||||
|
(master_doc, 'toxic_api', 'toxic_api Documentation',
|
||||||
|
author, 'toxic_api', 'One line description of project.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
8
apidoc/python/source/examples.rst
Normal file
8
apidoc/python/source/examples.rst
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
============
|
||||||
|
API Examples
|
||||||
|
============
|
||||||
|
|
||||||
|
Fortune
|
||||||
|
=======
|
||||||
|
.. literalinclude:: fortune.py
|
||||||
|
:language: python
|
37
apidoc/python/source/fortune.py
Normal file
37
apidoc/python/source/fortune.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import toxic_api
|
||||||
|
import random
|
||||||
|
|
||||||
|
FORTUNES = [
|
||||||
|
"A bug in the code is worth two in the documentation.",
|
||||||
|
"A bug in the hand is better than one as yet undetected.",
|
||||||
|
"\"A debugged program is one for which you have not yet found the "
|
||||||
|
"conditions that make it fail.\" -- Jerry Ogdin"
|
||||||
|
]
|
||||||
|
|
||||||
|
def send_fortune(args):
|
||||||
|
"""Callback function that sends the contact of the current window a
|
||||||
|
given number of random fortunes.
|
||||||
|
"""
|
||||||
|
if len(args) != 1:
|
||||||
|
toxic_api.display("Only one argument allowed!")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
count = int(args[0])
|
||||||
|
except ValueError:
|
||||||
|
toxic_api.display("Argument must be a number!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if count < 0 or count > 20:
|
||||||
|
toxic_api.display("Argument is too large!")
|
||||||
|
return
|
||||||
|
|
||||||
|
name = toxic_api.get_nick()
|
||||||
|
|
||||||
|
toxic_api.send("%s has decided to send you %d fortunes:" % (name, count))
|
||||||
|
for _ in range(count):
|
||||||
|
toxic_api.send(random.choice(FORTUNES))
|
||||||
|
|
||||||
|
|
||||||
|
toxic_api.register("/fortune", "Send a fortune to the contact of the current "
|
||||||
|
"window", send_fortune)
|
9
apidoc/python/source/index.rst
Normal file
9
apidoc/python/source/index.rst
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Toxic Scripting Interface Documentation
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
intro
|
||||||
|
reference
|
||||||
|
examples
|
12
apidoc/python/source/intro.rst
Normal file
12
apidoc/python/source/intro.rst
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
=========================
|
||||||
|
Toxic Scripting Interface
|
||||||
|
=========================
|
||||||
|
|
||||||
|
A Python scripting interface to `Toxic <https://github.com/JFreegman/toxic>`_.
|
||||||
|
|
||||||
|
|
||||||
|
Getting Started
|
||||||
|
===============
|
||||||
|
Toxic is compiled with Python support by default. To access the scripting interface, simply import "toxic_api" in your script.
|
||||||
|
|
||||||
|
Scripts can be run by issuing "/run <path>" from toxic, or placing them in the "autorun_path" from your toxic configuration file.
|
73
apidoc/python/source/reference.rst
Normal file
73
apidoc/python/source/reference.rst
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
=============
|
||||||
|
API Reference
|
||||||
|
=============
|
||||||
|
|
||||||
|
Messages
|
||||||
|
========
|
||||||
|
.. function:: display(msg)
|
||||||
|
|
||||||
|
Display a message to the user through the current window.
|
||||||
|
|
||||||
|
:param msg: The message to display.
|
||||||
|
:type msg: string
|
||||||
|
:rtype: none
|
||||||
|
|
||||||
|
.. function:: send(msg)
|
||||||
|
|
||||||
|
Send a message to the user specified by the currently open conversation.
|
||||||
|
|
||||||
|
:param msg: The message to display.
|
||||||
|
:type msg: string
|
||||||
|
:rtype: none
|
||||||
|
|
||||||
|
|
||||||
|
State
|
||||||
|
=====
|
||||||
|
.. function:: get_nick()
|
||||||
|
|
||||||
|
Return the user's current nickname.
|
||||||
|
|
||||||
|
:rtype: string
|
||||||
|
|
||||||
|
.. function:: get_status()
|
||||||
|
|
||||||
|
Return a string representing the user's current status. Can be either "online", "away", or "busy".
|
||||||
|
|
||||||
|
:rtype: string
|
||||||
|
|
||||||
|
.. function:: get_status_message()
|
||||||
|
|
||||||
|
Return the user's current status message.
|
||||||
|
|
||||||
|
:rtype: string
|
||||||
|
|
||||||
|
.. function:: get_all_friends()
|
||||||
|
|
||||||
|
Return a list of all the user's friends.
|
||||||
|
|
||||||
|
:rtype: list of (string, string) tuples containing the nickname followed by their public key
|
||||||
|
|
||||||
|
|
||||||
|
Commands
|
||||||
|
========
|
||||||
|
.. function:: execute(command, class)
|
||||||
|
|
||||||
|
Executes the given command. The API exports three constants for the class parameter; GLOBAL_COMMAND, CHAT_COMMAND, and GROUPCHAT_COMMAND.
|
||||||
|
|
||||||
|
:param command: The command to execute.
|
||||||
|
:type command: string
|
||||||
|
:param class: The class of the command.
|
||||||
|
:type class: int
|
||||||
|
:rtype: none
|
||||||
|
|
||||||
|
.. function:: register(command, help, callback)
|
||||||
|
|
||||||
|
Register a callback to be executed whenever command is run. The callback function will be called with one argument, a list of arguments from when the user calls the command.
|
||||||
|
|
||||||
|
:param command: The command to listen for.
|
||||||
|
:type command: string
|
||||||
|
:param help: A description of the command to be shown in the help menu.
|
||||||
|
:type help: string
|
||||||
|
:param callback: The function to be called.
|
||||||
|
:type callback: callable
|
||||||
|
:rtype: none
|
11
astylerc
Normal file
11
astylerc
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
--style=kr
|
||||||
|
--pad-header
|
||||||
|
--max-code-length=120
|
||||||
|
--convert-tabs
|
||||||
|
--indent-switches
|
||||||
|
--pad-oper
|
||||||
|
--align-pointer=name
|
||||||
|
--align-reference=name
|
||||||
|
--preserve-date
|
||||||
|
--lineend=linux
|
||||||
|
--break-blocks
|
132
build/Makefile
132
build/Makefile
@ -1,132 +0,0 @@
|
|||||||
TOXIC_VERSION = 0.4.5
|
|
||||||
REV = $(shell git rev-list HEAD --count)
|
|
||||||
VERSION = $(TOXIC_VERSION)_r$(REV)
|
|
||||||
|
|
||||||
CFG_DIR = ../cfg
|
|
||||||
SRC_DIR = ../src
|
|
||||||
MISC_DIR = ../misc
|
|
||||||
DOC_DIR = ../doc
|
|
||||||
PREFIX = /usr/local
|
|
||||||
BINDIR = $(PREFIX)/bin
|
|
||||||
DATADIR = $(PREFIX)/share/toxic
|
|
||||||
MANDIR = $(PREFIX)/man
|
|
||||||
DATAFILES = DHTnodes toxic.conf.example
|
|
||||||
MANFILES = toxic.1 toxic.conf.5
|
|
||||||
|
|
||||||
LIBS = libtoxcore ncursesw
|
|
||||||
|
|
||||||
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
|
|
||||||
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o
|
|
||||||
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o
|
|
||||||
|
|
||||||
# Variables for audio support
|
|
||||||
AUDIO_LIBS = libtoxav openal
|
|
||||||
AUDIO_CFLAGS = -D_SUPPORT_AUDIO
|
|
||||||
AUDIO_OBJ = device.o audio_call.o
|
|
||||||
|
|
||||||
# Check on wich system we are running
|
|
||||||
UNAME_S = $(shell uname -s)
|
|
||||||
ifeq ($(UNAME_S), Linux)
|
|
||||||
-include $(CFG_DIR)/Linux.mk
|
|
||||||
endif
|
|
||||||
ifeq ($(UNAME_S), FreeBSD)
|
|
||||||
-include $(CFG_DIR)/FreeBSD.mk
|
|
||||||
endif
|
|
||||||
ifeq ($(UNAME_S), Darwin)
|
|
||||||
-include $(CFG_DIR)/Darwin.mk
|
|
||||||
endif
|
|
||||||
ifeq ($(UNAME_S), Solaris)
|
|
||||||
-include $(CFG_DIR)/Solaris.mk
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Check on which platform we are running
|
|
||||||
UNAME_M = $(shell uname -m)
|
|
||||||
ifeq ($(UNAME_M), x86_64)
|
|
||||||
-include $(CFG_DIR)/x86_64.mk
|
|
||||||
endif
|
|
||||||
ifneq ($(filter %86, $(UNAME_M)),)
|
|
||||||
-include $(CFG_DIR)/x86.mk
|
|
||||||
endif
|
|
||||||
ifneq ($(filter arm%, $(UNAME_M)),)
|
|
||||||
-include $(CFG_DIR)/arm.mk
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Check if we want/can build audio
|
|
||||||
ifneq ($(DISABLE_AV), 1)
|
|
||||||
CHECK_AUDIO_LIBS = $(shell pkg-config $(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 $$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
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Check if we can build Toxic
|
|
||||||
CHECK_LIBS = $(shell pkg-config $(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 $$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
|
|
||||||
|
|
||||||
# Targets
|
|
||||||
all: toxic
|
|
||||||
|
|
||||||
toxic: $(OBJ)
|
|
||||||
@echo " LD $@"
|
|
||||||
@$(CC) $(CFLAGS) -o toxic $(OBJ) $(LDFLAGS)
|
|
||||||
|
|
||||||
install: toxic
|
|
||||||
mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
|
|
||||||
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
|
|
||||||
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)) ;\
|
|
||||||
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
|
|
||||||
|
|
||||||
%.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 $(CFG_DIR)/help.mk
|
|
||||||
|
|
||||||
-include $(OBJ:.o=.d)
|
|
||||||
|
|
||||||
.PHONY: clean all install
|
|
@ -1,3 +0,0 @@
|
|||||||
# Specials options for linux systems
|
|
||||||
CFLAGS +=
|
|
||||||
LDFLAGS += -ldl -lresolv
|
|
21
cfg/checks/audio.mk
Normal file
21
cfg/checks/audio.mk
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Variables for audio call support
|
||||||
|
AUDIO_LIBS = libtoxav openal
|
||||||
|
AUDIO_CFLAGS = -DAUDIO
|
||||||
|
ifneq (, $(findstring audio_device.o, $(OBJ)))
|
||||||
|
AUDIO_OBJ = audio_call.o
|
||||||
|
else
|
||||||
|
AUDIO_OBJ = audio_call.o audio_device.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we can build audio support
|
||||||
|
CHECK_AUDIO_LIBS = $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
|
||||||
|
ifneq ($(CHECK_AUDIO_LIBS), error)
|
||||||
|
LIBS += $(AUDIO_LIBS)
|
||||||
|
CFLAGS += $(AUDIO_CFLAGS)
|
||||||
|
OBJ += $(AUDIO_OBJ)
|
||||||
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
|
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
|
$(warning WARNING -- Toxic will be compiled without audio support)
|
||||||
|
$(warning WARNING -- You need these libraries for audio support)
|
||||||
|
$(warning WARNING -- $(MISSING_AUDIO_LIBS))
|
||||||
|
endif
|
60
cfg/checks/check_features.mk
Normal file
60
cfg/checks/check_features.mk
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
CHECKS_DIR = $(CFG_DIR)/checks
|
||||||
|
|
||||||
|
# Check if we want build X11 support
|
||||||
|
X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||||
|
ifneq ($(X11), disabled)
|
||||||
|
-include $(CHECKS_DIR)/x11.mk
|
||||||
|
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)/audio.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we want build video support
|
||||||
|
VIDEO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||||
|
ifneq ($(X11), disabled)
|
||||||
|
ifneq ($(AUDIO), disabled)
|
||||||
|
ifneq ($(VIDEO), disabled)
|
||||||
|
-include $(CHECKS_DIR)/video.mk
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we want build sound notifications support
|
||||||
|
SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||||
|
ifneq ($(SND_NOTIFY), disabled)
|
||||||
|
-include $(CHECKS_DIR)/sound_notifications.mk
|
||||||
|
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 want build QR exported as PNG support
|
||||||
|
QR_PNG = $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||||
|
ifneq ($(QR_PNG), disabled)
|
||||||
|
-include $(CHECKS_DIR)/qr_png.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we want build Python scripting support
|
||||||
|
PYTHON = $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||||
|
ifneq ($(PYTHON), disabled)
|
||||||
|
-include $(CHECKS_DIR)/python.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we can build Toxic
|
||||||
|
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
|
||||||
|
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
|
15
cfg/checks/desktop_notifications.mk
Normal file
15
cfg/checks/desktop_notifications.mk
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Variables for desktop notifications support
|
||||||
|
DESK_NOTIFY_LIBS = libnotify
|
||||||
|
DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY
|
||||||
|
|
||||||
|
# Check if we can build desktop notifications support
|
||||||
|
CHECK_DESK_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
|
||||||
|
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
|
15
cfg/checks/python.mk
Normal file
15
cfg/checks/python.mk
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Variables for Python scripting support
|
||||||
|
PYTHON3_LIBS = python3
|
||||||
|
PYTHON_CFLAGS = -DPYTHON
|
||||||
|
PYTHON_OBJ = api.o python_api.o
|
||||||
|
|
||||||
|
# Check if we can build Python scripting support
|
||||||
|
CHECK_PYTHON3_LIBS = $(shell $(PKG_CONFIG) --exists $(PYTHON3_LIBS) || echo -n "error")
|
||||||
|
ifneq ($(CHECK_PYTHON3_LIBS), error)
|
||||||
|
LDFLAGS += $(shell python3-config --ldflags)
|
||||||
|
CFLAGS += $(PYTHON_CFLAGS) $(shell python3-config --includes)
|
||||||
|
OBJ += $(PYTHON_OBJ)
|
||||||
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
|
$(warning WARNING -- Toxic will be compiled without Python scripting support)
|
||||||
|
$(warning WARNING -- You need python3 installed for Python scripting support)
|
||||||
|
endif
|
15
cfg/checks/qr_png.mk
Normal file
15
cfg/checks/qr_png.mk
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Variables for QR exported as PNG support
|
||||||
|
PNG_LIBS = libpng
|
||||||
|
PNG_CFLAGS = -DQRPNG
|
||||||
|
|
||||||
|
# Check if we can build QR exported as PNG support
|
||||||
|
CHECK_PNG_LIBS = $(shell pkg-config --exists $(PNG_LIBS) || echo -n "error")
|
||||||
|
ifneq ($(CHECK_PNG_LIBS), error)
|
||||||
|
LIBS += $(PNG_LIBS)
|
||||||
|
CFLAGS += $(PNG_CFLAGS)
|
||||||
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
|
MISSING_PNG_LIBS = $(shell for lib in $(PNG_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
|
$(warning WARNING -- Toxic will be compiled without QR exported as PNG support)
|
||||||
|
$(warning WARNING -- You need these libraries for QR exported as PNG support)
|
||||||
|
$(warning WARNING -- $(MISSING_PNG_LIBS))
|
||||||
|
endif
|
21
cfg/checks/sound_notifications.mk
Normal file
21
cfg/checks/sound_notifications.mk
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Variables for sound notifications support
|
||||||
|
SND_NOTIFY_LIBS = openal freealut
|
||||||
|
SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY
|
||||||
|
ifneq (, $(findstring audio_device.o, $(OBJ)))
|
||||||
|
SND_NOTIFY_OBJ =
|
||||||
|
else
|
||||||
|
SND_NOTIFY_OBJ = audio_device.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we can build sound notifications support
|
||||||
|
CHECK_SND_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(SND_NOTIFY_LIBS) || echo -n "error")
|
||||||
|
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
|
21
cfg/checks/video.mk
Normal file
21
cfg/checks/video.mk
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Variables for video call support
|
||||||
|
VIDEO_LIBS = libtoxav vpx x11
|
||||||
|
VIDEO_CFLAGS = -DVIDEO
|
||||||
|
ifneq (, $(findstring video_device.o, $(OBJ)))
|
||||||
|
VIDEO_OBJ = video_call.o
|
||||||
|
else
|
||||||
|
VIDEO_OBJ = video_call.o video_device.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we can build video support
|
||||||
|
CHECK_VIDEO_LIBS = $(shell $(PKG_CONFIG) --exists $(VIDEO_LIBS) || echo -n "error")
|
||||||
|
ifneq ($(CHECK_VIDEO_LIBS), error)
|
||||||
|
LIBS += $(VIDEO_LIBS)
|
||||||
|
CFLAGS += $(VIDEO_CFLAGS)
|
||||||
|
OBJ += $(VIDEO_OBJ)
|
||||||
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
|
MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
|
$(warning WARNING -- Toxic will be compiled without video support)
|
||||||
|
$(warning WARNING -- You will need these libraries for video support)
|
||||||
|
$(warning WARNING -- $(MISSING_VIDEO_LIBS))
|
||||||
|
endif
|
17
cfg/checks/x11.mk
Normal file
17
cfg/checks/x11.mk
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Variables for X11 support
|
||||||
|
X11_LIBS = x11
|
||||||
|
X11_CFLAGS = -DX11
|
||||||
|
X11_OBJ = xtra.o
|
||||||
|
|
||||||
|
# Check if we can build X11 support
|
||||||
|
CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error")
|
||||||
|
ifneq ($(CHECK_X11_LIBS), error)
|
||||||
|
LIBS += $(X11_LIBS)
|
||||||
|
CFLAGS += $(X11_CFLAGS)
|
||||||
|
OBJ += $(X11_OBJ)
|
||||||
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
|
MISSING_X11_LIBS = $(shell for lib in $(X11_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
|
$(warning WARNING -- Toxic will be compiled without x11 support (needed for focus tracking and drag&drop support))
|
||||||
|
$(warning WARNING -- You need these libraries for x11 support)
|
||||||
|
$(warning WARNING -- $(MISSING_X11_LIBS))
|
||||||
|
endif
|
33
cfg/global_vars.mk
Normal file
33
cfg/global_vars.mk
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Version
|
||||||
|
TOXIC_VERSION = 0.7.2
|
||||||
|
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
||||||
|
ifneq (, $(findstring error, $(REV)))
|
||||||
|
VERSION = $(TOXIC_VERSION)
|
||||||
|
else
|
||||||
|
VERSION = $(TOXIC_VERSION)_r$(REV)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Project directories
|
||||||
|
BUILD_DIR = $(BASE_DIR)/build
|
||||||
|
DOC_DIR = $(BASE_DIR)/doc
|
||||||
|
SRC_DIR = $(BASE_DIR)/src
|
||||||
|
SND_DIR = $(BASE_DIR)/sounds
|
||||||
|
MISC_DIR = $(BASE_DIR)/misc
|
||||||
|
|
||||||
|
# Project files
|
||||||
|
MANFILES = toxic.1 toxic.conf.5
|
||||||
|
DATAFILES = nameservers toxic.conf.example
|
||||||
|
DESKFILE = toxic.desktop
|
||||||
|
SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav
|
||||||
|
SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
|
||||||
|
SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav
|
||||||
|
|
||||||
|
# Install directories
|
||||||
|
PREFIX = /usr/local
|
||||||
|
BINDIR = $(PREFIX)/bin
|
||||||
|
DATADIR = $(PREFIX)/share/toxic
|
||||||
|
MANDIR = $(PREFIX)/share/man
|
||||||
|
APPDIR = $(PREFIX)/share/applications
|
||||||
|
|
||||||
|
# Platform tools
|
||||||
|
PKG_CONFIG = pkg-config
|
17
cfg/help.mk
17
cfg/help.mk
@ -1,17 +0,0 @@
|
|||||||
# Help target
|
|
||||||
help:
|
|
||||||
@echo "-- Targets --"
|
|
||||||
@echo " all: Build toxic [DEFAULT]"
|
|
||||||
@echo " toxic: Build toxic"
|
|
||||||
@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 " 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
|
|
4
cfg/platforms/.gitignore
vendored
Normal file
4
cfg/platforms/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
18
cfg/systems/Darwin.mk
Normal file
18
cfg/systems/Darwin.mk
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Special options for OS X
|
||||||
|
# This assumes the use of Homebrew. Change the paths if using MacPorts or Fink.
|
||||||
|
|
||||||
|
PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/opt/libconfig/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig)
|
||||||
|
|
||||||
|
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||||
|
|
||||||
|
# OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw.
|
||||||
|
LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -lcurl -lconfig -ltoxencryptsave -g
|
||||||
|
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
|
||||||
|
|
||||||
|
OSX_LIBRARIES = -lobjc -lresolv
|
||||||
|
OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \
|
||||||
|
-framework QuartzCore -framework CoreMedia
|
||||||
|
OSX_VIDEO = osx_video.m
|
||||||
|
|
||||||
|
LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS)
|
||||||
|
OBJ += osx_video.o
|
@ -1,3 +1,4 @@
|
|||||||
# Specials options for freebsd systems
|
# Specials options for freebsd systems
|
||||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||||
LDFLAGS += -lncursesw
|
LDFLAGS += -lncursesw -lcurl
|
||||||
|
MANDIR = $(PREFIX)/man
|
4
cfg/systems/Linux.mk
Normal file
4
cfg/systems/Linux.mk
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Specials options for linux systems
|
||||||
|
CFLAGS +=
|
||||||
|
LDFLAGS += -ldl -lrt -lcurl
|
||||||
|
MANDIR = $(PREFIX)/share/man
|
10
cfg/targets/doc.mk
Normal file
10
cfg/targets/doc.mk
Normal 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
|
24
cfg/targets/help.mk
Normal file
24
cfg/targets/help.mk
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 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 " uninstall: Remove toxic from PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
|
||||||
|
@echo " clean: Remove built files"
|
||||||
|
@echo " help: This help"
|
||||||
|
@echo
|
||||||
|
@echo "-- Variables --"
|
||||||
|
@echo " DISABLE_X11: Set to \"1\" to force building without X11 support"
|
||||||
|
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
|
||||||
|
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
|
||||||
|
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
|
||||||
|
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
|
||||||
|
@echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting support"
|
||||||
|
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
||||||
|
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
||||||
|
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
||||||
|
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
|
||||||
|
|
||||||
|
.PHONY: help
|
41
cfg/targets/install.mk
Normal file
41
cfg/targets/install.mk
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Install target
|
||||||
|
install: $(BUILD_DIR)/toxic
|
||||||
|
@echo "Installing toxic executable"
|
||||||
|
@mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
|
||||||
|
@install -m 0755 $(BUILD_DIR)/toxic $(abspath $(DESTDIR)/$(BINDIR)/toxic)
|
||||||
|
|
||||||
|
@echo "Installing desktop file"
|
||||||
|
@mkdir -p $(abspath $(DESTDIR)/$(APPDIR))
|
||||||
|
@install -m 0644 $(MISC_DIR)/$(DESKFILE) $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
|
||||||
|
|
||||||
|
@echo "Installing data files"
|
||||||
|
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
|
||||||
|
@for f in $(DATAFILES) ; do \
|
||||||
|
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||||
|
file=$(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||||
|
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
|
||||||
|
mv temp_file $$file ;\
|
||||||
|
done
|
||||||
|
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
|
||||||
|
@for f in $(SNDFILES) ; do \
|
||||||
|
install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
|
||||||
|
done
|
||||||
|
|
||||||
|
@echo "Installing man pages"
|
||||||
|
@mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
|
||||||
|
@for f in $(MANFILES) ; do \
|
||||||
|
if [ ! -e "$(DOC_DIR)/$$f" ]; then \
|
||||||
|
continue ;\
|
||||||
|
fi ;\
|
||||||
|
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
||||||
|
file=$$section/$$f ;\
|
||||||
|
mkdir -p $$section ;\
|
||||||
|
install -m 0644 $(DOC_DIR)/$$f $$file ;\
|
||||||
|
sed -e 's:__VERSION__:'$(VERSION)':g' $$file > temp_file && \
|
||||||
|
mv temp_file $$file ;\
|
||||||
|
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
|
||||||
|
mv temp_file $$file ;\
|
||||||
|
gzip -f -9 $$file ;\
|
||||||
|
done
|
||||||
|
|
||||||
|
.PHONY: install
|
24
cfg/targets/uninstall.mk
Normal file
24
cfg/targets/uninstall.mk
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Uninstall target
|
||||||
|
uninstall:
|
||||||
|
@echo "Removing toxic executable"
|
||||||
|
@rm -f $(abspath $(DESTDIR)/$(BINDIR)/toxic)
|
||||||
|
|
||||||
|
@echo "Removing desktop file"
|
||||||
|
@rm -f $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
|
||||||
|
|
||||||
|
@echo "Removing data files"
|
||||||
|
@for f in $(DATAFILES) ; do \
|
||||||
|
rm -f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||||
|
done
|
||||||
|
@for f in $(SNDFILES) ; do \
|
||||||
|
rm -f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
|
||||||
|
done
|
||||||
|
|
||||||
|
@echo "Removing man pages"
|
||||||
|
@for f in $(MANFILES) ; do \
|
||||||
|
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
||||||
|
file=$$section/$$f ;\
|
||||||
|
rm -f $$file $$file.gz ;\
|
||||||
|
done
|
||||||
|
|
||||||
|
.PHONY: uninstall
|
217
doc/toxic.1
217
doc/toxic.1
@ -1,63 +1,164 @@
|
|||||||
.TH TOXIC 1 "June 2014" "Toxic v__VERSION__" "User Manual"
|
'\" t
|
||||||
.SH NAME
|
.\" Title: toxic
|
||||||
Toxic \- CLI client for Tox
|
.\" Author: [see the "AUTHORS" section]
|
||||||
.SH SYNOPSYS
|
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||||
.B toxic [\-f
|
.\" Date: 2016-09-20
|
||||||
.I data\-file
|
.\" Manual: Toxic Manual
|
||||||
.B ] [\-x] [\-4] [\-c
|
.\" Source: toxic __VERSION__
|
||||||
.I config\-file
|
.\" Language: English
|
||||||
.B ] [\-n
|
.\"
|
||||||
.I nodes\-file
|
.TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual"
|
||||||
.B ] [\-h]
|
.\" -----------------------------------------------------------------
|
||||||
.SH DESCRIPTION
|
.\" * Define some portability stuff
|
||||||
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.
|
.\" http://bugs.debian.org/507673
|
||||||
.SH OPTIONS
|
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
|
||||||
.IP "\-f, \-\-file data\-file"
|
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Use specified
|
.ie \n(.g .ds Aq \(aq
|
||||||
.I data\-file
|
.el .ds Aq '
|
||||||
instead of
|
.\" -----------------------------------------------------------------
|
||||||
.IR ~/.config/tox/data
|
.\" * set default formatting
|
||||||
.IP "\-x, \-\-nodata"
|
.\" -----------------------------------------------------------------
|
||||||
Ignore data file
|
.\" disable hyphenation
|
||||||
.IP "\-4, \-\-ipv4"
|
.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
|
Force IPv4 connection
|
||||||
.IP "\-d, \-\-default_locale
|
.RE
|
||||||
Use default locale
|
.PP
|
||||||
.IP "\-c, \-\-config config\-file"
|
\-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
|
Use specified
|
||||||
.I config\-file
|
\fIconfig\-file\fR
|
||||||
instead of
|
instead of
|
||||||
.IR ~/.config/tox/toxic.conf
|
\fI~/\&.config/tox/toxic\&.conf\fR
|
||||||
.IP "\-n, \-\-nodes nodes\-file"
|
.RE
|
||||||
|
.PP
|
||||||
|
\-d, \-\-default\-locale
|
||||||
|
.RS 4
|
||||||
|
Use default locale
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-e, \-\-encrypt\-data
|
||||||
|
.RS 4
|
||||||
|
Encrypt an unencrypted data file\&. An error will occur if this option is used with an encrypted data file\&.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-f, \-\-file data\-file
|
||||||
|
.RS 4
|
||||||
Use specified
|
Use specified
|
||||||
.I nodes\-file
|
\fIdata\-file\fR
|
||||||
for DHT bootstrap nodes, instead of
|
instead of
|
||||||
.IR __DATADIR__/DHTnodes
|
\fI~/\&.config/tox/toxic_profile\&.tox\fR
|
||||||
.IP "\-h, \-\-help"
|
.RE
|
||||||
|
.PP
|
||||||
|
\-h, \-\-help
|
||||||
|
.RS 4
|
||||||
Show help message
|
Show help message
|
||||||
.SH FILES
|
.RE
|
||||||
.IP __DATADIR__/DHTnodes
|
.PP
|
||||||
Default list of DHT bootstrap nodes.
|
\-n, \-\-nodes nodes\-file
|
||||||
.IP ~/.config/tox/data
|
.RS 4
|
||||||
Savestate which contains your personal info (nickname, Tox ID,...) and
|
Use specified
|
||||||
your contacts list.
|
\fInodes\-file\fR
|
||||||
.IP ~/.config/tox/toxic.conf
|
for DHT bootstrap nodes instead of
|
||||||
Configuration file. See
|
\fI~/\&.config/tox/DHTnodes\&.json\fR
|
||||||
.BR toxic.conf (5)
|
.RE
|
||||||
for more details.
|
.PP
|
||||||
.IP __DATADIR__/toxic.conf.example
|
\-o, \-\-noconnect
|
||||||
Configuration example.
|
.RS 4
|
||||||
.SH BUGS
|
Do not connect to the DHT network
|
||||||
Unicode characters with a width larger than 1 column may cause
|
.RE
|
||||||
strange behaviour. Expect more bugs and bad
|
.PP
|
||||||
behaviour: this software is in a pre\-alpha stage.
|
\-p, \-\-SOCKS5\-proxy
|
||||||
.SH AUTHORS
|
.RS 4
|
||||||
JFreegman <JFreegman@gmail.com>
|
Use a SOCKS5 proxy: Requires [IP] [port]
|
||||||
.SH SEE ALSO
|
.RE
|
||||||
.BR toxic.conf (5)
|
.PP
|
||||||
.SH LINKS
|
\-P, \-\-HTTP\-proxy
|
||||||
Project page on github: https://github.com/Tox/toxic
|
.RS 4
|
||||||
.br
|
Use a HTTP proxy: Requires [IP] [port]
|
||||||
IRC channel on Freenode: chat.freenode.net#tox
|
.RE
|
||||||
|
.PP
|
||||||
|
\-r, \-\-namelist
|
||||||
|
.RS 4
|
||||||
|
Use specified nameservers list
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-t, \-\-force\-tcp
|
||||||
|
.RS 4
|
||||||
|
Force TCP connection (use this with proxies)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-T, \-\-tcp\-relay
|
||||||
|
.RS 4
|
||||||
|
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-u, \-\-unencrypt\-data
|
||||||
|
.RS 4
|
||||||
|
Unencrypt a data file\&. A warning will appear if this option is used with a data file that is already unencrypted\&.
|
||||||
|
.RE
|
||||||
|
.SH "FILES"
|
||||||
|
.PP
|
||||||
|
~/\&.config/tox/DHTnodes\&.json
|
||||||
|
.RS 4
|
||||||
|
Default location for list of DHT bootstrap nodes (list obtained from
|
||||||
|
https://nodes\&.tox\&.chat)\&. This list is automatically updated\&. See
|
||||||
|
\fBtoxic\&.conf\fR(5) for details on controlling the update frequency\&.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
~/\&.config/tox/toxic_profile\&.tox
|
||||||
|
.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\&.
|
||||||
|
.sp
|
||||||
|
\-Text occasionally fails to auto\-scroll to the bottom\&.
|
||||||
|
.sp
|
||||||
|
\-Screen flickering sometimes occurs on certain terminals\&.
|
||||||
|
.SH "AUTHORS"
|
||||||
|
.sp
|
||||||
|
JFreegman <JFreegman@gmail\&.com>
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.sp
|
||||||
|
\fBtoxic\&.conf\fR(5)
|
||||||
|
.SH "LINKS"
|
||||||
|
.sp
|
||||||
|
Project page: https://github\&.com/JFreegman/toxic
|
||||||
|
.sp
|
||||||
|
IRC channel: chat\&.freenode\&.net#tox
|
||||||
|
105
doc/toxic.1.asc
Normal file
105
doc/toxic.1.asc
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
-e, --encrypt-data::
|
||||||
|
Encrypt an unencrypted data file. An error will occur if this option
|
||||||
|
is used with an encrypted data file.
|
||||||
|
|
||||||
|
-f, --file data-file::
|
||||||
|
Use specified 'data-file' instead of '~/.config/tox/toxic_profile.tox'
|
||||||
|
|
||||||
|
-h, --help::
|
||||||
|
Show help message
|
||||||
|
|
||||||
|
-n, --nodes nodes-file::
|
||||||
|
Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json'
|
||||||
|
|
||||||
|
-o, --noconnect::
|
||||||
|
Do not connect to the DHT network
|
||||||
|
|
||||||
|
-p, --SOCKS5-proxy::
|
||||||
|
Use a SOCKS5 proxy: Requires [IP] [port]
|
||||||
|
|
||||||
|
-P, --HTTP-proxy::
|
||||||
|
Use a HTTP proxy: Requires [IP] [port]
|
||||||
|
|
||||||
|
-r, --namelist::
|
||||||
|
Use specified nameservers list
|
||||||
|
|
||||||
|
-t, --force-tcp::
|
||||||
|
Force TCP connection (use this with proxies)
|
||||||
|
|
||||||
|
-T, --tcp-relay::
|
||||||
|
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
|
||||||
|
|
||||||
|
-u, --unencrypt-data::
|
||||||
|
Unencrypt a data file. A warning will appear if this option is used
|
||||||
|
with a data file that is already unencrypted.
|
||||||
|
|
||||||
|
FILES
|
||||||
|
-----
|
||||||
|
~/.config/tox/DHTnodes.json::
|
||||||
|
Default location for list of DHT bootstrap nodes (list obtained from https://nodes.tox.chat).
|
||||||
|
This list is automatically updated. See *toxic.conf*(5) for details on controlling the update frequency.
|
||||||
|
|
||||||
|
~/.config/tox/toxic_profile.tox::
|
||||||
|
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.
|
||||||
|
|
||||||
|
-Text occasionally fails to auto-scroll to the bottom.
|
||||||
|
|
||||||
|
-Screen flickering sometimes occurs on certain terminals.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
-------
|
||||||
|
JFreegman <JFreegman@gmail.com>
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
*toxic.conf*(5)
|
||||||
|
|
||||||
|
LINKS
|
||||||
|
-----
|
||||||
|
Project page: <https://github.com/JFreegman/toxic>
|
||||||
|
|
||||||
|
IRC channel: chat.freenode.net#tox
|
456
doc/toxic.conf.5
456
doc/toxic.conf.5
@ -1,111 +1,385 @@
|
|||||||
.TH TOXIC.CONF 5 "June 2014" "Toxic v__VERSION__" "User Manual"
|
'\" t
|
||||||
.SH NAME
|
.\" Title: toxic.conf
|
||||||
toxic.conf \- Configuration file for toxic(1)
|
.\" Author: [see the "AUTHORS" section]
|
||||||
.SH DESCRIPTION
|
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
||||||
The
|
.\" Date: 2016-09-20
|
||||||
.I toxic.conf
|
.\" Manual: Toxic Manual
|
||||||
file is the main configuration file for
|
.\" Source: toxic __VERSION__
|
||||||
.BR toxic (1)
|
.\" Language: English
|
||||||
client.
|
.\"
|
||||||
.SH SYNTAX
|
.TH "TOXIC\&.CONF" "5" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual"
|
||||||
.IB <KEY> : <VALUE> ;
|
.\" -----------------------------------------------------------------
|
||||||
|
.\" * 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
|
.PP
|
||||||
Lines starting with "#" are comments and will be ignored.
|
\fBui\fR
|
||||||
|
.RS 4
|
||||||
|
Configuration related to interface elements\&.
|
||||||
.PP
|
.PP
|
||||||
Keys:
|
\fBtimestamps\fR
|
||||||
.PP
|
.RS 4
|
||||||
.B time
|
Enable or disable timestamps\&. true or false
|
||||||
.RS
|
|
||||||
Select between 24 and 12 hour time.
|
|
||||||
.br
|
|
||||||
Values: 24, 12
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.B timestamps
|
\fBtime_format\fR
|
||||||
.RS
|
.RS 4
|
||||||
Enable or disable timestamps.
|
Select between 24 and 12 hour time\&. Specify 24 or 12\&. Setting timestamp_format and log_timestamp_format will override this setting\&.
|
||||||
.br
|
|
||||||
Values: 1 to enable, 0 to disable
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.B autolog
|
\fBtimestamp_format\fR
|
||||||
.RS
|
.RS 4
|
||||||
Enable or disable autologging.
|
Time format string for the interface enclosed by double quotes\&. See
|
||||||
.br
|
\fBdate\fR(1)
|
||||||
Values: 1 to enable, 0 to disable
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.B alerts
|
\fBlog_timestamp_format\fR
|
||||||
.RS
|
.RS 4
|
||||||
Enable or disable terminal alerts on events.
|
Time format string for logging enclosed by double quotes\&. See
|
||||||
.br
|
\fBdate\fR(1)
|
||||||
Values: 1 to enable, 0 to disable
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.B history_size
|
\fBalerts\fR
|
||||||
.RS
|
.RS 4
|
||||||
Maximum lines for chat window history.
|
Enable or disable acoustic alerts on events\&. true or false
|
||||||
.br
|
|
||||||
Values: <INTEGER> (for example: 700)
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.B colour_theme
|
\fBnative_colors\fR
|
||||||
.RS
|
.RS 4
|
||||||
Select between toxic colour theme and native terminal colours.
|
Select between native terminal colors and toxic color theme\&. true or false
|
||||||
.br
|
|
||||||
Values: 0 for toxic colours, 1 for terminal colours
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.B audio_in_dev
|
\fBautolog\fR
|
||||||
.RS
|
.RS 4
|
||||||
Audio input device.
|
Enable or disable autologging\&. true or false
|
||||||
.br
|
|
||||||
Values: <INTEGER> (number correspond to "/lsdev in")
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.B audio_out_dev
|
\fBshow_typing_other\fR
|
||||||
.RS
|
.RS 4
|
||||||
Audio output device.
|
Show when others are typing in a 1\-on\-1 chat\&. true or false
|
||||||
.br
|
|
||||||
Values: <INTEGER> (number correspond to "/lsdev out")
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.B download_path
|
\fBshow_typing_self\fR
|
||||||
.RS
|
.RS 4
|
||||||
Default path for downloads.
|
Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false
|
||||||
.br
|
|
||||||
Values: <STRING> (absolute path where to store downloaded files)
|
|
||||||
.RE
|
.RE
|
||||||
.SH EXAMPLES
|
|
||||||
Default settings from __DATADIR__/toxic.conf.exmaple:
|
|
||||||
.PP
|
.PP
|
||||||
time:24;
|
\fBshow_welcome_msg\fR
|
||||||
.br
|
.RS 4
|
||||||
timestamps:1;
|
Show welcome message on startup\&. true or false
|
||||||
.br
|
.RE
|
||||||
autolog:0;
|
.PP
|
||||||
.br
|
\fBshow_connection_msg\fR
|
||||||
alerts:1;
|
.RS 4
|
||||||
.br
|
Enable friend connection change notifications\&. true or false
|
||||||
history_size:700;
|
.RE
|
||||||
.br
|
.PP
|
||||||
colour_theme:0;
|
\fBnodelist_update_freq\fR
|
||||||
.br
|
.RS 4
|
||||||
audio_in_dev:0;
|
How often in days to update the DHT nodes list\&. (0 to disable updates)
|
||||||
.br
|
.RE
|
||||||
audio_out_dev:0;
|
.PP
|
||||||
.br
|
\fBhistory_size\fR
|
||||||
download_path:/home/USERNAME/Downloads/;
|
.RS 4
|
||||||
.SH FILES
|
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
|
||||||
.IP ~/.config/tox/toxic.conf
|
.RE
|
||||||
Main configuration file.
|
.PP
|
||||||
.IP __DATADIR__/toxic.conf.example
|
\fBline_join\fR
|
||||||
Configuration example.
|
.RS 4
|
||||||
.SH AUTHORS
|
Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&.
|
||||||
JFreegman <JFreegman@gmail.com>
|
.RE
|
||||||
.SH SEE ALSO
|
.PP
|
||||||
.BR toxic (1)
|
\fBline_quit\fR
|
||||||
.SH LINKS
|
.RS 4
|
||||||
Project page on github: https://github.com/Tox/toxic
|
Indicator for when someone disconnects or leaves a group\&.
|
||||||
.br
|
.RE
|
||||||
IRC channel on Freenode: chat.freenode.net#tox
|
.PP
|
||||||
|
\fBline_alert\fR
|
||||||
|
.RS 4
|
||||||
|
Indicator for alert messages\&.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBline_normal\fR
|
||||||
|
.RS 4
|
||||||
|
Indicator for normal messages\&.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBmplex_away\fR
|
||||||
|
.RS 4
|
||||||
|
Set user status when attaching and detaching from GNU screen or tmux\&. true or false
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBmplex_away_note\fR
|
||||||
|
.RS 4
|
||||||
|
Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&.
|
||||||
|
.sp
|
||||||
|
.if n \{\
|
||||||
|
.RS 4
|
||||||
|
.\}
|
||||||
|
.nf
|
||||||
|
The following options control whether to output a terminal bell on certain events\&.
|
||||||
|
Some terminals mark the window as urgent when a bell is received\&. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window\&.
|
||||||
|
These options don\*(Aqt affect the "alerts" option\&.
|
||||||
|
.fi
|
||||||
|
.if n \{\
|
||||||
|
.RE
|
||||||
|
.\}
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBbell_on_message\fR
|
||||||
|
.RS 4
|
||||||
|
Enable/Disable the terminal bell when receiving a message\&. true or false
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBbell_on_filetrans\fR
|
||||||
|
.RS 4
|
||||||
|
Enable/Disable the terminal bell when receiving a filetransfer\&. true or false
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBbell_on_filetrans_accept\fR
|
||||||
|
.RS 4
|
||||||
|
Enable/Disable the terminal bell when a filetransfer was accepted\&. true or false
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBbell_on_invite\fR
|
||||||
|
.RS 4
|
||||||
|
Enable/Disable the terminal bell when receiving a group/call invite\&. true or false
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBaudio\fR
|
||||||
|
.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
|
||||||
|
\fBavatar_path\fR
|
||||||
|
.RS 4
|
||||||
|
Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBautorun_path\fR
|
||||||
|
.RS 4
|
||||||
|
Path for any scripts that should be run on startup
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBchatlogs_path\fR
|
||||||
|
.RS 4
|
||||||
|
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBpassword_eval\fR
|
||||||
|
.RS 4
|
||||||
|
Replace password prompt by running this command and using its output as the password\&.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBsounds\fR
|
||||||
|
.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
|
||||||
|
\fBnotif_error\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
|
||||||
|
.PP
|
||||||
|
\fBtoggle_peerlist\fR
|
||||||
|
.RS 4
|
||||||
|
Toggle the peer list on and off\&.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBtoggle_paste_mode\fR
|
||||||
|
.RS 4
|
||||||
|
Toggle treating linebreaks as enter key press\&.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.SH "FILES"
|
||||||
|
.PP
|
||||||
|
~/\&.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/JFreegman/toxic
|
||||||
|
.sp
|
||||||
|
IRC channel: chat\&.freenode\&.net#tox
|
||||||
|
.SH "AUTHORS"
|
||||||
|
.sp
|
||||||
|
JFreegman <JFreegman@gmail\&.com>
|
||||||
|
254
doc/toxic.conf.5.asc
Normal file
254
doc/toxic.conf.5.asc
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
*time_format*;;
|
||||||
|
Select between 24 and 12 hour time. Specify 24 or 12. Setting
|
||||||
|
timestamp_format and log_timestamp_format will override this setting.
|
||||||
|
|
||||||
|
*timestamp_format*;;
|
||||||
|
Time format string for the interface enclosed by double quotes.
|
||||||
|
See *date*(1)
|
||||||
|
|
||||||
|
*log_timestamp_format*;;
|
||||||
|
Time format string for logging enclosed by double quotes.
|
||||||
|
See *date*(1)
|
||||||
|
|
||||||
|
*alerts*;;
|
||||||
|
Enable or disable acoustic 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
|
||||||
|
|
||||||
|
*show_typing_other*;;
|
||||||
|
Show when others are typing in a 1-on-1 chat. true or false
|
||||||
|
|
||||||
|
*show_typing_self*;;
|
||||||
|
Show others when you're typing in a 1-on-1 chat. true or false
|
||||||
|
|
||||||
|
*show_welcome_msg*;;
|
||||||
|
Show welcome message on startup. true or false
|
||||||
|
|
||||||
|
*show_connection_msg*;;
|
||||||
|
Enable friend connection change notifications. true or false
|
||||||
|
|
||||||
|
*nodelist_update_freq*;;
|
||||||
|
How often in days to update the DHT nodes list. (0 to disable updates)
|
||||||
|
|
||||||
|
*history_size*;;
|
||||||
|
Maximum lines for chat window history. Integer value. (for example: 700)
|
||||||
|
|
||||||
|
*line_join*;;
|
||||||
|
Indicator for when someone connects or joins a group.
|
||||||
|
Three characters max for line_ settings.
|
||||||
|
|
||||||
|
*line_quit*;;
|
||||||
|
Indicator for when someone disconnects or leaves a group.
|
||||||
|
|
||||||
|
*line_alert*;;
|
||||||
|
Indicator for alert messages.
|
||||||
|
|
||||||
|
*line_normal*;;
|
||||||
|
Indicator for normal messages.
|
||||||
|
|
||||||
|
*mplex_away*;;
|
||||||
|
Set user status when attaching and detaching from GNU screen or tmux.
|
||||||
|
true or false
|
||||||
|
|
||||||
|
*mplex_away_note*;;
|
||||||
|
Status message to set when status is set to away due to screen/tmux
|
||||||
|
detach. When attaching, the status message is set back to the original
|
||||||
|
value.
|
||||||
|
|
||||||
|
The following options control whether to output a terminal bell on certain events.
|
||||||
|
Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window.
|
||||||
|
These options don't affect the "alerts" option.
|
||||||
|
|
||||||
|
*bell_on_message*;;
|
||||||
|
Enable/Disable the terminal bell when receiving a message. true or false
|
||||||
|
|
||||||
|
*bell_on_filetrans*;;
|
||||||
|
Enable/Disable the terminal bell when receiving a filetransfer. true or false
|
||||||
|
|
||||||
|
*bell_on_filetrans_accept*;;
|
||||||
|
Enable/Disable the terminal bell when a filetransfer was accepted. true or false
|
||||||
|
|
||||||
|
*bell_on_invite*;;
|
||||||
|
Enable/Disable the terminal bell when receiving a group/call invite. true or false
|
||||||
|
|
||||||
|
|
||||||
|
*audio*::
|
||||||
|
Configuration related to audio devices.
|
||||||
|
|
||||||
|
*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.
|
||||||
|
|
||||||
|
*avatar_path*;;
|
||||||
|
Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
|
||||||
|
|
||||||
|
*autorun_path*;;
|
||||||
|
Path for any scripts that should be run on startup
|
||||||
|
|
||||||
|
*chatlogs_path*;;
|
||||||
|
Default path for chatlogs. String value. Absolute path for chatlog files.
|
||||||
|
|
||||||
|
*password_eval*;;
|
||||||
|
Replace password prompt by running this command and using its output as
|
||||||
|
the password.
|
||||||
|
|
||||||
|
*sounds*::
|
||||||
|
Configuration related to notification sounds.
|
||||||
|
Special value "silent" can be used to disable a specific notification. +
|
||||||
|
Each value is a string which corresponds to the absolute path of a wav
|
||||||
|
sound file.
|
||||||
|
|
||||||
|
*notif_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.
|
||||||
|
|
||||||
|
*toggle_peerlist*;;
|
||||||
|
Toggle the peer list on and off.
|
||||||
|
|
||||||
|
*toggle_paste_mode*;;
|
||||||
|
Toggle treating linebreaks as enter key press.
|
||||||
|
|
||||||
|
|
||||||
|
FILES
|
||||||
|
-----
|
||||||
|
~/.config/tox/toxic.conf::
|
||||||
|
Main configuration file.
|
||||||
|
|
||||||
|
{datadir}/toxic.conf.example::
|
||||||
|
Configuration example.
|
||||||
|
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
*toxic*(1)
|
||||||
|
|
||||||
|
|
||||||
|
RESOURCES
|
||||||
|
---------
|
||||||
|
Project page: <https://github.com/JFreegman/toxic>
|
||||||
|
|
||||||
|
IRC channel: chat.freenode.net#tox
|
||||||
|
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
-------
|
||||||
|
JFreegman <JFreegman@gmail.com>
|
@ -1,8 +0,0 @@
|
|||||||
192.254.75.98 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
|
|
||||||
37.187.46.132 33445 A9D98212B3F972BD11DA52BEB0658C326FCCC1BFD49F347F9C2D3D8B61E1B927
|
|
||||||
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
|
|
||||||
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
|
||||||
37.187.20.216 33445 4FD54CFD426A338399767E56FD0F44F5E35FA8C38C8E87C8DC3FEAC0160F8E17
|
|
||||||
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
|
|
||||||
109.169.46.133 33445 7F31BFC93B8E4016A902144D0B110C3EA97CB7D43F1C4D21BCAE998A7C838821
|
|
||||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
|
1
misc/nameservers
Normal file
1
misc/nameservers
Normal file
@ -0,0 +1 @@
|
|||||||
|
toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25
|
@ -1,26 +1,123 @@
|
|||||||
# 24 or 12 hour time
|
// SAMPLE TOXIC CONFIGURATION
|
||||||
time:24;
|
// USES LIBCONFIG-ACCEPTED SYNTAX
|
||||||
|
|
||||||
# 1 to enable timestamps, 0 to disable
|
ui = {
|
||||||
timestamps:1;
|
// true to enable timestamps, false to disable
|
||||||
|
timestamps=true;
|
||||||
|
|
||||||
# 1 to enable autologging, 0 to disable
|
// true to enable acoustic alerts on messages, false to disable
|
||||||
autolog:0;
|
alerts=true;
|
||||||
|
|
||||||
# 1 to disbale terminal alerts on messages, 0 to enable
|
// Output a bell when receiving a message (see manpage)
|
||||||
alerts:1;
|
bell_on_message=true
|
||||||
|
|
||||||
# maximum lines for chat window history
|
// Output a bell when receiving a filetransfer (see manpage)
|
||||||
history_size:700;
|
bell_on_filetrans=true
|
||||||
|
|
||||||
# 1 to use native terminal colours, 0 to use toxic default colour theme
|
// Don't output a bell when a filetransfer was accepted (see manpage)
|
||||||
colour_theme:0;
|
bell_on_filetrans_accept=false
|
||||||
|
|
||||||
# preferred audio input device; numbers correspond to /lsdev in
|
// Output a bell when receiving a group/call invite (see manpage)
|
||||||
audio_in_dev:0;
|
bell_on_invite=true
|
||||||
|
|
||||||
# preferred audio output device; numbers correspond to /lsdev out
|
// true to use native terminal colours, false to use toxic default colour theme
|
||||||
audio_out_dev:0;
|
native_colors=false;
|
||||||
|
|
||||||
# preferred path for downloads
|
// true to enable autologging, false to disable
|
||||||
download_path:/home/USERNAME/Downloads/;
|
autolog=false;
|
||||||
|
|
||||||
|
// 24 or 12 hour time
|
||||||
|
time_format=24;
|
||||||
|
|
||||||
|
// Timestamp format string according to date/strftime format. Overrides time_format setting
|
||||||
|
timestamp_format="%H:%M:%S";
|
||||||
|
|
||||||
|
// true to show you when others are typing a message in 1-on-1 chats
|
||||||
|
show_typing_other=true;
|
||||||
|
|
||||||
|
// true to show others when you're typing a message in 1-on-1 chats
|
||||||
|
show_typing_self=true;
|
||||||
|
|
||||||
|
// true to show the welcome message on startup
|
||||||
|
show_welcome_msg=true;
|
||||||
|
|
||||||
|
// true to show friend connection change messages on the home screen
|
||||||
|
show_connection_msg=true;
|
||||||
|
|
||||||
|
// How often in days to update the DHT nodes list. (0 to disable updates)
|
||||||
|
nodeslist_update_freq=7;
|
||||||
|
|
||||||
|
// maximum lines for chat window history
|
||||||
|
history_size=700;
|
||||||
|
|
||||||
|
// Indicator for display when someone connects or joins a group.
|
||||||
|
line_join="-->";
|
||||||
|
|
||||||
|
// Indicator for display when someone disconnects or leaves a group.
|
||||||
|
line_quit="<--";
|
||||||
|
|
||||||
|
// Indicator for alert messages.
|
||||||
|
line_alert="-!-";
|
||||||
|
|
||||||
|
// Indicator for normal messages.
|
||||||
|
line_normal="---";
|
||||||
|
|
||||||
|
// true to change status based on screen/tmux attach/detach, false to disable
|
||||||
|
mplex_away=true;
|
||||||
|
|
||||||
|
// Status message to use when status set to away due to screen/tmux detach
|
||||||
|
mplex_away_note="Away from keyboard, be back soon!"
|
||||||
|
};
|
||||||
|
|
||||||
|
audio = {
|
||||||
|
// 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 your avatar (file must be a .png and cannot exceed 64 KiB)
|
||||||
|
// avatar_path="/home/USERNAME/Pictures/youravatar.png";
|
||||||
|
|
||||||
|
// Path for scripts that should be run on startup
|
||||||
|
// autorun_path="/home/USERNAME/toxic_scripts/";
|
||||||
|
|
||||||
|
// Path for chatlogs
|
||||||
|
// chatlogs_path="/home/USERNAME/toxic_chatlogs/";
|
||||||
|
};
|
||||||
|
|
||||||
|
// To disable a sound set the path to "silent"
|
||||||
|
sounds = {
|
||||||
|
error="__DATADIR__/sounds/ToxicError.wav";
|
||||||
|
user_log_in="__DATADIR__/sounds/ToxicContactOnline.wav";
|
||||||
|
user_log_out="__DATADIR__/sounds/ToxicContactOffline.wav";
|
||||||
|
call_incoming="__DATADIR__/sounds/ToxicIncomingCall.wav";
|
||||||
|
call_outgoing="__DATADIR__/sounds/ToxicOutgoingCall.wav";
|
||||||
|
generic_message="__DATADIR__/sounds/ToxicRecvMessage.wav";
|
||||||
|
transfer_pending="__DATADIR__/sounds/ToxicTransferStart.wav";
|
||||||
|
transfer_completed="__DATADIR__/sounds/ToxicTransferComplete.wav";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
|
||||||
|
// Note: Ctrl+M does not work
|
||||||
|
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+]";
|
||||||
|
toggle_peerlist="Ctrl+b";
|
||||||
|
toggle_paste_mode="Ctrl+T";
|
||||||
|
};
|
||||||
|
11
misc/toxic.desktop
Normal file
11
misc/toxic.desktop
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Type=Application
|
||||||
|
Name=Toxic
|
||||||
|
Comment=A CLI based Tox client
|
||||||
|
TryExec=toxic
|
||||||
|
Exec=toxic
|
||||||
|
Icon=utilities-terminal
|
||||||
|
Categories=InstantMessaging;AudioVideo;Network;
|
||||||
|
Terminal=true
|
||||||
|
MimeType=x-scheme-handler/tox;
|
BIN
sounds/ToxicContactOffline.wav
Normal file
BIN
sounds/ToxicContactOffline.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicContactOnline.wav
Normal file
BIN
sounds/ToxicContactOnline.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicError.wav
Normal file
BIN
sounds/ToxicError.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicIncomingCall.wav
Normal file
BIN
sounds/ToxicIncomingCall.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicOutgoingCall.wav
Normal file
BIN
sounds/ToxicOutgoingCall.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicRecvMessage.wav
Normal file
BIN
sounds/ToxicRecvMessage.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicTransferComplete.wav
Normal file
BIN
sounds/ToxicTransferComplete.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicTransferStart.wav
Normal file
BIN
sounds/ToxicTransferStart.wav
Normal file
Binary file not shown.
2
sounds/license
Normal file
2
sounds/license
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ToxicError.wav, ToxicRecvMessage.wav, ToxicContactOffline.wav, ToxicIncomingCall.wav, ToxicTransferComplete.wav, ToxicContactOnline.wav, ToxicOutgoingCall.wav and ToxicTransferStart.wav
|
||||||
|
are licensed under the "Creative Commons Attribution 3.0 Unported". All credit attributed to Jfreegman.
|
208
src/api.c
Normal file
208
src/api.c
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
/* api.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#include "execute.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "message_queue.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "python_api.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "toxic_strings.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
Tox *user_tox;
|
||||||
|
static WINDOW *cur_window;
|
||||||
|
static ToxWindow *self_window;
|
||||||
|
|
||||||
|
extern FriendsList Friends;
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
|
void api_display(const char *const msg)
|
||||||
|
{
|
||||||
|
if (msg == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self_window = get_active_window();
|
||||||
|
line_info_add(self_window, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
FriendsList api_get_friendslist(void)
|
||||||
|
{
|
||||||
|
return Friends;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *api_get_nick(void)
|
||||||
|
{
|
||||||
|
size_t len = tox_self_get_name_size(user_tox);
|
||||||
|
uint8_t *name = malloc(len + 1);
|
||||||
|
|
||||||
|
if (name == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
tox_self_get_name(user_tox, name);
|
||||||
|
name[len] = '\0';
|
||||||
|
return (char *) name;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_USER_STATUS api_get_status(void)
|
||||||
|
{
|
||||||
|
return tox_self_get_status(user_tox);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *api_get_status_message(void)
|
||||||
|
{
|
||||||
|
size_t len = tox_self_get_status_message_size(user_tox);
|
||||||
|
uint8_t *status = malloc(len + 1);
|
||||||
|
|
||||||
|
if (status == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
tox_self_get_status_message(user_tox, status);
|
||||||
|
status[len] = '\0';
|
||||||
|
return (char *) status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void api_send(const char *msg)
|
||||||
|
{
|
||||||
|
if (msg == NULL || self_window->chatwin->cqueue == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char *name = api_get_nick();
|
||||||
|
char timefrmt[TIME_STR_SIZE];
|
||||||
|
|
||||||
|
if (name == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self_window = get_active_window();
|
||||||
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
|
strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line));
|
||||||
|
add_line_to_hist(self_window->chatwin);
|
||||||
|
line_info_add(self_window, timefrmt, name, NULL, OUT_MSG, 0, 0, "%s", msg);
|
||||||
|
cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG,
|
||||||
|
self_window->chatwin->hst->line_end->id + 1);
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void api_execute(const char *input, int mode)
|
||||||
|
{
|
||||||
|
self_window = get_active_window();
|
||||||
|
execute(cur_window, self_window, user_tox, input, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
return do_python_command(num_args, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_registered_handlers(void)
|
||||||
|
{
|
||||||
|
return python_num_registered_handlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
int help_max_width(void)
|
||||||
|
{
|
||||||
|
return python_help_max_width();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_handler_help(WINDOW *win)
|
||||||
|
{
|
||||||
|
python_draw_handler_help(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
const char *error_str;
|
||||||
|
|
||||||
|
cur_window = window;
|
||||||
|
self_window = self;
|
||||||
|
|
||||||
|
if ( argc != 1 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Path must be specified!";
|
||||||
|
else error_str = "Only one argument allowed!";
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = fopen(argv[1], "r");
|
||||||
|
|
||||||
|
if ( fp == NULL ) {
|
||||||
|
error_str = "Path does not exist!";
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_python(fp, argv[1]);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke_autoruns(WINDOW *window, ToxWindow *self)
|
||||||
|
{
|
||||||
|
struct dirent *dir;
|
||||||
|
char abspath_buf[PATH_MAX + 1], err_buf[PATH_MAX + 1];
|
||||||
|
size_t path_len;
|
||||||
|
DIR *d;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (user_settings->autorun_path[0] == '\0')
|
||||||
|
return;
|
||||||
|
|
||||||
|
d = opendir(user_settings->autorun_path);
|
||||||
|
|
||||||
|
if (d == NULL) {
|
||||||
|
snprintf(err_buf, PATH_MAX + 1, "Autorun path does not exist: %s", user_settings->autorun_path);
|
||||||
|
api_display(err_buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_window = window;
|
||||||
|
self_window = self;
|
||||||
|
|
||||||
|
while ((dir = readdir(d)) != NULL) {
|
||||||
|
path_len = strlen(dir->d_name);
|
||||||
|
|
||||||
|
if (!strcmp(dir->d_name + path_len - 3, ".py")) {
|
||||||
|
snprintf(abspath_buf, PATH_MAX + 1, "%s%s", user_settings->autorun_path, dir->d_name);
|
||||||
|
fp = fopen(abspath_buf, "r");
|
||||||
|
|
||||||
|
if (fp == NULL) {
|
||||||
|
snprintf(err_buf, PATH_MAX + 1, "Invalid path: %s", abspath_buf);
|
||||||
|
api_display(err_buf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_python(fp, abspath_buf);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(d);
|
||||||
|
}
|
42
src/api.h
Normal file
42
src/api.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* api.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef API_H
|
||||||
|
#define API_H
|
||||||
|
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
void api_display(const char *const msg);
|
||||||
|
FriendsList api_get_friendslist(void);
|
||||||
|
char *api_get_nick(void);
|
||||||
|
TOX_USER_STATUS api_get_status(void);
|
||||||
|
char *api_get_status_message(void);
|
||||||
|
void api_send(const char *msg);
|
||||||
|
void api_execute(const char *input, int mode);
|
||||||
|
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE]);
|
||||||
|
int num_registered_handlers(void);
|
||||||
|
int help_max_width(void);
|
||||||
|
void draw_handler_help(WINDOW *win);
|
||||||
|
void invoke_autoruns(WINDOW *w, ToxWindow *self);
|
||||||
|
|
||||||
|
#endif /* #define API_H */
|
828
src/audio_call.c
828
src/audio_call.c
File diff suppressed because it is too large
Load Diff
@ -20,14 +20,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _audio_h
|
#ifndef AUDIO_CALL_H
|
||||||
#define _audio_h
|
#define AUDIO_CALL_H
|
||||||
|
|
||||||
#include <tox/toxav.h>
|
#include <tox/toxav.h>
|
||||||
|
|
||||||
#include "device.h"
|
#include "audio_device.h"
|
||||||
|
|
||||||
#define VAD_THRESHOLD_DEFAULT 40.0
|
#define MAX_CALLS 10
|
||||||
|
|
||||||
typedef enum _AudioError {
|
typedef enum _AudioError {
|
||||||
ae_None = 0,
|
ae_None = 0,
|
||||||
@ -36,12 +36,58 @@ typedef enum _AudioError {
|
|||||||
ae_StartingCoreAudio = 1 << 2
|
ae_StartingCoreAudio = 1 << 2
|
||||||
} AudioError;
|
} AudioError;
|
||||||
|
|
||||||
/* You will have to pass pointer to first member of 'windows'
|
#ifdef VIDEO
|
||||||
* declared in windows.c otherwise undefined behaviour will
|
typedef enum _VideoError {
|
||||||
*/
|
ve_None = 0,
|
||||||
ToxAv *init_audio(ToxWindow *self, Tox *tox);
|
ve_StartingCaptureDevice = 1 << 0,
|
||||||
void terminate_audio();
|
ve_StartingOutputDevice = 1 << 1,
|
||||||
|
ve_StartingCoreVideo = 1 << 2
|
||||||
|
} VideoError;
|
||||||
|
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
typedef struct Call {
|
||||||
|
pthread_t ttid; /* Transmission thread id */
|
||||||
|
bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
|
||||||
|
uint32_t in_idx, out_idx; /* Audio Index */
|
||||||
|
#ifdef VIDEO
|
||||||
|
uint32_t vin_idx, vout_idx; /* Video Index */
|
||||||
|
#endif /* VIDEO */
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
} Call;
|
||||||
|
|
||||||
|
struct CallControl {
|
||||||
|
AudioError audio_errors;
|
||||||
|
#ifdef VIDEO
|
||||||
|
VideoError video_errors;
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
ToxAV *av;
|
||||||
|
ToxWindow *prompt;
|
||||||
|
|
||||||
|
Call calls[MAX_CALLS];
|
||||||
|
uint32_t call_state;
|
||||||
|
bool pending_call;
|
||||||
|
bool audio_enabled;
|
||||||
|
bool video_enabled;
|
||||||
|
|
||||||
|
uint32_t audio_bit_rate;
|
||||||
|
int32_t audio_frame_duration;
|
||||||
|
uint32_t audio_sample_rate;
|
||||||
|
uint8_t audio_channels;
|
||||||
|
|
||||||
|
uint32_t video_bit_rate;
|
||||||
|
int32_t video_frame_duration;
|
||||||
|
|
||||||
|
} CallControl;
|
||||||
|
|
||||||
|
struct CallControl CallControl;
|
||||||
|
|
||||||
|
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
|
||||||
|
ToxAV *init_audio(ToxWindow *self, Tox *tox);
|
||||||
|
void terminate_audio();
|
||||||
|
int start_transmission(ToxWindow *self, Call *call);
|
||||||
|
int stop_transmission(Call *call, uint32_t friend_number);
|
||||||
void stop_current_call(ToxWindow *self);
|
void stop_current_call(ToxWindow *self);
|
||||||
|
|
||||||
#endif /* _audio_h */
|
#endif /* AUDIO_CALL_H */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* device.c
|
/* audio_device.c
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
@ -20,8 +20,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "audio_device.h"
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
#include "audio_call.h"
|
#include "audio_call.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include <OpenAL/al.h>
|
#include <OpenAL/al.h>
|
||||||
@ -29,44 +35,53 @@
|
|||||||
#else
|
#else
|
||||||
#include <AL/al.h>
|
#include <AL/al.h>
|
||||||
#include <AL/alc.h>
|
#include <AL/alc.h>
|
||||||
#endif
|
/* compatibility with older versions of OpenAL */
|
||||||
|
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||||
|
#include <AL/alext.h>
|
||||||
|
#endif /* ALC_ALL_DEVICES_SPECIFIER */
|
||||||
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include <tox/toxav.h>
|
|
||||||
|
|
||||||
#define openal_bufs 5
|
|
||||||
#define sample_rate 48000
|
|
||||||
#define inline__ inline __attribute__((always_inline))
|
#define inline__ inline __attribute__((always_inline))
|
||||||
#define frame_size (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000)
|
|
||||||
|
|
||||||
typedef struct _Device {
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
|
typedef struct Device {
|
||||||
ALCdevice *dhndl; /* Handle of device selected/opened */
|
ALCdevice *dhndl; /* Handle of device selected/opened */
|
||||||
ALCcontext *ctx; /* Device context */
|
ALCcontext *ctx; /* Device context */
|
||||||
DataHandleCallback cb; /* Use this to handle data from input device usually */
|
DataHandleCallback cb; /* Use this to handle data from input device usually */
|
||||||
void* cb_data; /* Data to be passed to callback */
|
void *cb_data; /* Data to be passed to callback */
|
||||||
int32_t call_idx; /* ToxAv call index */
|
int32_t friend_number; /* ToxAV friend number */
|
||||||
|
|
||||||
uint32_t source, buffers[openal_bufs]; /* Playback source/buffers */
|
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
|
||||||
size_t ref_count;
|
uint32_t ref_count;
|
||||||
int32_t selection;
|
int32_t selection;
|
||||||
_Bool enable_VAD;
|
bool enable_VAD;
|
||||||
_Bool muted;
|
bool muted;
|
||||||
float VAD_treshold; /* 40 is usually recommended value */
|
|
||||||
pthread_mutex_t mutex[1];
|
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;
|
} Device;
|
||||||
|
|
||||||
const char *ddevice_names[2]; /* Default device */
|
const char *ddevice_names[2]; /* Default device */
|
||||||
const char *devices_names[2][MAX_DEVICES]; /* Container of available devices */
|
const char *devices_names[2][MAX_DEVICES]; /* Container of available devices */
|
||||||
static int size[2]; /* Size of above containers */
|
static int size[2]; /* Size of above containers */
|
||||||
Device *running[2][MAX_DEVICES]; /* Running devices */
|
Device *running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
|
||||||
uint32_t primary_device[2]; /* Primary device */
|
uint32_t primary_device[2]; /* Primary device */
|
||||||
|
|
||||||
static ToxAv* av = NULL;
|
#ifdef AUDIO
|
||||||
|
static ToxAV *av = NULL;
|
||||||
|
#endif /* AUDIO */
|
||||||
|
|
||||||
/* q_mutex */
|
/* q_mutex */
|
||||||
#define lock pthread_mutex_lock(&mutex)
|
#define lock pthread_mutex_lock(&mutex)
|
||||||
@ -74,18 +89,21 @@ static ToxAv* av = NULL;
|
|||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
|
|
||||||
_Bool thread_running = _True,
|
bool thread_running = true,
|
||||||
thread_paused = _True; /* Thread control */
|
thread_paused = true; /* Thread control */
|
||||||
|
|
||||||
void* thread_poll(void*);
|
void *thread_poll(void *);
|
||||||
/* Meet devices */
|
/* Meet devices */
|
||||||
DeviceError init_devices(ToxAv* av_)
|
#ifdef AUDIO
|
||||||
|
DeviceError init_devices(ToxAV *av_)
|
||||||
|
#else
|
||||||
|
DeviceError init_devices()
|
||||||
|
#endif /* AUDIO */
|
||||||
{
|
{
|
||||||
const char *stringed_device_list;
|
const char *stringed_device_list;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
size[input] = 0;
|
size[input] = 0;
|
||||||
|
|
||||||
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
|
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
|
||||||
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
||||||
|
|
||||||
@ -95,10 +113,14 @@ DeviceError init_devices(ToxAv* av_)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
size[output] = 0;
|
size[output] = 0;
|
||||||
if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) {
|
|
||||||
|
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
|
||||||
|
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
||||||
|
else
|
||||||
|
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||||
|
|
||||||
|
if (stringed_device_list) {
|
||||||
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||||
|
|
||||||
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
|
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
|
||||||
@ -108,35 +130,43 @@ DeviceError init_devices(ToxAv* av_)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start poll thread
|
// Start poll thread
|
||||||
|
if (pthread_mutex_init(&mutex, NULL) != 0)
|
||||||
pthread_mutex_init(&mutex, NULL);
|
return de_InternalError;
|
||||||
|
|
||||||
pthread_t thread_id;
|
pthread_t thread_id;
|
||||||
|
|
||||||
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
|
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
|
||||||
return de_InternalError;
|
return de_InternalError;
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
av = av_;
|
av = av_;
|
||||||
|
#endif /* AUDIO */
|
||||||
|
|
||||||
return (DeviceError) ae_None;
|
return (DeviceError) de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError terminate_devices()
|
DeviceError terminate_devices()
|
||||||
{
|
{
|
||||||
/* Cleanup if needed */
|
/* Cleanup if needed */
|
||||||
|
lock;
|
||||||
thread_running = false;
|
thread_running = false;
|
||||||
|
unlock;
|
||||||
|
|
||||||
usleep(20000);
|
usleep(20000);
|
||||||
|
|
||||||
pthread_mutex_destroy(&mutex);
|
if (pthread_mutex_destroy(&mutex) != 0)
|
||||||
|
return (DeviceError) de_InternalError;
|
||||||
|
|
||||||
return (DeviceError) ae_None;
|
return (DeviceError) de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
||||||
{
|
{
|
||||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||||
|
|
||||||
lock;
|
lock;
|
||||||
|
|
||||||
Device* device = running[type][device_idx];
|
Device *device = running[type][device_idx];
|
||||||
|
|
||||||
if (!device) {
|
if (!device) {
|
||||||
unlock;
|
unlock;
|
||||||
@ -149,12 +179,14 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
|||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
|
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
|
||||||
{
|
{
|
||||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||||
|
|
||||||
lock;
|
lock;
|
||||||
|
|
||||||
Device* device = running[input][device_idx];
|
Device *device = running[input][device_idx];
|
||||||
|
|
||||||
if (!device) {
|
if (!device) {
|
||||||
unlock;
|
unlock;
|
||||||
@ -166,59 +198,84 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
|
|||||||
unlock;
|
unlock;
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
DeviceError set_primary_device(DeviceType type, int32_t selection)
|
DeviceError set_primary_device(DeviceType type, int32_t selection)
|
||||||
{
|
{
|
||||||
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
|
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
|
||||||
|
|
||||||
primary_device[type] = selection;
|
primary_device[type] = selection;
|
||||||
|
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx)
|
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);
|
return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void get_primary_device_name(DeviceType type, char *buf, int size)
|
||||||
|
{
|
||||||
|
memcpy(buf, ddevice_names[type], size);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: generate buffers separately
|
// TODO: generate buffers separately
|
||||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx)
|
DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate,
|
||||||
|
uint32_t frame_duration, uint8_t channels)
|
||||||
{
|
{
|
||||||
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
|
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
|
||||||
|
|
||||||
|
if (channels != 1 && channels != 2) return de_UnsupportedMode;
|
||||||
|
|
||||||
lock;
|
lock;
|
||||||
|
|
||||||
|
const uint32_t frame_size = (sample_rate * frame_duration / 1000);
|
||||||
|
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; i ++);
|
|
||||||
|
|
||||||
if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; }
|
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i);
|
||||||
else *device_idx = i;
|
|
||||||
|
|
||||||
Device* device = running[type][*device_idx] = calloc(1, sizeof(Device));;
|
if (i == MAX_DEVICES) {
|
||||||
device->selection = selection;
|
unlock;
|
||||||
|
return de_AllDevicesBusy;
|
||||||
|
} else *device_idx = i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
|
||||||
|
if ( running[type][i] && running[type][i]->selection == selection ) {
|
||||||
|
// printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl);
|
||||||
|
|
||||||
|
running[type][*device_idx] = running[type][i];
|
||||||
|
running[type][i]->ref_count ++;
|
||||||
|
|
||||||
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;
|
unlock;
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Device *device = running[type][*device_idx] = calloc(1, sizeof(Device));
|
||||||
|
device->selection = selection;
|
||||||
|
|
||||||
|
device->sample_rate = sample_rate;
|
||||||
|
device->frame_duration = frame_duration;
|
||||||
|
device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
||||||
|
|
||||||
|
if (pthread_mutex_init(device->mutex, NULL) != 0) {
|
||||||
|
free(device);
|
||||||
|
unlock;
|
||||||
|
return de_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
if (type == input) {
|
if (type == input) {
|
||||||
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
|
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
|
||||||
av_DefaultSettings.audio_sample_rate, AL_FORMAT_MONO16, frame_size * 2);
|
sample_rate, device->sound_mode, frame_size * 2);
|
||||||
device->VAD_treshold = VAD_THRESHOLD_DEFAULT;
|
#ifdef AUDIO
|
||||||
}
|
device->VAD_treshold = user_settings->VAD_treshold;
|
||||||
else {
|
#endif
|
||||||
|
} else {
|
||||||
device->dhndl = alcOpenDevice(devices_names[type][selection]);
|
device->dhndl = alcOpenDevice(devices_names[type][selection]);
|
||||||
|
|
||||||
if ( !device->dhndl ) {
|
if ( !device->dhndl ) {
|
||||||
free(device);
|
free(device);
|
||||||
running[type][*device_idx] = NULL;
|
running[type][*device_idx] = NULL;
|
||||||
@ -229,18 +286,18 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
|
|||||||
device->ctx = alcCreateContext(device->dhndl, NULL);
|
device->ctx = alcCreateContext(device->dhndl, NULL);
|
||||||
alcMakeContextCurrent(device->ctx);
|
alcMakeContextCurrent(device->ctx);
|
||||||
|
|
||||||
alGenBuffers(openal_bufs, device->buffers);
|
alGenBuffers(OPENAL_BUFS, device->buffers);
|
||||||
alGenSources((uint32_t)1, &device->source);
|
alGenSources((uint32_t)1, &device->source);
|
||||||
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
||||||
|
|
||||||
uint16_t zeros[frame_size];
|
uint16_t zeros[frame_size];
|
||||||
memset(zeros, 0, frame_size*2);
|
memset(zeros, 0, frame_size * 2);
|
||||||
|
|
||||||
for ( i =0; i < openal_bufs; ++i) {
|
for ( i = 0; i < OPENAL_BUFS; ++i ) {
|
||||||
alBufferData(device->buffers[i], AL_FORMAT_MONO16, zeros, frame_size*2, sample_rate);
|
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
alSourceQueueBuffers(device->source, openal_bufs, device->buffers);
|
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
|
||||||
alSourcePlay(device->source);
|
alSourcePlay(device->source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,10 +310,9 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
|
|||||||
|
|
||||||
if (type == input) {
|
if (type == input) {
|
||||||
alcCaptureStart(device->dhndl);
|
alcCaptureStart(device->dhndl);
|
||||||
thread_paused = _False;
|
thread_paused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_init(device->mutex, NULL);
|
|
||||||
unlock;
|
unlock;
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
@ -266,43 +322,41 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
|
|||||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||||
|
|
||||||
lock;
|
lock;
|
||||||
Device* device = running[type][device_idx];
|
Device *device = running[type][device_idx];
|
||||||
|
DeviceError rc = de_None;
|
||||||
|
|
||||||
if (!device) {
|
if (!device) {
|
||||||
unlock;
|
unlock;
|
||||||
return de_DeviceNotActive;
|
return de_DeviceNotActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !(device->ref_count--) ) {
|
running[type][device_idx] = NULL;
|
||||||
running[type][device_idx] = NULL;
|
|
||||||
unlock;
|
|
||||||
|
|
||||||
DeviceError rc = de_None;
|
|
||||||
|
|
||||||
|
if ( !device->ref_count ) {
|
||||||
if (type == input) {
|
if (type == input) {
|
||||||
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
|
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
|
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
|
||||||
|
|
||||||
alDeleteSources(1, &device->source);
|
alDeleteSources(1, &device->source);
|
||||||
alDeleteBuffers(openal_bufs, device->buffers);
|
alDeleteBuffers(OPENAL_BUFS, device->buffers);
|
||||||
|
|
||||||
|
alcMakeContextCurrent(NULL);
|
||||||
|
|
||||||
|
if ( device->ctx ) alcDestroyContext(device->ctx);
|
||||||
|
|
||||||
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
|
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
|
||||||
alcMakeContextCurrent(NULL);
|
|
||||||
if ( device->ctx ) alcDestroyContext(device->ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(device);
|
free(device);
|
||||||
return rc;
|
} else device->ref_count--;
|
||||||
}
|
|
||||||
|
|
||||||
unlock;
|
unlock;
|
||||||
|
return rc;
|
||||||
return de_None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD)
|
DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
|
||||||
|
void *data, bool enable_VAD)
|
||||||
{
|
{
|
||||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
||||||
return de_InvalidSelection;
|
return de_InvalidSelection;
|
||||||
@ -311,17 +365,18 @@ DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, Dat
|
|||||||
running[input][device_idx]->cb = callback;
|
running[input][device_idx]->cb = callback;
|
||||||
running[input][device_idx]->cb_data = data;
|
running[input][device_idx]->cb_data = data;
|
||||||
running[input][device_idx]->enable_VAD = enable_VAD;
|
running[input][device_idx]->enable_VAD = enable_VAD;
|
||||||
running[input][device_idx]->call_idx = call_idx;
|
running[input][device_idx]->friend_number = friend_number;
|
||||||
unlock;
|
unlock;
|
||||||
|
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline__ DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels)
|
inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
|
||||||
|
uint32_t sample_rate)
|
||||||
{
|
{
|
||||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||||
|
|
||||||
Device* device = running[output][device_idx];
|
Device *device = running[output][device_idx];
|
||||||
|
|
||||||
if (!device || device->muted) return de_DeviceNotActive;
|
if (!device || device->muted) return de_DeviceNotActive;
|
||||||
|
|
||||||
@ -333,33 +388,33 @@ inline__ DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t leng
|
|||||||
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
|
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
|
||||||
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
|
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
|
||||||
|
|
||||||
if(processed) {
|
if (processed) {
|
||||||
ALuint bufids[processed];
|
ALuint bufids[processed];
|
||||||
alSourceUnqueueBuffers(device->source, processed, bufids);
|
alSourceUnqueueBuffers(device->source, processed, bufids);
|
||||||
alDeleteBuffers(processed - 1, bufids + 1);
|
alDeleteBuffers(processed - 1, bufids + 1);
|
||||||
bufid = bufids[0];
|
bufid = bufids[0];
|
||||||
}
|
} else if (queued < 16) alGenBuffers(1, &bufid);
|
||||||
else if(queued < 16) alGenBuffers(1, &bufid);
|
|
||||||
else {
|
else {
|
||||||
pthread_mutex_unlock(device->mutex);
|
pthread_mutex_unlock(device->mutex);
|
||||||
return de_Busy;
|
return de_Busy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
alBufferData(bufid, AL_FORMAT_MONO16, data, lenght * 2 * channels, av_DefaultSettings.audio_sample_rate);
|
alBufferData(bufid, channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data, sample_count * 2 * channels,
|
||||||
|
sample_rate);
|
||||||
alSourceQueueBuffers(device->source, 1, &bufid);
|
alSourceQueueBuffers(device->source, 1, &bufid);
|
||||||
|
|
||||||
ALint state;
|
ALint state;
|
||||||
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
|
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
|
||||||
|
|
||||||
if(state != AL_PLAYING) alSourcePlay(device->source);
|
if (state != AL_PLAYING) alSourcePlay(device->source);
|
||||||
|
|
||||||
|
|
||||||
pthread_mutex_unlock(device->mutex);
|
pthread_mutex_unlock(device->mutex);
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* thread_poll (void* arg) // TODO: maybe use thread for every input source
|
void *thread_poll (void *arg) // TODO: maybe use thread for every input source
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* NOTE: We only need to poll input devices for data.
|
* NOTE: We only need to poll input devices for data.
|
||||||
@ -368,37 +423,53 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
|
|||||||
uint32_t i;
|
uint32_t i;
|
||||||
int32_t sample = 0;
|
int32_t sample = 0;
|
||||||
|
|
||||||
int f_size = frame_size;
|
|
||||||
|
|
||||||
while (thread_running)
|
while (1) {
|
||||||
{
|
lock;
|
||||||
if (thread_paused) usleep(10000); /* Wait for unpause. */
|
|
||||||
else
|
if (!thread_running) {
|
||||||
{
|
unlock;
|
||||||
for (i = 0; i < size[input]; i ++)
|
break;
|
||||||
{
|
}
|
||||||
|
|
||||||
|
bool paused = thread_paused;
|
||||||
|
unlock;
|
||||||
|
|
||||||
|
/* Wait for unpause. */
|
||||||
|
if (paused) {
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
for (i = 0; i < size[input]; ++i) {
|
||||||
lock;
|
lock;
|
||||||
if (running[input][i] != NULL)
|
|
||||||
{
|
if (running[input][i] != NULL) {
|
||||||
alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
|
alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
|
||||||
|
|
||||||
|
int f_size = (running[input][i]->sample_rate * running[input][i]->frame_duration / 1000);
|
||||||
|
|
||||||
if (sample < f_size) {
|
if (sample < f_size) {
|
||||||
unlock;
|
unlock;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Device* device = running[input][i];
|
|
||||||
|
|
||||||
int16_t frame[4096];
|
Device *device = running[input][i];
|
||||||
|
|
||||||
|
int16_t frame[16000];
|
||||||
alcCaptureSamples(device->dhndl, frame, f_size);
|
alcCaptureSamples(device->dhndl, frame, f_size);
|
||||||
|
|
||||||
if ( device->muted ||
|
if (device->muted) {
|
||||||
(device->enable_VAD && !toxav_has_activity(av, device->call_idx, frame, f_size, device->VAD_treshold)))
|
unlock;
|
||||||
{ unlock; continue; } /* Skip if no voice activity */
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
|
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock;
|
unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
usleep(5000);
|
usleep(5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -406,14 +477,12 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
|
|||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_devices(ToxWindow* self, DeviceType type)
|
void print_devices(ToxWindow *self, DeviceType type)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i;
|
||||||
for ( ; i < size[type]; i ++) {
|
|
||||||
char msg[MAX_STR_SIZE];
|
for (i = 0; i < size[type]; ++i)
|
||||||
snprintf(msg, sizeof(msg), "%d: %s", i, devices_names[type][i]);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]);
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -423,7 +492,7 @@ DeviceError selection_valid(DeviceType type, int32_t selection)
|
|||||||
return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
|
return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* get_device_callback_data(uint32_t device_idx)
|
void *get_device_callback_data(uint32_t device_idx)
|
||||||
{
|
{
|
||||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
||||||
return NULL;
|
return NULL;
|
@ -1,4 +1,4 @@
|
|||||||
/* device.h
|
/* audio_device.h
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
@ -26,16 +26,14 @@
|
|||||||
* Read from running input device(s) via select()/callback combo.
|
* Read from running input device(s) via select()/callback combo.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _device_h
|
#ifndef AUDIO_DEVICE_H
|
||||||
#define _device_h
|
#define AUDIO_DEVICE_H
|
||||||
|
|
||||||
|
#define OPENAL_BUFS 5
|
||||||
#define MAX_DEVICES 32
|
#define MAX_DEVICES 32
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
|
|
||||||
#define _True 1
|
|
||||||
#define _False 0
|
|
||||||
|
|
||||||
typedef enum DeviceType {
|
typedef enum DeviceType {
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
@ -50,35 +48,48 @@ typedef enum DeviceError {
|
|||||||
de_AllDevicesBusy = -5,
|
de_AllDevicesBusy = -5,
|
||||||
de_DeviceNotActive = -6,
|
de_DeviceNotActive = -6,
|
||||||
de_BufferError = -7,
|
de_BufferError = -7,
|
||||||
de_AlError = -8,
|
de_UnsupportedMode = -8,
|
||||||
|
de_AlError = -9,
|
||||||
} DeviceError;
|
} DeviceError;
|
||||||
|
|
||||||
typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data);
|
typedef void (*DataHandleCallback) (const int16_t *, uint32_t size, void *data);
|
||||||
|
|
||||||
|
|
||||||
DeviceError init_devices(ToxAv* av);
|
#ifdef AUDIO
|
||||||
|
DeviceError init_devices(ToxAV *av);
|
||||||
|
#else
|
||||||
|
DeviceError init_devices();
|
||||||
|
#endif /* AUDIO */
|
||||||
|
|
||||||
DeviceError terminate_devices();
|
DeviceError terminate_devices();
|
||||||
|
|
||||||
/* Callback handles ready data from INPUT device */
|
/* Callback handles ready data from INPUT device */
|
||||||
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD);
|
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
|
||||||
void* get_device_callback_data(uint32_t device_idx);
|
void *data, bool enable_VAD);
|
||||||
|
void *get_device_callback_data(uint32_t device_idx);
|
||||||
|
|
||||||
/* toggle device mute */
|
/* toggle device mute */
|
||||||
DeviceError device_mute(DeviceType type, uint32_t device_idx);
|
DeviceError device_mute(DeviceType type, uint32_t device_idx);
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
|
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
|
||||||
|
#endif
|
||||||
|
|
||||||
DeviceError set_primary_device(DeviceType type, int32_t selection);
|
DeviceError set_primary_device(DeviceType type, int32_t selection);
|
||||||
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx);
|
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
|
||||||
|
uint8_t channels);
|
||||||
/* Start device */
|
/* Start device */
|
||||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx);
|
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 */
|
/* Stop device */
|
||||||
DeviceError close_device(DeviceType type, uint32_t device_idx);
|
DeviceError close_device(DeviceType type, uint32_t device_idx);
|
||||||
|
|
||||||
/* Write data to device */
|
/* Write data to device */
|
||||||
DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels);
|
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels,
|
||||||
|
uint32_t sample_rate);
|
||||||
|
|
||||||
void print_devices(ToxWindow* self, DeviceType type);
|
void print_devices(ToxWindow *self, DeviceType type);
|
||||||
|
void get_primary_device_name(DeviceType type, char *buf, int size);
|
||||||
|
|
||||||
DeviceError selection_valid(DeviceType type, int32_t selection);
|
DeviceError selection_valid(DeviceType type, int32_t selection);
|
||||||
#endif /* _device_h */
|
#endif /* AUDIO_DEVICE_H */
|
313
src/autocomplete.c
Normal file
313
src/autocomplete.c
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
/* 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 match.
|
||||||
|
*
|
||||||
|
* Returns the length of the match.
|
||||||
|
*/
|
||||||
|
static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n)
|
||||||
|
{
|
||||||
|
if (n == 1) {
|
||||||
|
return snprintf(match, match_sz, "%s", matches[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
snprintf(match, match_sz, "%s", matches[0]);
|
||||||
|
match[i] = '\0';
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return snprintf(match, match_sz, "%s", matches[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* looks for all instances in list that begin with the last entered word in line according to pos,
|
||||||
|
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||||
|
with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
|
||||||
|
|
||||||
|
list is a pointer to the list of strings being compared, n_items is the number of items
|
||||||
|
in the list, and size is the size of each item in the list.
|
||||||
|
|
||||||
|
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||||
|
int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
const char *L = (char *) list;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* TODO: generalize this */
|
||||||
|
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|
||||||
|
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
dir_search = dir_search || !strncmp(ubuf, "/run", strlen("/run"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 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 = calloc(1, 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 (!sub[0]) {
|
||||||
|
free(sub);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int s_len = strlen(sub);
|
||||||
|
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) {
|
||||||
|
char str[MAX_CMDNAME_SIZE + 1];
|
||||||
|
snprintf(str, sizeof(str), "%s", &L[i * size]);
|
||||||
|
|
||||||
|
if (strncasecmp(str, sub, s_len) == 0)
|
||||||
|
strcpy(matches[n_matches++], str);
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches);
|
||||||
|
|
||||||
|
if (match_len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir_search) {
|
||||||
|
if (n_matches == 1)
|
||||||
|
endchrs = char_rfind(match, '.', match_len) ? "\"" : "/";
|
||||||
|
else
|
||||||
|
endchrs = "";
|
||||||
|
} else if (n_matches > 1) {
|
||||||
|
endchrs = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* put match in correct spot in buf and append endchars */
|
||||||
|
int n_endchrs = strlen(endchrs);
|
||||||
|
int strt = ctx->pos - s_len;
|
||||||
|
int diff = match_len - s_len + n_endchrs;
|
||||||
|
|
||||||
|
if (ctx->len + diff >= MAX_STR_SIZE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char tmpend[MAX_STR_SIZE];
|
||||||
|
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
|
||||||
|
|
||||||
|
if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(&ubuf[strt], match);
|
||||||
|
strcpy(&ubuf[strt + match_len], endchrs);
|
||||||
|
strcpy(&ubuf[strt + match_len + n_endchrs], tmpend);
|
||||||
|
|
||||||
|
/* convert to widechar and copy back to original buf */
|
||||||
|
wchar_t newbuf[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wcscpy(ctx->line, newbuf);
|
||||||
|
|
||||||
|
ctx->len += diff;
|
||||||
|
ctx->pos += diff;
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* transforms a tab complete starting with the shorthand "~" into the full home directory.*/
|
||||||
|
static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
char homedir[MAX_STR_SIZE] = {0};
|
||||||
|
get_home_dir(homedir, sizeof(homedir));
|
||||||
|
|
||||||
|
char newline[MAX_STR_SIZE];
|
||||||
|
snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1);
|
||||||
|
snprintf(path, pathsize, "%s", &newline[cmdlen]);
|
||||||
|
|
||||||
|
wchar_t wline[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int newlen = wcslen(wline);
|
||||||
|
|
||||||
|
if (ctx->len + newlen >= MAX_STR_SIZE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wmemcpy(ctx->line, wline, newlen + 1);
|
||||||
|
ctx->pos = newlen;
|
||||||
|
ctx->len = ctx->pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||||
|
|
||||||
|
if only one match, auto-complete line.
|
||||||
|
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
|
||||||
|
#define MAX_DIRS 512
|
||||||
|
|
||||||
|
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||||
|
{
|
||||||
|
char b_path[MAX_STR_SIZE];
|
||||||
|
char b_name[MAX_STR_SIZE];
|
||||||
|
char b_cmd[MAX_STR_SIZE];
|
||||||
|
const wchar_t *tmpline = &line[wcslen(cmd) + 2]; /* start after "/command \"" */
|
||||||
|
|
||||||
|
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (b_path[0] == '~')
|
||||||
|
complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
|
||||||
|
|
||||||
|
int si = char_rfind(b_path, '/', strlen(b_path));
|
||||||
|
|
||||||
|
if (!b_path[0]) { /* list everything in pwd */
|
||||||
|
b_path[0] = '.';
|
||||||
|
b_path[1] = '\0';
|
||||||
|
} else if (!si && b_path[0] != '/') { /* look for matches in pwd */
|
||||||
|
char tmp[MAX_STR_SIZE];
|
||||||
|
snprintf(tmp, sizeof(tmp), ".%s", b_path);
|
||||||
|
snprintf(b_path, sizeof(b_path), "%s", tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]);
|
||||||
|
b_path[si + 1] = '\0';
|
||||||
|
int b_name_len = strlen(b_name);
|
||||||
|
DIR *dp = opendir(b_path);
|
||||||
|
|
||||||
|
if (dp == NULL)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dp);
|
||||||
|
|
||||||
|
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
42
src/autocomplete.h
Normal 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 /command "<incomplete-dir>" line to matching directories.
|
||||||
|
|
||||||
|
if only one match, auto-complete line.
|
||||||
|
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
|
||||||
|
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd);
|
||||||
|
|
||||||
|
#endif /* #define AUTOCOMPLETE_H */
|
207
src/avatars.c
Normal file
207
src/avatars.c
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
/* avatars.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "file_transfers.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "avatars.h"
|
||||||
|
|
||||||
|
extern FriendsList Friends;
|
||||||
|
|
||||||
|
static struct Avatar {
|
||||||
|
char name[TOX_MAX_FILENAME_LENGTH + 1];
|
||||||
|
size_t name_len;
|
||||||
|
char path[PATH_MAX + 1];
|
||||||
|
size_t path_len;
|
||||||
|
off_t size;
|
||||||
|
} Avatar;
|
||||||
|
|
||||||
|
|
||||||
|
static void avatar_clear(void)
|
||||||
|
{
|
||||||
|
memset(&Avatar, 0, sizeof(struct Avatar));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sends avatar to friendnum.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int avatar_send(Tox *m, uint32_t friendnum)
|
||||||
|
{
|
||||||
|
TOX_ERR_FILE_SEND err;
|
||||||
|
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
|
||||||
|
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
|
||||||
|
|
||||||
|
if (Avatar.size == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (err != TOX_ERR_FILE_SEND_OK) {
|
||||||
|
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
|
||||||
|
|
||||||
|
if (!ft)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ft->file = fopen(Avatar.path, "r");
|
||||||
|
|
||||||
|
if (ft->file == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
|
||||||
|
ft->file_size = Avatar.size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sends avatar to all friends */
|
||||||
|
static void avatar_send_all(Tox *m)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < Friends.max_idx; ++i) {
|
||||||
|
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE)
|
||||||
|
avatar_send(m, Friends.list[i].num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets avatar to path and sends it to all online contacts.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int avatar_set(Tox *m, const char *path, size_t path_len)
|
||||||
|
{
|
||||||
|
if (path_len == 0 || path_len >= sizeof(Avatar.path))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
FILE *fp = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||||
|
|
||||||
|
if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
off_t size = file_size(path);
|
||||||
|
|
||||||
|
if (size == 0 || size > MAX_AVATAR_FILE_SIZE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
get_file_name(Avatar.name, sizeof(Avatar.name), path);
|
||||||
|
Avatar.name_len = strlen(Avatar.name);
|
||||||
|
snprintf(Avatar.path, sizeof(Avatar.path), "%s", path);
|
||||||
|
Avatar.path_len = path_len;
|
||||||
|
Avatar.size = size;
|
||||||
|
|
||||||
|
avatar_send_all(m);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unsets avatar and sends to all online contacts.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
void avatar_unset(Tox *m)
|
||||||
|
{
|
||||||
|
avatar_clear();
|
||||||
|
avatar_send_all(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control)
|
||||||
|
{
|
||||||
|
switch (control) {
|
||||||
|
case TOX_FILE_CONTROL_RESUME:
|
||||||
|
if (ft->state == FILE_TRANSFER_PENDING) {
|
||||||
|
ft->state = FILE_TRANSFER_STARTED;
|
||||||
|
} else if (ft->state == FILE_TRANSFER_PAUSED) {
|
||||||
|
ft->state = FILE_TRANSFER_STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_FILE_CONTROL_PAUSE:
|
||||||
|
ft->state = FILE_TRANSFER_PAUSED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_FILE_CONTROL_CANCEL:
|
||||||
|
close_file_transfer(NULL, m, ft, -1, NULL, silent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length)
|
||||||
|
{
|
||||||
|
if (ft->state != FILE_TRANSFER_STARTED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (length == 0) {
|
||||||
|
close_file_transfer(NULL, m, ft, -1, NULL, silent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ft->file == NULL) {
|
||||||
|
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ft->position != position) {
|
||||||
|
if (fseek(ft->file, position, SEEK_SET) == -1) {
|
||||||
|
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ft->position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t send_data[length];
|
||||||
|
size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
|
||||||
|
|
||||||
|
if (send_length != length) {
|
||||||
|
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FILE_SEND_CHUNK err;
|
||||||
|
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
|
||||||
|
fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
|
||||||
|
|
||||||
|
ft->position += send_length;
|
||||||
|
ft->last_keep_alive = get_unix_time();
|
||||||
|
}
|
52
src/avatars.h
Normal file
52
src/avatars.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/* avatars.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AVATARS_H
|
||||||
|
#define AVATARS_H
|
||||||
|
|
||||||
|
#define MAX_AVATAR_FILE_SIZE 65536
|
||||||
|
|
||||||
|
/* Sends avatar to friendnum.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int avatar_send(Tox *m, uint32_t friendnum);
|
||||||
|
|
||||||
|
/* Sets avatar to path and sends it to all online contacts.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int avatar_set(Tox *m, const char *path, size_t length);
|
||||||
|
|
||||||
|
/* Unsets avatar and sends to all online contacts.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
void avatar_unset(Tox *m);
|
||||||
|
|
||||||
|
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
|
||||||
|
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control);
|
||||||
|
|
||||||
|
#endif /* AVATARS_H */
|
586
src/bootstrap.c
Normal file
586
src/bootstrap.c
Normal file
@ -0,0 +1,586 @@
|
|||||||
|
/* bootstrap.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "configdir.h"
|
||||||
|
#include "curl_util.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
extern struct arg_opts arg_opts;
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
extern struct Winthread Winthread;
|
||||||
|
|
||||||
|
/* URL that we get the JSON encoded nodes list from. */
|
||||||
|
#define NODES_LIST_URL "https://nodes.tox.chat/json"
|
||||||
|
|
||||||
|
#define DEFAULT_NODES_FILENAME "DHTnodes.json"
|
||||||
|
|
||||||
|
/* Time to wait between bootstrap attempts */
|
||||||
|
#define TRY_BOOTSTRAP_INTERVAL 5
|
||||||
|
|
||||||
|
/* Number of nodes to bootstrap to per try */
|
||||||
|
#define NUM_BOOTSTRAP_NODES 5
|
||||||
|
|
||||||
|
/* Number of seconds since last successful ping before we consider a node offline */
|
||||||
|
#define NODE_OFFLINE_TIMOUT (60*60*24*2)
|
||||||
|
|
||||||
|
#define IP_MAX_SIZE 45
|
||||||
|
#define IP_MIN_SIZE 7
|
||||||
|
#define PORT_MAX_SIZE 5
|
||||||
|
|
||||||
|
#define LAST_SCAN_JSON_KEY "\"last_scan\":"
|
||||||
|
#define LAST_SCAN_JSON_KEY_LEN (sizeof(LAST_SCAN_JSON_KEY) - 1)
|
||||||
|
|
||||||
|
#define IPV4_JSON_KEY "\"ipv4\":\""
|
||||||
|
#define IPV4_JSON_KEY_LEN (sizeof(IPV4_JSON_KEY) - 1)
|
||||||
|
|
||||||
|
#define IPV6_JSON_KEY "\"ipv6\":\""
|
||||||
|
#define IPV6_JSON_KEY_LEN (sizeof(IPV6_JSON_KEY) - 1)
|
||||||
|
|
||||||
|
#define PORT_JSON_KEY "\"port\":"
|
||||||
|
#define PORT_JSON_KEY_LEN (sizeof(PORT_JSON_KEY) - 1)
|
||||||
|
|
||||||
|
#define PK_JSON_KEY "\"public_key\":\""
|
||||||
|
#define PK_JSON_KEY_LEN (sizeof(PK_JSON_KEY) - 1)
|
||||||
|
|
||||||
|
#define LAST_PING_JSON_KEY "\"last_ping\":"
|
||||||
|
#define LAST_PING_JSON_KEY_LEN (sizeof(LAST_PING_JSON_KEY) - 1)
|
||||||
|
|
||||||
|
/* Maximum allowable size of the nodes list */
|
||||||
|
#define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE)
|
||||||
|
|
||||||
|
|
||||||
|
static struct Thread_Data {
|
||||||
|
pthread_t tid;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
volatile bool active;
|
||||||
|
} thread_data;
|
||||||
|
|
||||||
|
#define MAX_NODES 50
|
||||||
|
struct Node {
|
||||||
|
char ip4[IP_MAX_SIZE + 1];
|
||||||
|
bool have_ip4;
|
||||||
|
|
||||||
|
char ip6[IP_MAX_SIZE + 1];
|
||||||
|
bool have_ip6;
|
||||||
|
|
||||||
|
char key[TOX_PUBLIC_KEY_SIZE];
|
||||||
|
uint16_t port;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct DHT_Nodes {
|
||||||
|
struct Node list[MAX_NODES];
|
||||||
|
size_t count;
|
||||||
|
time_t last_updated;
|
||||||
|
} Nodes;
|
||||||
|
|
||||||
|
|
||||||
|
/* Determine if a node is offline by comparing the age of the nodeslist
|
||||||
|
* to the last time the node was successfully pinged.
|
||||||
|
*/
|
||||||
|
#define NODE_IS_OFFLINE(last_scan, last_ping) ((last_ping + NODE_OFFLINE_TIMOUT) <= (last_ping))
|
||||||
|
|
||||||
|
/* Return true if nodeslist pointed to by fp needs to be updated.
|
||||||
|
* This will be the case if the file is empty, has an invalid format,
|
||||||
|
* or if the file is older than the given timeout.
|
||||||
|
*/
|
||||||
|
static bool nodeslist_needs_update(const char *nodes_path)
|
||||||
|
{
|
||||||
|
if (user_settings->nodeslist_update_freq <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fp = fopen(nodes_path, "r+");
|
||||||
|
|
||||||
|
if (fp == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* last_scan value should be at beginning of file */
|
||||||
|
char line[LAST_SCAN_JSON_KEY_LEN + 32];
|
||||||
|
|
||||||
|
if (fgets(line, sizeof(line), fp) == NULL) {
|
||||||
|
fclose(fp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
const char *last_scan_val = strstr(line, LAST_SCAN_JSON_KEY);
|
||||||
|
|
||||||
|
if (last_scan_val == NULL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long int last_scan = strtoll(last_scan_val + LAST_SCAN_JSON_KEY_LEN, NULL, 10);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&thread_data.lock);
|
||||||
|
Nodes.last_updated = last_scan;
|
||||||
|
pthread_mutex_unlock(&thread_data.lock);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
bool is_timeout = timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
if (is_timeout) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fetches the JSON encoded DHT nodeslist from NODES_LIST_URL.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data)
|
||||||
|
{
|
||||||
|
CURL *c_handle = curl_easy_init();
|
||||||
|
|
||||||
|
if (c_handle == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = -1;
|
||||||
|
|
||||||
|
struct curl_slist *headers = NULL;
|
||||||
|
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||||
|
headers = curl_slist_append(headers, "charsets: utf-8");
|
||||||
|
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_URL, NODES_LIST_URL);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_HTTPGET, 1L);
|
||||||
|
|
||||||
|
int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);
|
||||||
|
|
||||||
|
if (proxy_ret != 0) {
|
||||||
|
fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
fprintf(stderr, "TLSv1.2 could not be set (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
fprintf(stderr, "Failed to set TLS cipher list (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_perform(c_handle);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
/* If system doesn't support any of the specified ciphers suites, fall back to default */
|
||||||
|
if (ret == CURLE_SSL_CIPHER) {
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL);
|
||||||
|
ret = curl_easy_perform(c_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
on_exit:
|
||||||
|
curl_slist_free_all(headers);
|
||||||
|
curl_easy_cleanup(c_handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempts to update the DHT nodeslist.
|
||||||
|
*
|
||||||
|
* Return 1 if list was updated successfully.
|
||||||
|
* Return 0 if list does not need to be updated.
|
||||||
|
* Return -1 if file cannot be opened.
|
||||||
|
* Return -2 if http lookup failed.
|
||||||
|
* Return -3 if http reponse was empty.
|
||||||
|
* Return -4 if data could not be written to disk.
|
||||||
|
*/
|
||||||
|
static int update_DHT_nodeslist(const char *nodes_path)
|
||||||
|
{
|
||||||
|
if (!nodeslist_needs_update(nodes_path)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fp = fopen(nodes_path, "r+");
|
||||||
|
|
||||||
|
if (fp == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Recv_Curl_Data recv_data;
|
||||||
|
|
||||||
|
memset(&recv_data, 0, sizeof(struct Recv_Curl_Data));
|
||||||
|
|
||||||
|
if (curl_fetch_nodes_JSON(&recv_data) == -1) {
|
||||||
|
fclose(fp);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recv_data.length == 0) {
|
||||||
|
fclose(fp);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(recv_data.data, recv_data.length, 1, fp) != 1) {
|
||||||
|
fclose(fp);
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_nodeslist_path(char *buf, size_t buf_size)
|
||||||
|
{
|
||||||
|
char *config_dir = NULL;
|
||||||
|
|
||||||
|
if (arg_opts.nodes_path[0]) {
|
||||||
|
snprintf(buf, buf_size, "%s", arg_opts.nodes_path);
|
||||||
|
} else if ((config_dir = get_user_config_dir()) != NULL) {
|
||||||
|
snprintf(buf, buf_size, "%s%s%s", config_dir, CONFIGDIR, DEFAULT_NODES_FILENAME);
|
||||||
|
free(config_dir);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, buf_size, "%s", DEFAULT_NODES_FILENAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if json encoded string s contains a valid IP address and puts address in ip_buf.
|
||||||
|
*
|
||||||
|
* ip_type should be set to 1 for ipv4 address, or 0 for ipv6 addresses.
|
||||||
|
* ip_buf must have room for at least IP_MAX_SIZE + 1 bytes.
|
||||||
|
*/
|
||||||
|
static bool extract_val_ip(const char *s, char *ip_buf, unsigned short int ip_type)
|
||||||
|
{
|
||||||
|
int ip_len = char_find(0, s, '"');
|
||||||
|
|
||||||
|
if (ip_len < IP_MIN_SIZE || ip_len > IP_MAX_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ip_buf, s, ip_len);
|
||||||
|
ip_buf[ip_len] = 0;
|
||||||
|
|
||||||
|
return (ip_type == 1) ? is_ip4_address(ip_buf) : is_ip6_address(ip_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extracts the port from json encoded string s.
|
||||||
|
*
|
||||||
|
* Return port number on success.
|
||||||
|
* Return 0 on failure.
|
||||||
|
*/
|
||||||
|
static uint16_t extract_val_port(const char *s)
|
||||||
|
{
|
||||||
|
long int port = strtol(s, NULL, 10);
|
||||||
|
return (port > 0 && port <= MAX_PORT_RANGE) ? port : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extracts the last pinged value from json encoded string s.
|
||||||
|
*
|
||||||
|
* Return timestamp on success.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
static long long int extract_val_last_pinged(const char *s)
|
||||||
|
{
|
||||||
|
long long int last_pinged = strtoll(s, NULL, 10);
|
||||||
|
return (last_pinged <= 0) ? -1 : last_pinged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extracts DHT public key from json encoded string s and puts key in key_buf.
|
||||||
|
* key_buf must have room for at least TOX_PUBLIC_KEY_SIZE * 2 + 1 bytes.
|
||||||
|
*
|
||||||
|
* Return number of bytes copied to key_buf on success.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
static int extract_val_pk(const char *s, char *key_buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
int key_len = char_find(0, s, '"');
|
||||||
|
|
||||||
|
if (key_len != TOX_PUBLIC_KEY_SIZE * 2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(key_buf, s, key_len);
|
||||||
|
key_buf[key_len] = 0;
|
||||||
|
|
||||||
|
return key_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extracts values from json formatted string, validats them, and puts them in node.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 if line is empty.
|
||||||
|
* Return -2 if line does not appear to be a valid nodes list entry.
|
||||||
|
* Return -3 if node appears to be offline.
|
||||||
|
* Return -4 if entry does not contain either a valid ipv4 or ipv6 address.
|
||||||
|
* Return -5 if port value is invalid.
|
||||||
|
* Return -6 if public key is invalid.
|
||||||
|
*/
|
||||||
|
static int extract_node(const char *line, struct Node *node)
|
||||||
|
{
|
||||||
|
if (!line) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ip4_start = strstr(line, IPV4_JSON_KEY);
|
||||||
|
const char *ip6_start = strstr(line, IPV6_JSON_KEY);
|
||||||
|
const char *port_start = strstr(line, PORT_JSON_KEY);
|
||||||
|
const char *key_start = strstr(line, PK_JSON_KEY);
|
||||||
|
const char *last_pinged_str = strstr(line, LAST_PING_JSON_KEY);
|
||||||
|
|
||||||
|
if (!ip4_start || !ip6_start || !port_start || !key_start || !last_pinged_str) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN);
|
||||||
|
|
||||||
|
if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
char ip4_string[IP_MAX_SIZE + 1];
|
||||||
|
bool have_ip4 = extract_val_ip(ip4_start + IPV4_JSON_KEY_LEN, ip4_string, 1);
|
||||||
|
|
||||||
|
char ip6_string[IP_MAX_SIZE + 1];
|
||||||
|
bool have_ip6 = extract_val_ip(ip6_start + IPV6_JSON_KEY_LEN, ip6_string, 0);
|
||||||
|
|
||||||
|
if (!have_ip6 && !have_ip4) {
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t port = extract_val_port(port_start + PORT_JSON_KEY_LEN);
|
||||||
|
|
||||||
|
if (port == 0) {
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||||
|
int key_len = extract_val_pk(key_start + PK_JSON_KEY_LEN, key_string);
|
||||||
|
|
||||||
|
if (key_len == -1) {
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hex_string_to_bin(key_string, key_len, node->key, TOX_PUBLIC_KEY_SIZE) == -1) {
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_ip4) {
|
||||||
|
snprintf(node->ip4, sizeof(node->ip4), "%s", ip4_string);
|
||||||
|
node->have_ip4 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_ip6) {
|
||||||
|
snprintf(node->ip6, sizeof(node->ip6), "%s", ip6_string);
|
||||||
|
node->have_ip6 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->port = port;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loads the DHT nodeslist to memory from json encoded nodes file. */
|
||||||
|
void *load_nodeslist_thread(void *data)
|
||||||
|
{
|
||||||
|
char nodes_path[PATH_MAX];
|
||||||
|
get_nodeslist_path(nodes_path, sizeof(nodes_path));
|
||||||
|
|
||||||
|
FILE *fp = NULL;
|
||||||
|
|
||||||
|
if (!file_exists(nodes_path)) {
|
||||||
|
if ((fp = fopen(nodes_path, "w+")) == NULL) {
|
||||||
|
fprintf(stderr, "nodeslist load error: failed to create file '%s'\n", nodes_path);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
} else if ((fp = fopen(nodes_path, "r+")) == NULL) {
|
||||||
|
fprintf(stderr, "nodeslist load error: failed to open file '%s'\n", nodes_path);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int update_err = update_DHT_nodeslist(nodes_path);
|
||||||
|
|
||||||
|
if (update_err < 0) {
|
||||||
|
fprintf(stderr, "update_DHT_nodeslist() failed with error %d\n", update_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[MAX_NODELIST_SIZE + 1];
|
||||||
|
|
||||||
|
if (fgets(line, sizeof(line), fp) == NULL) {
|
||||||
|
fclose(fp);
|
||||||
|
fprintf(stderr, "nodeslist load error: file empty.\n");
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t idx = 0;
|
||||||
|
const char *line_start = line;
|
||||||
|
|
||||||
|
while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY))) {
|
||||||
|
pthread_mutex_lock(&thread_data.lock);
|
||||||
|
idx = Nodes.count;
|
||||||
|
|
||||||
|
if (idx >= MAX_NODES) {
|
||||||
|
pthread_mutex_unlock(&thread_data.lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_node(line_start, &Nodes.list[idx]) == 0) {
|
||||||
|
++Nodes.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&thread_data.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If nodeslist does not contain any valid entries we set the last_scan value
|
||||||
|
* to 0 so that it will fetch a new list the next time this function is called.
|
||||||
|
*/
|
||||||
|
if (Nodes.count == 0) {
|
||||||
|
const char *s = "{\"last_scan\":0}";
|
||||||
|
rewind(fp);
|
||||||
|
fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails
|
||||||
|
fclose(fp);
|
||||||
|
fprintf(stderr, "nodeslist load error: List did not contain any valid entries.\n");
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
on_exit:
|
||||||
|
thread_data.active = false;
|
||||||
|
pthread_attr_destroy(&thread_data.attr);
|
||||||
|
pthread_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Creates a new thread that will load the DHT nodeslist to memory
|
||||||
|
* from json encoded nodes file obtained at NODES_LIST_URL. Only one
|
||||||
|
* thread may run at a time.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 if a thread is already active.
|
||||||
|
* Return -2 if mutex fails to init.
|
||||||
|
* Return -3 if pthread attribute fails to init.
|
||||||
|
* Return -4 if pthread fails to set detached state.
|
||||||
|
* Return -5 if thread creation fails.
|
||||||
|
*/
|
||||||
|
int load_DHT_nodeslist(void)
|
||||||
|
{
|
||||||
|
if (thread_data.active) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&thread_data.lock, NULL) != 0) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_attr_init(&thread_data.attr) != 0) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_attr_setdetachstate(&thread_data.attr, PTHREAD_CREATE_DETACHED) != 0) {
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_data.active = true;
|
||||||
|
|
||||||
|
if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) {
|
||||||
|
thread_data.active = false;
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */
|
||||||
|
static void DHT_bootstrap(Tox *m)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&thread_data.lock);
|
||||||
|
size_t num_nodes = Nodes.count;
|
||||||
|
pthread_mutex_unlock(&thread_data.lock);
|
||||||
|
|
||||||
|
if (num_nodes == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&thread_data.lock);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) {
|
||||||
|
struct Node *node = &Nodes.list[rand() % Nodes.count];
|
||||||
|
const char *addr = node->have_ip4 ? node->ip4 : node->ip6;
|
||||||
|
|
||||||
|
if (!addr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_BOOTSTRAP err;
|
||||||
|
tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
||||||
|
fprintf(stderr, "Failed to bootstrap %s:%d\n", addr, node->port);
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_add_tcp_relay(m, addr, node->port, (uint8_t *) node->key, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
||||||
|
fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&thread_data.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manages connection to the Tox DHT network. */
|
||||||
|
void do_tox_connection(Tox *m)
|
||||||
|
{
|
||||||
|
static time_t last_bootstrap_time = 0;
|
||||||
|
bool connected = tox_self_get_connection_status(m) != TOX_CONNECTION_NONE;
|
||||||
|
|
||||||
|
if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) {
|
||||||
|
DHT_bootstrap(m);
|
||||||
|
last_bootstrap_time = get_unix_time();
|
||||||
|
}
|
||||||
|
}
|
42
src/bootstrap.h
Normal file
42
src/bootstrap.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* bootstrap.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BOOTSTRAP_H
|
||||||
|
#define BOOTSTRAP_H
|
||||||
|
|
||||||
|
/* Manages connection to the Tox DHT network. */
|
||||||
|
void do_tox_connection(Tox *m);
|
||||||
|
|
||||||
|
/* Creates a new thread that will load the DHT nodeslist to memory
|
||||||
|
* from json encoded nodes file obtained at NODES_LIST_URL. Only one
|
||||||
|
* thread may run at a time.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 if a thread is already active.
|
||||||
|
* Return -2 if mutex fails to init.
|
||||||
|
* Return -3 if pthread attribute fails to init.
|
||||||
|
* Return -4 if pthread fails to set detached state.
|
||||||
|
* Return -5 if thread creation fails.
|
||||||
|
*/
|
||||||
|
int load_DHT_nodeslist(void);
|
||||||
|
|
||||||
|
#endif /* BOOTSTRAP_H */
|
1099
src/chat.c
1099
src/chat.c
File diff suppressed because it is too large
Load Diff
11
src/chat.h
11
src/chat.h
@ -20,13 +20,16 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CHAT_H_6489PZ13
|
#ifndef CHAT_H
|
||||||
#define CHAT_H_6489PZ13
|
#define CHAT_H
|
||||||
|
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
|
|
||||||
void kill_chat_window(ToxWindow *self);
|
/* 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);
|
ToxWindow new_chat(Tox *m, int32_t friendnum);
|
||||||
|
|
||||||
#endif /* end of include guard: CHAT_H_6489PZ13 */
|
#endif /* end of include guard: CHAT_H */
|
||||||
|
@ -30,201 +30,284 @@
|
|||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
#include "groupchat.h"
|
#include "groupchat.h"
|
||||||
|
#include "chat.h"
|
||||||
|
#include "file_transfers.h"
|
||||||
|
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
extern FriendsList Friends;
|
||||||
|
|
||||||
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
|
||||||
extern FileSender file_senders[MAX_FILES];
|
|
||||||
extern uint8_t max_file_senders_index;
|
|
||||||
|
|
||||||
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
|
||||||
{
|
{
|
||||||
char *errmsg;
|
if (argc < 2) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
|
||||||
if (argc < 1) {
|
|
||||||
errmsg = "Invalid syntax";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int groupnum = atoi(argv[1]);
|
|
||||||
|
|
||||||
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
|
|
||||||
errmsg = "Invalid syntax.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tox_invite_friend(m, self->num, groupnum) == -1) {
|
|
||||||
errmsg = "Failed to invite contact to group.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char msg[MAX_STR_SIZE];
|
char msg[MAX_STR_SIZE];
|
||||||
snprintf(msg, sizeof(msg), "Invited contact to Group %d.", groupnum);
|
const char *inoutstr = argv[1];
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
long int idx = strtol(argv[2], NULL, 10);
|
||||||
|
|
||||||
|
if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileTransfer *ft = NULL;
|
||||||
|
|
||||||
|
/* cancel an incoming file transfer */
|
||||||
|
if (strcasecmp(inoutstr, "in") == 0) {
|
||||||
|
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||||
|
} else if (strcasecmp(inoutstr, "out") == 0) {
|
||||||
|
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
|
||||||
|
} else {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ft) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "File transfer for '%s' aborted.", ft->file_name);
|
||||||
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
if (argc < 1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long int groupnum = strtol(argv[1], NULL, 10);
|
||||||
|
|
||||||
|
if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == LONG_MAX) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_CONFERENCE_INVITE err;
|
||||||
|
|
||||||
|
if (!tox_conference_invite(m, self->num, groupnum, &err)) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d)", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *errmsg;
|
|
||||||
|
|
||||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||||
errmsg = " * Warning: Too many windows are open.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *groupkey = friends[self->num].groupchat_key;
|
const char *groupkey = Friends.list[self->num].group_invite.key;
|
||||||
|
uint16_t length = Friends.list[self->num].group_invite.length;
|
||||||
|
uint8_t type = Friends.list[self->num].group_invite.type;
|
||||||
|
|
||||||
if (!friends[self->num].groupchat_pending) {
|
if (!Friends.list[self->num].group_invite.pending) {
|
||||||
errmsg = "No pending group chat invite.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey);
|
if (type != TOX_CONFERENCE_TYPE_TEXT) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
|
||||||
if (groupnum == -1) {
|
|
||||||
errmsg = "Group chat instance failed to initialize.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
TOX_ERR_CONFERENCE_JOIN err;
|
||||||
errmsg = "Group chat window failed to initialize.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
uint32_t groupnum = tox_conference_join(m, self->num, (uint8_t *) groupkey, length, &err);
|
||||||
tox_del_groupchat(m, groupnum);
|
|
||||||
|
if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||||
|
tox_conference_delete(m, groupnum, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *errmsg;
|
if (argc < 1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
|
||||||
if (argc != 1) {
|
|
||||||
errmsg = "Invalid syntax.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t filenum = atoi(argv[1]);
|
long int idx = strtol(argv[1], NULL, 10);
|
||||||
|
|
||||||
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
|
if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) {
|
||||||
errmsg = "No pending file transfers with that number.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!friends[self->num].file_receiver.pending[filenum]) {
|
struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||||
errmsg = "No pending file transfers with that number.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
if (!ft) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *filename = friends[self->num].file_receiver.filenames[filenum];
|
if (ft->state != FILE_TRANSFER_PENDING) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||||
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
|
return;
|
||||||
char msg[MAX_STR_SIZE];
|
|
||||||
snprintf(msg, sizeof(msg), "Saving file as: '%s' (%.1f%%)", filename, 0.0);
|
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
|
||||||
friends[self->num].file_receiver.line_id[filenum] = self->chatwin->hst->line_end->id + 1;
|
|
||||||
|
|
||||||
if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) {
|
|
||||||
errmsg = "* Error writing to file.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
|
||||||
tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
errmsg = "File transfer failed.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
friends[self->num].file_receiver.pending[filenum] = false;
|
if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
|
||||||
|
const char *msg = "File transfer failed: Invalid file path.";
|
||||||
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FILE_CONTROL err;
|
||||||
|
tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_FILE_CONTROL_OK)
|
||||||
|
goto on_recv_error;
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
|
||||||
|
|
||||||
|
/* prep progress bar line */
|
||||||
|
char progline[MAX_STR_SIZE];
|
||||||
|
init_progress_bar(progline);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||||
|
|
||||||
|
ft->line_id = self->chatwin->hst->line_end->id + 2;
|
||||||
|
ft->state = FILE_TRANSFER_STARTED;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
on_recv_error:
|
||||||
|
|
||||||
|
switch (err) {
|
||||||
|
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND:
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED:
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case TOX_ERR_FILE_CONTROL_NOT_FOUND:
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case TOX_ERR_FILE_CONTROL_SENDQ:
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *errmsg;
|
const char *errmsg = NULL;
|
||||||
|
|
||||||
if (max_file_senders_index >= (MAX_FILES - 1)) {
|
|
||||||
errmsg = "Please wait for some of your outgoing file transfers to complete.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
errmsg = "Invalid syntax.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *path = argv[1];
|
if (argv[1][0] != '\"') {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes.");
|
||||||
if (path[0] != '\"') {
|
|
||||||
errmsg = "File path must be enclosed in quotes.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
++path;
|
/* remove opening and closing quotes */
|
||||||
|
char path[MAX_STR_SIZE];
|
||||||
|
snprintf(path, sizeof(path), "%s", &argv[1][1]);
|
||||||
int path_len = strlen(path) - 1;
|
int path_len = strlen(path) - 1;
|
||||||
path[path_len] = '\0';
|
path[path_len] = '\0';
|
||||||
|
|
||||||
if (path_len > MAX_STR_SIZE) {
|
if (path_len >= MAX_STR_SIZE) {
|
||||||
errmsg = "File path exceeds character limit.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *file_to_send = fopen(path, "r");
|
FILE *file_to_send = fopen(path, "r");
|
||||||
|
|
||||||
if (file_to_send == NULL) {
|
if (file_to_send == NULL) {
|
||||||
errmsg = "File not found.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(file_to_send, 0, SEEK_END);
|
off_t filesize = file_size(path);
|
||||||
uint64_t filesize = ftell(file_to_send);
|
|
||||||
fseek(file_to_send, 0, SEEK_SET);
|
|
||||||
|
|
||||||
char filename[MAX_STR_SIZE];
|
if (filesize == 0) {
|
||||||
get_file_name(filename, sizeof(filename), path);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
|
||||||
int filenum = tox_new_file_sender(m, self->num, filesize, (const uint8_t *) filename, strlen(filename));
|
fclose(file_to_send);
|
||||||
|
|
||||||
if (filenum == -1) {
|
|
||||||
errmsg = "Error sending file.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i;
|
char file_name[TOX_MAX_FILENAME_LENGTH];
|
||||||
|
size_t namelen = get_file_name(file_name, sizeof(file_name), path);
|
||||||
|
|
||||||
for (i = 0; i < MAX_FILES; ++i) {
|
TOX_ERR_FILE_SEND err;
|
||||||
if (!file_senders[i].active) {
|
uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL,
|
||||||
memcpy(file_senders[i].pathname, path, path_len + 1);
|
(uint8_t *) file_name, namelen, &err);
|
||||||
file_senders[i].active = true;
|
|
||||||
file_senders[i].toxwin = self;
|
|
||||||
file_senders[i].file = file_to_send;
|
|
||||||
file_senders[i].filenum = filenum;
|
|
||||||
file_senders[i].friendnum = self->num;
|
|
||||||
file_senders[i].timestamp = get_unix_time();
|
|
||||||
file_senders[i].size = filesize;
|
|
||||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
|
||||||
tox_file_data_size(m, self->num), file_to_send);
|
|
||||||
|
|
||||||
char msg[MAX_STR_SIZE];
|
if (err != TOX_ERR_FILE_SEND_OK)
|
||||||
snprintf(msg, sizeof(msg), "Sending file: '%s'", path);
|
goto on_send_error;
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
|
||||||
|
|
||||||
if (i == max_file_senders_index)
|
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
|
||||||
++max_file_senders_index;
|
|
||||||
|
|
||||||
return;
|
if (!ft) {
|
||||||
}
|
err = TOX_ERR_FILE_SEND_TOO_MANY;
|
||||||
|
goto on_send_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(ft->file_name, file_name, namelen + 1);
|
||||||
|
ft->file = file_to_send;
|
||||||
|
ft->file_size = filesize;
|
||||||
|
tox_file_get_file_id(m, self->num, filenum, ft->file_id, NULL);
|
||||||
|
|
||||||
|
char sizestr[32];
|
||||||
|
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
on_send_error:
|
||||||
|
|
||||||
|
switch (err) {
|
||||||
|
case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND:
|
||||||
|
errmsg = "File transfer failed: Invalid friend.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED:
|
||||||
|
errmsg = "File transfer failed: Friend is offline.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_FILE_SEND_NAME_TOO_LONG:
|
||||||
|
errmsg = "File transfer failed: Filename is too long.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_FILE_SEND_TOO_MANY:
|
||||||
|
errmsg = "File transfer failed: Too many concurrent file transfers.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
errmsg = "File transfer failed.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
|
||||||
|
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
|
||||||
|
fclose(file_to_send);
|
||||||
}
|
}
|
||||||
|
@ -20,18 +20,19 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _chat_commands_h
|
#ifndef CHAT_COMMANDS_H
|
||||||
#define _chat_commands_h
|
#define CHAT_COMMANDS_H
|
||||||
|
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "toxic.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_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_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_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_sendfile(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_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_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_reject(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
@ -40,6 +41,11 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
|
|||||||
void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_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_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
#endif /* _SUPPORT_AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
#endif /* #define _chat_commands_h */
|
#ifdef VIDEO
|
||||||
|
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
#endif /* #define CHAT_COMMANDS_H */
|
||||||
|
106
src/configdir.c
106
src/configdir.c
@ -29,66 +29,68 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
#include "configdir.h"
|
#include "configdir.h"
|
||||||
|
#include "misc_tools.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.
|
* @return The users config dir or NULL on error.
|
||||||
*/
|
*/
|
||||||
char *get_user_config_dir(void)
|
char *get_user_config_dir(void)
|
||||||
{
|
{
|
||||||
char *user_config_dir;
|
char home[NSS_BUFLEN_PASSWD] = {0};
|
||||||
|
get_home_dir(home, sizeof(home));
|
||||||
|
|
||||||
#ifndef NSS_BUFLEN_PASSWD
|
char *user_config_dir = NULL;
|
||||||
#define NSS_BUFLEN_PASSWD 4096
|
size_t len = 0;
|
||||||
#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__)
|
# if defined(__APPLE__)
|
||||||
len = strlen(home) + strlen("/Library/Application Support") + 1;
|
len = strlen(home) + strlen("/Library/Application Support") + 1;
|
||||||
user_config_dir = malloc(len);
|
user_config_dir = malloc(len);
|
||||||
|
|
||||||
if (user_config_dir == NULL) {
|
if (user_config_dir == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
|
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
|
||||||
# else /* __APPLE__ */
|
# else /* __APPLE__ */
|
||||||
|
|
||||||
const char *tmp;
|
const char *tmp = getenv("XDG_CONFIG_HOME");
|
||||||
|
|
||||||
if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
|
if (tmp == NULL) {
|
||||||
len = strlen(home) + strlen("/.config") + 1;
|
len = strlen(home) + strlen("/.config") + 1;
|
||||||
user_config_dir = malloc(len);
|
user_config_dir = malloc(len);
|
||||||
|
|
||||||
if (user_config_dir == NULL) {
|
if (user_config_dir == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(user_config_dir, len, "%s/.config", home);
|
snprintf(user_config_dir, len, "%s/.config", home);
|
||||||
} else {
|
} else {
|
||||||
@ -98,34 +100,50 @@ char *get_user_config_dir(void)
|
|||||||
# endif /* __APPLE__ */
|
# endif /* __APPLE__ */
|
||||||
|
|
||||||
return user_config_dir;
|
return user_config_dir;
|
||||||
#undef NSS_BUFLEN_PASSWD
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Creates the config and chatlog directories.
|
||||||
* Creates the config directory.
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
*/
|
*/
|
||||||
int create_user_config_dir(char *path)
|
int create_user_config_dirs(char *path)
|
||||||
{
|
{
|
||||||
int mkdir_err;
|
|
||||||
|
|
||||||
mkdir_err = mkdir(path, 0700);
|
|
||||||
struct stat buf;
|
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;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 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);
|
strcpy(fullpath, path);
|
||||||
strcat(fullpath, CONFIGDIR);
|
strcat(fullpath, CONFIGDIR);
|
||||||
|
|
||||||
|
strcpy(logpath, path);
|
||||||
|
strcat(logpath, LOGDIR);
|
||||||
|
|
||||||
mkdir_err = mkdir(fullpath, 0700);
|
mkdir_err = mkdir(fullpath, 0700);
|
||||||
|
|
||||||
if (mkdir_err && (errno != EEXIST || stat(fullpath, &buf) || !S_ISDIR(buf.st_mode))) {
|
if (mkdir_err && (errno != EEXIST || stat(fullpath, &buf) || !S_ISDIR(buf.st_mode))) {
|
||||||
free(fullpath);
|
free(fullpath);
|
||||||
|
free(logpath);
|
||||||
return -1;
|
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);
|
free(fullpath);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,37 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _configdir_h
|
#ifndef CONFIGDIR_H
|
||||||
#define _configdir_h
|
#define CONFIGDIR_H
|
||||||
|
|
||||||
|
#ifndef NSS_BUFLEN_PASSWD
|
||||||
|
#define NSS_BUFLEN_PASSWD 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
#define CONFIGDIR "/tox/"
|
#define CONFIGDIR "/tox/"
|
||||||
|
#define LOGDIR "/tox/chatlogs/"
|
||||||
|
|
||||||
#ifndef S_ISDIR
|
#ifndef S_ISDIR
|
||||||
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the user's config directory.
|
||||||
|
*
|
||||||
|
* This is without a trailing slash. Resulting string must be freed.
|
||||||
|
*
|
||||||
|
* @return The users config dir or NULL on error.
|
||||||
|
*/
|
||||||
char *get_user_config_dir(void);
|
char *get_user_config_dir(void);
|
||||||
|
|
||||||
int create_user_config_dir(char *path);
|
/* get the user's home directory. */
|
||||||
|
void get_home_dir(char *home, int size);
|
||||||
|
|
||||||
#endif /* #define _configdir_h */
|
/* Creates the config and chatlog directories.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int create_user_config_dirs(char *path);
|
||||||
|
|
||||||
|
#endif /* #define CONFIGDIR_H */
|
||||||
|
93
src/curl_util.c
Normal file
93
src/curl_util.c
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/* curl_util.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#include "curl_util.h"
|
||||||
|
|
||||||
|
/* Sets proxy info for given CURL handler.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or if no proxy is set by the client.
|
||||||
|
* Returns -1 if proxy info is invalid.
|
||||||
|
* Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
|
||||||
|
*/
|
||||||
|
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
|
||||||
|
{
|
||||||
|
if (proxy_type == TOX_PROXY_TYPE_NONE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (proxy_address == NULL || port == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP;
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback function for CURL to write received data.
|
||||||
|
*
|
||||||
|
* This function will append data from an http request to the data buffer
|
||||||
|
* until the request is complete or the buffer is full. Buffer will be null terminated.
|
||||||
|
*
|
||||||
|
* Returns number of bytes received from http request on success (don't change this).
|
||||||
|
* Returns 0 if data exceeds buffer size.
|
||||||
|
*/
|
||||||
|
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer)
|
||||||
|
{
|
||||||
|
struct Recv_Curl_Data *recv_data = (struct Recv_Curl_Data *) user_pointer;
|
||||||
|
|
||||||
|
size_t length = size * nmemb;
|
||||||
|
size_t total_size = length + recv_data->length;
|
||||||
|
|
||||||
|
if (total_size > MAX_RECV_CURL_DATA_SIZE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(recv_data->data + recv_data->length, data, length);
|
||||||
|
recv_data->data[total_size] = '\0';
|
||||||
|
recv_data->length += length;
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
55
src/curl_util.h
Normal file
55
src/curl_util.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* curl_util.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CURL_UTIL_H
|
||||||
|
#define CURL_UTIL_H
|
||||||
|
|
||||||
|
/* List based on Mozilla's recommended configurations for modern browsers */
|
||||||
|
#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"
|
||||||
|
|
||||||
|
/* Max size of an http response that we can store in Recv_Data */
|
||||||
|
#define MAX_RECV_CURL_DATA_SIZE 32767
|
||||||
|
|
||||||
|
/* Holds data received from curl lookup */
|
||||||
|
struct Recv_Curl_Data {
|
||||||
|
char data[MAX_RECV_CURL_DATA_SIZE + 1]; /* Data received from curl write data callback */
|
||||||
|
size_t length; /* Total number of bytes written to data buffer (doesn't include null) */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Sets proxy info for given CURL handler.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or if no proxy is set by the client.
|
||||||
|
* Returns -1 if proxy info is invalid.
|
||||||
|
* Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
|
||||||
|
*/
|
||||||
|
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type);
|
||||||
|
|
||||||
|
/* Callback function for CURL to write received data.
|
||||||
|
*
|
||||||
|
* This function will append data from an http request to the data buffer
|
||||||
|
* until the request is complete or the buffer is full. Buffer will be null terminated.
|
||||||
|
*
|
||||||
|
* Returns size of bytes written to the data buffer.
|
||||||
|
*/
|
||||||
|
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer);
|
||||||
|
|
||||||
|
#endif /* CURL_UTIL_H */
|
316
src/dns.c
316
src/dns.c
@ -1,316 +0,0 @@
|
|||||||
/* dns.c
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This file is part of Toxic.
|
|
||||||
*
|
|
||||||
* Toxic is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Toxic is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <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"
|
|
||||||
|
|
||||||
#define MAX_DNS_REQST_SIZE 256
|
|
||||||
#define NUM_DNS3_SERVERS 2 /* must correspond to number of items in dns3_servers array */
|
|
||||||
#define TOX_DNS3_TXT_PREFIX "v=tox3;id="
|
|
||||||
#define DNS3_KEY_SZ 32
|
|
||||||
|
|
||||||
extern struct _Winthread Winthread;
|
|
||||||
|
|
||||||
/* TODO: process keys from key file instead of hard-coding like a noob */
|
|
||||||
static struct dns3_server {
|
|
||||||
char *name;
|
|
||||||
char key[DNS3_KEY_SZ];
|
|
||||||
} dns3_servers[] = {
|
|
||||||
{
|
|
||||||
"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;
|
|
||||||
|
|
||||||
|
|
||||||
static int dns_error(ToxWindow *self, char *errmsg)
|
|
||||||
{
|
|
||||||
char msg[MAX_STR_SIZE];
|
|
||||||
snprintf(msg, sizeof(msg), "User lookup failed: %s", errmsg);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
|
||||||
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.");
|
|
||||||
|
|
||||||
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(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 domain[MAX_STR_SIZE];
|
|
||||||
char name[MAX_STR_SIZE];
|
|
||||||
|
|
||||||
int namelen = parse_addr(t_data.addr, name, domain);
|
|
||||||
|
|
||||||
if (namelen == -1) {
|
|
||||||
dns_error(self, "Must be a Tox ID or an address in the form username@domain");
|
|
||||||
kill_dns_thread(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get domain name/pub key */
|
|
||||||
char *DNS_pubkey = NULL;
|
|
||||||
char *domname = NULL;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < NUM_DNS3_SERVERS; ++i) {
|
|
||||||
if (strcmp(dns3_servers[i].name, domain) == 0) {
|
|
||||||
DNS_pubkey = dns3_servers[i].key;
|
|
||||||
domname = dns3_servers[i].name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (domname == NULL) {
|
|
||||||
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];
|
|
||||||
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_DNS_REQST_SIZE];
|
|
||||||
|
|
||||||
/* format string and create dns query */
|
|
||||||
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domname);
|
|
||||||
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];
|
|
||||||
|
|
||||||
/* 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];
|
|
||||||
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, char *id_bin, char *addr, char *msg)
|
|
||||||
{
|
|
||||||
if (t_data.busy) {
|
|
||||||
char *err = "Please wait for previous user lookup to finish.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, err, SYS_MSG, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin);
|
|
||||||
snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr);
|
|
||||||
snprintf(t_data.msg, sizeof(t_data.msg), "%s", 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);
|
|
||||||
}
|
|
116
src/execute.c
116
src/execute.c
@ -29,8 +29,11 @@
|
|||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "chat_commands.h"
|
#include "chat_commands.h"
|
||||||
#include "global_commands.h"
|
#include "global_commands.h"
|
||||||
|
#include "group_commands.h"
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "notify.h"
|
||||||
|
#include "api.h"
|
||||||
|
|
||||||
struct cmd_func {
|
struct cmd_func {
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -40,86 +43,119 @@ struct cmd_func {
|
|||||||
static struct cmd_func global_commands[] = {
|
static struct cmd_func global_commands[] = {
|
||||||
{ "/accept", cmd_accept },
|
{ "/accept", cmd_accept },
|
||||||
{ "/add", cmd_add },
|
{ "/add", cmd_add },
|
||||||
|
{ "/avatar", cmd_avatar },
|
||||||
{ "/clear", cmd_clear },
|
{ "/clear", cmd_clear },
|
||||||
{ "/connect", cmd_connect },
|
{ "/connect", cmd_connect },
|
||||||
|
{ "/decline", cmd_decline },
|
||||||
{ "/exit", cmd_quit },
|
{ "/exit", cmd_quit },
|
||||||
{ "/groupchat", cmd_groupchat },
|
{ "/group", cmd_groupchat },
|
||||||
{ "/help", cmd_prompt_help },
|
{ "/help", cmd_prompt_help },
|
||||||
{ "/log", cmd_log },
|
{ "/log", cmd_log },
|
||||||
{ "/myid", cmd_myid },
|
{ "/myid", cmd_myid },
|
||||||
|
{ "/myqr", cmd_myqr },
|
||||||
{ "/nick", cmd_nick },
|
{ "/nick", cmd_nick },
|
||||||
{ "/note", cmd_note },
|
{ "/note", cmd_note },
|
||||||
|
{ "/nospam", cmd_nospam },
|
||||||
{ "/q", cmd_quit },
|
{ "/q", cmd_quit },
|
||||||
{ "/quit", cmd_quit },
|
{ "/quit", cmd_quit },
|
||||||
|
{ "/requests", cmd_requests },
|
||||||
{ "/status", cmd_status },
|
{ "/status", cmd_status },
|
||||||
|
#ifdef AUDIO
|
||||||
#ifdef _SUPPORT_AUDIO
|
|
||||||
{ "/lsdev", cmd_list_devices },
|
{ "/lsdev", cmd_list_devices },
|
||||||
{ "/sdev", cmd_change_device },
|
{ "/sdev", cmd_change_device },
|
||||||
#endif /* _SUPPORT_AUDIO */
|
#endif /* AUDIO */
|
||||||
|
#ifdef VIDEO
|
||||||
|
{ "/lsvdev", cmd_list_video_devices },
|
||||||
|
{ "/svdev" , cmd_change_video_device },
|
||||||
|
#endif /* VIDEO */
|
||||||
|
#ifdef PYTHON
|
||||||
|
{ "/run", cmd_run },
|
||||||
|
#endif /* PYTHON */
|
||||||
|
{ NULL, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct cmd_func chat_commands[] = {
|
static struct cmd_func chat_commands[] = {
|
||||||
|
{ "/cancel", cmd_cancelfile },
|
||||||
{ "/invite", cmd_groupinvite },
|
{ "/invite", cmd_groupinvite },
|
||||||
{ "/join", cmd_join_group },
|
{ "/join", cmd_join_group },
|
||||||
{ "/savefile", cmd_savefile },
|
{ "/savefile", cmd_savefile },
|
||||||
{ "/sendfile", cmd_sendfile },
|
{ "/sendfile", cmd_sendfile },
|
||||||
|
#ifdef AUDIO
|
||||||
#ifdef _SUPPORT_AUDIO
|
|
||||||
{ "/call", cmd_call },
|
{ "/call", cmd_call },
|
||||||
{ "/cancel", cmd_cancel },
|
|
||||||
{ "/answer", cmd_answer },
|
{ "/answer", cmd_answer },
|
||||||
{ "/reject", cmd_reject },
|
{ "/reject", cmd_reject },
|
||||||
{ "/hangup", cmd_hangup },
|
{ "/hangup", cmd_hangup },
|
||||||
{ "/sdev", cmd_ccur_device },
|
|
||||||
{ "/mute", cmd_mute },
|
{ "/mute", cmd_mute },
|
||||||
{ "/sense", cmd_sense },
|
{ "/sense", cmd_sense },
|
||||||
#endif /* _SUPPORT_AUDIO */
|
#endif /* AUDIO */
|
||||||
|
#ifdef VIDEO
|
||||||
|
{ "/video", cmd_video },
|
||||||
|
#endif /* VIDEO */
|
||||||
|
{ NULL, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct cmd_func group_commands[] = {
|
||||||
|
{ "/title", cmd_set_title },
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
{ "/mute", cmd_mute },
|
||||||
|
{ "/sense", cmd_sense },
|
||||||
|
#endif /* AUDIO */
|
||||||
|
{ NULL, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Parses input command and puts args into arg array.
|
/* Parses input command and puts args into arg array.
|
||||||
Returns number of arguments on success, -1 on failure. */
|
Returns number of arguments on success, -1 on failure. */
|
||||||
static int parse_command(WINDOW *w, ToxWindow *self, 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;
|
int num_args = 0;
|
||||||
bool cmd_end = false; /* flags when we get to the end of cmd */
|
int i = 0; /* index of last char in an argument */
|
||||||
char *end; /* points to the end of the current arg */
|
|
||||||
|
|
||||||
/* characters wrapped in double quotes count as one arg */
|
/* characters wrapped in double quotes count as one arg */
|
||||||
while (!cmd_end && num_args < MAX_NUM_ARGS) {
|
while (num_args < MAX_NUM_ARGS) {
|
||||||
if (*cmd == '\"') {
|
int qt_ofst = 0; /* set to 1 to offset index for quote char at end of arg */
|
||||||
end = strchr(cmd + 1, '\"');
|
|
||||||
|
|
||||||
if (end++ == NULL) { /* Increment past the end quote */
|
if (*cmd == '\"') {
|
||||||
char *errmsg = "Invalid argument. Did you forget a closing \"?";
|
qt_ofst = 1;
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
i = char_find(1, cmd, '\"');
|
||||||
|
|
||||||
|
if (cmd[i] == '\0') {
|
||||||
|
const char *errmsg = "Invalid argument. Did you forget a closing \"?";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||||
|
free(cmd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_end = *end == '\0';
|
|
||||||
} else {
|
} else {
|
||||||
end = strchr(cmd, ' ');
|
i = char_find(0, cmd, ' ');
|
||||||
cmd_end = end == NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cmd_end)
|
memcpy(args[num_args], cmd, i + qt_ofst);
|
||||||
*end++ = '\0'; /* mark end of current argument */
|
args[num_args++][i + qt_ofst] = '\0';
|
||||||
|
|
||||||
/* Copy from start of current arg to where we just inserted the null byte */
|
if (cmd[i] == '\0') /* no more args */
|
||||||
strcpy(args[num_args++], cmd);
|
break;
|
||||||
cmd = end;
|
|
||||||
|
char tmp[MAX_STR_SIZE];
|
||||||
|
snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
|
||||||
|
strcpy(cmd, tmp); /* tmp will always fit inside cmd */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(cmd);
|
||||||
return num_args;
|
return num_args;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Matches command to respective function. Returns 0 on match, 1 on no match */
|
/* Matches command to respective function. Returns 0 on match, 1 on no match */
|
||||||
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_cmds,
|
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
|
||||||
struct cmd_func *commands, char (*args)[MAX_STR_SIZE])
|
char (*args)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < num_cmds; ++i) {
|
for (i = 0; commands[i].name != NULL; ++i) {
|
||||||
if (strcmp(args[0], commands[i].name) == 0) {
|
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;
|
return 0;
|
||||||
@ -129,13 +165,13 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_
|
|||||||
return 1;
|
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;
|
return;
|
||||||
|
|
||||||
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
|
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
|
||||||
int num_args = parse_command(w, self, cmd, args);
|
int num_args = parse_command(w, self, input, args);
|
||||||
|
|
||||||
if (num_args == -1)
|
if (num_args == -1)
|
||||||
return;
|
return;
|
||||||
@ -146,17 +182,25 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode)
|
|||||||
Note: Global commands must come last in case of duplicate command names */
|
Note: Global commands must come last in case of duplicate command names */
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case CHAT_COMMAND_MODE:
|
case CHAT_COMMAND_MODE:
|
||||||
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
|
if (do_command(w, self, m, num_args, chat_commands, args) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GROUPCHAT_COMMAND_MODE:
|
case GROUPCHAT_COMMAND_MODE:
|
||||||
|
if (do_command(w, self, m, num_args, group_commands, args) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0)
|
if (do_command(w, self, m, num_args, global_commands, args) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, "Invalid command.", SYS_MSG, 0, 0);
|
#ifdef PYTHON
|
||||||
|
if (do_plugin_command(num_args, args) == 0)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||||
}
|
}
|
||||||
|
@ -20,28 +20,20 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _execute_h
|
#ifndef EXECUTE_H
|
||||||
#define _execute_h
|
#define EXECUTE_H
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
|
|
||||||
#define MAX_NUM_ARGS 4 /* Includes command */
|
#define MAX_NUM_ARGS 4 /* Includes command */
|
||||||
|
|
||||||
#ifdef _SUPPORT_AUDIO
|
|
||||||
#define GLOBAL_NUM_COMMANDS 16
|
|
||||||
#define CHAT_NUM_COMMANDS 12
|
|
||||||
#else
|
|
||||||
#define GLOBAL_NUM_COMMANDS 14
|
|
||||||
#define CHAT_NUM_COMMANDS 4
|
|
||||||
#endif /* _SUPPORT_AUDIO */
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GLOBAL_COMMAND_MODE,
|
GLOBAL_COMMAND_MODE,
|
||||||
CHAT_COMMAND_MODE,
|
CHAT_COMMAND_MODE,
|
||||||
GROUPCHAT_COMMAND_MODE,
|
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 */
|
#endif /* #define EXECUTE_H */
|
||||||
|
@ -1,129 +0,0 @@
|
|||||||
/* file_senders.c
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This file is part of Toxic.
|
|
||||||
*
|
|
||||||
* Toxic is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Toxic is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "toxic.h"
|
|
||||||
#include "windows.h"
|
|
||||||
#include "file_senders.h"
|
|
||||||
#include "line_info.h"
|
|
||||||
#include "misc_tools.h"
|
|
||||||
|
|
||||||
FileSender file_senders[MAX_FILES];
|
|
||||||
uint8_t max_file_senders_index;
|
|
||||||
|
|
||||||
static void set_max_file_senders_index(void)
|
|
||||||
{
|
|
||||||
int j;
|
|
||||||
|
|
||||||
for (j = max_file_senders_index; j > 0; --j) {
|
|
||||||
if (file_senders[j - 1].active)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
max_file_senders_index = j;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void close_file_sender(ToxWindow *self, Tox *m, int i, char *msg, int CTRL, int filenum, int32_t friendnum)
|
|
||||||
{
|
|
||||||
if (self->chatwin != NULL) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
|
||||||
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
void close_all_file_senders(Tox *m)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < max_file_senders_index; ++i) {
|
|
||||||
if (file_senders[i].active) {
|
|
||||||
fclose(file_senders[i].file);
|
|
||||||
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenum,
|
|
||||||
TOX_FILECONTROL_KILL, 0, 0);
|
|
||||||
memset(&file_senders[i], 0, sizeof(FileSender));
|
|
||||||
}
|
|
||||||
|
|
||||||
set_max_file_senders_index();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_file_senders(Tox *m)
|
|
||||||
{
|
|
||||||
char msg[MAX_STR_SIZE];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < max_file_senders_index; ++i) {
|
|
||||||
if (!file_senders[i].active)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ToxWindow *self = file_senders[i].toxwin;
|
|
||||||
char *pathname = file_senders[i].pathname;
|
|
||||||
int filenum = file_senders[i].filenum;
|
|
||||||
int32_t friendnum = file_senders[i].friendnum;
|
|
||||||
FILE *fp = file_senders[i].file;
|
|
||||||
|
|
||||||
/* If file transfer has timed out kill transfer and send kill control */
|
|
||||||
if (timed_out(file_senders[i].timestamp, get_unix_time(), TIMEOUT_FILESENDER)) {
|
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", pathname);
|
|
||||||
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (tox_file_send_data(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece,
|
|
||||||
file_senders[i].piecelen) == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
uint64_t curtime = get_unix_time();
|
|
||||||
file_senders[i].timestamp = curtime;
|
|
||||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
|
||||||
tox_file_data_size(m, friendnum), fp);
|
|
||||||
|
|
||||||
long double remain = (long double) tox_file_data_remaining(m, friendnum, filenum, 0);
|
|
||||||
|
|
||||||
/* refresh line with percentage complete */
|
|
||||||
if ((self->chatwin != NULL && timed_out(file_senders[i].last_progress, curtime, 1)) || !remain) {
|
|
||||||
file_senders[i].last_progress = curtime;
|
|
||||||
uint64_t size = file_senders[i].size;
|
|
||||||
long double pct_remain = remain ? (1 - (remain / size)) * 100 : 100;
|
|
||||||
|
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' accepted (%.1Lf%%)", pathname, pct_remain);
|
|
||||||
line_info_set(self, file_senders[i].line_id, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_senders[i].piecelen == 0) {
|
|
||||||
snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", pathname);
|
|
||||||
close_file_sender(self, m, i, msg, TOX_FILECONTROL_FINISHED, filenum, friendnum);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
277
src/file_transfers.c
Normal file
277
src/file_transfers.c
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
/* file_transfers.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "file_transfers.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "notify.h"
|
||||||
|
|
||||||
|
extern FriendsList Friends;
|
||||||
|
|
||||||
|
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||||
|
#define NUM_PROG_MARKS 50
|
||||||
|
|
||||||
|
/* creates initial progress line that will be updated during file transfer.
|
||||||
|
Assumes progline has room for at least MAX_STR_SIZE bytes */
|
||||||
|
void init_progress_bar(char *progline)
|
||||||
|
{
|
||||||
|
strcpy(progline, "0% [");
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_PROG_MARKS; ++i)
|
||||||
|
strcat(progline, "-");
|
||||||
|
|
||||||
|
strcat(progline, "] 0.0 B/s");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prints a progress bar for file transfers. */
|
||||||
|
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
|
||||||
|
{
|
||||||
|
if (bps < 0 || pct_done < 0 || pct_done > 100)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char pct_str[24];
|
||||||
|
snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done);
|
||||||
|
|
||||||
|
char bps_str[24];
|
||||||
|
bytes_convert_str(bps_str, sizeof(bps_str), bps);
|
||||||
|
|
||||||
|
char prog_line[NUM_PROG_MARKS + 1] = {0};
|
||||||
|
int n = pct_done / (100 / NUM_PROG_MARKS);
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < n; ++i)
|
||||||
|
strcat(prog_line, "=");
|
||||||
|
|
||||||
|
if (pct_done < 100)
|
||||||
|
strcpy(prog_line + n, ">");
|
||||||
|
|
||||||
|
for (j = i; j < NUM_PROG_MARKS - 1; ++j)
|
||||||
|
strcat(prog_line, "-");
|
||||||
|
|
||||||
|
char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7];
|
||||||
|
snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str);
|
||||||
|
|
||||||
|
line_info_set(self, line_id, full_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
|
||||||
|
{
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Timeout must be set to 1 second to show correct bytes per second */
|
||||||
|
if (!timed_out(ft->last_line_progress, 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
double remain = ft->file_size - ft->position;
|
||||||
|
double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
|
||||||
|
print_progress_bar(self, ft->bps, pct_done, ft->line_id);
|
||||||
|
|
||||||
|
ft->bps = 0;
|
||||||
|
ft->last_line_progress = get_unix_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* refreshes active file transfer status bars. */
|
||||||
|
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]);
|
||||||
|
refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||||
|
* Returns NULL if filenum is invalid.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
|
||||||
|
|
||||||
|
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum)
|
||||||
|
return ft_send;
|
||||||
|
|
||||||
|
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
|
||||||
|
|
||||||
|
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum)
|
||||||
|
return ft_recv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||||
|
* Returns NULL on failure.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||||
|
FILE_TRANSFER_DIRECTION direction)
|
||||||
|
{
|
||||||
|
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
|
||||||
|
&Friends.list[friendnum].file_sender[i] :
|
||||||
|
&Friends.list[friendnum].file_receiver[i];
|
||||||
|
|
||||||
|
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a pointer to an unused file sender.
|
||||||
|
* Returns NULL if all file senders are in use.
|
||||||
|
*/
|
||||||
|
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||||
|
memset(ft, 0, sizeof(struct FileTransfer));
|
||||||
|
ft->window = window;
|
||||||
|
ft->index = i;
|
||||||
|
ft->friendnum = friendnum;
|
||||||
|
ft->filenum = filenum;
|
||||||
|
ft->file_type = type;
|
||||||
|
ft->last_keep_alive = get_unix_time();
|
||||||
|
ft->state = FILE_TRANSFER_PENDING;
|
||||||
|
ft->direction = FILE_TRANSFER_SEND;
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a pointer to an unused file receiver.
|
||||||
|
* Returns NULL if all file receivers are in use.
|
||||||
|
*/
|
||||||
|
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i];
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||||
|
memset(ft, 0, sizeof(struct FileTransfer));
|
||||||
|
ft->window = window;
|
||||||
|
ft->index = i;
|
||||||
|
ft->friendnum = friendnum;
|
||||||
|
ft->filenum = filenum;
|
||||||
|
ft->file_type = type;
|
||||||
|
ft->last_keep_alive = get_unix_time();
|
||||||
|
ft->state = FILE_TRANSFER_PENDING;
|
||||||
|
ft->direction = FILE_TRANSFER_RECV;
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initializes an unused file transfer and returns its pointer.
|
||||||
|
* Returns NULL on failure.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||||
|
FILE_TRANSFER_DIRECTION direction, uint8_t type)
|
||||||
|
{
|
||||||
|
if (direction == FILE_TRANSFER_RECV)
|
||||||
|
return new_file_receiver(window, friendnum, filenum, type);
|
||||||
|
|
||||||
|
if (direction == FILE_TRANSFER_SEND)
|
||||||
|
return new_file_sender(window, friendnum, filenum, type);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Closes file transfer ft.
|
||||||
|
*
|
||||||
|
* Set CTRL to -1 if we don't want to send a control signal.
|
||||||
|
* Set message or self to NULL if we don't want to display a message.
|
||||||
|
*/
|
||||||
|
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||||
|
Notification sound_type)
|
||||||
|
{
|
||||||
|
if (!ft)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ft->file)
|
||||||
|
fclose(ft->file);
|
||||||
|
|
||||||
|
if (CTRL >= 0)
|
||||||
|
tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
|
||||||
|
|
||||||
|
if (message && self) {
|
||||||
|
if (self->active_box != -1 && sound_type != silent)
|
||||||
|
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
|
||||||
|
else
|
||||||
|
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(ft, 0, sizeof(struct FileTransfer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kills all active file transfers for friendnum */
|
||||||
|
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kill_all_file_transfers(Tox *m)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < Friends.max_idx; ++i)
|
||||||
|
kill_all_file_transfers_friend(m, Friends.list[i].num);
|
||||||
|
}
|
111
src/file_transfers.h
Normal file
111
src/file_transfers.h
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/* file_transfers.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FILE_TRANSFERS_H
|
||||||
|
#define FILE_TRANSFERS_H
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "notify.h"
|
||||||
|
|
||||||
|
#define KiB 1024
|
||||||
|
#define MiB 1048576 /* 1024^2 */
|
||||||
|
#define GiB 1073741824 /* 1024^3 */
|
||||||
|
|
||||||
|
#define MAX_FILES 32
|
||||||
|
|
||||||
|
typedef enum FILE_TRANSFER_STATE {
|
||||||
|
FILE_TRANSFER_INACTIVE,
|
||||||
|
FILE_TRANSFER_PAUSED,
|
||||||
|
FILE_TRANSFER_PENDING,
|
||||||
|
FILE_TRANSFER_STARTED,
|
||||||
|
} FILE_TRANSFER_STATE;
|
||||||
|
|
||||||
|
typedef enum FILE_TRANSFER_DIRECTION {
|
||||||
|
FILE_TRANSFER_SEND,
|
||||||
|
FILE_TRANSFER_RECV
|
||||||
|
} FILE_TRANSFER_DIRECTION;
|
||||||
|
|
||||||
|
struct FileTransfer {
|
||||||
|
ToxWindow *window;
|
||||||
|
FILE *file;
|
||||||
|
FILE_TRANSFER_STATE state;
|
||||||
|
FILE_TRANSFER_DIRECTION direction;
|
||||||
|
uint8_t file_type;
|
||||||
|
char file_name[TOX_MAX_FILENAME_LENGTH + 1];
|
||||||
|
char file_path[PATH_MAX + 1]; /* Not used by senders */
|
||||||
|
double bps;
|
||||||
|
uint32_t filenum;
|
||||||
|
uint32_t friendnum;
|
||||||
|
size_t index;
|
||||||
|
uint64_t file_size;
|
||||||
|
uint64_t position;
|
||||||
|
time_t last_line_progress; /* The last time we updated the progress bar */
|
||||||
|
time_t last_keep_alive; /* The last time we sent or received data */
|
||||||
|
uint32_t line_id;
|
||||||
|
uint8_t file_id[TOX_FILE_ID_LENGTH];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* creates initial progress line that will be updated during file transfer.
|
||||||
|
progline must be at lesat MAX_STR_SIZE bytes */
|
||||||
|
void init_progress_bar(char *progline);
|
||||||
|
|
||||||
|
/* prints a progress bar for file transfers */
|
||||||
|
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
|
||||||
|
|
||||||
|
/* refreshes active file transfer status bars. */
|
||||||
|
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
|
||||||
|
|
||||||
|
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||||
|
* Returns NULL if filenum is invalid.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum);
|
||||||
|
|
||||||
|
|
||||||
|
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||||
|
* Returns NULL on failure.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||||
|
FILE_TRANSFER_DIRECTION direction);
|
||||||
|
|
||||||
|
/* Initializes an unused file transfer and returns its pointer.
|
||||||
|
* Returns NULL on failure.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||||
|
FILE_TRANSFER_DIRECTION direction, uint8_t type);
|
||||||
|
|
||||||
|
/* Closes file transfer ft.
|
||||||
|
*
|
||||||
|
* Set CTRL to -1 if we don't want to send a control signal.
|
||||||
|
* Set message or self to NULL if we don't want to display a message.
|
||||||
|
*/
|
||||||
|
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||||
|
Notification sound_type);
|
||||||
|
|
||||||
|
/* Kills all active file transfers for friendnum */
|
||||||
|
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
|
||||||
|
|
||||||
|
void kill_all_file_transfers(Tox *m);
|
||||||
|
|
||||||
|
#endif /* #define FILE_TRANSFERS_H */
|
1117
src/friendlist.c
1117
src/friendlist.c
File diff suppressed because it is too large
Load Diff
@ -20,23 +20,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FRIENDLIST_H_53I41IM
|
#ifndef FRIENDLIST_H
|
||||||
#define FRIENDLIST_H_53I41IM
|
#define FRIENDLIST_H
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "file_senders.h"
|
#include "file_transfers.h"
|
||||||
|
|
||||||
struct FileReceiver {
|
|
||||||
char filenames[MAX_FILES][MAX_STR_SIZE];
|
|
||||||
FILE *files[MAX_FILES];
|
|
||||||
bool pending[MAX_FILES];
|
|
||||||
uint64_t size[MAX_FILES];
|
|
||||||
uint64_t last_progress[MAX_FILES];
|
|
||||||
uint32_t line_id[MAX_FILES];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LastOnline {
|
struct LastOnline {
|
||||||
uint64_t last_on;
|
uint64_t last_on;
|
||||||
@ -44,32 +35,60 @@ struct LastOnline {
|
|||||||
char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */
|
char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GroupChatInvite {
|
||||||
|
char *key;
|
||||||
|
uint16_t length;
|
||||||
|
uint8_t type;
|
||||||
|
bool pending;
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char name[TOXIC_MAX_NAME_LENGTH];
|
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||||
int namelength;
|
int namelength;
|
||||||
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
|
||||||
uint16_t statusmsg_len;
|
size_t statusmsg_len;
|
||||||
char groupchat_key[TOX_CLIENT_ID_SIZE];
|
char pub_key[TOX_PUBLIC_KEY_SIZE];
|
||||||
bool groupchat_pending;
|
uint32_t num;
|
||||||
char pub_key[TOX_CLIENT_ID_SIZE];
|
|
||||||
int32_t num;
|
|
||||||
int chatwin;
|
int chatwin;
|
||||||
bool active;
|
bool active;
|
||||||
bool online;
|
TOX_CONNECTION connection_status;
|
||||||
uint8_t is_typing;
|
bool is_typing;
|
||||||
bool logging_on; /* saves preference for friend irrespective of chat windows */
|
bool logging_on; /* saves preference for friend irrespective of global settings */
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
|
|
||||||
struct LastOnline last_online;
|
struct LastOnline last_online;
|
||||||
struct FileReceiver file_receiver;
|
struct GroupChatInvite group_invite;
|
||||||
|
|
||||||
|
struct FileTransfer file_receiver[MAX_FILES];
|
||||||
|
struct FileTransfer file_sender[MAX_FILES];
|
||||||
} ToxicFriend;
|
} ToxicFriend;
|
||||||
|
|
||||||
ToxWindow new_friendlist(void);
|
typedef struct {
|
||||||
void disable_chatwin(int32_t f_num);
|
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||||
int get_friendnum(uint8_t *name);
|
int namelength;
|
||||||
|
char pub_key[TOX_PUBLIC_KEY_SIZE];
|
||||||
|
uint32_t num;
|
||||||
|
bool active;
|
||||||
|
uint64_t last_on;
|
||||||
|
} BlockedFriend;
|
||||||
|
|
||||||
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort);
|
typedef struct {
|
||||||
|
int num_selected;
|
||||||
|
size_t num_friends;
|
||||||
|
size_t num_online;
|
||||||
|
size_t max_idx; /* 1 + the index of the last friend in list */
|
||||||
|
uint32_t *index;
|
||||||
|
ToxicFriend *list;
|
||||||
|
} FriendsList;
|
||||||
|
|
||||||
|
ToxWindow new_friendlist(void);
|
||||||
|
void disable_chatwin(uint32_t f_num);
|
||||||
|
int get_friendnum(uint8_t *name);
|
||||||
|
int load_blocklist(char *data);
|
||||||
|
void kill_friendlist(void);
|
||||||
|
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
|
||||||
|
|
||||||
/* sorts friendlist_index first by connection status then alphabetically */
|
/* sorts friendlist_index first by connection status then alphabetically */
|
||||||
void sort_friendlist_index(void);
|
void sort_friendlist_index(void);
|
||||||
|
|
||||||
#endif /* end of include guard: FRIENDLIST_H_53I41IM */
|
#endif /* end of include guard: FRIENDLIST_H */
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
@ -30,158 +29,164 @@
|
|||||||
#include "friendlist.h"
|
#include "friendlist.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
#include "dns.h"
|
|
||||||
#include "groupchat.h"
|
#include "groupchat.h"
|
||||||
#include "prompt.h"
|
#include "prompt.h"
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
|
#include "term_mplex.h"
|
||||||
|
#include "avatars.h"
|
||||||
|
#include "name_lookup.h"
|
||||||
|
#include "qr_code.h"
|
||||||
|
#include "toxic_strings.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
extern FriendsList Friends;
|
||||||
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
extern FriendRequests FrndRequests;
|
||||||
|
|
||||||
extern char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
|
|
||||||
extern uint8_t num_frnd_requests;
|
|
||||||
|
|
||||||
/* command functions */
|
/* command functions */
|
||||||
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *msg;
|
if (argc < 1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
|
||||||
if (argc != 1) {
|
|
||||||
msg = "Invalid syntax.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int req = atoi(argv[1]);
|
long int req = strtol(argv[1], NULL, 10);
|
||||||
|
|
||||||
if ((req == 0 && strcmp(argv[1], "0")) || req >= MAX_FRIENDS_NUM) {
|
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
|
||||||
msg = "No pending friend request with that number.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strlen(pending_frnd_requests[req])) {
|
if (!FrndRequests.request[req].active) {
|
||||||
msg = "No pending friend request with that number.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t friendnum = tox_add_friend_norequest(m, (uint8_t *) pending_frnd_requests[req]);
|
TOX_ERR_FRIEND_ADD err;
|
||||||
|
uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err);
|
||||||
|
|
||||||
if (friendnum == -1)
|
if (err != TOX_ERR_FRIEND_ADD_OK) {
|
||||||
msg = "Failed to add friend.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err);
|
||||||
else {
|
return;
|
||||||
msg = "Friend request accepted.";
|
} else {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
|
||||||
on_friendadded(m, friendnum, true);
|
on_friendadded(m, friendnum, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE);
|
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = num_frnd_requests; i > 0; --i) {
|
for (i = FrndRequests.max_idx; i > 0; --i) {
|
||||||
if (!strlen(pending_frnd_requests[i - 1]))
|
if (FrndRequests.request[i - 1].active)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_frnd_requests = i;
|
FrndRequests.max_idx = i;
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
--FrndRequests.num_requests;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg)
|
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg)
|
||||||
{
|
{
|
||||||
char *errmsg;
|
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) {
|
TOX_ERR_FRIEND_ADD err;
|
||||||
case TOX_FAERR_TOOLONG:
|
uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err);
|
||||||
|
|
||||||
|
switch (err) {
|
||||||
|
case TOX_ERR_FRIEND_ADD_TOO_LONG:
|
||||||
errmsg = "Message is too long.";
|
errmsg = "Message is too long.";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_FAERR_NOMESSAGE:
|
case TOX_ERR_FRIEND_ADD_NO_MESSAGE:
|
||||||
errmsg = "Please add a message to your request.";
|
errmsg = "Please add a message to your request.";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_FAERR_OWNKEY:
|
case TOX_ERR_FRIEND_ADD_OWN_KEY:
|
||||||
errmsg = "That appears to be your own ID.";
|
errmsg = "That appears to be your own ID.";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_FAERR_ALREADYSENT:
|
case TOX_ERR_FRIEND_ADD_ALREADY_SENT:
|
||||||
errmsg = "Friend request has already been sent.";
|
errmsg = "Friend request has already been sent.";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_FAERR_UNKNOWN:
|
case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM:
|
||||||
errmsg = "Undefined error when adding friend.";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TOX_FAERR_BADCHECKSUM:
|
|
||||||
errmsg = "Bad checksum in address.";
|
errmsg = "Bad checksum in address.";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_FAERR_SETNEWNOSPAM:
|
case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM:
|
||||||
errmsg = "Nospam was different.";
|
errmsg = "Nospam was different.";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case TOX_ERR_FRIEND_ADD_MALLOC:
|
||||||
|
errmsg = "Core memory allocation failed.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_FRIEND_ADD_OK:
|
||||||
errmsg = "Friend request sent.";
|
errmsg = "Friend request sent.";
|
||||||
on_friendadded(m, f_num, true);
|
on_friendadded(m, f_num, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_FRIEND_ADD_NULL:
|
||||||
|
|
||||||
|
/* fallthrough */
|
||||||
|
default:
|
||||||
|
errmsg = "Faile to add friend: Unknown error.";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
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])
|
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *errmsg;
|
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
errmsg = "Invalid syntax.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *id = argv[1];
|
const char *id = argv[1];
|
||||||
char msg[MAX_STR_SIZE];
|
char msg[MAX_STR_SIZE];
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
char *temp = argv[2];
|
if (argv[2][0] != '\"') {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes.");
|
||||||
if (temp[0] != '\"') {
|
|
||||||
errmsg = "Message must be enclosed in quotes.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
++temp;
|
/* remove opening and closing quotes */
|
||||||
temp[strlen(temp) - 1] = '\0';
|
char tmp[MAX_STR_SIZE];
|
||||||
snprintf(msg, sizeof(msg), "%s", temp);
|
snprintf(tmp, sizeof(tmp), "%s", &argv[2][1]);
|
||||||
|
int len = strlen(tmp) - 1;
|
||||||
|
tmp[len] = '\0';
|
||||||
|
snprintf(msg, sizeof(msg), "%s", tmp);
|
||||||
} else {
|
} else {
|
||||||
char selfname[TOX_MAX_NAME_LENGTH];
|
char selfname[TOX_MAX_NAME_LENGTH];
|
||||||
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfname);
|
tox_self_get_name(m, (uint8_t *) selfname);
|
||||||
|
|
||||||
|
size_t n_len = tox_self_get_name_size(m);
|
||||||
selfname[n_len] = '\0';
|
selfname[n_len] = '\0';
|
||||||
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
|
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
|
||||||
}
|
}
|
||||||
|
|
||||||
char id_bin[TOX_FRIEND_ADDRESS_SIZE] = {0};
|
char id_bin[TOX_ADDRESS_SIZE] = {0};
|
||||||
uint16_t id_len = (uint16_t) strlen(id);
|
uint16_t id_len = (uint16_t) strlen(id);
|
||||||
|
|
||||||
/* try to add tox ID */
|
/* try to add tox ID */
|
||||||
if (id_len == 2 * TOX_FRIEND_ADDRESS_SIZE) {
|
if (id_len == 2 * TOX_ADDRESS_SIZE) {
|
||||||
size_t i;
|
size_t i;
|
||||||
char xx[3];
|
char xx[3];
|
||||||
uint32_t x;
|
uint32_t x;
|
||||||
|
|
||||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
|
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
|
||||||
xx[0] = id[2 * i];
|
xx[0] = id[2 * i];
|
||||||
xx[1] = id[2 * i + 1];
|
xx[1] = id[2 * i + 1];
|
||||||
xx[2] = '\0';
|
xx[2] = '\0';
|
||||||
|
|
||||||
if (sscanf(xx, "%02x", &x) != 1) {
|
if (sscanf(xx, "%02x", &x) != 1) {
|
||||||
errmsg = "Invalid ID.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,170 +194,329 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd_add_helper(self, m, id_bin, msg);
|
cmd_add_helper(self, m, id_bin, msg);
|
||||||
} else { /* assume id is a username@domain address and do DNS lookup */
|
} else { /* assume id is a username@domain address and do http name server lookup */
|
||||||
dns3_lookup(self, m, id_bin, id, msg);
|
name_lookup(self, m, id_bin, id, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
if (argc < 2 || strlen(argv[1]) < 3) {
|
||||||
|
avatar_unset(m);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar is not set.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv[1][0] != '\"') {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove opening and closing quotes */
|
||||||
|
char path[MAX_STR_SIZE];
|
||||||
|
snprintf(path, sizeof(path), "%s", &argv[1][1]);
|
||||||
|
int len = strlen(path) - 1;
|
||||||
|
|
||||||
|
if (len <= 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
path[len] = '\0';
|
||||||
|
char filename[MAX_STR_SIZE];
|
||||||
|
get_file_name(filename, sizeof(filename), path);
|
||||||
|
|
||||||
|
if (avatar_set(m, path, len) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||||
|
"Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.",
|
||||||
|
MAX_AVATAR_FILE_SIZE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
|
||||||
|
}
|
||||||
|
|
||||||
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
line_info_clear(self->chatwin->hst);
|
line_info_clear(self->chatwin->hst);
|
||||||
wclear(window);
|
force_refresh(window);
|
||||||
endwin();
|
|
||||||
refresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *errmsg;
|
|
||||||
|
|
||||||
/* check arguments */
|
|
||||||
if (argc != 3) {
|
if (argc != 3) {
|
||||||
errmsg = "Invalid syntax.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *ip = argv[1];
|
const char *ip = argv[1];
|
||||||
const char *port = argv[2];
|
const char *port_str = argv[2];
|
||||||
const char *key = argv[3];
|
const char *ascii_key = argv[3];
|
||||||
|
|
||||||
if (atoi(port) == 0) {
|
long int port = strtol(port_str, NULL, 10);
|
||||||
errmsg = "Invalid syntax.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
if (port <= 0 || port > MAX_PORT_RANGE) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *binary_string = hex_string_to_bin(key);
|
char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||||
tox_bootstrap_from_address(m, ip, TOX_ENABLE_IPV6_DEFAULT, htons(atoi(port)), (uint8_t *) binary_string);
|
|
||||||
free(binary_string);
|
if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_BOOTSTRAP err;
|
||||||
|
tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err);
|
||||||
|
tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err);
|
||||||
|
|
||||||
|
switch (err) {
|
||||||
|
case TOX_ERR_BOOTSTRAP_BAD_HOST:
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_BOOTSTRAP_BAD_PORT:
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_BOOTSTRAP_NULL:
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
if (argc < 1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long int req = strtol(argv[1], NULL, 10);
|
||||||
|
|
||||||
|
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 (!FrndRequests.request[req].active) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = FrndRequests.max_idx; i > 0; --i) {
|
||||||
|
if (FrndRequests.request[i - 1].active)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrndRequests.max_idx = i;
|
||||||
|
--FrndRequests.num_requests;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *errmsg;
|
|
||||||
|
|
||||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||||
errmsg = " * Warning: Too many windows are open.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int groupnum = tox_add_groupchat(m);
|
if (argc < 1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio");
|
||||||
if (groupnum == -1) {
|
|
||||||
errmsg = "Group chat instance failed to initialize.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
uint8_t type;
|
||||||
errmsg = "Group chat window failed to initialize.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
if (!strcasecmp(argv[1], "audio"))
|
||||||
tox_del_groupchat(m, groupnum);
|
type = TOX_CONFERENCE_TYPE_AV;
|
||||||
|
else if (!strcasecmp(argv[1], "text"))
|
||||||
|
type = TOX_CONFERENCE_TYPE_TEXT;
|
||||||
|
else {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char msg[MAX_STR_SIZE];
|
if (type != TOX_CONFERENCE_TYPE_TEXT) {
|
||||||
snprintf(msg, sizeof(msg), "Group chat created as %d.", groupnum);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_CONFERENCE_NEW err;
|
||||||
|
|
||||||
|
uint32_t groupnum = tox_conference_new(m, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_CONFERENCE_NEW_OK) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||||
|
tox_conference_delete(m, groupnum, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *msg;
|
const char *msg;
|
||||||
struct chatlog *log = self->chatwin->log;
|
struct chatlog *log = self->chatwin->log;
|
||||||
|
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
if (log->log_on)
|
if (log->log_on)
|
||||||
msg = "Logging for this window is ON. Type \"/log off\" to disable.";
|
msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)";
|
||||||
else
|
else
|
||||||
msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
|
msg = "Logging for this window is OFF; type \"/log on\" to enable.";
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *swch = argv[1];
|
const char *swch = argv[1];
|
||||||
|
|
||||||
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
|
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
|
||||||
|
char myid[TOX_ADDRESS_SIZE];
|
||||||
|
tox_self_get_address(m, (uint8_t *) myid);
|
||||||
|
|
||||||
|
int log_ret = -1;
|
||||||
|
|
||||||
if (self->is_chat) {
|
if (self->is_chat) {
|
||||||
friends[self->num].logging_on = true;
|
Friends.list[self->num].logging_on = true;
|
||||||
log_enable(self->name, friends[self->num].pub_key, log);
|
log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
|
||||||
} else if (self->is_prompt) {
|
} else if (self->is_prompt) {
|
||||||
char myid[TOX_FRIEND_ADDRESS_SIZE];
|
log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT);
|
||||||
tox_get_address(m, (uint8_t *) myid);
|
|
||||||
log_enable(self->name, myid, log);
|
|
||||||
} else if (self->is_groupchat) {
|
} else if (self->is_groupchat) {
|
||||||
log_enable(self->name, NULL, log);
|
log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = "Logging enabled";
|
msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize.";
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||||
return;
|
return;
|
||||||
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
||||||
if (self->is_chat)
|
if (self->is_chat)
|
||||||
friends[self->num].logging_on = false;
|
Friends.list[self->num].logging_on = false;
|
||||||
|
|
||||||
log_disable(log);
|
log_disable(log);
|
||||||
|
|
||||||
msg = "Logging disabled";
|
msg = "Logging disabled.";
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.";
|
msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.";
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
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])
|
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0};
|
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||||
char address[TOX_FRIEND_ADDRESS_SIZE];
|
char bin_id[TOX_ADDRESS_SIZE];
|
||||||
tox_get_address(m, (uint8_t *) address);
|
tox_self_get_address(m, (uint8_t *) bin_id);
|
||||||
|
|
||||||
size_t i;
|
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID.");
|
||||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
|
return;
|
||||||
char xx[3];
|
|
||||||
snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff);
|
|
||||||
strcat(id, xx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, id, SYS_MSG, 0, 0);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||||
|
char bin_id[TOX_ADDRESS_SIZE];
|
||||||
|
tox_self_get_address(m, (uint8_t *) bin_id);
|
||||||
|
|
||||||
|
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
|
tox_self_get_name(m, (uint8_t *) nick);
|
||||||
|
size_t nick_len = tox_self_get_name_size(m);
|
||||||
|
nick[nick_len] = '\0';
|
||||||
|
|
||||||
|
size_t data_file_len = strlen(DATA_FILE);
|
||||||
|
char dir[data_file_len + 1];
|
||||||
|
size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir);
|
||||||
|
|
||||||
|
#ifdef QRPNG
|
||||||
|
|
||||||
|
if (argc == 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'");
|
||||||
|
return;
|
||||||
|
} else if (!strcmp(argv[1], "txt")) {
|
||||||
|
|
||||||
|
#endif /* QRPNG */
|
||||||
|
char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1];
|
||||||
|
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
|
||||||
|
|
||||||
|
if (ID_to_QRcode_txt(id_string, qr_path) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
|
||||||
|
|
||||||
|
#ifdef QRPNG
|
||||||
|
} else if (!strcmp(argv[1], "png")) {
|
||||||
|
char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT_PNG) + 1];
|
||||||
|
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG);
|
||||||
|
|
||||||
|
if (ID_to_QRcode_png(id_string, qr_path) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* QRPNG */
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *errmsg;
|
|
||||||
|
|
||||||
/* check arguments */
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
errmsg = "Invalid name.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *nick = argv[1];
|
char nick[MAX_STR_SIZE];
|
||||||
int len = strlen(nick);
|
size_t len = 0;
|
||||||
|
|
||||||
if (nick[0] == '\"') {
|
if (argv[1][0] == '\"') { /* remove opening and closing quotes */
|
||||||
++nick;
|
snprintf(nick, sizeof(nick), "%s", &argv[1][1]);
|
||||||
len -= 2;
|
len = strlen(nick) - 1;
|
||||||
nick[len] = '\0';
|
nick[len] = '\0';
|
||||||
|
} else {
|
||||||
|
snprintf(nick, sizeof(nick), "%s", argv[1]);
|
||||||
|
len = strlen(nick);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid_nick(nick)) {
|
if (!valid_nick(nick)) {
|
||||||
errmsg = "Invalid name.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||||
nick[len] = '\0';
|
nick[len] = '\0';
|
||||||
|
|
||||||
tox_set_name(m, (uint8_t *) nick, (uint16_t) len);
|
tox_self_set_name(m, (uint8_t *) nick, len, NULL);
|
||||||
prompt_update_nick(prompt, nick);
|
prompt_update_nick(prompt, nick);
|
||||||
|
|
||||||
store_data(m, DATA_FILE);
|
store_data(m, DATA_FILE);
|
||||||
@ -360,27 +524,49 @@ 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])
|
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *errmsg;
|
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
errmsg = "Wrong number of arguments.";
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *msg = argv[1];
|
if (argv[1][0] != '\"') {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
|
||||||
if (msg[0] != '\"') {
|
|
||||||
errmsg = "Note must be enclosed in quotes.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
++msg;
|
/* remove opening and closing quotes and replace linebreaks with spaces */
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
snprintf(msg, sizeof(msg), "%s", &argv[1][1]);
|
||||||
int len = strlen(msg) - 1;
|
int len = strlen(msg) - 1;
|
||||||
msg[len] = '\0';
|
msg[len] = '\0';
|
||||||
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
|
strsubst(msg, '\n', ' ');
|
||||||
prompt_update_statusmessage(prompt, msg);
|
|
||||||
|
prompt_update_statusmessage(prompt, m, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
long int nospam = rand();
|
||||||
|
|
||||||
|
if (argc > 0) {
|
||||||
|
nospam = strtol(argv[1], NULL, 16);
|
||||||
|
|
||||||
|
if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t old_nospam = tox_self_get_nospam(m);
|
||||||
|
tox_self_set_nospam(m, (uint32_t) nospam);
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:");
|
||||||
|
cmd_myid(window, self, m, 0, NULL);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||||
|
"Any services that relied on your old ID will need to be updated manually.");
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'",
|
||||||
|
old_nospam);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
@ -393,50 +579,84 @@ void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
|||||||
exit_toxic_success(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])
|
||||||
{
|
{
|
||||||
char *msg = NULL;
|
if (FrndRequests.num_requests == 0) {
|
||||||
char *errmsg;
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (argc >= 2) {
|
int i, j;
|
||||||
msg = argv[2];
|
int count = 0;
|
||||||
|
|
||||||
if (msg[0] != '\"') {
|
for (i = 0; i < FrndRequests.max_idx; ++i) {
|
||||||
errmsg = "Note must be enclosed in quotes.";
|
if (!FrndRequests.request[i].active)
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
continue;
|
||||||
return;
|
|
||||||
|
char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0};
|
||||||
|
|
||||||
|
for (j = 0; j < TOX_PUBLIC_KEY_SIZE; ++j) {
|
||||||
|
char d[3];
|
||||||
|
snprintf(d, sizeof(d), "%02X", FrndRequests.request[i].key[j] & 0xff);
|
||||||
|
strcat(id, d);
|
||||||
}
|
}
|
||||||
} else if (argc != 1) {
|
|
||||||
errmsg = "Wrong number of arguments.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *status = argv[1];
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
|
||||||
str_to_lower(status);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg);
|
||||||
|
|
||||||
TOX_USERSTATUS status_kind;
|
if (++count < FrndRequests.num_requests)
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||||
if (!strcmp(status, "online"))
|
|
||||||
status_kind = TOX_USERSTATUS_NONE;
|
|
||||||
else if (!strcmp(status, "away"))
|
|
||||||
status_kind = TOX_USERSTATUS_AWAY;
|
|
||||||
else if (!strcmp(status, "busy"))
|
|
||||||
status_kind = TOX_USERSTATUS_BUSY;
|
|
||||||
else {
|
|
||||||
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tox_set_user_status(m, status_kind);
|
|
||||||
prompt_update_status(prompt, status_kind);
|
|
||||||
|
|
||||||
if (msg != NULL) {
|
|
||||||
++msg;
|
|
||||||
int len = strlen(msg) - 1;
|
|
||||||
msg[len] = '\0'; /* remove opening and closing quotes */
|
|
||||||
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
|
|
||||||
prompt_update_statusmessage(prompt, msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
bool have_note = false;
|
||||||
|
const char *errmsg;
|
||||||
|
|
||||||
|
lock_status ();
|
||||||
|
|
||||||
|
if (argc >= 2) {
|
||||||
|
have_note = true;
|
||||||
|
} else if (argc < 1) {
|
||||||
|
errmsg = "Require a status. Statuses are: online, busy and away.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *status_str = argv[1];
|
||||||
|
TOX_USER_STATUS status;
|
||||||
|
|
||||||
|
if (!strcasecmp(status_str, "online"))
|
||||||
|
status = TOX_USER_STATUS_NONE;
|
||||||
|
else if (!strcasecmp(status_str, "away"))
|
||||||
|
status = TOX_USER_STATUS_AWAY;
|
||||||
|
else if (!strcasecmp(status_str, "busy"))
|
||||||
|
status = TOX_USER_STATUS_BUSY;
|
||||||
|
else {
|
||||||
|
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_self_set_status(m, status);
|
||||||
|
prompt_update_status(prompt, status);
|
||||||
|
|
||||||
|
if (have_note) {
|
||||||
|
if (argv[2][0] != '\"') {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove opening and closing quotes */
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
snprintf(msg, sizeof(msg), "%s", &argv[2][1]);
|
||||||
|
int len = strlen(msg) - 1;
|
||||||
|
msg[len] = '\0';
|
||||||
|
|
||||||
|
prompt_update_statusmessage(prompt, m, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
unlock_status ();
|
||||||
|
}
|
||||||
|
@ -20,30 +20,44 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _global_commands_h
|
#ifndef GLOBAL_COMMANDS_H
|
||||||
#define _global_commands_h
|
#define GLOBAL_COMMANDS_H
|
||||||
|
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
|
|
||||||
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
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_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_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_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_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_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_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_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_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_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
|
||||||
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg);
|
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg);
|
||||||
|
|
||||||
#ifdef _SUPPORT_AUDIO
|
#ifdef AUDIO
|
||||||
void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
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]);
|
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
#endif /* _SUPPORT_AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
#endif /* #define _global_commands_h */
|
#ifdef VIDEO
|
||||||
|
void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* #define GLOBAL_COMMANDS_H */
|
||||||
|
86
src/group_commands.c
Normal file
86
src/group_commands.c
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/* group_commands.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
TOX_ERR_CONFERENCE_TITLE err;
|
||||||
|
char title[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
if (argc < 1) {
|
||||||
|
size_t tlen = tox_conference_get_title_size(m, self->num, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_CONFERENCE_TITLE_OK) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
title[tlen] = '\0';
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv[1][0] != '\"') {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove opening and closing quotes */
|
||||||
|
snprintf(title, sizeof(title), "%s", &argv[1][1]);
|
||||||
|
int len = strlen(title) - 1;
|
||||||
|
title[len] = '\0';
|
||||||
|
|
||||||
|
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_window_title(self, title, len);
|
||||||
|
|
||||||
|
char timefrmt[TIME_STR_SIZE];
|
||||||
|
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||||
|
|
||||||
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
|
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||||
|
size_t sn_len = tox_self_get_name_size(m);
|
||||||
|
selfnick[sn_len] = '\0';
|
||||||
|
|
||||||
|
line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
|
||||||
|
|
||||||
|
char tmp_event[MAX_STR_SIZE];
|
||||||
|
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||||
|
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
/* dns.c
|
/* group_commands.h
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
@ -20,13 +20,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Does DNS lookup for addr and puts resulting tox id in id_bin.
|
#ifndef GROUP_COMMANDS_H
|
||||||
Return 0 on success, -1 on failure. */
|
#define GROUP_COMMANDS_H
|
||||||
|
|
||||||
#ifndef _dns_h
|
#include "windows.h"
|
||||||
#define _dns_h
|
#include "toxic.h"
|
||||||
|
|
||||||
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
|
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void dns3_lookup(ToxWindow *self, Tox *m, char *id_bin, char *addr, char *msg);
|
|
||||||
|
|
||||||
#endif /* #define _dns_h */
|
#endif /* GROUP_COMMANDS_H */
|
534
src/groupchat.c
534
src/groupchat.c
@ -26,8 +26,24 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <OpenAL/al.h>
|
||||||
|
#include <OpenAL/alc.h>
|
||||||
|
#else
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
/* compatibility with older versions of OpenAL */
|
||||||
|
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||||
|
#include <AL/alext.h>
|
||||||
|
#endif /* ALC_ALL_DEVICES_SPECIFIER */
|
||||||
|
#endif /* __APPLE__ */
|
||||||
|
#endif /* AUDIO */
|
||||||
|
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
@ -41,6 +57,9 @@
|
|||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
|
#include "notify.h"
|
||||||
|
#include "autocomplete.h"
|
||||||
|
#include "audio_device.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
|
|
||||||
@ -48,27 +67,74 @@ static GroupChat groupchats[MAX_GROUPCHAT_NUM];
|
|||||||
static int max_groupchat_index = 0;
|
static int max_groupchat_index = 0;
|
||||||
|
|
||||||
extern struct user_settings *user_settings;
|
extern struct user_settings *user_settings;
|
||||||
|
extern struct Winthread Winthread;
|
||||||
|
|
||||||
/* temporary until group chats have unique commands */
|
#if defined(AUDIO) && defined(PYTHON)
|
||||||
extern const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
|
#define AC_NUM_GROUP_COMMANDS 25
|
||||||
|
#elif AUDIO
|
||||||
|
#define AC_NUM_GROUP_COMMANDS 24
|
||||||
|
#elif PYTHON
|
||||||
|
#define AC_NUM_GROUP_COMMANDS 21
|
||||||
|
#else
|
||||||
|
#define AC_NUM_GROUP_COMMANDS 20
|
||||||
|
#endif /* AUDIO */
|
||||||
|
|
||||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
/* Array of groupchat command names used for tab completion. */
|
||||||
|
static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||||
|
{ "/accept" },
|
||||||
|
{ "/add" },
|
||||||
|
{ "/avatar" },
|
||||||
|
{ "/clear" },
|
||||||
|
{ "/close" },
|
||||||
|
{ "/connect" },
|
||||||
|
{ "/decline" },
|
||||||
|
{ "/exit" },
|
||||||
|
{ "/group" },
|
||||||
|
{ "/help" },
|
||||||
|
{ "/log" },
|
||||||
|
{ "/myid" },
|
||||||
|
{ "/myqr" },
|
||||||
|
{ "/nick" },
|
||||||
|
{ "/note" },
|
||||||
|
{ "/nospam" },
|
||||||
|
{ "/quit" },
|
||||||
|
{ "/requests" },
|
||||||
|
{ "/status" },
|
||||||
|
{ "/title" },
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
|
||||||
|
{ "/run" },
|
||||||
|
|
||||||
|
#endif /* PYTHON */
|
||||||
|
};
|
||||||
|
|
||||||
|
int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type)
|
||||||
{
|
{
|
||||||
if (groupnum > MAX_GROUPCHAT_NUM)
|
if (groupnum > MAX_GROUPCHAT_NUM) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxWindow self = new_group_chat(m, groupnum);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i <= max_groupchat_index; ++i) {
|
for (i = 0; i <= max_groupchat_index; ++i) {
|
||||||
if (!groupchats[i].active) {
|
if (!groupchats[i].active) {
|
||||||
groupchats[i].chatwin = add_window(m, new_group_chat(m, groupnum));
|
groupchats[i].chatwin = add_window(m, self);
|
||||||
groupchats[i].active = true;
|
groupchats[i].active = true;
|
||||||
groupchats[i].num_peers = 0;
|
groupchats[i].num_peers = 0;
|
||||||
|
groupchats[i].type = type;
|
||||||
|
groupchats[i].start_time = get_unix_time();
|
||||||
|
|
||||||
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
||||||
groupchats[i].oldpeer_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].peer_name_lengths = malloc(sizeof(uint16_t));
|
||||||
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
|
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
|
||||||
|
|
||||||
|
if (groupchats[i].peer_names == NULL || groupchats[i].oldpeer_names == NULL
|
||||||
|
|| groupchats[i].peer_name_lengths == NULL || groupchats[i].oldpeer_name_lengths == NULL)
|
||||||
|
exit_toxic_err("failed in init_groupchat_win", FATALERR_MEMORY);
|
||||||
|
|
||||||
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
||||||
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
|
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
|
||||||
|
|
||||||
@ -84,7 +150,7 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kill_groupchat_window(ToxWindow *self)
|
static void kill_groupchat_window(ToxWindow *self)
|
||||||
{
|
{
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
@ -94,15 +160,14 @@ void kill_groupchat_window(ToxWindow *self)
|
|||||||
delwin(ctx->history);
|
delwin(ctx->history);
|
||||||
delwin(ctx->sidebar);
|
delwin(ctx->sidebar);
|
||||||
free(ctx->log);
|
free(ctx->log);
|
||||||
free(ctx->hst);
|
|
||||||
free(ctx);
|
free(ctx);
|
||||||
free(self->help);
|
free(self->help);
|
||||||
del_window(self);
|
del_window(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum)
|
||||||
{
|
{
|
||||||
tox_del_groupchat(m, groupnum);
|
tox_conference_delete(m, groupnum, NULL);
|
||||||
|
|
||||||
free(groupchats[groupnum].peer_names);
|
free(groupchats[groupnum].peer_names);
|
||||||
free(groupchats[groupnum].oldpeer_names);
|
free(groupchats[groupnum].oldpeer_names);
|
||||||
@ -121,8 +186,47 @@ static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
|||||||
kill_groupchat_window(self);
|
kill_groupchat_window(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
/* destroys and re-creates groupchat window with or without the peerlist */
|
||||||
const char *msg, uint16_t len)
|
void redraw_groupchat_win(ToxWindow *self)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
endwin();
|
||||||
|
refresh();
|
||||||
|
clear();
|
||||||
|
|
||||||
|
int x2, y2;
|
||||||
|
getmaxyx(stdscr, y2, x2);
|
||||||
|
y2 -= 2;
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ctx->sidebar) {
|
||||||
|
delwin(ctx->sidebar);
|
||||||
|
ctx->sidebar = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
delwin(ctx->linewin);
|
||||||
|
delwin(ctx->history);
|
||||||
|
delwin(self->window);
|
||||||
|
|
||||||
|
self->window = newwin(y2, x2, 0, 0);
|
||||||
|
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||||
|
|
||||||
|
if (self->show_peerlist) {
|
||||||
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||||
|
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||||
|
} else {
|
||||||
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollok(ctx->history, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
|
||||||
|
TOX_MESSAGE_TYPE type, const char *msg, size_t len)
|
||||||
{
|
{
|
||||||
if (self->num != groupnum)
|
if (self->num != groupnum)
|
||||||
return;
|
return;
|
||||||
@ -130,76 +234,65 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
|
|||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
char nick[TOX_MAX_NAME_LENGTH];
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
int n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
|
get_group_nick_truncate(m, nick, peernum, groupnum);
|
||||||
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];
|
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||||
uint16_t sn_len = tox_get_self_name(m, (uint8_t *) selfnick);
|
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||||
|
|
||||||
|
size_t sn_len = tox_self_get_name_size(m);
|
||||||
selfnick[sn_len] = '\0';
|
selfnick[sn_len] = '\0';
|
||||||
|
|
||||||
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
|
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
|
||||||
|
|
||||||
bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH - 1);
|
/* Only play sound if mentioned by someone else */
|
||||||
|
if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) {
|
||||||
|
sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, 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;
|
nick_clr = RED;
|
||||||
|
} else {
|
||||||
|
sound_notify(self, silent, NT_WNDALERT_1, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
alert_window(self, alert_type, beep);
|
|
||||||
|
|
||||||
char timefrmt[TIME_STR_SIZE];
|
char timefrmt[TIME_STR_SIZE];
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
line_info_add(self, timefrmt, nick, NULL, msg, IN_MSG, 0, nick_clr);
|
line_info_add(self, timefrmt, nick, NULL, type == TOX_MESSAGE_TYPE_NORMAL ? IN_MSG : IN_ACTION, 0, nick_clr, "%s", msg);
|
||||||
write_to_log(msg, nick, ctx->log, false);
|
write_to_log(msg, nick, ctx->log, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *action,
|
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum, const char *title,
|
||||||
uint16_t len)
|
size_t length)
|
||||||
{
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
if (self->num != groupnum)
|
if (self->num != groupnum)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
set_window_title(self, title, length);
|
||||||
|
|
||||||
/* 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';
|
|
||||||
|
|
||||||
bool nick_match = strcasestr(action, selfnick);
|
|
||||||
|
|
||||||
if (nick_match) {
|
|
||||||
alert_type = WINDOW_ALERT_0;
|
|
||||||
beep = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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';
|
|
||||||
|
|
||||||
char timefrmt[TIME_STR_SIZE];
|
char timefrmt[TIME_STR_SIZE];
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
line_info_add(self, timefrmt, nick, NULL, action, ACTION, 0, 0);
|
/* don't announce title when we join the room */
|
||||||
write_to_log(action, nick, ctx->log, true);
|
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
|
||||||
|
return;
|
||||||
|
|
||||||
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
|
get_group_nick_truncate(m, nick, peernum, groupnum);
|
||||||
|
line_info_add(self, timefrmt, nick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
|
||||||
|
|
||||||
|
char tmp_event[MAX_STR_SIZE];
|
||||||
|
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||||
|
write_to_log(tmp_event, nick, ctx->log, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Puts two copies of peerlist/lengths in chat instance */
|
/* 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)
|
static void copy_peernames(Tox *m, uint32_t gnum, size_t npeers)
|
||||||
{
|
{
|
||||||
/* Assumes these are initiated in init_groupchat_win */
|
/* Assumes these are initiated in init_groupchat_win */
|
||||||
free(groupchats[gnum].peer_names);
|
free(groupchats[gnum].peer_names);
|
||||||
@ -209,41 +302,93 @@ static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], ui
|
|||||||
|
|
||||||
int N = TOX_MAX_NAME_LENGTH;
|
int N = TOX_MAX_NAME_LENGTH;
|
||||||
|
|
||||||
groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N);
|
groupchats[gnum].peer_names = calloc(1, sizeof(uint8_t) * npeers * N);
|
||||||
groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N);
|
groupchats[gnum].oldpeer_names = calloc(1, sizeof(uint8_t) * npeers * N);
|
||||||
groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers);
|
groupchats[gnum].peer_name_lengths = calloc(1, sizeof(uint16_t) * npeers);
|
||||||
groupchats[gnum].oldpeer_name_lengths = malloc(sizeof(uint16_t) * npeers);
|
groupchats[gnum].oldpeer_name_lengths = calloc(1, sizeof(uint16_t) * npeers);
|
||||||
|
|
||||||
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
|
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
|
||||||
|| groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
|
|| groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
|
||||||
exit_toxic_err("failed in copy_peernames", FATALERR_MEMORY);
|
exit_toxic_err("failed in copy_peernames()", FATALERR_MEMORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t unknown_len = (uint16_t) strlen(UNKNOWN_NAME);
|
uint16_t u_len = strlen(UNKNOWN_NAME);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < npeers; ++i) {
|
for (i = 0; i < npeers; ++i) {
|
||||||
if (string_is_empty((char *) peerlist[i])) {
|
uint8_t name[TOX_MAX_NAME_LENGTH];
|
||||||
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
TOX_ERR_CONFERENCE_PEER_QUERY err;
|
||||||
groupchats[gnum].peer_name_lengths[i] = unknown_len;
|
|
||||||
|
size_t n_len = tox_conference_peer_get_name_size(m, gnum, i, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
|
||||||
|
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, u_len);
|
||||||
|
groupchats[gnum].peer_names[i * N + u_len] = '\0';
|
||||||
|
groupchats[gnum].peer_name_lengths[i] = u_len;
|
||||||
} else {
|
} else {
|
||||||
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], N);
|
tox_conference_peer_get_name(m, gnum, i, name, NULL);
|
||||||
uint16_t n_len = lengths[i];
|
|
||||||
|
|
||||||
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
|
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||||
|
memcpy(&groupchats[gnum].peer_names[i * N], name, n_len);
|
||||||
groupchats[gnum].peer_names[i * N + n_len] = '\0';
|
groupchats[gnum].peer_names[i * N + n_len] = '\0';
|
||||||
groupchats[gnum].peer_name_lengths[i] = n_len;
|
groupchats[gnum].peer_name_lengths[i] = n_len;
|
||||||
|
filter_str((char *) &groupchats[gnum].peer_names[i * N], n_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
|
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
|
||||||
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths,
|
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths, sizeof(uint16_t) * npeers);
|
||||||
sizeof(uint16_t) * npeers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
struct group_add_thrd {
|
||||||
uint8_t change)
|
Tox *m;
|
||||||
|
ToxWindow *self;
|
||||||
|
uint32_t peernum;
|
||||||
|
uint32_t groupnum;
|
||||||
|
time_t timestamp;
|
||||||
|
pthread_t tid;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Waits GROUP_EVENT_WAIT seconds for a new peer to set their name before announcing them */
|
||||||
|
void *group_add_wait(void *data)
|
||||||
|
{
|
||||||
|
struct group_add_thrd *thrd = (struct group_add_thrd *) data;
|
||||||
|
ToxWindow *self = thrd->self;
|
||||||
|
Tox *m = thrd->m;
|
||||||
|
char peername[TOX_MAX_NAME_LENGTH];
|
||||||
|
|
||||||
|
/* keep polling for a name that differs from the default until we run out of time */
|
||||||
|
while (true) {
|
||||||
|
usleep(100000);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
get_group_nick_truncate(m, peername, thrd->peernum, thrd->groupnum);
|
||||||
|
|
||||||
|
if (strcmp(peername, DEFAULT_TOX_NAME) || timed_out(thrd->timestamp, GROUP_EVENT_WAIT)) {
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *event = "has joined the room";
|
||||||
|
char timefrmt[TIME_STR_SIZE];
|
||||||
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
line_info_add(self, timefrmt, (char *) peername, NULL, CONNECTION, 0, GREEN, event);
|
||||||
|
write_to_log(event, (char *) peername, self->chatwin->log, true);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
pthread_attr_destroy(&thrd->attr);
|
||||||
|
free(thrd);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
|
||||||
|
TOX_CONFERENCE_STATE_CHANGE change)
|
||||||
{
|
{
|
||||||
if (self->num != groupnum)
|
if (self->num != groupnum)
|
||||||
return;
|
return;
|
||||||
@ -251,8 +396,15 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
if (groupnum > max_groupchat_index)
|
if (groupnum > max_groupchat_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum);
|
TOX_ERR_CONFERENCE_PEER_QUERY err;
|
||||||
int num_peers = groupchats[groupnum].num_peers;
|
|
||||||
|
uint32_t num_peers = tox_conference_peer_count(m, groupnum, &err);
|
||||||
|
|
||||||
|
if (err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
|
||||||
|
groupchats[groupnum].num_peers = num_peers;
|
||||||
|
} else {
|
||||||
|
num_peers = groupchats[groupnum].num_peers;
|
||||||
|
}
|
||||||
|
|
||||||
if (peernum > num_peers)
|
if (peernum > num_peers)
|
||||||
return;
|
return;
|
||||||
@ -260,23 +412,19 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
/* get old peer name before updating name list */
|
/* get old peer name before updating name list */
|
||||||
uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
|
uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
|
||||||
|
|
||||||
if (change != TOX_CHAT_CHANGE_PEER_ADD) {
|
if (change != TOX_CONFERENCE_STATE_CHANGE_PEER_JOIN) {
|
||||||
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH],
|
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(oldpeername));
|
||||||
sizeof(oldpeername));
|
|
||||||
uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
|
uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
|
||||||
oldpeername[old_n_len] = '\0';
|
oldpeername[old_n_len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update name/len lists */
|
/* Update name/len lists */
|
||||||
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
|
copy_peernames(m, groupnum, num_peers);
|
||||||
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 */
|
/* get current peername then sort namelist */
|
||||||
uint8_t peername[TOX_MAX_NAME_LENGTH];
|
uint8_t peername[TOX_MAX_NAME_LENGTH];
|
||||||
|
|
||||||
if (change != TOX_CHAT_CHANGE_PEER_DEL) {
|
if (change != TOX_CONFERENCE_STATE_CHANGE_PEER_EXIT) {
|
||||||
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
|
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
|
||||||
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
|
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
|
||||||
peername[n_len] = '\0';
|
peername[n_len] = '\0';
|
||||||
@ -286,20 +434,44 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
char *event;
|
const char *event;
|
||||||
char timefrmt[TIME_STR_SIZE];
|
char timefrmt[TIME_STR_SIZE];
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
switch (change) {
|
switch (change) {
|
||||||
case TOX_CHAT_CHANGE_PEER_ADD:
|
case TOX_CONFERENCE_STATE_CHANGE_PEER_JOIN:
|
||||||
event = "has joined the room";
|
if (!timed_out(groupchats[groupnum].start_time, GROUP_EVENT_WAIT))
|
||||||
line_info_add(self, timefrmt, (char *) peername, NULL, event, CONNECTION, 0, GREEN);
|
break;
|
||||||
write_to_log(event, (char *) peername, ctx->log, true);
|
|
||||||
|
struct group_add_thrd *thrd = malloc(sizeof(struct group_add_thrd));
|
||||||
|
thrd->m = m;
|
||||||
|
thrd->peernum = peernum;
|
||||||
|
thrd->groupnum = groupnum;
|
||||||
|
thrd->self = self;
|
||||||
|
thrd->timestamp = get_unix_time();
|
||||||
|
|
||||||
|
if (pthread_attr_init(&thrd->attr) != 0) {
|
||||||
|
free(thrd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_attr_setdetachstate(&thrd->attr, PTHREAD_CREATE_DETACHED) != 0) {
|
||||||
|
pthread_attr_destroy(&thrd->attr);
|
||||||
|
free(thrd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_create(&thrd->tid, &thrd->attr, group_add_wait, (void *) thrd) != 0) {
|
||||||
|
pthread_attr_destroy(&thrd->attr);
|
||||||
|
free(thrd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_CHAT_CHANGE_PEER_DEL:
|
case TOX_CONFERENCE_STATE_CHANGE_PEER_EXIT:
|
||||||
event = "has left the room";
|
event = "has left the room";
|
||||||
line_info_add(self, timefrmt, (char *) oldpeername, NULL, event, CONNECTION, 0, 0);
|
line_info_add(self, timefrmt, (char *) oldpeername, NULL, DISCONNECTION, 0, RED, event);
|
||||||
|
|
||||||
if (groupchats[self->num].side_pos > 0)
|
if (groupchats[self->num].side_pos > 0)
|
||||||
--groupchats[self->num].side_pos;
|
--groupchats[self->num].side_pos;
|
||||||
@ -307,9 +479,16 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
write_to_log(event, (char *) oldpeername, ctx->log, true);
|
write_to_log(event, (char *) oldpeername, ctx->log, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_CHAT_CHANGE_PEER_NAME:
|
case TOX_CONFERENCE_STATE_CHANGE_PEER_NAME_CHANGE:
|
||||||
|
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* ignore initial name change (TODO: this is a bad way to do this) */
|
||||||
|
if (strcmp((char *) oldpeername, DEFAULT_TOX_NAME) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
event = " is now known as ";
|
event = " is now known as ";
|
||||||
line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, event, NAME_CHANGE, 0, 0);
|
line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, NAME_CHANGE, 0, 0, event);
|
||||||
|
|
||||||
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
|
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
|
||||||
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername);
|
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername);
|
||||||
@ -317,7 +496,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
break;
|
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, char *action)
|
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
|
||||||
@ -327,9 +506,10 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *a
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tox_group_action_send(m, self->num, (uint8_t *) action, strlen(action)) == -1) {
|
TOX_ERR_CONFERENCE_SEND_MESSAGE err;
|
||||||
char *errmsg = " * Failed to send action.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_ACTION, (uint8_t *) action, strlen(action), &err)) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send action (error %d)", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,7 +521,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (x2 <= 0)
|
if (x2 <= 0 || y2 <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (self->help->active) {
|
if (self->help->active) {
|
||||||
@ -349,7 +529,10 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ltr) { /* char is printable */
|
if (ctx->pastemode && key == '\r')
|
||||||
|
key = '\n';
|
||||||
|
|
||||||
|
if (ltr || key == '\n') { /* char is printable */
|
||||||
input_new_char(self, key, x, y, x2, y2);
|
input_new_char(self, key, x, y, x2, y2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -364,57 +547,71 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
if (ctx->len > 0) {
|
if (ctx->len > 0) {
|
||||||
int diff;
|
int diff;
|
||||||
|
|
||||||
if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e'))
|
/* TODO: make this not suck */
|
||||||
diff = complete_line(ctx, groupchats[self->num].peer_names,
|
if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) {
|
||||||
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
|
diff = complete_line(self, groupchats[self->num].peer_names, groupchats[self->num].num_peers,
|
||||||
else
|
TOX_MAX_NAME_LENGTH);
|
||||||
diff = complete_line(ctx, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
|
} else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) {
|
||||||
|
diff = dir_match(self, m, ctx->line, L"/avatar");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
else if (wcsncmp(ctx->line, L"/run \"", wcslen(L"/run \"")) == 0) {
|
||||||
|
diff = dir_match(self, m, ctx->line, L"/run");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
else {
|
||||||
|
diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
if (diff != -1) {
|
if (diff != -1) {
|
||||||
if (x + diff > x2 - 1) {
|
if (x + diff > x2 - 1) {
|
||||||
wmove(self->window, y, x + diff);
|
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||||
ctx->start += diff;
|
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
||||||
} else {
|
|
||||||
wmove(self->window, y, x + diff);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
beep();
|
sound_notify(self, notif_error, 0, NULL);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
beep();
|
sound_notify(self, notif_error, 0, NULL);
|
||||||
}
|
}
|
||||||
} else if (key == T_KEY_C_RB) { /* Scroll peerlist up and down one position */
|
} else if (key == user_settings->key_peer_list_down) { /* Scroll peerlist up and down one position */
|
||||||
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
|
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
|
||||||
|
|
||||||
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
|
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
|
||||||
++groupchats[self->num].side_pos;
|
++groupchats[self->num].side_pos;
|
||||||
} else if (key == T_KEY_C_LB) {
|
} else if (key == user_settings->key_peer_list_up) {
|
||||||
if (groupchats[self->num].side_pos > 0)
|
if (groupchats[self->num].side_pos > 0)
|
||||||
--groupchats[self->num].side_pos;
|
--groupchats[self->num].side_pos;
|
||||||
} else if (key == '\n') {
|
} else if (key == '\r') {
|
||||||
rm_trailing_spaces_buf(ctx);
|
rm_trailing_spaces_buf(ctx);
|
||||||
|
|
||||||
char line[MAX_STR_SIZE];
|
if (!wstring_is_empty(ctx->line)) {
|
||||||
|
|
||||||
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(ctx);
|
add_line_to_hist(ctx);
|
||||||
|
|
||||||
if (line[0] == '/') {
|
wstrsubst(ctx->line, L'¶', L'\n');
|
||||||
if (strcmp(line, "/close") == 0) {
|
|
||||||
close_groupchat(self, m, self->num);
|
char line[MAX_STR_SIZE];
|
||||||
return;
|
|
||||||
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
|
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
|
||||||
send_group_action(self, ctx, m, line + strlen("/me "));
|
memset(&line, 0, sizeof(line));
|
||||||
|
|
||||||
|
if (line[0] == '/') {
|
||||||
|
if (strcmp(line, "/close") == 0) {
|
||||||
|
close_groupchat(self, m, self->num);
|
||||||
|
return;
|
||||||
|
} 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 {
|
} else {
|
||||||
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
|
TOX_ERR_CONFERENCE_SEND_MESSAGE err;
|
||||||
}
|
|
||||||
} else if (!string_is_empty(line)) {
|
if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) line, strlen(line), &err)) {
|
||||||
if (tox_group_message_send(m, self->num, (uint8_t *) line, strlen(line)) == -1) {
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err);
|
||||||
char *errmsg = " * Failed to send message.";
|
}
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,9 +626,15 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (x2 <= 0 || y2 <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
line_info_print(self);
|
line_info_print(self);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
|
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
@ -440,41 +643,52 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
||||||
|
|
||||||
wclear(ctx->sidebar);
|
wclear(ctx->sidebar);
|
||||||
|
|
||||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
||||||
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
|
|
||||||
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
|
|
||||||
|
|
||||||
int num_peers = groupchats[self->num].num_peers;
|
if (self->show_peerlist) {
|
||||||
|
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
|
||||||
|
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
|
||||||
|
|
||||||
wmove(ctx->sidebar, 0, 1);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
wattron(ctx->sidebar, A_BOLD);
|
int num_peers = groupchats[self->num].num_peers;
|
||||||
wprintw(ctx->sidebar, "Peers: %d\n", num_peers);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
wattroff(ctx->sidebar, A_BOLD);
|
|
||||||
|
|
||||||
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
wmove(ctx->sidebar, 0, 1);
|
||||||
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
wattron(ctx->sidebar, A_BOLD);
|
||||||
|
wprintw(ctx->sidebar, "Peers: %d\n", num_peers);
|
||||||
|
wattroff(ctx->sidebar, A_BOLD);
|
||||||
|
|
||||||
int N = TOX_MAX_NAME_LENGTH;
|
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
||||||
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
||||||
wmove(ctx->sidebar, i + 2, 1);
|
int i;
|
||||||
int peer = i + groupchats[self->num].side_pos;
|
|
||||||
|
|
||||||
/* truncate nick to fit in side panel without modifying list */
|
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
||||||
char tmpnck[TOX_MAX_NAME_LENGTH];
|
wmove(ctx->sidebar, i + 2, 1);
|
||||||
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * N], SIDEBAR_WIDTH - 2);
|
|
||||||
tmpnck[SIDEBAR_WIDTH - 2] = '\0';
|
|
||||||
|
|
||||||
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
uint32_t peer = i + groupchats[self->num].side_pos;
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
/* truncate nick to fit in side panel without modifying list */
|
||||||
|
char tmpnck[TOX_MAX_NAME_LENGTH];
|
||||||
|
int maxlen = SIDEBAR_WIDTH - 2;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * TOX_MAX_NAME_LENGTH], maxlen);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
tmpnck[maxlen] = '\0';
|
||||||
|
|
||||||
|
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int y, x;
|
int y, x;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
(void) x;
|
(void) x;
|
||||||
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
|
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||||
wmove(self->window, y + 1, new_x);
|
wmove(self->window, y + 1, new_x);
|
||||||
|
|
||||||
wrefresh(self->window);
|
wrefresh(self->window);
|
||||||
@ -488,6 +702,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
|
|||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (x2 <= 0 || y2 <= 0)
|
||||||
|
exit_toxic_err("failed in groupchat_onInit", FATALERR_CURSES);
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||||
@ -502,8 +719,13 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
line_info_init(ctx->hst);
|
line_info_init(ctx->hst);
|
||||||
|
|
||||||
if (user_settings->autolog == AUTOLOG_ON)
|
if (user_settings->autolog == AUTOLOG_ON) {
|
||||||
log_enable(self->name, NULL, ctx->log);
|
char myid[TOX_ADDRESS_SIZE];
|
||||||
|
tox_self_get_address(m, (uint8_t *) myid);
|
||||||
|
|
||||||
|
if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1)
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||||
|
}
|
||||||
|
|
||||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
||||||
|
|
||||||
@ -511,7 +733,7 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
|
|||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxWindow new_group_chat(Tox *m, int groupnum)
|
ToxWindow new_group_chat(Tox *m, uint32_t groupnum)
|
||||||
{
|
{
|
||||||
ToxWindow ret;
|
ToxWindow ret;
|
||||||
memset(&ret, 0, sizeof(ret));
|
memset(&ret, 0, sizeof(ret));
|
||||||
@ -524,7 +746,7 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
|
|||||||
ret.onInit = &groupchat_onInit;
|
ret.onInit = &groupchat_onInit;
|
||||||
ret.onGroupMessage = &groupchat_onGroupMessage;
|
ret.onGroupMessage = &groupchat_onGroupMessage;
|
||||||
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
|
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
|
||||||
ret.onGroupAction = &groupchat_onGroupAction;
|
ret.onGroupTitleChange = &groupchat_onGroupTitleChange;
|
||||||
|
|
||||||
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
|
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
|
||||||
|
|
||||||
@ -538,6 +760,8 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
|
|||||||
ret.help = help;
|
ret.help = help;
|
||||||
|
|
||||||
ret.num = groupnum;
|
ret.num = groupnum;
|
||||||
|
ret.show_peerlist = true;
|
||||||
|
ret.active_box = -1;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _groupchat_h
|
#ifndef GROUPCHAT_H
|
||||||
#define _groupchat_h
|
#define GROUPCHAT_H
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
@ -29,20 +29,27 @@
|
|||||||
#define SIDEBAR_WIDTH 16
|
#define SIDEBAR_WIDTH 16
|
||||||
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
||||||
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
|
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
|
||||||
|
#define GROUP_EVENT_WAIT 3
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int chatwin;
|
int chatwin;
|
||||||
bool active;
|
bool active;
|
||||||
int num_peers;
|
uint8_t type;
|
||||||
|
uint32_t num_peers;
|
||||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||||
|
time_t start_time;
|
||||||
uint8_t *peer_names;
|
uint8_t *peer_names;
|
||||||
uint8_t *oldpeer_names;
|
uint8_t *oldpeer_names;
|
||||||
uint16_t *peer_name_lengths;
|
uint16_t *peer_name_lengths;
|
||||||
uint16_t *oldpeer_name_lengths;
|
uint16_t *oldpeer_name_lengths;
|
||||||
} GroupChat;
|
} GroupChat;
|
||||||
|
|
||||||
void kill_groupchat_window(ToxWindow *self);
|
void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum);
|
||||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum);
|
int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type);
|
||||||
ToxWindow new_group_chat(Tox *m, int groupnum);
|
|
||||||
|
|
||||||
#endif /* #define _groupchat_h */
|
/* destroys and re-creates groupchat window with or without the peerlist */
|
||||||
|
void redraw_groupchat_win(ToxWindow *self);
|
||||||
|
|
||||||
|
ToxWindow new_group_chat(Tox *m, uint32_t groupnum);
|
||||||
|
|
||||||
|
#endif /* #define GROUPCHAT_H */
|
||||||
|
217
src/help.c
217
src/help.c
@ -26,8 +26,13 @@
|
|||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "api.h"
|
||||||
|
|
||||||
#define HELP_MENU_HEIGHT 7
|
#ifdef PYTHON
|
||||||
|
#define HELP_MENU_HEIGHT 10
|
||||||
|
#else
|
||||||
|
#define HELP_MENU_HEIGHT 9
|
||||||
|
#endif /* PYTHON */
|
||||||
#define HELP_MENU_WIDTH 26
|
#define HELP_MENU_WIDTH 26
|
||||||
|
|
||||||
void help_init_menu(ToxWindow *self)
|
void help_init_menu(ToxWindow *self)
|
||||||
@ -60,6 +65,9 @@ static void help_init_window(ToxWindow *self, int height, int width)
|
|||||||
int y2, x2;
|
int y2, x2;
|
||||||
getmaxyx(stdscr, y2, x2);
|
getmaxyx(stdscr, y2, x2);
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
height = MIN(height, y2);
|
height = MIN(height, y2);
|
||||||
width = MIN(width, x2);
|
width = MIN(width, x2);
|
||||||
|
|
||||||
@ -77,21 +85,39 @@ static void help_draw_menu(ToxWindow *self)
|
|||||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, " G");
|
wprintw(win, " g");
|
||||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "lobal commands\n");
|
wprintw(win, "lobal commands\n");
|
||||||
|
|
||||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, " C");
|
wprintw(win, " c");
|
||||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "hat commands\n");
|
wprintw(win, "hat commands\n");
|
||||||
|
|
||||||
|
wprintw(win, " g");
|
||||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, " K");
|
wprintw(win, "r");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "oup commands\n");
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, " p");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "lugin commands\n");
|
||||||
|
#endif /* PYTHON */
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, " f");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "riendlist controls\n");
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, " k");
|
||||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "ey bindings\n");
|
wprintw(win, "ey bindings\n");
|
||||||
|
|
||||||
wprintw(win, " E");
|
wprintw(win, " e");
|
||||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "x");
|
wprintw(win, "x");
|
||||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
@ -110,11 +136,11 @@ static void help_draw_bottom_menu(WINDOW *win)
|
|||||||
wmove(win, y2 - 2, 1);
|
wmove(win, y2 - 2, 1);
|
||||||
|
|
||||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, " M");
|
wprintw(win, " m");
|
||||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "ain menu |");
|
wprintw(win, "ain menu |");
|
||||||
|
|
||||||
wprintw(win, " E");
|
wprintw(win, " e");
|
||||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "x");
|
wprintw(win, "x");
|
||||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
@ -131,27 +157,53 @@ static void help_draw_global(ToxWindow *self)
|
|||||||
wprintw(win, "Global Commands:\n");
|
wprintw(win, "Global Commands:\n");
|
||||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
wprintw(win, " /add <id> <msg> : Add contact with optional message\n");
|
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
|
||||||
wprintw(win, " /accept <n> : Accept friend request\n");
|
wprintw(win, " /accept <id> : Accept friend request\n");
|
||||||
|
wprintw(win, " /avatar <path> : Set an avatar (leave path empty to unset)\n");
|
||||||
|
wprintw(win, " /decline <id> : Decline friend request\n");
|
||||||
|
wprintw(win, " /requests : List pending friend requests\n");
|
||||||
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
|
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
|
||||||
wprintw(win, " /status <type> <msg> : Set status with optional note\n");
|
wprintw(win, " /status <type> <msg> : Set status with optional note\n");
|
||||||
wprintw(win, " /note <msg> : Set a personal note\n");
|
wprintw(win, " /note <msg> : Set a personal note\n");
|
||||||
wprintw(win, " /nick <nick> : Set your nickname\n");
|
wprintw(win, " /nick <nick> : Set your nickname\n");
|
||||||
|
wprintw(win, " /nospam <value> : Change part of your Tox ID to stop spam\n");
|
||||||
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
|
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
|
||||||
wprintw(win, " /groupchat : Create a group chat\n");
|
wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
|
||||||
wprintw(win, " /myid : Print your ID\n");
|
wprintw(win, " /myid : Print your Tox ID\n");
|
||||||
|
#ifdef QRPNG
|
||||||
|
wprintw(win, " /myqr <txt> or <png> : Print your Tox ID's QR code to a file.\n");
|
||||||
|
#else
|
||||||
|
wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n");
|
||||||
|
#endif /* QRPNG */
|
||||||
wprintw(win, " /clear : Clear window history\n");
|
wprintw(win, " /clear : Clear window history\n");
|
||||||
wprintw(win, " /close : Close the current chat window\n");
|
wprintw(win, " /close : Close the current chat window\n");
|
||||||
wprintw(win, " /quit or /exit : Exit Toxic\n");
|
wprintw(win, " /quit or /exit : Exit Toxic\n");
|
||||||
|
|
||||||
#ifdef _SUPPORT_AUDIO
|
#ifdef AUDIO
|
||||||
wattron(win, A_BOLD);
|
wattron(win, A_BOLD);
|
||||||
wprintw(win, "\n Audio:\n");
|
wprintw(win, "\n Audio:\n");
|
||||||
wattroff(win, A_BOLD);
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
wprintw(win, " /lsdev <type> : List devices where type: in|out\n");
|
wprintw(win, " /lsdev <type> : List devices where type: in|out\n");
|
||||||
wprintw(win, " /sdev <type> <id> : Set active device\n");
|
wprintw(win, " /sdev <type> <id> : Set active device\n");
|
||||||
#endif /* _SUPPORT_AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "\n Video:\n");
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
|
wprintw(win, " /lsvdev <type> : List video devices where type: in|out\n");
|
||||||
|
wprintw(win, " /svdev <type> <id> : Set active video device\n");
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "\n Scripting:\n");
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
|
wprintw(win, " /run <path> : Load and run the script at path\n");
|
||||||
|
#endif /* PYTHON */
|
||||||
|
|
||||||
help_draw_bottom_menu(win);
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
@ -172,22 +224,29 @@ static void help_draw_chat(ToxWindow *self)
|
|||||||
wprintw(win, " /invite <n> : Invite contact to a group chat\n");
|
wprintw(win, " /invite <n> : Invite contact to a group chat\n");
|
||||||
wprintw(win, " /join : Join a pending group chat\n");
|
wprintw(win, " /join : Join a pending group chat\n");
|
||||||
wprintw(win, " /sendfile <path> : Send a file\n");
|
wprintw(win, " /sendfile <path> : Send a file\n");
|
||||||
wprintw(win, " /savefile <n> : Receive 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 _SUPPORT_AUDIO
|
#ifdef AUDIO
|
||||||
wattron(win, A_BOLD);
|
wattron(win, A_BOLD);
|
||||||
wprintw(win, "\n Audio:\n");
|
wprintw(win, "\n Audio:\n");
|
||||||
wattroff(win, A_BOLD);
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
wprintw(win, " /call : Audio call\n");
|
wprintw(win, " /call : Audio call\n");
|
||||||
wprintw(win, " /cancel : Cancel call\n");
|
wprintw(win, " /answer : Answer incoming call\n");
|
||||||
wprintw(win, " /answer : Answer incomming call\n");
|
|
||||||
wprintw(win, " /reject : Reject incoming call\n");
|
wprintw(win, " /reject : Reject incoming call\n");
|
||||||
wprintw(win, " /hangup : Hangup active call\n");
|
wprintw(win, " /hangup : Hangup active call\n");
|
||||||
wprintw(win, " /sdev <type> <id> : Change active device\n");
|
wprintw(win, " /sdev <type> <id> : Change active device\n");
|
||||||
wprintw(win, " /mute <type> : Mute active device if in call\n");
|
wprintw(win, " /mute <type> : Mute active device if in call\n");
|
||||||
wprintw(win, " /sense <n> : VAD sensitivity treshold\n");
|
wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
|
||||||
#endif /* _SUPPORT_AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "\n Video:\n");
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
wprintw(win, " /video : Toggle video call\n");
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
help_draw_bottom_menu(win);
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
@ -210,6 +269,70 @@ static void help_draw_keys(ToxWindow *self)
|
|||||||
wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\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+H : Move to the bottom of window history\n");
|
||||||
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n");
|
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n");
|
||||||
|
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n");
|
||||||
|
wprintw(win, " Ctrl+J : Insert new line\n");
|
||||||
|
wprintw(win, " Ctrl+T : Toggle paste mode\n\n");
|
||||||
|
wprintw(win, " (Note: Custom keybindings override these defaults.)\n\n");
|
||||||
|
|
||||||
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
|
wrefresh(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help_draw_group(ToxWindow *self)
|
||||||
|
{
|
||||||
|
WINDOW *win = self->help->win;
|
||||||
|
|
||||||
|
wmove(win, 1, 1);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
wprintw(win, "Group commands:\n");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
wprintw(win, " /title <msg> : Set group title (show current title if no msg)\n\n");
|
||||||
|
|
||||||
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
|
wrefresh(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
static void help_draw_plugin(ToxWindow *self)
|
||||||
|
{
|
||||||
|
WINDOW *win = self->help->win;
|
||||||
|
|
||||||
|
wmove(win, 1, 1);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
wprintw(win, "Plugin commands:\n");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
draw_handler_help(win);
|
||||||
|
|
||||||
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
|
wrefresh(win);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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);
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
@ -219,32 +342,57 @@ static void help_draw_keys(ToxWindow *self)
|
|||||||
|
|
||||||
void help_onKey(ToxWindow *self, wint_t key)
|
void help_onKey(ToxWindow *self, wint_t key)
|
||||||
{
|
{
|
||||||
switch(key) {
|
int height;
|
||||||
|
switch (key) {
|
||||||
case 'x':
|
case 'x':
|
||||||
case T_KEY_ESC:
|
case T_KEY_ESC:
|
||||||
help_exit(self);
|
help_exit(self);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'c':
|
case 'c':
|
||||||
#ifdef _SUPPORT_AUDIO
|
#ifdef VIDEO
|
||||||
|
help_init_window(self, 22, 80);
|
||||||
|
#elif AUDIO
|
||||||
help_init_window(self, 19, 80);
|
help_init_window(self, 19, 80);
|
||||||
#else
|
#else
|
||||||
help_init_window(self, 9, 80);
|
help_init_window(self, 10, 80);
|
||||||
#endif
|
#endif
|
||||||
self->help->type = HELP_CHAT;
|
self->help->type = HELP_CHAT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'g':
|
case 'g':
|
||||||
#ifdef _SUPPORT_AUDIO
|
height = 22;
|
||||||
help_init_window(self, 21, 80);
|
#ifdef VIDEO
|
||||||
#else
|
height += 8;
|
||||||
help_init_window(self, 17, 80);
|
#elif AUDIO
|
||||||
|
height += 4;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef PYTHON
|
||||||
|
height += 2;
|
||||||
|
#endif
|
||||||
|
help_init_window(self, height, 80);
|
||||||
self->help->type = HELP_GLOBAL;
|
self->help->type = HELP_GLOBAL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'k':
|
case 'r':
|
||||||
|
help_init_window(self, 6, 80);
|
||||||
|
self->help->type = HELP_GROUP;
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
case 'p':
|
||||||
|
help_init_window(self, 4 + num_registered_handlers(), help_max_width());
|
||||||
|
self->help->type = HELP_PLUGIN;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case 'f':
|
||||||
help_init_window(self, 10, 80);
|
help_init_window(self, 10, 80);
|
||||||
|
self->help->type = HELP_CONTACTS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'k':
|
||||||
|
help_init_window(self, 15, 80);
|
||||||
self->help->type = HELP_KEYS;
|
self->help->type = HELP_KEYS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -259,7 +407,7 @@ void help_onDraw(ToxWindow *self)
|
|||||||
{
|
{
|
||||||
curs_set(0);
|
curs_set(0);
|
||||||
|
|
||||||
switch(self->help->type) {
|
switch (self->help->type) {
|
||||||
case HELP_MENU:
|
case HELP_MENU:
|
||||||
help_draw_menu(self);
|
help_draw_menu(self);
|
||||||
return;
|
return;
|
||||||
@ -276,7 +424,18 @@ void help_onDraw(ToxWindow *self)
|
|||||||
help_draw_keys(self);
|
help_draw_keys(self);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HELP_GROUP:
|
case HELP_CONTACTS:
|
||||||
|
help_draw_contacts(self);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HELP_GROUP:
|
||||||
|
help_draw_group(self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
case HELP_PLUGIN:
|
||||||
|
help_draw_plugin(self);
|
||||||
|
break;
|
||||||
|
#endif /* PYTHON */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
src/help.h
10
src/help.h
@ -20,8 +20,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _help_h
|
#ifndef HELP_H
|
||||||
#define _help_h
|
#define HELP_H
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
@ -32,10 +32,14 @@ typedef enum {
|
|||||||
HELP_CHAT,
|
HELP_CHAT,
|
||||||
HELP_GROUP,
|
HELP_GROUP,
|
||||||
HELP_KEYS,
|
HELP_KEYS,
|
||||||
|
HELP_CONTACTS,
|
||||||
|
#ifdef PYTHON
|
||||||
|
HELP_PLUGIN,
|
||||||
|
#endif
|
||||||
} HELP_TYPES;
|
} HELP_TYPES;
|
||||||
|
|
||||||
void help_onDraw(ToxWindow *self);
|
void help_onDraw(ToxWindow *self);
|
||||||
void help_init_menu(ToxWindow *self);
|
void help_init_menu(ToxWindow *self);
|
||||||
void help_onKey(ToxWindow *self, wint_t key);
|
void help_onKey(ToxWindow *self, wint_t key);
|
||||||
|
|
||||||
#endif /* #define _help_h */
|
#endif /* #define HELP_H */
|
||||||
|
79
src/input.c
79
src/input.c
@ -31,22 +31,30 @@
|
|||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "toxic_strings.h"
|
#include "toxic_strings.h"
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
|
#include "notify.h"
|
||||||
|
#include "groupchat.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
/* add a char to input field and buffer */
|
/* add a char to input field and buffer */
|
||||||
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||||
{
|
{
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
/* this is the only place we need to do this check */
|
||||||
|
if (key == '\n')
|
||||||
|
key = L'¶';
|
||||||
|
|
||||||
int cur_len = wcwidth(key);
|
int cur_len = wcwidth(key);
|
||||||
|
|
||||||
/* this is the only place we need to do this check */
|
|
||||||
if (cur_len == -1) {
|
if (cur_len == -1) {
|
||||||
beep();
|
sound_notify(self, notif_error, 0, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (add_char_to_buf(ctx, key) == -1) {
|
if (add_char_to_buf(ctx, key) == -1) {
|
||||||
beep();
|
sound_notify(self, notif_error, 0, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,17 +70,15 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
|
|||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
if (del_char_buf_bck(ctx) == -1) {
|
if (del_char_buf_bck(ctx) == -1) {
|
||||||
beep();
|
sound_notify(self, notif_error, 0, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cur_len = wcwidth(ctx->line[ctx->pos - 1]);
|
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
|
||||||
int s_len = wcwidth(ctx->line[ctx->start - 1]);
|
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
|
||||||
|
|
||||||
if (ctx->start && (x >= mx_x - cur_len))
|
if (ctx->start && (x >= mx_x - cur_len))
|
||||||
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
|
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
|
||||||
else if (ctx->start && (ctx->pos == ctx->len))
|
|
||||||
ctx->start = MAX(0, ctx->start - cur_len);
|
|
||||||
else if (ctx->start)
|
else if (ctx->start)
|
||||||
ctx->start = MAX(0, ctx->start - cur_len);
|
ctx->start = MAX(0, ctx->start - cur_len);
|
||||||
}
|
}
|
||||||
@ -81,21 +87,32 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
|
|||||||
static void input_delete(ToxWindow *self)
|
static void input_delete(ToxWindow *self)
|
||||||
{
|
{
|
||||||
if (del_char_buf_frnt(self->chatwin) == -1)
|
if (del_char_buf_frnt(self->chatwin) == -1)
|
||||||
beep();
|
sound_notify(self, notif_error, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* delete last typed word */
|
||||||
|
static void input_del_word(ToxWindow *self, int x, int mx_x)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (del_word_buf(ctx) == -1) {
|
||||||
|
sound_notify(self, notif_error, 0, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* deletes entire line before cursor from input field and buffer */
|
/* deletes entire line before cursor from input field and buffer */
|
||||||
static void input_discard(ToxWindow *self)
|
static void input_discard(ToxWindow *self)
|
||||||
{
|
{
|
||||||
if (discard_buf(self->chatwin) == -1)
|
if (discard_buf(self->chatwin) == -1)
|
||||||
beep();
|
sound_notify(self, notif_error, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* deletes entire line after cursor from input field and buffer */
|
/* deletes entire line after cursor from input field and buffer */
|
||||||
static void input_kill(ChatContext *ctx)
|
static void input_kill(ChatContext *ctx)
|
||||||
{
|
{
|
||||||
if (kill_buf(ctx) == -1)
|
if (kill_buf(ctx) == -1)
|
||||||
beep();
|
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void input_yank(ToxWindow *self, int x, int mx_x)
|
static void input_yank(ToxWindow *self, int x, int mx_x)
|
||||||
@ -103,7 +120,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
|
|||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
if (yank_buf(ctx) == -1) {
|
if (yank_buf(ctx) == -1) {
|
||||||
beep();
|
sound_notify(self, notif_error, 0, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +128,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
|
|||||||
|
|
||||||
if (x + yank_cols >= mx_x) {
|
if (x + yank_cols >= mx_x) {
|
||||||
int rmdr = MAX(0, (x + yank_cols) - mx_x);
|
int rmdr = MAX(0, (x + yank_cols) - mx_x);
|
||||||
int s_len = wcswidth(&ctx->line[ctx->start], rmdr);
|
int s_len = MAX(0, wcswidth(&ctx->line[ctx->start], rmdr));
|
||||||
ctx->start += s_len + 1;
|
ctx->start += s_len + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +140,7 @@ static void input_mv_end(ToxWindow *self, int y, int mx_x)
|
|||||||
|
|
||||||
ctx->pos = ctx->len;
|
ctx->pos = ctx->len;
|
||||||
|
|
||||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||||
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
|
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,16 +164,13 @@ static void input_mv_left(ToxWindow *self, int x, int mx_x)
|
|||||||
if (ctx->pos <= 0)
|
if (ctx->pos <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int cur_len = wcwidth(ctx->line[ctx->pos - 1]);
|
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;
|
--ctx->pos;
|
||||||
|
|
||||||
int s_len = wcwidth(ctx->line[ctx->start - 1]);
|
|
||||||
|
|
||||||
if (ctx->start && (x >= mx_x - cur_len))
|
if (ctx->start && (x >= mx_x - cur_len))
|
||||||
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
|
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
|
||||||
else if (ctx->start && (ctx->pos == ctx->len))
|
|
||||||
ctx->start = MAX(0, ctx->start - cur_len);
|
|
||||||
else if (ctx->start)
|
else if (ctx->start)
|
||||||
ctx->start = MAX(0, ctx->start - cur_len);
|
ctx->start = MAX(0, ctx->start - cur_len);
|
||||||
}
|
}
|
||||||
@ -185,7 +199,8 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x)
|
|||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
fetch_hist_item(ctx, key);
|
fetch_hist_item(ctx, key);
|
||||||
ctx->start = mx_x * (ctx->len / mx_x);
|
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||||
|
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.
|
/* Handles non-printable input keys that behave the same for all types of chat windows.
|
||||||
@ -216,6 +231,10 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
|||||||
input_yank(self, x, mx_x);
|
input_yank(self, x, mx_x);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_KEY_C_W:
|
||||||
|
input_del_word(self, x, mx_x);
|
||||||
|
break;
|
||||||
|
|
||||||
case KEY_HOME:
|
case KEY_HOME:
|
||||||
case T_KEY_C_A:
|
case T_KEY_C_A:
|
||||||
input_mv_home(self);
|
input_mv_home(self);
|
||||||
@ -239,10 +258,30 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
|||||||
input_history(self, key, mx_x);
|
input_history(self, key, mx_x);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_KEY_C_L:
|
||||||
|
force_refresh(self->chatwin->history);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
match = false;
|
match = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: this special case is ugly.
|
||||||
|
maybe convert entire function to if/else and make them all customizable keys? */
|
||||||
|
if (!match) {
|
||||||
|
if (key == user_settings->key_toggle_peerlist) {
|
||||||
|
if (self->is_groupchat) {
|
||||||
|
self->show_peerlist ^= 1;
|
||||||
|
redraw_groupchat_win(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
match = true;
|
||||||
|
} else if (key == user_settings->key_toggle_pastemode) {
|
||||||
|
self->chatwin->pastemode ^= 1;
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _input_h
|
#ifndef INPUT_H
|
||||||
#define _input_h
|
#define INPUT_H
|
||||||
|
|
||||||
/* add a char to input field and buffer for given chatcontext */
|
/* add a char to input field and buffer for given chatcontext */
|
||||||
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
|
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
|
||||||
@ -30,4 +30,4 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_
|
|||||||
return true if key matches a function, false otherwise */
|
return true if key matches a function, false otherwise */
|
||||||
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
|
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
|
||||||
|
|
||||||
#endif /* #define _input_h */
|
#endif /* #define INPUT_H */
|
242
src/line_info.c
242
src/line_info.c
@ -23,12 +23,16 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
#include "groupchat.h"
|
#include "groupchat.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "notify.h"
|
||||||
|
#include "message_queue.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
|
||||||
extern struct user_settings *user_settings;
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
@ -44,8 +48,8 @@ void line_info_init(struct history *hst)
|
|||||||
hst->queue_sz = 0;
|
hst->queue_sz = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* resets line_start (page end) */
|
/* resets line_start (moves to end of chat history) */
|
||||||
static void line_info_reset_start(ToxWindow *self, struct history *hst)
|
void line_info_reset_start(ToxWindow *self, struct history *hst)
|
||||||
{
|
{
|
||||||
struct line_info *line = hst->line_end;
|
struct line_info *line = hst->line_end;
|
||||||
|
|
||||||
@ -55,7 +59,7 @@ static void line_info_reset_start(ToxWindow *self, struct history *hst)
|
|||||||
int y2, x2;
|
int y2, x2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
int side_offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;
|
int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;
|
||||||
int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
|
int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
|
||||||
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
|
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
|
||||||
|
|
||||||
@ -87,6 +91,8 @@ void line_info_cleanup(struct history *hst)
|
|||||||
if (hst->queue[i])
|
if (hst->queue[i])
|
||||||
free(hst->queue[i]);
|
free(hst->queue[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(hst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* moves root forward and frees previous root */
|
/* moves root forward and frees previous root */
|
||||||
@ -105,22 +111,13 @@ static void line_info_root_fwd(struct history *hst)
|
|||||||
hst->line_root = tmp;
|
hst->line_root = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* adds a line_info line to queue */
|
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
|
||||||
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)
|
static struct line_info *line_info_ret_queue(struct history *hst)
|
||||||
{
|
{
|
||||||
if (hst->queue_sz <= 0)
|
if (hst->queue_sz <= 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
struct line_info *ret = hst->queue[0];
|
struct line_info *line = hst->queue[0];
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -129,31 +126,66 @@ static struct line_info *line_info_ret_queue(struct history *hst)
|
|||||||
|
|
||||||
--hst->queue_sz;
|
--hst->queue_sz;
|
||||||
|
|
||||||
return ret;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* creates new line_info line and puts it in the queue */
|
/* creates new line_info line and puts it in the queue. */
|
||||||
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, const char *msg, uint8_t type,
|
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||||
uint8_t bold, uint8_t colour)
|
uint8_t bold, uint8_t colour, const char *msg, ...)
|
||||||
{
|
{
|
||||||
|
if (!self)
|
||||||
|
return;
|
||||||
|
|
||||||
struct history *hst = self->chatwin->hst;
|
struct history *hst = self->chatwin->hst;
|
||||||
|
|
||||||
|
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
|
||||||
|
return;
|
||||||
|
|
||||||
struct line_info *new_line = calloc(1, sizeof(struct line_info));
|
struct line_info *new_line = calloc(1, sizeof(struct line_info));
|
||||||
|
|
||||||
if (new_line == NULL)
|
if (new_line == NULL)
|
||||||
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
|
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
char frmt_msg[MAX_LINE_INFO_MSG_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 */
|
int len = 1; /* there will always be a newline */
|
||||||
|
|
||||||
/* for type-specific formatting in print function */
|
/* for type-specific formatting in print function */
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ACTION:
|
case IN_ACTION:
|
||||||
|
|
||||||
|
/* fallthrough */
|
||||||
|
case OUT_ACTION:
|
||||||
|
len += strlen(user_settings->line_normal) + 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IN_MSG:
|
||||||
|
|
||||||
|
/* fallthrough */
|
||||||
|
case OUT_MSG:
|
||||||
|
len += strlen(user_settings->line_normal) + 3;
|
||||||
|
break;
|
||||||
|
|
||||||
case CONNECTION:
|
case CONNECTION:
|
||||||
len += 3;
|
len += strlen(user_settings->line_join) + 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DISCONNECTION:
|
||||||
|
len += strlen(user_settings->line_quit) + 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SYS_MSG:
|
case SYS_MSG:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NAME_CHANGE:
|
||||||
|
len += strlen(user_settings->line_alert) + 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case PROMPT:
|
case PROMPT:
|
||||||
++len;
|
++len;
|
||||||
break;
|
break;
|
||||||
@ -163,21 +195,21 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, cons
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg) {
|
if (frmt_msg[0]) {
|
||||||
snprintf(new_line->msg, sizeof(new_line->msg), "%s", msg);
|
snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg);
|
||||||
len += strlen(new_line->msg);
|
len += strlen(new_line->msg);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; msg[i]; ++i) {
|
for (i = 0; frmt_msg[i]; ++i) {
|
||||||
if (msg[i] == '\n')
|
if (frmt_msg[i] == '\n')
|
||||||
++new_line->newlines;
|
++new_line->newlines;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmstmp) {
|
if (timestr) {
|
||||||
snprintf(new_line->timestamp, sizeof(new_line->timestamp), "%s", tmstmp);
|
snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr);
|
||||||
len += strlen(new_line->timestamp);
|
len += strlen(new_line->timestr) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name1) {
|
if (name1) {
|
||||||
@ -194,8 +226,10 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, cons
|
|||||||
new_line->type = type;
|
new_line->type = type;
|
||||||
new_line->bold = bold;
|
new_line->bold = bold;
|
||||||
new_line->colour = colour;
|
new_line->colour = colour;
|
||||||
|
new_line->noread_flag = false;
|
||||||
|
new_line->timestamp = get_unix_time();
|
||||||
|
|
||||||
line_info_add_queue(hst, new_line);
|
hst->queue[hst->queue_sz++] = new_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
|
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
|
||||||
@ -223,7 +257,7 @@ static void line_info_check_queue(ToxWindow *self)
|
|||||||
if (x2 <= SIDEBAR_WIDTH)
|
if (x2 <= SIDEBAR_WIDTH)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
|
int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
|
||||||
int lines = 1 + line->newlines + (line->len / (x2 - offst));
|
int lines = 1 + line->newlines + (line->len / (x2 - offst));
|
||||||
int max_y = y2 - CHATBOX_HEIGHT;
|
int max_y = y2 - CHATBOX_HEIGHT;
|
||||||
|
|
||||||
@ -237,6 +271,8 @@ static void line_info_check_queue(ToxWindow *self)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define NOREAD_FLAG_TIMEOUT 5 /* seconds before a sent message with no read receipt is flagged as unread */
|
||||||
|
|
||||||
void line_info_print(ToxWindow *self)
|
void line_info_print(ToxWindow *self)
|
||||||
{
|
{
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
@ -270,9 +306,14 @@ void line_info_print(ToxWindow *self)
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case OUT_MSG:
|
case OUT_MSG:
|
||||||
|
|
||||||
|
/* fallthrough */
|
||||||
|
case OUT_MSG_READ:
|
||||||
|
|
||||||
|
/* fallthrough */
|
||||||
case IN_MSG:
|
case IN_MSG:
|
||||||
wattron(win, COLOR_PAIR(BLUE));
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "%s", line->timestamp);
|
wprintw(win, "%s ", line->timestr);
|
||||||
wattroff(win, COLOR_PAIR(BLUE));
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
int nameclr = GREEN;
|
int nameclr = GREEN;
|
||||||
@ -283,34 +324,78 @@ void line_info_print(ToxWindow *self)
|
|||||||
nameclr = CYAN;
|
nameclr = CYAN;
|
||||||
|
|
||||||
wattron(win, COLOR_PAIR(nameclr));
|
wattron(win, COLOR_PAIR(nameclr));
|
||||||
wprintw(win, "%s: ", line->name1);
|
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
|
||||||
wattroff(win, COLOR_PAIR(nameclr));
|
wattroff(win, COLOR_PAIR(nameclr));
|
||||||
|
|
||||||
if (line->msg[0] == '>')
|
char *msg = line->msg;
|
||||||
wattron(win, COLOR_PAIR(GREEN));
|
|
||||||
|
|
||||||
wprintw(win, "%s\n", line->msg);
|
while (msg) {
|
||||||
|
char *line = strsep(&msg, "\n");
|
||||||
|
|
||||||
if (line->msg[0] == '>')
|
if (line[0] == '>')
|
||||||
wattroff(win, COLOR_PAIR(GREEN));
|
wattron(win, COLOR_PAIR(GREEN));
|
||||||
|
else if (line[0] == '<')
|
||||||
|
wattron(win, COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
wprintw(win, "%s%c", line, msg ? '\n' : '\0');
|
||||||
|
|
||||||
|
if (line[0] == '>')
|
||||||
|
wattroff(win, COLOR_PAIR(GREEN));
|
||||||
|
else if (line[0] == '<')
|
||||||
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
// change the \0 set by strsep back to \n
|
||||||
|
if (msg)
|
||||||
|
msg[-1] = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||||
|
wattron(win, COLOR_PAIR(RED));
|
||||||
|
wprintw(win, " x", line->msg);
|
||||||
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
if (line->noread_flag == false) {
|
||||||
|
line->noread_flag = true;
|
||||||
|
line->len += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wprintw(win, "\n", line->msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION:
|
case OUT_ACTION_READ:
|
||||||
|
|
||||||
|
/* fallthrough */
|
||||||
|
case OUT_ACTION:
|
||||||
|
|
||||||
|
/* fallthrough */
|
||||||
|
case IN_ACTION:
|
||||||
wattron(win, COLOR_PAIR(BLUE));
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "%s", line->timestamp);
|
wprintw(win, "%s ", line->timestr);
|
||||||
wattroff(win, COLOR_PAIR(BLUE));
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
wattron(win, COLOR_PAIR(YELLOW));
|
wattron(win, COLOR_PAIR(YELLOW));
|
||||||
wprintw(win, "* %s %s\n", line->name1, line->msg);
|
wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg);
|
||||||
wattroff(win, COLOR_PAIR(YELLOW));
|
wattroff(win, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
|
if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||||
|
wattron(win, COLOR_PAIR(RED));
|
||||||
|
wprintw(win, " x", line->msg);
|
||||||
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
if (line->noread_flag == false) {
|
||||||
|
line->noread_flag = true;
|
||||||
|
line->len += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wprintw(win, "\n", line->msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SYS_MSG:
|
case SYS_MSG:
|
||||||
if (line->timestamp[0]) {
|
if (line->timestr[0]) {
|
||||||
wattron(win, COLOR_PAIR(BLUE));
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "%s", line->timestamp);
|
wprintw(win, "%s ", line->timestr);
|
||||||
wattroff(win, COLOR_PAIR(BLUE));
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,13 +428,33 @@ void line_info_print(ToxWindow *self)
|
|||||||
|
|
||||||
case CONNECTION:
|
case CONNECTION:
|
||||||
wattron(win, COLOR_PAIR(BLUE));
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "%s", line->timestamp);
|
wprintw(win, "%s ", line->timestr);
|
||||||
wattroff(win, COLOR_PAIR(BLUE));
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
wattron(win, COLOR_PAIR(line->colour));
|
wattron(win, COLOR_PAIR(line->colour));
|
||||||
|
wprintw(win, "%s ", user_settings->line_join);
|
||||||
|
|
||||||
wattron(win, A_BOLD);
|
wattron(win, A_BOLD);
|
||||||
wprintw(win, "* %s ", line->name1);
|
wprintw(win, "%s ", line->name1);
|
||||||
wattroff(win, A_BOLD);
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
|
wprintw(win, "%s\n", line->msg);
|
||||||
|
wattroff(win, COLOR_PAIR(line->colour));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DISCONNECTION:
|
||||||
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "%s ", line->timestr);
|
||||||
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
|
wattron(win, COLOR_PAIR(line->colour));
|
||||||
|
wprintw(win, "%s ", user_settings->line_quit);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "%s ", line->name1);
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
wprintw(win, "%s\n", line->msg);
|
wprintw(win, "%s\n", line->msg);
|
||||||
wattroff(win, COLOR_PAIR(line->colour));
|
wattroff(win, COLOR_PAIR(line->colour));
|
||||||
|
|
||||||
@ -357,12 +462,13 @@ void line_info_print(ToxWindow *self)
|
|||||||
|
|
||||||
case NAME_CHANGE:
|
case NAME_CHANGE:
|
||||||
wattron(win, COLOR_PAIR(BLUE));
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "%s", line->timestamp);
|
wprintw(win, "%s ", line->timestr);
|
||||||
wattroff(win, COLOR_PAIR(BLUE));
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
wattron(win, COLOR_PAIR(MAGENTA));
|
wattron(win, COLOR_PAIR(MAGENTA));
|
||||||
|
wprintw(win, "%s ", user_settings->line_alert);
|
||||||
wattron(win, A_BOLD);
|
wattron(win, A_BOLD);
|
||||||
wprintw(win, "* %s", line->name1);
|
wprintw(win, "%s", line->name1);
|
||||||
wattroff(win, A_BOLD);
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
wprintw(win, "%s", line->msg);
|
wprintw(win, "%s", line->msg);
|
||||||
@ -383,6 +489,7 @@ void line_info_print(ToxWindow *self)
|
|||||||
line_info_print(self);
|
line_info_print(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* puts msg in specified line_info msg buffer */
|
||||||
void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
||||||
{
|
{
|
||||||
struct line_info *line = self->chatwin->hst->line_end;
|
struct line_info *line = self->chatwin->hst->line_end;
|
||||||
@ -406,14 +513,14 @@ static void line_info_scroll_up(struct history *hst)
|
|||||||
{
|
{
|
||||||
if (hst->line_start->prev)
|
if (hst->line_start->prev)
|
||||||
hst->line_start = hst->line_start->prev;
|
hst->line_start = hst->line_start->prev;
|
||||||
else beep();
|
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_info_scroll_down(struct history *hst)
|
static void line_info_scroll_down(struct history *hst)
|
||||||
{
|
{
|
||||||
if (hst->line_start->next)
|
if (hst->line_start->next)
|
||||||
hst->line_start = hst->line_start->next;
|
hst->line_start = hst->line_start->next;
|
||||||
else beep();
|
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
||||||
@ -445,35 +552,18 @@ bool line_info_onKey(ToxWindow *self, wint_t key)
|
|||||||
struct history *hst = self->chatwin->hst;
|
struct history *hst = self->chatwin->hst;
|
||||||
bool match = true;
|
bool match = true;
|
||||||
|
|
||||||
switch (key) {
|
if (key == user_settings->key_half_page_up) {
|
||||||
/* TODO: Find good key bindings for all this stuff */
|
line_info_page_up(self, hst);
|
||||||
case T_KEY_C_F:
|
} else if (key == user_settings->key_half_page_down) {
|
||||||
line_info_page_up(self, hst);
|
line_info_page_down(self, hst);
|
||||||
break;
|
} else if (key == user_settings->key_scroll_line_up) {
|
||||||
|
line_info_scroll_up(hst);
|
||||||
case T_KEY_C_V:
|
} else if (key == user_settings->key_scroll_line_down) {
|
||||||
line_info_page_down(self, hst);
|
line_info_scroll_down(hst);
|
||||||
break;
|
} else if (key == user_settings->key_page_bottom) {
|
||||||
|
line_info_reset_start(self, hst);
|
||||||
case KEY_PPAGE:
|
} else {
|
||||||
line_info_scroll_up(hst);
|
match = false;
|
||||||
break;
|
|
||||||
|
|
||||||
case KEY_NPAGE:
|
|
||||||
line_info_scroll_down(hst);
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* case ?:
|
|
||||||
line_info_goto_root(hst);
|
|
||||||
break; */
|
|
||||||
|
|
||||||
case T_KEY_C_H:
|
|
||||||
line_info_reset_start(self, hst);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
match = false;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return match;
|
return match;
|
||||||
|
@ -20,36 +20,43 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _line_info_h
|
#ifndef LINE_INFO_H
|
||||||
#define _line_info_h
|
#define LINE_INFO_H
|
||||||
|
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
|
|
||||||
#define MAX_HISTORY 10000
|
#define MAX_HISTORY 100000
|
||||||
#define MIN_HISTORY 40
|
#define MIN_HISTORY 40
|
||||||
#define MAX_QUEUE 128
|
#define MAX_LINE_INFO_QUEUE 1024
|
||||||
|
#define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* needs extra room for log loading */
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
SYS_MSG,
|
SYS_MSG,
|
||||||
IN_MSG,
|
IN_MSG,
|
||||||
OUT_MSG,
|
OUT_MSG,
|
||||||
|
OUT_MSG_READ, /* for sent messages that have received a read reply. don't set this with line_info_add */
|
||||||
|
IN_ACTION,
|
||||||
|
OUT_ACTION,
|
||||||
|
OUT_ACTION_READ, /* same as OUT_MSG_READ but for actions */
|
||||||
PROMPT,
|
PROMPT,
|
||||||
ACTION,
|
|
||||||
CONNECTION,
|
CONNECTION,
|
||||||
|
DISCONNECTION,
|
||||||
NAME_CHANGE,
|
NAME_CHANGE,
|
||||||
} LINE_TYPE;
|
} LINE_TYPE;
|
||||||
|
|
||||||
struct line_info {
|
struct line_info {
|
||||||
char timestamp[TIME_STR_SIZE];
|
char timestr[TIME_STR_SIZE];
|
||||||
char name1[TOXIC_MAX_NAME_LENGTH];
|
char name1[TOXIC_MAX_NAME_LENGTH + 1];
|
||||||
char name2[TOXIC_MAX_NAME_LENGTH];
|
char name2[TOXIC_MAX_NAME_LENGTH + 1];
|
||||||
char msg[TOX_MAX_MESSAGE_LENGTH];
|
char msg[MAX_LINE_INFO_MSG_SIZE];
|
||||||
|
time_t timestamp;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t bold;
|
uint8_t bold;
|
||||||
uint8_t colour;
|
uint8_t colour;
|
||||||
|
uint8_t noread_flag; /* true if a line should be flagged as unread */
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint16_t len; /* combined len of all strings */
|
uint16_t len; /* combined len of entire line */
|
||||||
uint8_t newlines;
|
uint8_t newlines;
|
||||||
|
|
||||||
struct line_info *prev;
|
struct line_info *prev;
|
||||||
@ -63,13 +70,13 @@ struct history {
|
|||||||
struct line_info *line_end;
|
struct line_info *line_end;
|
||||||
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
|
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
|
||||||
|
|
||||||
struct line_info *queue[MAX_QUEUE];
|
struct line_info *queue[MAX_LINE_INFO_QUEUE];
|
||||||
int queue_sz;
|
int queue_sz;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* creates new line_info line and puts it in the queue */
|
/* creates new line_info line and puts it in the queue. */
|
||||||
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, const char *msg,
|
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||||
uint8_t type, uint8_t bold, uint8_t colour);
|
uint8_t bold, uint8_t colour, const char *msg, ...);
|
||||||
|
|
||||||
/* Prints a section of history starting at line_start */
|
/* Prints a section of history starting at line_start */
|
||||||
void line_info_print(ToxWindow *self);
|
void line_info_print(ToxWindow *self);
|
||||||
@ -83,7 +90,10 @@ void line_info_clear(struct history *hst);
|
|||||||
/* puts msg in specified line_info msg buffer */
|
/* puts msg in specified line_info msg buffer */
|
||||||
void line_info_set(ToxWindow *self, uint32_t id, char *msg);
|
void line_info_set(ToxWindow *self, uint32_t id, char *msg);
|
||||||
|
|
||||||
|
/* resets line_start (moves to end of chat history) */
|
||||||
|
void line_info_reset_start(ToxWindow *self, struct history *hst);
|
||||||
|
|
||||||
void line_info_init(struct history *hst);
|
void line_info_init(struct history *hst);
|
||||||
bool line_info_onKey(ToxWindow *self, wint_t key); /* returns true if key is a match */
|
bool line_info_onKey(ToxWindow *self, wint_t key); /* returns true if key is a match */
|
||||||
|
|
||||||
#endif /* #define _line_info_h */
|
#endif /* #define LINE_INFO_H */
|
||||||
|
239
src/log.c
239
src/log.c
@ -23,6 +23,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "configdir.h"
|
#include "configdir.h"
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
@ -30,59 +31,93 @@
|
|||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
|
||||||
extern struct user_settings *user_settings;
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
|
/* There are three types of logs: chat logs, groupchat logs, and prompt logs (see LOG_TYPE in log.h)
|
||||||
void init_logging_session(char *name, char *key, struct chatlog *log)
|
A prompt log is in the format: LOGDIR/selfkey-home.log
|
||||||
{
|
A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log
|
||||||
if (!log->log_on)
|
A groupchat log is in the format: LOGDIR/selfkey-groupname-date[time].log
|
||||||
return;
|
|
||||||
|
|
||||||
|
Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used.
|
||||||
|
|
||||||
|
Returns 0 on success, -1 if the path is too long */
|
||||||
|
static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype)
|
||||||
|
{
|
||||||
if (!valid_nick(name))
|
if (!valid_nick(name))
|
||||||
name = UNKNOWN_NAME;
|
name = UNKNOWN_NAME;
|
||||||
|
|
||||||
|
const char *namedash = logtype == LOG_PROMPT ? "" : "-";
|
||||||
|
const char *set_path = user_settings->chatlogs_path;
|
||||||
|
|
||||||
char *user_config_dir = get_user_config_dir();
|
char *user_config_dir = get_user_config_dir();
|
||||||
int path_len = strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(name);
|
int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash);
|
||||||
|
path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR);
|
||||||
|
|
||||||
/* use first 4 digits of key as log ident. If no key use a timestamp */
|
/* first 6 digits of selfkey */
|
||||||
char ident[32];
|
char self_id[32];
|
||||||
|
path_len += KEY_IDENT_DIGITS * 2;
|
||||||
|
sprintf(&self_id[0], "%02X", selfkey[0] & 0xff);
|
||||||
|
sprintf(&self_id[2], "%02X", selfkey[1] & 0xff);
|
||||||
|
sprintf(&self_id[4], "%02X", selfkey[2] & 0xff);
|
||||||
|
self_id[KEY_IDENT_DIGITS * 2] = '\0';
|
||||||
|
|
||||||
if (key != NULL) {
|
char other_id[32] = {0};
|
||||||
path_len += (KEY_IDENT_DIGITS * 2 + 5);
|
|
||||||
|
|
||||||
sprintf(&ident[0], "%02X", key[0] & 0xff);
|
switch (logtype) {
|
||||||
sprintf(&ident[2], "%02X", key[1] & 0xff);
|
case LOG_CHAT:
|
||||||
ident[KEY_IDENT_DIGITS * 2 + 1] = '\0';
|
path_len += KEY_IDENT_DIGITS * 2;
|
||||||
} else {
|
sprintf(&other_id[0], "%02X", otherkey[0] & 0xff);
|
||||||
strftime(ident, sizeof(ident), "%Y-%m-%d[%H:%M:%S]", get_time());
|
sprintf(&other_id[2], "%02X", otherkey[1] & 0xff);
|
||||||
path_len += strlen(ident) + 1;
|
sprintf(&other_id[4], "%02X", otherkey[2] & 0xff);
|
||||||
|
other_id[KEY_IDENT_DIGITS * 2] = '\0';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOG_GROUP:
|
||||||
|
strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time());
|
||||||
|
path_len += strlen(other_id);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path_len > MAX_STR_SIZE) {
|
if (path_len >= destsize) {
|
||||||
log->log_on = false;
|
|
||||||
free(user_config_dir);
|
free(user_config_dir);
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char log_path[MAX_STR_SIZE];
|
if (!string_is_empty(set_path))
|
||||||
|
snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id);
|
||||||
snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
|
else
|
||||||
user_config_dir, CONFIGDIR, name, ident);
|
snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id);
|
||||||
|
|
||||||
free(user_config_dir);
|
free(user_config_dir);
|
||||||
|
|
||||||
log->file = fopen(log_path, "a");
|
return 0;
|
||||||
|
|
||||||
if (log->file == NULL) {
|
|
||||||
log->log_on = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_to_log(const char *msg, char *name, struct chatlog *log, bool event)
|
/* Opens log file or creates a new one */
|
||||||
|
static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
||||||
|
{
|
||||||
|
if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char log_path[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
log->file = fopen(log_path, "a+");
|
||||||
|
snprintf(log->path, sizeof(log->path), "%s", log_path);
|
||||||
|
|
||||||
|
if (log->file == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOG_FLUSH_LIMIT 1 /* limits calls to fflush to a max of one per LOG_FLUSH_LIMIT seconds */
|
||||||
|
|
||||||
|
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
|
||||||
{
|
{
|
||||||
if (!log->log_on)
|
if (!log->log_on)
|
||||||
return;
|
return;
|
||||||
@ -99,33 +134,137 @@ void write_to_log(const char *msg, char *name, struct chatlog *log, bool event)
|
|||||||
else
|
else
|
||||||
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
|
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
|
||||||
|
|
||||||
const char *t = user_settings->time == TIME_12 ? "%Y/%m/%d [%I:%M:%S %p]" : "%Y/%m/%d [%H:%M:%S]";
|
const char *t = user_settings->log_timestamp_format;
|
||||||
char s[MAX_STR_SIZE];
|
char s[MAX_STR_SIZE];
|
||||||
strftime(s, MAX_STR_SIZE, t, get_time());
|
strftime(s, MAX_STR_SIZE, t, get_time());
|
||||||
fprintf(log->file, "%s %s %s\n", s, name_frmt, msg);
|
fprintf(log->file, "%s %s %s\n", s, name_frmt, msg);
|
||||||
|
|
||||||
uint64_t curtime = get_unix_time();
|
if (timed_out(log->lastwrite, LOG_FLUSH_LIMIT)) {
|
||||||
|
|
||||||
if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) {
|
|
||||||
fflush(log->file);
|
fflush(log->file);
|
||||||
log->lastwrite = curtime;
|
log->lastwrite = get_unix_time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_enable(char *name, char *key, struct chatlog *log)
|
|
||||||
{
|
|
||||||
log->log_on = true;
|
|
||||||
|
|
||||||
if (log->file == NULL)
|
|
||||||
init_logging_session(name, key, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
void log_disable(struct chatlog *log)
|
void log_disable(struct chatlog *log)
|
||||||
{
|
{
|
||||||
log->log_on = false;
|
if (log->file != NULL)
|
||||||
|
|
||||||
if (log->file != NULL) {
|
|
||||||
fclose(log->file);
|
fclose(log->file);
|
||||||
log->file = NULL;
|
|
||||||
}
|
memset(log, 0, sizeof(struct chatlog));
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
||||||
|
{
|
||||||
|
log->log_on = true;
|
||||||
|
|
||||||
|
if (log->file != NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
|
||||||
|
log_disable(log);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loads previous history from chat log */
|
||||||
|
void load_chat_history(ToxWindow *self, struct chatlog *log)
|
||||||
|
{
|
||||||
|
if (log->file == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
off_t sz = file_size(log->path);
|
||||||
|
|
||||||
|
if (sz <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char *hstbuf = malloc(sz + 1);
|
||||||
|
|
||||||
|
if (hstbuf == NULL)
|
||||||
|
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
if (fseek(log->file, 0L, SEEK_SET) == -1) {
|
||||||
|
free(hstbuf);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fread(hstbuf, sz, 1, log->file) != 1) {
|
||||||
|
free(hstbuf);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hstbuf[sz] = '\0';
|
||||||
|
|
||||||
|
/* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
|
||||||
|
int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
|
||||||
|
int start, count = 0;
|
||||||
|
|
||||||
|
/* start at end and backtrace L lines or to the beginning of buffer */
|
||||||
|
for (start = sz - 1; start >= 0 && count < L; --start) {
|
||||||
|
if (hstbuf[start] == '\n')
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *line = strtok(&hstbuf[start + 1], "\n");
|
||||||
|
|
||||||
|
if (line == NULL) {
|
||||||
|
free(hstbuf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (line != NULL && count--) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
|
||||||
|
line = strtok(NULL, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||||
|
free(hstbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* renames chatlog file replacing src with dest.
|
||||||
|
Returns 0 on success or if no log exists, -1 on failure. */
|
||||||
|
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum)
|
||||||
|
{
|
||||||
|
ToxWindow *toxwin = get_window_ptr(winnum);
|
||||||
|
struct chatlog *log = NULL;
|
||||||
|
bool log_on = false;
|
||||||
|
|
||||||
|
/* disable log if necessary and save its state */
|
||||||
|
if (toxwin != NULL) {
|
||||||
|
log = toxwin->chatwin->log;
|
||||||
|
log_on = log->log_on;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_on)
|
||||||
|
log_disable(log);
|
||||||
|
|
||||||
|
char newpath[MAX_STR_SIZE];
|
||||||
|
char oldpath[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
if (!file_exists(oldpath))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
if (rename(oldpath, newpath) != 0)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
if (log_on)
|
||||||
|
log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
on_error:
|
||||||
|
|
||||||
|
if (log_on)
|
||||||
|
log_enable(src, selfkey, otherkey, log, LOG_CHAT);
|
||||||
|
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
36
src/log.h
36
src/log.h
@ -20,28 +20,40 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _log_h
|
#ifndef LOG_H
|
||||||
#define _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 {
|
struct chatlog {
|
||||||
FILE *file;
|
FILE *file;
|
||||||
uint64_t lastwrite;
|
time_t lastwrite;
|
||||||
int pos;
|
char path[MAX_STR_SIZE];
|
||||||
bool log_on; /* specific to current chat window */
|
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 */
|
enum {
|
||||||
void init_logging_session(char *name, char *key, struct chatlog *log);
|
LOG_GROUP,
|
||||||
|
LOG_PROMPT,
|
||||||
|
LOG_CHAT,
|
||||||
|
} LOG_TYPE;
|
||||||
|
|
||||||
/* formats/writes line to log file */
|
/* formats/writes line to log file */
|
||||||
void write_to_log(const char *msg, char *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 */
|
/* enables logging for specified log and creates/fetches file if necessary.
|
||||||
void log_enable(char *name, char *key, struct chatlog *log);
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype);
|
||||||
|
|
||||||
/* disables logging for specified log and closes file */
|
/* disables logging for specified log and closes file */
|
||||||
void log_disable(struct chatlog *log);
|
void log_disable(struct chatlog *log);
|
||||||
|
|
||||||
#endif /* #define _log_h */
|
/* Loads previous history from chat log */
|
||||||
|
void load_chat_history(ToxWindow *self, struct chatlog *log);
|
||||||
|
|
||||||
|
/* renames chatlog file replacing src with dest.
|
||||||
|
Returns 0 on success or if no log exists, -1 on failure. */
|
||||||
|
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
|
||||||
|
|
||||||
|
#endif /* #define LOG_H */
|
||||||
|
154
src/message_queue.c
Normal file
154
src/message_queue.c
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/* message_queue.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "message_queue.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
void cqueue_cleanup(struct chat_queue *q)
|
||||||
|
{
|
||||||
|
struct cqueue_msg *tmp1 = q->root;
|
||||||
|
|
||||||
|
while (tmp1) {
|
||||||
|
struct cqueue_msg *tmp2 = tmp1->next;
|
||||||
|
free(tmp1);
|
||||||
|
tmp1 = tmp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id)
|
||||||
|
{
|
||||||
|
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
|
||||||
|
|
||||||
|
if (new_m == NULL)
|
||||||
|
exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
snprintf(new_m->message, sizeof(new_m->message), "%s", msg);
|
||||||
|
new_m->len = len;
|
||||||
|
new_m->type = type;
|
||||||
|
new_m->line_id = line_id;
|
||||||
|
new_m->last_send_try = 0;
|
||||||
|
new_m->receipt = 0;
|
||||||
|
new_m->next = NULL;
|
||||||
|
|
||||||
|
if (q->root == NULL) {
|
||||||
|
new_m->prev = NULL;
|
||||||
|
q->root = new_m;
|
||||||
|
} else {
|
||||||
|
new_m->prev = q->end;
|
||||||
|
q->end->next = new_m;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->end = new_m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update line to show receipt was received after queue removal */
|
||||||
|
static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg)
|
||||||
|
{
|
||||||
|
struct line_info *line = self->chatwin->hst->line_end;
|
||||||
|
|
||||||
|
while (line) {
|
||||||
|
if (line->id != msg->line_id) {
|
||||||
|
line = line->prev;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ;
|
||||||
|
|
||||||
|
if (line->noread_flag == true) {
|
||||||
|
line->len -= 2;
|
||||||
|
line->noread_flag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
|
||||||
|
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
|
||||||
|
{
|
||||||
|
struct chat_queue *q = self->chatwin->cqueue;
|
||||||
|
struct cqueue_msg *msg = q->root;
|
||||||
|
|
||||||
|
while (msg) {
|
||||||
|
if (msg->receipt != receipt) {
|
||||||
|
msg = msg->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char selfname[TOX_MAX_NAME_LENGTH];
|
||||||
|
tox_self_get_name(m, (uint8_t *) selfname);
|
||||||
|
|
||||||
|
size_t len = tox_self_get_name_size(m);
|
||||||
|
selfname[len] = '\0';
|
||||||
|
|
||||||
|
write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION);
|
||||||
|
cqueue_mark_read(self, msg);
|
||||||
|
|
||||||
|
struct cqueue_msg *next = msg->next;
|
||||||
|
|
||||||
|
if (msg->prev == NULL) { /* root */
|
||||||
|
if (next)
|
||||||
|
next->prev = NULL;
|
||||||
|
|
||||||
|
free(msg);
|
||||||
|
q->root = next;
|
||||||
|
} else {
|
||||||
|
struct cqueue_msg *prev = msg->prev;
|
||||||
|
free(msg);
|
||||||
|
prev->next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CQUEUE_TRY_SEND_INTERVAL 60
|
||||||
|
|
||||||
|
/* Tries to send the oldest unsent message in queue. */
|
||||||
|
void cqueue_try_send(ToxWindow *self, Tox *m)
|
||||||
|
{
|
||||||
|
struct chat_queue *q = self->chatwin->cqueue;
|
||||||
|
struct cqueue_msg *msg = q->root;
|
||||||
|
|
||||||
|
if (!msg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t receipt = 0;
|
||||||
|
|
||||||
|
TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
|
||||||
|
receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
|
||||||
|
|
||||||
|
msg->last_send_try = get_unix_time();
|
||||||
|
msg->receipt = receipt;
|
||||||
|
return;
|
||||||
|
}
|
51
src/message_queue.h
Normal file
51
src/message_queue.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/* message_queue.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MESSAGE_QUEUE_H
|
||||||
|
#define MESSAGE_QUEUE_H
|
||||||
|
|
||||||
|
struct cqueue_msg {
|
||||||
|
char message[MAX_STR_SIZE];
|
||||||
|
size_t len;
|
||||||
|
int line_id;
|
||||||
|
uint8_t type;
|
||||||
|
uint32_t receipt;
|
||||||
|
time_t last_send_try;
|
||||||
|
struct cqueue_msg *next;
|
||||||
|
struct cqueue_msg *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct chat_queue {
|
||||||
|
struct cqueue_msg *root;
|
||||||
|
struct cqueue_msg *end;
|
||||||
|
};
|
||||||
|
|
||||||
|
void cqueue_cleanup(struct chat_queue *q);
|
||||||
|
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id);
|
||||||
|
|
||||||
|
/* Tries to send the oldest unsent message in queue. */
|
||||||
|
void cqueue_try_send(ToxWindow *self, Tox *m);
|
||||||
|
|
||||||
|
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
|
||||||
|
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt);
|
||||||
|
|
||||||
|
#endif /* #define MESSAGE_QUEUE_H */
|
433
src/misc_tools.c
433
src/misc_tools.c
@ -25,39 +25,56 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#if defined(__FreeBSD__)
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#else
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "file_transfers.h"
|
||||||
|
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
extern struct user_settings *user_settings;
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
static uint64_t current_unix_time;
|
void hst_to_net(uint8_t *num, uint16_t numbytes)
|
||||||
|
|
||||||
void update_unix_time(void)
|
|
||||||
{
|
{
|
||||||
current_unix_time = (uint64_t) time(NULL);
|
#ifndef WORDS_BIGENDIAN
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t buff[numbytes];
|
||||||
|
|
||||||
|
for (i = 0; i < numbytes; ++i) {
|
||||||
|
buff[i] = num[numbytes - i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(num, buff, numbytes);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t get_unix_time(void)
|
time_t get_unix_time(void)
|
||||||
{
|
{
|
||||||
return current_unix_time;
|
return time(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||||
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
|
int timed_out(time_t timestamp, time_t timeout)
|
||||||
{
|
{
|
||||||
return timestamp + timeout <= curtime;
|
return timestamp + timeout <= get_unix_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the current local time */
|
/* Get the current local time */
|
||||||
struct tm *get_time(void)
|
struct tm *get_time(void)
|
||||||
{
|
{
|
||||||
struct tm *timeinfo;
|
struct tm *timeinfo;
|
||||||
uint64_t t = get_unix_time();
|
time_t t = get_unix_time();
|
||||||
timeinfo = localtime((const time_t*) &t);
|
timeinfo = localtime((const time_t *) &t);
|
||||||
return timeinfo;
|
return timeinfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,12 +86,12 @@ void get_time_str(char *buf, int bufsize)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *t = user_settings->time == TIME_12 ? "[%-I:%M:%S] " : "[%H:%M:%S] ";
|
const char *t = user_settings->timestamp_format;
|
||||||
strftime(buf, bufsize, t, get_time());
|
strftime(buf, bufsize, t, get_time());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
|
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
|
||||||
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
|
void get_elapsed_time_str(char *buf, int bufsize, time_t secs)
|
||||||
{
|
{
|
||||||
if (!secs)
|
if (!secs)
|
||||||
return;
|
return;
|
||||||
@ -91,28 +108,81 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
|
|||||||
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
|
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *hex_string_to_bin(const char *hex_string)
|
/*
|
||||||
|
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
|
||||||
|
* output_size must be exactly half of hex_len.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size)
|
||||||
{
|
{
|
||||||
size_t len = strlen(hex_string);
|
if (output_size == 0 || hex_len != output_size * 2)
|
||||||
char *val = malloc(len);
|
return -1;
|
||||||
|
|
||||||
if (val == NULL)
|
for (size_t i = 0; i < output_size; ++i) {
|
||||||
exit_toxic_err("failed in hex_string_to_bin", FATALERR_MEMORY);
|
sscanf(hex_string, "%2hhx", &output[i]);
|
||||||
|
hex_string += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Converts a binary representation of a Tox ID into a string.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size)
|
||||||
|
{
|
||||||
|
if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < len; ++i, hex_string += 2)
|
for (i = 0; i < TOX_ADDRESS_SIZE; ++i)
|
||||||
sscanf(hex_string, "%2hhx", &val[i]);
|
snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
|
||||||
|
|
||||||
return val;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 1 if the string is empty, 0 otherwise */
|
/* Returns 1 if the string is empty, 0 otherwise */
|
||||||
int string_is_empty(char *string)
|
int string_is_empty(const char *string)
|
||||||
{
|
{
|
||||||
|
if (!string)
|
||||||
|
return true;
|
||||||
|
|
||||||
return string[0] == '\0';
|
return string[0] == '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns 1 if the string is empty, 0 otherwise */
|
||||||
|
int wstring_is_empty(const wchar_t *string)
|
||||||
|
{
|
||||||
|
if (!string)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return string[0] == L'\0';
|
||||||
|
}
|
||||||
|
|
||||||
/* convert a multibyte string to a wide character string and puts in buf. */
|
/* 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)
|
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
|
||||||
{
|
{
|
||||||
@ -121,7 +191,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
|
|||||||
if (n < len)
|
if (n < len)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ((len = mbstowcs(buf, string, n)) == (size_t) -1)
|
if ((len = mbstowcs(buf, string, n)) == (size_t) - 1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
@ -135,47 +205,25 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
|
|||||||
if (n < len)
|
if (n < len)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ((len = wcstombs(buf, string, n)) == (size_t) -1)
|
if ((len = wcstombs(buf, string, n)) == (size_t) - 1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 && user_settings->alerts == ALERTS_ENABLED)
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* case-insensitive string compare function for use with qsort */
|
/* 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:
|
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||||
- cannot be empty
|
- cannot be empty
|
||||||
- cannot start with a space
|
- cannot start with a space
|
||||||
- must not contain a forward slash (for logfile naming purposes)
|
- must not contain a forward slash (for logfile naming purposes)
|
||||||
- must not contain contiguous spaces */
|
- must not contain contiguous spaces
|
||||||
int valid_nick(char *nick)
|
- must not contain a newline or tab seqeunce */
|
||||||
|
int valid_nick(const char *nick)
|
||||||
{
|
{
|
||||||
if (!nick[0] || nick[0] == ' ')
|
if (!nick[0] || nick[0] == ' ')
|
||||||
return 0;
|
return 0;
|
||||||
@ -183,37 +231,82 @@ int valid_nick(char *nick)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; nick[i]; ++i) {
|
for (i = 0; nick[i]; ++i) {
|
||||||
if (nick[i] == ' ' && nick[i + 1] == ' ')
|
if ((nick[i] == ' ' && nick[i + 1] == ' ')
|
||||||
return 0;
|
|| nick[i] == '/'
|
||||||
|
|| nick[i] == '\n'
|
||||||
|
|| nick[i] == '\t'
|
||||||
|
|| nick[i] == '\v'
|
||||||
|
|| nick[i] == '\r')
|
||||||
|
|
||||||
if (nick[i] == '/')
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* gets base file name from path or original file name if no path is supplied */
|
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
|
||||||
void get_file_name(char *namebuf, int bufsize, const char *pathname)
|
void filter_str(char *str, size_t len)
|
||||||
{
|
{
|
||||||
int idx = strlen(pathname) - 1;
|
size_t i;
|
||||||
|
|
||||||
char tmpname[MAX_STR_SIZE];
|
for (i = 0; i < len; ++i) {
|
||||||
snprintf(tmpname, sizeof(tmpname), "%s", pathname);
|
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0')
|
||||||
|
str[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (idx >= 0 && pathname[idx] == '/')
|
/* gets base file name from path or original file name if no path is supplied.
|
||||||
tmpname[idx--] = '\0';
|
* Returns the file name length
|
||||||
|
*/
|
||||||
|
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
|
||||||
|
{
|
||||||
|
int len = strlen(pathname) - 1;
|
||||||
|
char *path = strdup(pathname);
|
||||||
|
|
||||||
char *filename = strrchr(tmpname, '/');
|
if (path == NULL)
|
||||||
|
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
|
||||||
|
|
||||||
if (filename != NULL) {
|
while (len >= 0 && pathname[len] == '/')
|
||||||
if (!strlen(++filename))
|
path[len--] = '\0';
|
||||||
filename = tmpname;
|
|
||||||
} else {
|
char *finalname = strdup(path);
|
||||||
filename = tmpname;
|
|
||||||
|
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, bufsize, "%s", filename);
|
snprintf(namebuf, bufsize, "%s", finalname);
|
||||||
|
free(finalname);
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
return strlen(namebuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gets the base directory of path and puts it in dir.
|
||||||
|
* dir must have at least as much space as path_len + 1.
|
||||||
|
*
|
||||||
|
* Returns the length of the base directory.
|
||||||
|
*/
|
||||||
|
size_t get_base_dir(const char *path, size_t path_len, char *dir)
|
||||||
|
{
|
||||||
|
if (path_len == 0 || path == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t dir_len = char_rfind(path, '/', path_len);
|
||||||
|
|
||||||
|
if (dir_len != 0 && dir_len < path_len)
|
||||||
|
++dir_len; /* Leave trailing slash */
|
||||||
|
|
||||||
|
memcpy(dir, path, dir_len);
|
||||||
|
dir[dir_len] = '\0';
|
||||||
|
|
||||||
|
return dir_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* converts str to all lowercase */
|
/* converts str to all lowercase */
|
||||||
@ -226,11 +319,211 @@ void str_to_lower(char *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
|
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
|
||||||
Returns nick len on success, -1 on failure */
|
if toxcore API call fails, put UNKNOWN_NAME in buf
|
||||||
int get_nick_truncate(Tox *m, char *buf, int friendnum)
|
Returns nick len */
|
||||||
|
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
|
||||||
{
|
{
|
||||||
int len = tox_get_name(m, friendnum, (uint8_t *) buf);
|
TOX_ERR_FRIEND_QUERY err;
|
||||||
|
size_t len = tox_friend_get_name_size(m, friendnum, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_FRIEND_QUERY_OK) {
|
||||||
|
goto on_error;
|
||||||
|
} else {
|
||||||
|
if (!tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL)) {
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
|
filter_str(buf, len);
|
||||||
|
return len;
|
||||||
|
|
||||||
|
on_error:
|
||||||
|
strcpy(buf, UNKNOWN_NAME);
|
||||||
|
len = strlen(UNKNOWN_NAME);
|
||||||
|
buf[len] = '\0';
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* same as get_nick_truncate but for groupchats */
|
||||||
|
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum)
|
||||||
|
{
|
||||||
|
TOX_ERR_CONFERENCE_PEER_QUERY err;
|
||||||
|
size_t len = tox_conference_peer_get_name_size(m, groupnum, peernum, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
|
||||||
|
goto on_error;
|
||||||
|
} else {
|
||||||
|
if (!tox_conference_peer_get_name(m, groupnum, peernum, (uint8_t *) buf, NULL)) {
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||||
|
buf[len] = '\0';
|
||||||
|
filter_str(buf, len);
|
||||||
|
return len;
|
||||||
|
|
||||||
|
on_error:
|
||||||
|
strcpy(buf, UNKNOWN_NAME);
|
||||||
|
len = strlen(UNKNOWN_NAME);
|
||||||
|
buf[len] = '\0';
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copies data to msg buffer.
|
||||||
|
returns length of msg, which will be no larger than size-1 */
|
||||||
|
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length)
|
||||||
|
{
|
||||||
|
size_t len = MIN(length, size - 1);
|
||||||
|
memcpy(msg, data, len);
|
||||||
|
msg[len] = '\0';
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns index of the first instance of ch in s starting at idx.
|
||||||
|
returns length of s if char not found or 0 if s is NULL. */
|
||||||
|
int char_find(int idx, const char *s, char ch)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = idx;
|
||||||
|
|
||||||
|
for (i = idx; s[i]; ++i) {
|
||||||
|
if (s[i] == ch)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns index of the last instance of ch in s starting at len.
|
||||||
|
returns 0 if char not found or s is NULL (skips 0th index). */
|
||||||
|
int char_rfind(const char *s, char ch, int len)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (i = len; i > 0; --i) {
|
||||||
|
if (s[i] == ch)
|
||||||
|
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 *path)
|
||||||
|
{
|
||||||
|
struct stat s;
|
||||||
|
return stat(path, &s) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns file size. If file doesn't exist returns 0. */
|
||||||
|
off_t file_size(const char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (stat(path, &st) == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return st.st_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compares the first size bytes of fp to signature.
|
||||||
|
Returns 0 if they are the same, 1 if they differ, and -1 on error.
|
||||||
|
|
||||||
|
On success this function will seek back to the beginning of fp */
|
||||||
|
int check_file_signature(const char *signature, size_t size, FILE *fp)
|
||||||
|
{
|
||||||
|
char buf[size];
|
||||||
|
|
||||||
|
if (fread(buf, size, 1, fp) != 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int ret = memcmp(signature, buf, size);
|
||||||
|
|
||||||
|
if (fseek(fp, 0L, SEEK_SET) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return ret == 0 ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sets window title in tab bar. */
|
||||||
|
void set_window_title(ToxWindow *self, const char *title, int len)
|
||||||
|
{
|
||||||
|
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
|
||||||
|
|
||||||
|
if (self->is_groupchat) /* keep groupnumber in title */
|
||||||
|
snprintf(cpy, sizeof(cpy), "%d %s", self->num, title);
|
||||||
|
else
|
||||||
|
snprintf(cpy, sizeof(cpy), "%s", title);
|
||||||
|
|
||||||
|
if (len > MAX_WINDOW_NAME_LENGTH) {
|
||||||
|
strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "...");
|
||||||
|
cpy[MAX_WINDOW_NAME_LENGTH] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(self->name, sizeof(self->name), "%s", cpy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if address appears to be a valid ipv4 address. */
|
||||||
|
bool is_ip4_address(const char *address)
|
||||||
|
{
|
||||||
|
struct sockaddr_in s_addr;
|
||||||
|
return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if address roughly appears to be a valid ipv6 address.
|
||||||
|
*
|
||||||
|
* TODO: Improve this function (inet_pton behaves strangely with ipv6).
|
||||||
|
* for now the only guarantee is that it won't return true if the
|
||||||
|
* address is a domain or ipv4 address, and should only be used if you're
|
||||||
|
* reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
|
||||||
|
*/
|
||||||
|
bool is_ip6_address(const char *address)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
size_t num_colons = 0;
|
||||||
|
char ch = 0;
|
||||||
|
|
||||||
|
for (i = 0; (ch = address[i]); ++i) {
|
||||||
|
if (ch == '.') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == ':') {
|
||||||
|
++num_colons;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_colons > 1 && num_colons < 8;
|
||||||
|
}
|
||||||
|
121
src/misc_tools.h
121
src/misc_tools.h
@ -19,8 +19,10 @@
|
|||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _misc_tools_h
|
#ifndef MISC_TOOLS_H
|
||||||
#define _misc_tools_h
|
#define MISC_TOOLS_H
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
@ -33,26 +35,48 @@
|
|||||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* convert a hex string to binary */
|
#ifndef net_to_host
|
||||||
char *hex_string_to_bin(const char *hex_string);
|
#define net_to_host(x, y) hst_to_net(x, y)
|
||||||
|
#endif
|
||||||
|
|
||||||
/* get the current unix time */
|
void hst_to_net(uint8_t *num, uint16_t numbytes);
|
||||||
uint64_t get_unix_time(void);
|
|
||||||
|
|
||||||
/*Puts the current time in buf in the format of [HH:mm:ss] */
|
/*
|
||||||
|
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
|
||||||
|
* output_size must be exactly half of hex_len.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size);
|
||||||
|
|
||||||
|
/* convert a hex string to bytes. returns 0 on success, -1 on failure */
|
||||||
|
int hex_string_to_bytes(char *buf, int size, const char *keystr);
|
||||||
|
|
||||||
|
/* Converts a binary representation of a Tox ID into a string.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size);
|
||||||
|
|
||||||
|
/* get the current unix time (not thread safe) */
|
||||||
|
time_t get_unix_time(void);
|
||||||
|
|
||||||
|
/* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */
|
||||||
void get_time_str(char *buf, int bufsize);
|
void get_time_str(char *buf, int bufsize);
|
||||||
|
|
||||||
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
|
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
|
||||||
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs);
|
void get_elapsed_time_str(char *buf, int bufsize, time_t secs);
|
||||||
|
|
||||||
/* get the current local time */
|
/* get the current local time (not thread safe) */
|
||||||
struct tm *get_time(void);
|
struct tm *get_time(void);
|
||||||
|
|
||||||
/* updates current unix time (should be run once per do_toxic loop) */
|
|
||||||
void update_unix_time(void);
|
|
||||||
|
|
||||||
/* Returns 1 if the string is empty, 0 otherwise */
|
/* Returns 1 if the string is empty, 0 otherwise */
|
||||||
int string_is_empty(char *string);
|
int string_is_empty(const char *string);
|
||||||
|
|
||||||
|
/* Same as above but for wide character strings */
|
||||||
|
int wstring_is_empty(const wchar_t *string);
|
||||||
|
|
||||||
/* convert a multibyte string to a wide character string (must provide buffer) */
|
/* convert a multibyte string to a wide character string (must provide buffer) */
|
||||||
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
||||||
@ -64,29 +88,86 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
|
|||||||
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
||||||
|
|
||||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||||
int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
int timed_out(time_t timestamp, time_t timeout);
|
||||||
|
|
||||||
/* Colours the window tab according to type. Beeps if is_beep is true */
|
/* Colours the window tab according to type. Beeps if is_beep is true */
|
||||||
void alert_window(ToxWindow *self, int type, bool is_beep);
|
void alert_window(ToxWindow *self, int type, bool is_beep);
|
||||||
|
|
||||||
/* case-insensitive string compare function for use with qsort */
|
/* 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 1 if nick is valid, 0 if not. A valid toxic nick:
|
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||||
- cannot be empty
|
- cannot be empty
|
||||||
- cannot start with a space
|
- cannot start with a space
|
||||||
- must not contain a forward slash (for logfile naming purposes)
|
- must not contain a forward slash (for logfile naming purposes)
|
||||||
- must not contain contiguous spaces */
|
- must not contain contiguous spaces
|
||||||
int valid_nick(char *nick);
|
- must not contain a newline or tab seqeunce */
|
||||||
|
int valid_nick(const char *nick);
|
||||||
|
|
||||||
|
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
|
||||||
|
void filter_str(char *str, size_t len);
|
||||||
|
|
||||||
/* gets base file name from path or original file name if no path is supplied */
|
/* gets base file name from path or original file name if no path is supplied */
|
||||||
void get_file_name(char *namebuf, int bufsize, const char *pathname);
|
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname);
|
||||||
|
|
||||||
|
/* Gets the base directory of path and puts it in dir.
|
||||||
|
* dir must have at least as much space as path_len.
|
||||||
|
*
|
||||||
|
* Returns the length of the base directory on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
size_t get_base_dir(const char *path, size_t path_len, char *dir);
|
||||||
|
|
||||||
/* converts str to all lowercase */
|
/* converts str to all lowercase */
|
||||||
void str_to_lower(char *str);
|
void str_to_lower(char *str);
|
||||||
|
|
||||||
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
|
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
|
||||||
Returns nick len on success, -1 on failure */
|
Returns nick len on success, -1 on failure */
|
||||||
int get_nick_truncate(Tox *m, char *buf, int friendnum);
|
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
|
||||||
|
|
||||||
#endif /* #define _misc_tools_h */
|
/* same as get_nick_truncate but for groupchats */
|
||||||
|
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum);
|
||||||
|
|
||||||
|
/* copies data to msg buffer.
|
||||||
|
returns length of msg, which will be no larger than size-1 */
|
||||||
|
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length);
|
||||||
|
|
||||||
|
/* returns index of the first instance of ch in s starting at idx.
|
||||||
|
returns length of s if char not found or 0 if s is NULL. */
|
||||||
|
int char_find(int idx, const char *s, char ch);
|
||||||
|
|
||||||
|
/* returns index of the last instance of ch in s starting at len.
|
||||||
|
returns 0 if char not found or s is NULL (skips 0th index). */
|
||||||
|
int char_rfind(const char *s, char ch, int len);
|
||||||
|
|
||||||
|
/* Converts bytes to appropriate unit and puts in buf as a string */
|
||||||
|
void bytes_convert_str(char *buf, int size, uint64_t bytes);
|
||||||
|
|
||||||
|
/* checks if a file exists. Returns true or false */
|
||||||
|
bool file_exists(const char *path);
|
||||||
|
|
||||||
|
/* returns file size. If file doesn't exist returns 0. */
|
||||||
|
off_t file_size(const char *path);
|
||||||
|
|
||||||
|
/* compares the first size bytes of fp and signature.
|
||||||
|
Returns 0 if they are the same, 1 if they differ, and -1 on error.
|
||||||
|
|
||||||
|
On success this function will seek back to the beginning of fp */
|
||||||
|
int check_file_signature(const char *signature, size_t size, FILE *fp);
|
||||||
|
|
||||||
|
/* sets window title in tab bar. */
|
||||||
|
void set_window_title(ToxWindow *self, const char *title, int len);
|
||||||
|
|
||||||
|
/* Return true if address appears to be a valid ipv4 address. */
|
||||||
|
bool is_ip4_address(const char *address);
|
||||||
|
|
||||||
|
/* Return true if address roughly appears to be a valid ipv6 address.
|
||||||
|
*
|
||||||
|
* TODO: Improve this function (inet_pton behaves strangely with ipv6).
|
||||||
|
* for now the only guarantee is that it won't return true if the
|
||||||
|
* address is a domain or ipv4 address, and should only be used if you're
|
||||||
|
* reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
|
||||||
|
*/
|
||||||
|
bool is_ip6_address(const char *address);
|
||||||
|
|
||||||
|
#endif /* #define MISC_TOOLS_H */
|
||||||
|
401
src/name_lookup.c
Normal file
401
src/name_lookup.c
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
/* name_lookup.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "global_commands.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "configdir.h"
|
||||||
|
#include "curl_util.h"
|
||||||
|
|
||||||
|
extern struct arg_opts arg_opts;
|
||||||
|
extern struct Winthread Winthread;
|
||||||
|
|
||||||
|
#define NAMESERVER_API_PATH "api"
|
||||||
|
#define SERVER_KEY_SIZE 32
|
||||||
|
#define MAX_SERVERS 50
|
||||||
|
#define MAX_DOMAIN_SIZE 32
|
||||||
|
#define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3
|
||||||
|
|
||||||
|
struct Nameservers {
|
||||||
|
int lines;
|
||||||
|
char names[MAX_SERVERS][MAX_DOMAIN_SIZE];
|
||||||
|
char keys[MAX_SERVERS][SERVER_KEY_SIZE];
|
||||||
|
} Nameservers;
|
||||||
|
|
||||||
|
static struct thread_data {
|
||||||
|
Tox *m;
|
||||||
|
ToxWindow *self;
|
||||||
|
char id_bin[TOX_ADDRESS_SIZE];
|
||||||
|
char addr[MAX_STR_SIZE];
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
bool disabled;
|
||||||
|
volatile bool busy;
|
||||||
|
} t_data;
|
||||||
|
|
||||||
|
static struct lookup_thread {
|
||||||
|
pthread_t tid;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
} lookup_thread;
|
||||||
|
|
||||||
|
static int lookup_error(ToxWindow *self, const char *errmsg, ...)
|
||||||
|
{
|
||||||
|
char frmt_msg[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, errmsg);
|
||||||
|
vsnprintf(frmt_msg, sizeof(frmt_msg), errmsg, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kill_lookup_thread(void)
|
||||||
|
{
|
||||||
|
memset(&t_data, 0, sizeof(struct thread_data));
|
||||||
|
pthread_attr_destroy(&lookup_thread.attr);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempts to load the nameserver list pointed at by path into the Nameservers structure.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* -1 is reserved.
|
||||||
|
* Returns -2 if the supplied path does not exist.
|
||||||
|
* Returns -3 if the list does not contain any valid entries.
|
||||||
|
*/
|
||||||
|
static int load_nameserver_list(const char *path)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(path, "r");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
char line[MAX_SERVER_LINE];
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) {
|
||||||
|
int linelen = strlen(line);
|
||||||
|
|
||||||
|
if (linelen < SERVER_KEY_SIZE * 2 + 5)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (line[linelen - 1] == '\n')
|
||||||
|
line[--linelen] = '\0';
|
||||||
|
|
||||||
|
const char *name = strtok(line, " ");
|
||||||
|
const char *keystr = strtok(NULL, " ");
|
||||||
|
|
||||||
|
if (name == NULL || keystr == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strlen(keystr) != SERVER_KEY_SIZE * 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
snprintf(Nameservers.names[Nameservers.lines], sizeof(Nameservers.names[Nameservers.lines]), "%s", name);
|
||||||
|
int res = hex_string_to_bytes(Nameservers.keys[Nameservers.lines], SERVER_KEY_SIZE, keystr);
|
||||||
|
|
||||||
|
if (res == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
++Nameservers.lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (Nameservers.lines < 1)
|
||||||
|
return -3;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Takes address addr in the form "username@domain", puts the username in namebuf,
|
||||||
|
* and the domain in dombuf.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure
|
||||||
|
*/
|
||||||
|
static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz)
|
||||||
|
{
|
||||||
|
if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char tmpaddr[MAX_STR_SIZE];
|
||||||
|
char *tmpname = NULL;
|
||||||
|
char *tmpdom = NULL;
|
||||||
|
|
||||||
|
snprintf(tmpaddr, sizeof(tmpaddr), "%s", addr);
|
||||||
|
tmpname = strtok(tmpaddr, "@");
|
||||||
|
tmpdom = strtok(NULL, "");
|
||||||
|
|
||||||
|
if (tmpname == NULL || tmpdom == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
str_to_lower(tmpdom);
|
||||||
|
snprintf(namebuf, namebuf_sz, "%s", tmpname);
|
||||||
|
snprintf(dombuf, dombuf_sz, "%s", tmpdom);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* matches input domain name with domains in list and obtains key.
|
||||||
|
* Turns out_domain into the full domain we need to make a POST request.
|
||||||
|
*
|
||||||
|
* Return true on match.
|
||||||
|
* Returns false on no match.
|
||||||
|
*/
|
||||||
|
static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_size, const char *inputdomain)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < Nameservers.lines; ++i) {
|
||||||
|
if (strcmp(Nameservers.names[i], inputdomain) == 0) {
|
||||||
|
memcpy(pubkey, Nameservers.keys[i], SERVER_KEY_SIZE);
|
||||||
|
snprintf(out_domain, out_domain_size, "https://%s/%s", Nameservers.names[i], NAMESERVER_API_PATH);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Converts Tox ID string contained in recv_data to binary format and puts it in thread's ID buffer.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
#define ID_PREFIX "\"tox_id\": \""
|
||||||
|
static int process_response(struct Recv_Curl_Data *recv_data)
|
||||||
|
{
|
||||||
|
size_t prefix_size = strlen(ID_PREFIX);
|
||||||
|
|
||||||
|
if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
const char *IDstart = strstr(recv_data->data, ID_PREFIX);
|
||||||
|
|
||||||
|
if (IDstart == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char ID_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||||
|
memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2);
|
||||||
|
ID_string[TOX_ADDRESS_SIZE * 2] = 0;
|
||||||
|
|
||||||
|
if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *lookup_thread_func(void *data)
|
||||||
|
{
|
||||||
|
ToxWindow *self = t_data.self;
|
||||||
|
|
||||||
|
char input_domain[MAX_STR_SIZE];
|
||||||
|
char name[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
if (parse_addr(t_data.addr, name, sizeof(name), input_domain, sizeof(input_domain)) == -1) {
|
||||||
|
lookup_error(self, "Input must be a 76 character Tox ID or an address in the form: username@domain");
|
||||||
|
kill_lookup_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
char nameserver_key[SERVER_KEY_SIZE];
|
||||||
|
char real_domain[MAX_DOMAIN_SIZE];
|
||||||
|
|
||||||
|
if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) {
|
||||||
|
if (!strcasecmp(input_domain, "utox.org"))
|
||||||
|
lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic.");
|
||||||
|
else
|
||||||
|
lookup_error(self, "Name server domain not found.");
|
||||||
|
|
||||||
|
kill_lookup_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
CURL *c_handle = curl_easy_init();
|
||||||
|
|
||||||
|
if (!c_handle) {
|
||||||
|
lookup_error(self, "curl handler error");
|
||||||
|
kill_lookup_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Recv_Curl_Data recv_data;
|
||||||
|
|
||||||
|
memset(&recv_data, 0, sizeof(struct Recv_Curl_Data));
|
||||||
|
|
||||||
|
char post_data[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name);
|
||||||
|
|
||||||
|
struct curl_slist *headers = NULL;
|
||||||
|
|
||||||
|
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||||
|
|
||||||
|
headers = curl_slist_append(headers, "charsets: utf-8");
|
||||||
|
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers);
|
||||||
|
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_URL, real_domain);
|
||||||
|
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data);
|
||||||
|
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, &recv_data);
|
||||||
|
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||||||
|
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data);
|
||||||
|
|
||||||
|
int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);
|
||||||
|
|
||||||
|
if (proxy_ret != 0) {
|
||||||
|
lookup_error(self, "Failed to set proxy (error %d)\n");
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "TLS could not be enabled (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "TLSv1.2 could not be set (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "Failed to set TLS cipher list (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_perform(c_handle);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
/* If system doesn't support any of the specified ciphers suites, fall back to default */
|
||||||
|
if (ret == CURLE_SSL_CIPHER) {
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL);
|
||||||
|
ret = curl_easy_perform(c_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "HTTPS lookup error (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process_response(&recv_data) == -1) {
|
||||||
|
lookup_error(self, "Bad response.");
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
on_exit:
|
||||||
|
curl_slist_free_all(headers);
|
||||||
|
curl_easy_cleanup(c_handle);
|
||||||
|
kill_lookup_thread();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message)
|
||||||
|
{
|
||||||
|
if (t_data.disabled) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t_data.busy) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin);
|
||||||
|
snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr);
|
||||||
|
snprintf(t_data.msg, sizeof(t_data.msg), "%s", message);
|
||||||
|
t_data.self = self;
|
||||||
|
t_data.m = m;
|
||||||
|
t_data.busy = true;
|
||||||
|
|
||||||
|
if (pthread_attr_init(&lookup_thread.attr) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init");
|
||||||
|
memset(&t_data, 0, sizeof(struct thread_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set");
|
||||||
|
pthread_attr_destroy(&lookup_thread.attr);
|
||||||
|
memset(&t_data, 0, sizeof(struct thread_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init");
|
||||||
|
pthread_attr_destroy(&lookup_thread.attr);
|
||||||
|
memset(&t_data, 0, sizeof(struct thread_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initializes http based name lookups. Note: This function must be called only once before additional
|
||||||
|
* threads are spawned.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 if curl failed to init.
|
||||||
|
* Returns -2 if the nameserver list cannot be found.
|
||||||
|
* Returns -3 if the nameserver list does not contain any valid entries.
|
||||||
|
*/
|
||||||
|
int name_lookup_init(int curl_init_status)
|
||||||
|
{
|
||||||
|
if (curl_init_status != 0) {
|
||||||
|
t_data.disabled = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *path = arg_opts.nameserver_path[0] ? arg_opts.nameserver_path : PACKAGE_DATADIR "/nameservers";
|
||||||
|
int ret = load_nameserver_list(path);
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
t_data.disabled = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
36
src/name_lookup.h
Normal file
36
src/name_lookup.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* name_lookup.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NAME_LOOKUP
|
||||||
|
#define NAME_LOOKUP
|
||||||
|
|
||||||
|
/* Initializes http based name lookups. Note: This function must be called only once before additional
|
||||||
|
* threads are spawned.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int name_lookup_init(int curl_init_status);
|
||||||
|
|
||||||
|
int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message);
|
||||||
|
|
||||||
|
#endif /* NAME_LOOKUP */
|
846
src/notify.c
Normal file
846
src/notify.c
Normal file
@ -0,0 +1,846 @@
|
|||||||
|
/* 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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "notify.h"
|
||||||
|
#include "audio_device.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "xtra.h"
|
||||||
|
|
||||||
|
#if defined(AUDIO) || defined(SOUND_NOTIFY)
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <OpenAL/al.h>
|
||||||
|
#include <OpenAL/alc.h>
|
||||||
|
#else
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
/* compatibility with older versions of OpenAL */
|
||||||
|
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||||
|
#include <AL/alext.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifdef SOUND_NOTIFY
|
||||||
|
#include <AL/alut.h> /* freealut packet */
|
||||||
|
#endif
|
||||||
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
#ifdef BOX_NOTIFY
|
||||||
|
#include <libnotify/notify.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_BOX_MSG_LEN 127
|
||||||
|
#define SOUNDS_SIZE 10
|
||||||
|
#define ACTIVE_NOTIFS_MAX 50
|
||||||
|
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
|
struct Control {
|
||||||
|
time_t cooldown;
|
||||||
|
time_t notif_timeout;
|
||||||
|
|
||||||
|
#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[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
|
||||||
|
char title[64];
|
||||||
|
size_t size;
|
||||||
|
time_t n_timeout;
|
||||||
|
#endif
|
||||||
|
} actives[ACTIVE_NOTIFS_MAX];
|
||||||
|
/**********************************************************************************/
|
||||||
|
/**********************************************************************************/
|
||||||
|
/**********************************************************************************/
|
||||||
|
/**********************************************************************************/
|
||||||
|
/**********************************************************************************/
|
||||||
|
|
||||||
|
/* coloured tab notifications: primary notification type */
|
||||||
|
static void tab_notify(ToxWindow *self, uint64_t flags)
|
||||||
|
{
|
||||||
|
if (self == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool notifications_are_disabled(uint64_t flags)
|
||||||
|
{
|
||||||
|
if (user_settings->alerts != ALERTS_ENABLED)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time());
|
||||||
|
#ifdef X11
|
||||||
|
return res || ((flags & NT_NOFOCUS) && is_focused());
|
||||||
|
#else
|
||||||
|
return res;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO maybe find better way to do this */
|
||||||
|
/* cooldown is in seconds */
|
||||||
|
#define DEVICE_COOLDOWN 5 /* TODO perhaps load this from config? */
|
||||||
|
static bool device_opened = false;
|
||||||
|
time_t last_opened_update = 0;
|
||||||
|
|
||||||
|
/* Opens primary device. Returns true on succe*/
|
||||||
|
void m_open_device()
|
||||||
|
{
|
||||||
|
last_opened_update = get_unix_time();
|
||||||
|
|
||||||
|
if (device_opened) return;
|
||||||
|
|
||||||
|
/* Blah error check */
|
||||||
|
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
|
||||||
|
|
||||||
|
device_opened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_close_device()
|
||||||
|
{
|
||||||
|
if (!device_opened) return;
|
||||||
|
|
||||||
|
close_device(output, Control.device_idx);
|
||||||
|
|
||||||
|
device_opened = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Terminate all sounds but wait for them to finish first */
|
||||||
|
void graceful_clear()
|
||||||
|
{
|
||||||
|
control_lock();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
m_close_device(); /* In case it's opened */
|
||||||
|
control_unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
control_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void *do_playing(void *_p)
|
||||||
|
{
|
||||||
|
(void)_p;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
control_lock();
|
||||||
|
|
||||||
|
if (!Control.poll_active) {
|
||||||
|
control_unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_looping = false;
|
||||||
|
bool test_active_notify = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||||
|
|
||||||
|
if (actives[i].looping) has_looping = true;
|
||||||
|
|
||||||
|
test_active_notify = actives[i].active && !actives[i].looping;
|
||||||
|
#ifdef BOX_NOTIFY
|
||||||
|
test_active_notify = test_active_notify && !actives[i].box;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (test_active_notify) {
|
||||||
|
if (actives[i].id_indicator)
|
||||||
|
*actives[i].id_indicator = -1; /* reset indicator value */
|
||||||
|
|
||||||
|
if (!is_playing(actives[i].source)) {
|
||||||
|
/* Close */
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
|
||||||
|
if (device_opened && !has_looping &&
|
||||||
|
(time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
|
||||||
|
m_close_device();
|
||||||
|
}
|
||||||
|
|
||||||
|
has_looping = false;
|
||||||
|
|
||||||
|
control_unlock();
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int play_source(uint32_t source, uint32_t buffer, bool looping)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
|
||||||
|
|
||||||
|
if ( i == ACTIVE_NOTIFS_MAX ) {
|
||||||
|
return -1; /* Full */
|
||||||
|
}
|
||||||
|
|
||||||
|
alSourcePlay(source);
|
||||||
|
|
||||||
|
actives[i].active = 1;
|
||||||
|
actives[i].source = source;
|
||||||
|
actives[i].buffer = buffer;
|
||||||
|
actives[i].looping = looping;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif BOX_NOTIFY
|
||||||
|
void *do_playing(void *_p)
|
||||||
|
{
|
||||||
|
(void)_p;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
control_lock();
|
||||||
|
|
||||||
|
if (!Control.poll_active) {
|
||||||
|
control_unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||||
|
if (actives[i].box && time(NULL) >= actives[i].n_timeout) {
|
||||||
|
GError *ignore;
|
||||||
|
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);
|
||||||
|
#endif /* SOUND_NOTIFY */
|
||||||
|
|
||||||
|
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
|
||||||
|
|
||||||
|
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
Control.poll_active = 1;
|
||||||
|
pthread_t thread;
|
||||||
|
|
||||||
|
if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) {
|
||||||
|
pthread_mutex_destroy(Control.poll_mutex);
|
||||||
|
Control.poll_active = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
Control.cooldown = time(NULL) + login_cooldown;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef BOX_NOTIFY
|
||||||
|
notify_init("Toxic");
|
||||||
|
#endif
|
||||||
|
Control.notif_timeout = notification_timeout;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminate_notify()
|
||||||
|
{
|
||||||
|
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
|
||||||
|
control_lock();
|
||||||
|
|
||||||
|
if ( !Control.poll_active ) {
|
||||||
|
control_unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Control.poll_active = 0;
|
||||||
|
control_unlock();
|
||||||
|
|
||||||
|
graceful_clear();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SOUND_NOTIFY
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
m_open_device();
|
||||||
|
|
||||||
|
alGenSources(1, &source);
|
||||||
|
alGenBuffers(1, &buffer);
|
||||||
|
buffer = alutCreateBufferFromFile(Control.sounds[what]);
|
||||||
|
alSourcei(source, AL_BUFFER, buffer);
|
||||||
|
alSourcei(source, AL_LOOPING, loop);
|
||||||
|
|
||||||
|
int rc = play_source(source, buffer, loop);
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
alSourceStop(source);
|
||||||
|
alDeleteSources(1, &source);
|
||||||
|
alDeleteBuffers(1, &buffer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int play_notify_sound(Notification notif, uint64_t flags)
|
||||||
|
{
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
if (flags & NT_BEEP) beep();
|
||||||
|
|
||||||
|
if (notif != silent) {
|
||||||
|
if ( !Control.poll_active || !Control.sounds[notif] )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
if (actives[id].id_indicator)
|
||||||
|
*actives[id].id_indicator = -1;
|
||||||
|
|
||||||
|
// alSourcei(actives[id].source, AL_LOOPING, false);
|
||||||
|
alSourceStop(actives[id].source);
|
||||||
|
alDeleteSources(1, &actives[id].source);
|
||||||
|
alDeleteBuffers(1, &actives[id].buffer);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
tab_notify(self, flags);
|
||||||
|
|
||||||
|
if (notifications_are_disabled(flags))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int id = -1;
|
||||||
|
control_lock();
|
||||||
|
|
||||||
|
if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY))
|
||||||
|
id = m_play_sound(notif, flags);
|
||||||
|
else if (flags & NT_ALWAYS)
|
||||||
|
id = m_play_sound(notif, flags);
|
||||||
|
|
||||||
|
#if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY)
|
||||||
|
|
||||||
|
if (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)
|
||||||
|
{
|
||||||
|
tab_notify(self, flags);
|
||||||
|
|
||||||
|
if (notifications_are_disabled(flags))
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_open_device();
|
||||||
|
|
||||||
|
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, const char *title,
|
||||||
|
const char *format, ...)
|
||||||
|
{
|
||||||
|
if (notifications_are_disabled(flags)) {
|
||||||
|
tab_notify(self, flags);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
if (id == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
#endif /* SOUND_NOTIFY */
|
||||||
|
|
||||||
|
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
|
||||||
|
|
||||||
|
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
|
||||||
|
|
||||||
|
va_list __ARGS__;
|
||||||
|
va_start (__ARGS__, format);
|
||||||
|
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||||
|
va_end (__ARGS__);
|
||||||
|
|
||||||
|
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
|
||||||
|
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
|
||||||
|
|
||||||
|
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
|
||||||
|
actives[id].size++;
|
||||||
|
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
|
||||||
|
|
||||||
|
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
|
||||||
|
notify_notification_set_app_name(actives[id].box, "toxic");
|
||||||
|
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
|
||||||
|
notify_notification_show(actives[id].box, NULL);
|
||||||
|
|
||||||
|
control_unlock();
|
||||||
|
return id;
|
||||||
|
#else
|
||||||
|
return sound_notify(self, notif, flags, id_indicator);
|
||||||
|
#endif /* BOX_NOTIFY */
|
||||||
|
}
|
||||||
|
|
||||||
|
int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...)
|
||||||
|
{
|
||||||
|
if (notifications_are_disabled(flags)) {
|
||||||
|
tab_notify(self, flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BOX_NOTIFY
|
||||||
|
|
||||||
|
if (sound_notify2(self, notif, flags, id) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
control_lock();
|
||||||
|
|
||||||
|
if (!actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1) {
|
||||||
|
control_unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list __ARGS__;
|
||||||
|
va_start (__ARGS__, format);
|
||||||
|
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||||
|
va_end (__ARGS__);
|
||||||
|
|
||||||
|
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
|
||||||
|
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
|
||||||
|
|
||||||
|
actives[id].size++;
|
||||||
|
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
|
||||||
|
|
||||||
|
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, ...)
|
||||||
|
{
|
||||||
|
tab_notify(self, flags);
|
||||||
|
|
||||||
|
if (notifications_are_disabled(flags))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
#ifdef BOX_NOTIFY
|
||||||
|
|
||||||
|
control_lock();
|
||||||
|
|
||||||
|
int id;
|
||||||
|
|
||||||
|
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
|
||||||
|
|
||||||
|
if ( id == ACTIVE_NOTIFS_MAX ) {
|
||||||
|
control_unlock();
|
||||||
|
return -1; /* Full */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id_indicator) {
|
||||||
|
actives[id].id_indicator = id_indicator;
|
||||||
|
*id_indicator = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
|
||||||
|
|
||||||
|
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
|
||||||
|
|
||||||
|
va_list __ARGS__;
|
||||||
|
va_start (__ARGS__, format);
|
||||||
|
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||||
|
va_end (__ARGS__);
|
||||||
|
|
||||||
|
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
|
||||||
|
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
|
||||||
|
|
||||||
|
actives[id].active = 1;
|
||||||
|
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
|
||||||
|
actives[id].size ++;
|
||||||
|
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
|
||||||
|
|
||||||
|
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
|
||||||
|
notify_notification_set_app_name(actives[id].box, "toxic");
|
||||||
|
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
|
||||||
|
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, ...)
|
||||||
|
{
|
||||||
|
tab_notify(self, flags);
|
||||||
|
|
||||||
|
if (notifications_are_disabled(flags))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
#ifdef BOX_NOTIFY
|
||||||
|
control_lock();
|
||||||
|
|
||||||
|
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1 ) {
|
||||||
|
control_unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
va_list __ARGS__;
|
||||||
|
va_start (__ARGS__, format);
|
||||||
|
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||||
|
va_end (__ARGS__);
|
||||||
|
|
||||||
|
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
|
||||||
|
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
|
||||||
|
|
||||||
|
actives[id].size ++;
|
||||||
|
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user